Esempio n. 1
0
def app_verify_session( app_session_token, data_pubkey_hex, config_path=CONFIG_PATH ):
    """
    Verify and decode a JWT app session token.
    The session is valid if the signature matches and the token is not expired.
    Return the decoded session token payload on success
    Return None on error
    """
    pubkey = str(data_pubkey_hex)
    verifier = jsontokens.TokenVerifier()
    valid = verifier.verify( app_session_token, pubkey )
    if not valid:
        log.debug("Failed to verify with {}".format(pubkey))
        return None

    session_jwt = jsontokens.decode_token(app_session_token)
    session = session_jwt['payload']

    # must match session structure 
    try:
        jsonschema.validate(session, APP_SESSION_SCHEMA)
    except ValidationError as ve:
        if BLOCKSTACK_TEST:
            log.exception(ve)

        return None

    # session must not be expired
    if session['expires'] < time.time():
        log.debug("Token is expired")
        return None

    return session
def wrap_token(token):
    token_record = {
        "token": token,
        "decodedToken": decode_token(token),
        "encrypted": False
    }
    return token_record
Esempio n. 3
0
def wrap_token(token):
    token_record = {
        "token": token,
        "decodedToken": decode_token(token),
        "encrypted": False
    }
    return token_record
Esempio n. 4
0
def app_make_session(blockchain_id,
                     app_public_key,
                     app_domain,
                     methods,
                     app_public_keys,
                     requester_device_id,
                     master_data_privkey,
                     session_lifetime=None,
                     config_path=CONFIG_PATH):
    """
    Make a session JWT for this application.
    Verify with user private key
    Sign with master private key

    Return {'session': session jwt, 'session_token': session token} on success
    Return {'error': ...} on error
    """
    conf = get_config(path=config_path)
    assert conf

    if session_lifetime is None:
        session_lifetime = conf.get('default_session_lifetime', 1e80)

    # ysi-storage.js assumes it needs to use an
    #  uncompressed address. let's do that if we need to

    app_datastore_public_key = keylib.key_formatting.decompress(app_public_key)

    app_user_id = data.datastore_get_id(app_datastore_public_key)

    api_endpoint_host = conf.get('api_endpoint_host', DEFAULT_API_HOST)
    api_endpoint_port = conf.get('api_endpoint_port', DEFAULT_API_PORT)

    api_endpoint = '{}:{}'.format(api_endpoint_host, api_endpoint_port)

    ses = {
        'version': 1,
        'blockchain_id': blockchain_id,
        'app_domain': app_domain,
        'methods': methods,
        'app_public_keys': app_public_keys,
        'app_user_id': app_user_id,
        'api_endpoint': api_endpoint,
        'device_id': requester_device_id,
        'storage': {
            'classes': classify_storage_drivers(),
            'preferences': {}
        },
        'timestamp': int(time.time()),
        'expires': int(time.time() + session_lifetime),
    }

    jsonschema.validate(ses, APP_SESSION_SCHEMA)

    signer = jsontokens.TokenSigner()
    session_token = signer.sign(ses, master_data_privkey)
    session = jsontokens.decode_token(session_token)

    return {'session': session, 'session_token': session_token}
def verify_token(token, public_key_or_address, signing_algorithm="ES256K"):
    """ A function for validating an individual token.
    """
    decoded_token = decode_token(token)
    decoded_token_payload = decoded_token["payload"]

    if "subject" not in decoded_token_payload:
        raise ValueError("Token doesn't have a subject")
    if "publicKey" not in decoded_token_payload["subject"]:
        raise ValueError("Token doesn't have a subject public key")
    if "issuer" not in decoded_token_payload:
        raise ValueError("Token doesn't have an issuer")
    if "publicKey" not in decoded_token_payload["issuer"]:
        raise ValueError("Token doesn't have an issuer public key")
    if "claim" not in decoded_token_payload:
        raise ValueError("Token doesn't have a claim")

    issuer_public_key = str(decoded_token_payload["issuer"]["publicKey"])
    public_key_object = ECPublicKey(issuer_public_key)

    compressed_public_key = compress(issuer_public_key)
    decompressed_public_key = decompress(issuer_public_key)

    if public_key_object._type == PubkeyType.compressed:
        compressed_address = public_key_object.address()
        uncompressed_address = bin_hash160_to_address(
            bin_hash160(
                decompress(public_key_object.to_bin())
            )
        )
    elif public_key_object._type == PubkeyType.uncompressed:
        compressed_address = bin_hash160_to_address(
            bin_hash160(
                compress(public_key_object.to_bin())
            )
        )
        uncompressed_address = public_key_object.address()
    else:
        raise ValueError("Invalid issuer public key format")
   
    if public_key_or_address == compressed_public_key:
        pass
    elif public_key_or_address == decompressed_public_key:
        pass
    elif public_key_or_address == compressed_address:
        pass
    elif public_key_or_address == uncompressed_address:
        pass
    else:
        raise ValueError("Token public key doesn't match the verifying value")

    token_verifier = TokenVerifier()

    if not token_verifier.verify(token, public_key_object.to_pem()):
        raise ValueError("Token was not signed by the issuer public key")

    return decoded_token
def verify_token(token, public_key_or_address, signing_algorithm="ES256K"):
    """ A function for validating an individual token.
    """
    decoded_token = decode_token(token)
    decoded_token_payload = decoded_token["payload"]

    if "subject" not in decoded_token_payload:
        raise ValueError("Token doesn't have a subject")
    if "publicKey" not in decoded_token_payload["subject"]:
        raise ValueError("Token doesn't have a subject public key")
    if "issuer" not in decoded_token_payload:
        raise ValueError("Token doesn't have an issuer")
    if "publicKey" not in decoded_token_payload["issuer"]:
        raise ValueError("Token doesn't have an issuer public key")
    if "claim" not in decoded_token_payload:
        raise ValueError("Token doesn't have a claim")

    issuer_public_key = str(decoded_token_payload["issuer"]["publicKey"])
    public_key_object = ECPublicKey(issuer_public_key)

    compressed_public_key = compress(issuer_public_key)
    decompressed_public_key = decompress(issuer_public_key)

    if public_key_object._type == PubkeyType.compressed:
        compressed_address = public_key_object.address()
        uncompressed_address = bin_hash160_to_address(
            bin_hash160(decompress(public_key_object.to_bin())))
    elif public_key_object._type == PubkeyType.uncompressed:
        compressed_address = bin_hash160_to_address(
            bin_hash160(compress(public_key_object.to_bin())))
        uncompressed_address = public_key_object.address()
    else:
        raise ValueError("Invalid issuer public key format")

    if public_key_or_address == compressed_public_key:
        pass
    elif public_key_or_address == decompressed_public_key:
        pass
    elif public_key_or_address == compressed_address:
        pass
    elif public_key_or_address == uncompressed_address:
        pass
    else:
        raise ValueError("Token public key doesn't match the verifying value")

    token_verifier = TokenVerifier()

    if not token_verifier.verify(token, public_key_object.to_pem()):
        raise ValueError("Token was not signed by the issuer public key")

    return decoded_token
def app_make_session( blockchain_id, app_private_key, app_domain, methods, app_public_keys, requester_device_id, master_data_privkey, session_lifetime=None, config_path=CONFIG_PATH ):
    """
    Make a session JWT for this application.
    Verify with user private key
    Sign with master private key

    Return {'session': session jwt, 'session_token': session token} on success
    Return {'error': ...} on error
    """
    conf = get_config(path=config_path)
    assert conf

    if session_lifetime is None:
        session_lifetime = conf.get('default_session_lifetime', 1e80)

    app_public_key = get_pubkey_hex(app_private_key)
    app_user_id = data.datastore_get_id(app_public_key)

    api_endpoint_host = conf.get('api_endpoint_host', DEFAULT_API_HOST)
    api_endpoint_port = conf.get('api_endpoint_port', DEFAULT_API_PORT)

    api_endpoint = '{}:{}'.format(api_endpoint_host, api_endpoint_port)

    ses = {
        'version': 1,
        'blockchain_id': blockchain_id,
        'app_domain': app_domain,
        'methods': methods,
        'app_public_keys': app_public_keys,
        'app_user_id': app_user_id,
        'api_endpoint': api_endpoint,
        'device_id': requester_device_id,
        'storage': {
            'classes': classify_storage_drivers(),
            'preferences': {}
        },
        'timestamp': int(time.time()),
        'expires': int(time.time() + session_lifetime),
    }

    jsonschema.validate(ses, APP_SESSION_SCHEMA)

    signer = jsontokens.TokenSigner()
    session_token = signer.sign( ses, master_data_privkey )
    session = jsontokens.decode_token(session_token)

    return {'session': session, 'session_token': session_token}
Esempio n. 8
0
    def test_signing_decoding_and_verifying(self):
        token_signer = TokenSigner(crypto_backend=openssl_backend)
        token = token_signer.sign(self.sample_payload, self.private_key_hex)
        #print token
        self.assertTrue(isinstance(token, (unicode, str)))

        decoded_token = TokenVerifier.decode(token)
        self.assertTrue(isinstance(decoded_token, dict))

        decoded_token_2 = decode_token(token)
        #print json.dumps(decoded_token_2, indent=2)
        self.assertTrue(isinstance(decoded_token_2, dict))

        token_verifier = TokenVerifier()
        token_is_valid = token_verifier.verify(token, self.public_key_hex)
        #print token_is_valid
        self.assertTrue(token_is_valid)
def app_verify_session( app_session_token, data_pubkey_hex, config_path=CONFIG_PATH ):
    """
    Verify and decode a JWT app session token.
    The session is valid if the signature matches and the token is not expired.
    Return the decoded session token payload on success
    Return None on error
    """
    pubkey = str(data_pubkey_hex)
    verifier = jsontokens.TokenVerifier()
    
    valid = False

    try:
        valid = verifier.verify( app_session_token, pubkey )
        if not valid:
            log.debug("Failed to verify with {}".format(pubkey))
            return None
    except:
        log.debug("Not a valid token")
        return None

    session = None
    session_jwt = None

    try:
        session_jwt = jsontokens.decode_token(app_session_token)
        session = session_jwt['payload']
    except:
        log.debug("Failed to decode token")
        return None

    # must match session structure 
    try:
        jsonschema.validate(session, APP_SESSION_SCHEMA)
    except ValidationError as ve:
        if BLOCKSTACK_TEST:
            log.exception(ve)

        return None

    # session must not be expired
    if session['expires'] < time.time():
        log.debug("Token is expired")
        return None

    return session
Esempio n. 10
0
def func(username):
    prof_json = subprocess.Popen(['blockstack', 'lookup', username],
                                 stdout=subprocess.PIPE).stdout.read()
    data = json.loads(prof_json)
    if data.get('error'):
        return
    zonefile = data['zonefile']
    last_index = zonefile.rfind('"')
    url = zonefile[zonefile.rfind('"', 0, last_index) + 1:last_index]
    if (not "https://" in url):
        url = "https://" + url
    opened_url = urllib.urlopen(url)
    data = json.loads(opened_url.read())
    token = jsontokens.decode_token(data[0]['token'])
    encoded_PK = token['payload']['subject']['publicKey']
    key = keylib.public_key.ECPublicKey(str(encoded_PK))
    return key.address()
Esempio n. 11
0
    def test_signing_decoding_and_verifying(self):
        token_signer = TokenSigner(crypto_backend=openssl_backend)
        token = token_signer.sign(self.sample_payload, self.private_key_hex)
        #print token
        self.assertTrue(isinstance(token, (unicode, str)))

        decoded_token = TokenVerifier.decode(token)
        self.assertTrue(isinstance(decoded_token, dict))

        decoded_token_2 = decode_token(token)
        #print json.dumps(decoded_token_2, indent=2)
        self.assertTrue(isinstance(decoded_token_2, dict))

        token_verifier = TokenVerifier()
        token_is_valid = token_verifier.verify(token, self.public_key_hex)
        #print token_is_valid
        self.assertTrue(token_is_valid)
def get_data_pubkeys(blockchain_id):
    """
    Get device IDs and public keys for a blockchain ID
    TODO: load these from the token file
    """
    ses = sessions[blockchain_id]
    session = jsontokens.decode_token(ses)['payload']

    device_ids = [dk['device_id'] for dk in session['app_public_keys']]
    device_pubkeys = [dk['public_key'] for dk in session['app_public_keys']]

    data_pubkeys = [{
        'device_id': dev_id,
        'public_key': pubkey,
    } for (dev_id, pubkey) in zip(device_ids, device_pubkeys)]

    print "\ndata public keys for {} are\n{}\n".format(blockchain_id, json.dumps(data_pubkeys, indent=4, sort_keys=True))
    return data_pubkeys
Esempio n. 13
0
def app_make_session( app_domain, methods, master_data_privkey_hex, app_user_id=None, app_user_privkey=None, session_lifetime=None, blockchain_ids=None, config_path=CONFIG_PATH ):
    """
    Make a session JWT for this application.
    Verify with user private key
    Sign with master private key
    Return {'session': session jwt, 'session_token': session token} on success
    Return {'error': ...} on error
    """
    if session_lifetime is None:
        conf = get_config(path=config_path)
        assert conf
        session_lifetime = conf.get('default_session_lifetime', 1e80)

    if app_user_id is None:
        if app_user_privkey is None:
            if master_data_privkey_hex is not None:
                assert app_domain is not None, "need app domain to derive app key"
                app_user_privkey = data.datastore_get_privkey(master_data_privkey_hex, app_domain, config_path=config_path)

            else:
                # TODO: load from disk
                raise NotImplemented("Local app user private keys are not supported at this time")

        app_user_pubkey = get_pubkey_hex(app_user_privkey)
        app_user_id = data.datastore_get_id(app_user_pubkey)

    ses = {
        'app_domain': app_domain,
        'methods': methods,
        'app_user_id': app_user_id,
        'timestamp': int(time.time()),
        'expires': int(time.time() + session_lifetime),
    }

    if blockchain_ids is not None:
        ses['blockchain_ids'] = blockchain_ids

    jsonschema.validate(ses, APP_SESSION_SCHEMA)

    signer = jsontokens.TokenSigner()
    session_token = signer.sign( ses, master_data_privkey_hex )
    session = jsontokens.decode_token(session_token)

    return {'session': session, 'session_token': session_token}
Esempio n. 14
0
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