def verify_id_token(msg, check_hash=False, claim='id_token', **kwargs):
    # Try to decode the JWT, checks the signature
    args = {}
    for arg in ID_TOKEN_VERIFY_ARGS:
        try:
            args[arg] = kwargs[arg]
        except KeyError:
            pass

    _jws = jws_factory(msg[claim])
    if not _jws:
        raise ValueError('{} not a signed JWT'.format(claim))

    if _jws.jwt.headers['alg'] == 'none':
        try:
            _allow_none = kwargs['allow_sign_alg_none']
        except KeyError:
            logger.info('Signing algorithm None not allowed')
            return False
        else:
            if not _allow_none:
                logger.info('Signing algorithm None not allowed')
                return False

    _body = _jws.jwt.payload()
    if 'keyjar' in kwargs:
        try:
            if _body['iss'] not in kwargs['keyjar']:
                raise ValueError('Unknown issuer')
        except KeyError:
            raise MissingRequiredAttribute('iss')

    idt = IdToken().from_jwt(str(msg[claim]), **args)
    if not idt.verify(**kwargs):
        return False

    if check_hash:
        _alg = idt.jws_header["alg"]
        hfunc = "HS" + _alg[-3:]

        if "access_token" in msg:
            if "at_hash" not in idt:
                raise MissingRequiredAttribute("Missing at_hash property", idt)
            if idt["at_hash"] != left_hash(msg["access_token"], hfunc):
                raise AtHashError("Failed to verify access_token hash", idt)

        if "code" in msg:
            if "c_hash" not in idt:
                raise MissingRequiredAttribute("Missing c_hash property", idt)
            if idt["c_hash"] != left_hash(msg["code"], hfunc):
                raise CHashError("Failed to verify code hash", idt)

    msg[verified_claim_name(claim)] = idt
    logger.info('Verified {}: {}'.format(claim, idt.to_dict()))

    return True
Exemple #2
0
    def val_hash(self, alg):
        halg = "HS%s" % alg[-3:]

        for attr, hash_attr in self.hashable.items():
            try:
                self[hash_attr] = left_hash(as_unicode(self[attr]), halg)
            except KeyError:
                pass
            else:
                del self[attr]
    def test_update_service_context_with_idtoken(self):
        req_args = {'response_type': 'code', 'state': 'state', 'nonce': 'nonce'}
        self.service.endpoint = 'https://example.com/authorize'
        _info = self.service.get_request_parameters(request_args=req_args)
        # Build an ID Token
        idt = JWT(key_jar=ISS_KEY, iss=ISS, lifetime=3600)
        payload = {'sub': '123456789', 'aud': ['client_id'], 'nonce': 'nonce'}
        # have to calculate c_hash
        alg = 'RS256'
        halg = "HS%s" % alg[-3:]
        payload["c_hash"] = left_hash('code', halg)

        _idt = idt.pack(payload)
        resp = AuthorizationResponse(state='state', code='code', id_token=_idt)
        resp = self.service.parse_response(resp.to_urlencoded())
        self.service.update_service_context(resp, 'state')
Exemple #4
0
def verify_id_token(msg, check_hash=False, claim="id_token", **kwargs):
    # Try to decode the JWT, checks the signature
    args = {}
    for arg in ID_TOKEN_VERIFY_ARGS:
        try:
            args[arg] = kwargs[arg]
        except KeyError:
            pass

    _jws = jws_factory(msg[claim])
    if not _jws:
        raise ValueError("{} not a signed JWT".format(claim))

    if _jws.jwt.headers["alg"] == "none":
        _signed = False
        _sign_alg = kwargs.get("sigalg")
        if _sign_alg == "none":
            _allowed = True
        else:  # There might or might not be a specified signing alg
            if kwargs.get("allow_sign_alg_none", False) is False:
                logger.info("Signing algorithm None not allowed")
                raise UnsupportedAlgorithm("Signing algorithm None not allowed")
    else:
        _signed = True
        if "allowed_sign_alg" in kwargs:
            if _jws.jwt.headers["alg"] != kwargs["allowed_sign_alg"]:
                _msg = "Wrong token signing algorithm, {} != {}".format(
                    _jws.jwt.headers["alg"], kwargs["allowed_sign_alg"]
                )
                logger.error(_msg)
                raise UnsupportedAlgorithm(_msg)

    _body = _jws.jwt.payload()
    if _signed and "keyjar" in kwargs:
        try:
            if _body["iss"] not in kwargs["keyjar"]:
                logger.info("KeyJar issuers: {}".format(kwargs["keyjar"]))
                raise ValueError('Unknown issuer: "{}"'.format(_body["iss"]))
        except KeyError:
            raise MissingRequiredAttribute("iss")

    idt = IdToken().from_jwt(str(msg[claim]), **args)
    if not idt.verify(**kwargs):
        return False

    if _signed and check_hash:
        _alg = idt.jws_header["alg"]
        hfunc = "HS" + _alg[-3:]

        if "access_token" in msg:
            if "at_hash" not in idt:
                raise MissingRequiredAttribute("Missing at_hash property", idt)
            if idt["at_hash"] != left_hash(msg["access_token"], hfunc):
                raise AtHashError("Failed to verify access_token hash", idt)

        if "code" in msg:
            if "c_hash" not in idt:
                raise MissingRequiredAttribute("Missing c_hash property", idt)
            if idt["c_hash"] != left_hash(msg["code"], hfunc):
                raise CHashError("Failed to verify code hash", idt)

    msg[verified_claim_name(claim)] = idt
    logger.info("Verified {}: {}".format(claim, idt.to_dict()))

    return True
Exemple #5
0
def test_left_hash_hs512():
    hsh = left_hash("Please take a moment to register today", "HS512")
    assert hsh == "_h6feWLt8zbYcOFnaBmekTzMJYEHdVTaXlDgJSWsEeY"
Exemple #6
0
def test_left_hash_hs256():
    hsh = left_hash("Please take a moment to register today")
    assert hsh == "rCFHVJuxTqRxOsn2IUzgvA"
Exemple #7
0
    def payload(
        self,
        session,
        acr="",
        alg="RS256",
        code=None,
        access_token=None,
        user_info=None,
        auth_time=0,
        lifetime=None,
        extra_claims=None,
    ):
        """

        :param session: Session information
        :param acr: Default Assurance/Authentication context class reference
        :param alg: Which signing algorithm to use for the IdToken
        :param code: Access grant
        :param access_token: Access Token
        :param user_info: If user info are to be part of the IdToken
        :param auth_time:
        :param lifetime: Life time of the ID Token
        :param extra_claims: extra claims to be added to the ID Token
        :return: IDToken instance
        """

        _args = {"sub": session["sub"]}

        if lifetime is None:
            lifetime = DEF_LIFETIME

        if auth_time:
            _args["auth_time"] = auth_time
        if acr:
            _args["acr"] = acr

        if user_info:
            try:
                user_info = user_info.to_dict()
            except AttributeError:
                pass

            # Make sure that there are no name clashes
            for key in [
                    "iss", "sub", "aud", "exp", "acr", "nonce", "auth_time"
            ]:
                try:
                    del user_info[key]
                except KeyError:
                    pass

            _args.update(user_info)

        if extra_claims is not None:
            _args.update(extra_claims)

        # Left hashes of code and/or access_token
        halg = "HS%s" % alg[-3:]
        if code:
            _args["c_hash"] = left_hash(code.encode("utf-8"), halg)
        if access_token:
            _args["at_hash"] = left_hash(access_token.encode("utf-8"), halg)

        authn_req = session["authn_req"]
        if authn_req:
            try:
                _args["nonce"] = authn_req["nonce"]
            except KeyError:
                pass

        return {"payload": _args, "lifetime": lifetime}
Exemple #8
0
def id_token_payload(session,
                     loa="2",
                     alg="RS256",
                     code=None,
                     access_token=None,
                     user_info=None,
                     auth_time=0,
                     lifetime=300,
                     extra_claims=None):
    """

    :param session: Session information
    :param loa: Level of Assurance/Authentication context
    :param alg: Which signing algorithm to use for the IdToken
    :param code: Access grant
    :param access_token: Access Token
    :param user_info: If user info are to be part of the IdToken
    :param auth_time:
    :param lifetime: Life time of the ID Token
    :param extra_claims: extra claims to be added to the ID Token
    :return: IDToken instance
    """

    _args = {'sub': session['sub']}

    # Handle the idtoken_claims
    itc = id_token_claims(session)

    if itc.keys():
        try:
            lifetime = itc["max_age"]
        except KeyError:
            pass

        for key, val in itc.items():
            if key == "auth_time":
                _args["auth_time"] = auth_time
            elif key == "acr":
                # ["2","http://id.incommon.org/assurance/bronze"]
                _args["acr"] = verify_acr_level(val, loa)
    else:
        if auth_time:
            _args["auth_time"] = auth_time
        if loa:
            _args["acr"] = loa

    if user_info:
        try:
            user_info = user_info.to_dict()
        except AttributeError:
            pass

        # Make sure that there are no name clashes
        for key in ["iss", "sub", "aud", "exp", "acr", "nonce", "auth_time"]:
            try:
                del user_info[key]
            except KeyError:
                pass

        _args.update(user_info)

    if extra_claims is not None:
        _args.update(extra_claims)

    # Left hashes of code and/or access_token
    halg = "HS%s" % alg[-3:]
    if code:
        _args["c_hash"] = left_hash(code.encode("utf-8"), halg)
    if access_token:
        _args["at_hash"] = left_hash(access_token.encode("utf-8"), halg)

    authn_req = session['authn_req']
    if authn_req:
        try:
            _args["nonce"] = authn_req["nonce"]
        except KeyError:
            pass

    return {'payload': _args, 'lifetime': lifetime}
Exemple #9
0
    def payload(
        self,
        session_id,
        alg="RS256",
        code=None,
        access_token=None,
        extra_claims=None,
    ):
        """

        :param session_id: Session identifier
        :param alg: Which signing algorithm to use for the IdToken
        :param code: Access grant
        :param access_token: Access Token
        :param extra_claims: extra claims to be added to the ID Token
        :return: IDToken instance
        """

        _context = self.server_get("endpoint_context")
        _mngr = _context.session_manager
        session_information = _mngr.get_session_info(session_id, grant=True)
        grant = session_information["grant"]
        _args = {"sub": grant.sub}
        if grant.authentication_event:
            for claim, attr in {
                    "authn_time": "auth_time",
                    "authn_info": "acr"
            }.items():
                _val = grant.authentication_event.get(claim)
                if _val:
                    _args[attr] = _val

        _claims_restriction = grant.claims.get("id_token")
        if _claims_restriction == {}:
            user_info = None
        else:
            user_info = _context.claims_interface.get_user_claims(
                user_id=session_information["user_id"],
                claims_restriction=_claims_restriction,
            )
            if _claims_restriction and "acr" in _claims_restriction and "acr" in _args:
                if claims_match(_args["acr"],
                                _claims_restriction["acr"]) is False:
                    raise ValueError("Could not match expected 'acr'")

        if user_info:
            try:
                user_info = user_info.to_dict()
            except AttributeError:
                pass

            # Make sure that there are no name clashes
            for key in [
                    "iss", "sub", "aud", "exp", "acr", "nonce", "auth_time"
            ]:
                try:
                    del user_info[key]
                except KeyError:
                    pass

            _args.update(user_info)

        if extra_claims is not None:
            _args.update(extra_claims)

        # Left hashes of code and/or access_token
        halg = f"HS{alg[-3:]}"
        if code:
            _args["c_hash"] = left_hash(code.encode("utf-8"), halg)
        if access_token:
            _args["at_hash"] = left_hash(access_token.encode("utf-8"), halg)

        authn_req = grant.authorization_request
        if authn_req:
            try:
                _args["nonce"] = authn_req["nonce"]
            except KeyError:
                pass

        return _args