Ejemplo n.º 1
0
    def dec_setup(self, token, key=None, **kwargs):
        """

        :param token: signed JSON Web token
        :param key: Private Elliptic Curve Key
        :param kwargs:
        :return:
        """
        self.headers = token.headers
        self.iv = token.initialization_vector()
        self.ctxt = token.ciphertext()
        self.tag = token.authentication_tag()

        # Handle EPK / Curve
        if "epk" not in self.headers or "crv" not in self.headers["epk"]:
            raise Exception(
                "Ephemeral Public Key Missing in ECDH-ES Computation")

        epubkey = ECKey(**self.headers["epk"])
        apu = apv = ""
        if "apu" in self.headers:
            apu = b64d(self.headers["apu"].encode())
        if "apv" in self.headers:
            apv = b64d(self.headers["apv"].encode())

        if self.headers["alg"] == "ECDH-ES":
            try:
                dk_len = KEY_LEN[self.headers["enc"]]
            except KeyError:
                raise Exception("Unknown key length for algorithm")

            self.cek = ecdh_derive_key(key, epubkey.key, apu, apv,
                                       str(self.headers["enc"]).encode(),
                                       dk_len)
        elif self.headers["alg"] in ["ECDH-ES+A128KW", "ECDH-ES+A192KW",
                                     "ECDH-ES+A256KW"]:
            _pre, _post = self.headers['alg'].split("+")
            klen = int(_post[1:4])
            kek = ecdh_derive_key(key, epubkey.key, apu, apv,
                                  str(_post).encode(), klen)
            self.cek = aes_key_unwrap(kek, token.encrypted_key(),
                                      default_backend())
        else:
            raise Exception("Unsupported algorithm %s" % self.headers["alg"])

        return self.cek
def test_signer_protected_headers():
    payload = "Please take a moment to register today"
    eck = ec.generate_private_key(ec.SECP256R1(), default_backend())
    _key = ECKey().load_key(eck)
    keys = [_key]
    _jws = JWS(payload, alg="ES256")
    protected = dict(header1=u"header1 is protected",
                     header2="header2 is protected too", a=1)
    _jwt = _jws.sign_compact(keys, protected=protected)

    exp_protected = protected.copy()
    exp_protected['alg'] = 'ES256'
    enc_header, enc_payload, sig = _jwt.split('.')
    assert json.loads(
        b64d(enc_header.encode("utf-8")).decode("utf-8")) == exp_protected
    assert b64d(enc_payload.encode("utf-8")).decode("utf-8") == payload

    _pub_key = ECKey().load_key(eck.public_key())
    _rj = JWS()
    info = _rj.verify_compact(_jwt, [_pub_key])
    assert info == payload
Ejemplo n.º 3
0
 def __init__(self,
              kty="oct",
              alg="",
              use="",
              kid="",
              key=None,
              x5c=None,
              x5t="",
              x5u="",
              k="",
              mtrl="",
              **kwargs):
     Key.__init__(self, kty, alg, use, kid, as_bytes(key), x5c, x5t, x5u,
                  **kwargs)
     self.k = k
     if not self.key and self.k:
         if isinstance(self.k, str):
             self.k = self.k.encode("utf-8")
         self.key = b64d(bytes(self.k))
Ejemplo n.º 4
0
    def process_request(self, request=None, cookie=None, **kwargs):
        """
        Perform user logout

        :param request:
        :param cookie:
        :param kwargs:
        :return:
        """
        _cntx = self.endpoint_context
        _sdb = _cntx.sdb

        if "post_logout_redirect_uri" in request:
            if "id_token_hint" not in request:
                raise InvalidRequest(
                    "If post_logout_redirect_uri then id_token_hint is a MUST")
        _cookie_name = self.endpoint_context.cookie_name["session"]
        try:
            part = self.endpoint_context.cookie_dealer.get_cookie_value(
                cookie, cookie_name=_cookie_name)
        except IndexError:
            raise InvalidRequest("Cookie error")
        except KeyError:
            part = None

        if part:
            # value is a base64 encoded JSON document
            _cookie_info = json.loads(as_unicode(b64d(as_bytes(part[0]))))
            logger.debug("Cookie info: {}".format(_cookie_info))
            _sid = _cookie_info["sid"]
        else:
            logger.debug("No relevant cookie")
            _sid = ""
            _cookie_info = {}

        if "id_token_hint" in request:
            logger.debug("ID token hint: {}".format(
                request[verified_claim_name("id_token_hint")]))

            auds = request[verified_claim_name("id_token_hint")]["aud"]
            _ith_sid = ""
            _sids = _sdb.sso_db.get_sids_by_sub(
                request[verified_claim_name("id_token_hint")]["sub"])

            if _sids is None:
                raise ValueError("Unknown subject identifier")

            for _isid in _sids:
                if _sdb[_isid]["authn_req"]["client_id"] in auds:
                    _ith_sid = _isid
                    break

            if not _ith_sid:
                raise ValueError("Unknown subject")

            if _sid:
                if _ith_sid != _sid:  # someone's messing with me
                    raise ValueError("Wrong ID Token hint")
            else:
                _sid = _ith_sid
        else:
            auds = []

        try:
            session = _sdb[_sid]
        except KeyError:
            raise ValueError("Can't find any corresponding session")

        client_id = session["authn_req"]["client_id"]
        # Does this match what's in the cookie ?
        if _cookie_info:
            if client_id != _cookie_info["client_id"]:
                logger.warning(
                    "Client ID in authz request and in cookie does not match")
                raise ValueError("Wrong Client")

        if auds:
            if client_id not in auds:
                raise ValueError("Incorrect ID Token hint")

        _cinfo = _cntx.cdb[client_id]

        # verify that the post_logout_redirect_uri if present are among the ones
        # registered

        try:
            _uri = request["post_logout_redirect_uri"]
        except KeyError:
            if _cntx.issuer.endswith("/"):
                _uri = "{}{}".format(_cntx.issuer,
                                     self.kwargs["post_logout_uri_path"])
            else:
                _uri = "{}/{}".format(_cntx.issuer,
                                      self.kwargs["post_logout_uri_path"])
            plur = False
        else:
            plur = True
            verify_uri(_cntx,
                       request,
                       "post_logout_redirect_uri",
                       client_id=client_id)

        payload = {
            "sid": _sid,
            "client_id": client_id,
            "user": session["authn_event"]["uid"],
        }

        # redirect user to OP logout verification page
        if plur and "state" in request:
            _uri = "{}?{}".format(_uri, urlencode({"state": request["state"]}))
            payload["state"] = request["state"]

        payload["redirect_uri"] = _uri

        logger.debug("JWS payload: {}".format(payload))
        # From me to me
        _jws = JWT(
            _cntx.keyjar,
            iss=_cntx.issuer,
            lifetime=86400,
            sign_alg=self.kwargs["signing_alg"],
        )
        sjwt = _jws.pack(payload=payload, recv=_cntx.issuer)

        location = "{}?{}".format(self.kwargs["logout_verify_url"],
                                  urlencode({"sjwt": sjwt}))
        return {"redirect_location": location}
Ejemplo n.º 5
0
    def setup_auth(
        self,
        request: Optional[Union[Message, dict]],
        redirect_uri: str,
        cinfo: dict,
        cookie: List[dict] = None,
        acr: str = None,
        **kwargs,
    ):
        """

        :param request: The authorization/authentication request
        :param redirect_uri:
        :param cinfo: client info
        :param cookie: List of cookies
        :param acr: Default ACR, if nothing else is specified
        :param kwargs:
        :return:
        """

        res = self.pick_authn_method(request, redirect_uri, acr, **kwargs)

        authn = res["method"]
        authn_class_ref = res["acr"]

        client_id = request.get("client_id")
        _context = self.server_get("endpoint_context")
        try:
            _auth_info = kwargs.get("authn", "")
            if "upm_answer" in request and request["upm_answer"] == "true":
                _max_age = 0
            else:
                _max_age = max_age(request)
            identity, _ts = authn.authenticated_as(client_id,
                                                   cookie,
                                                   authorization=_auth_info,
                                                   max_age=_max_age)
        except (NoSuchAuthentication, TamperAllert):
            identity = None
            _ts = 0
        except ToOld:
            logger.info("Too old authentication")
            identity = None
            _ts = 0
        except UnknownToken:
            logger.info("Unknown Token")
            identity = None
            _ts = 0
        else:
            if identity:
                try:  # If identity['uid'] is in fact a base64 encoded JSON string
                    _id = b64d(as_bytes(identity["uid"]))
                except BadSyntax:
                    pass
                else:
                    identity = json.loads(as_unicode(_id))

                    try:
                        _csi = _context.session_manager[identity.get("sid")]
                    except Revoked:
                        identity = None
                    else:
                        if _csi.is_active() is False:
                            identity = None

        authn_args = authn_args_gather(request, authn_class_ref, cinfo,
                                       **kwargs)
        _mngr = _context.session_manager
        _session_id = ""

        # To authenticate or Not
        if not identity:  # No!
            logger.info("No active authentication")
            logger.debug("Known clients: {}".format(list(_context.cdb.keys())))

            if "prompt" in request and "none" in request["prompt"]:
                # Need to authenticate but not allowed
                return {
                    "error": "login_required",
                    "return_uri": redirect_uri,
                    "return_type": request["response_type"],
                }
            else:
                return {"function": authn, "args": authn_args}
        else:
            logger.info("Active authentication")
            if re_authenticate(request, authn):
                # demand re-authentication
                return {"function": authn, "args": authn_args}
            else:
                # I got back a dictionary
                user = identity["uid"]
                if "req_user" in kwargs:
                    if user != kwargs["req_user"]:
                        logger.debug("Wanted to be someone else!")
                        if "prompt" in request and "none" in request["prompt"]:
                            # Need to authenticate but not allowed
                            return {
                                "error": "login_required",
                                "return_uri": redirect_uri,
                            }
                        else:
                            return {"function": authn, "args": authn_args}

                if "sid" in identity:
                    _session_id = identity["sid"]

                    # make sure the client is the same
                    _uid, _cid, _gid = _mngr.decrypt_session_id(_session_id)
                    if request["client_id"] != _cid:
                        return {"function": authn, "args": authn_args}

                    grant = _mngr[_session_id]
                    if grant.is_active() is False:
                        return {"function": authn, "args": authn_args}
                    elif request != grant.authorization_request:
                        authn_event = _mngr.get_authentication_event(
                            session_id=_session_id)
                        if authn_event.is_valid(
                        ) is False:  # if not valid, do new login
                            return {"function": authn, "args": authn_args}

                        # create new grant
                        _session_id = _mngr.create_grant(
                            authn_event=authn_event,
                            auth_req=request,
                            user_id=user,
                            client_id=request["client_id"],
                        )

        if _session_id:
            authn_event = _mngr.get_authentication_event(
                session_id=_session_id)
            if authn_event.is_valid() is False:  # if not valid, do new login
                return {"function": authn, "args": authn_args}
        else:
            _session_id = self.create_session(request, identity["uid"],
                                              authn_class_ref, _ts, authn)

        return {"session_id": _session_id, "identity": identity, "user": user}
Ejemplo n.º 6
0
 def deserialize(self):
     self.key = b64d(bytes(self.k))
Ejemplo n.º 7
0
    def enc_setup(self, msg, key=None, auth_data=b'', **kwargs):
        """

        :param msg: Message to be encrypted
        :param auth_data:
        :param key: An EC key
        :param kwargs:
        :return:
        """
        encrypted_key = ""
        self.msg = msg
        self.auth_data = auth_data

        # Generate the input parameters
        try:
            apu = b64d(kwargs["apu"])
        except KeyError:
            apu = get_random_bytes(16)
        try:
            apv = b64d(kwargs["apv"])
        except KeyError:
            apv = get_random_bytes(16)

        # Handle Local Key and Ephemeral Public Key
        if not key:
            raise Exception("EC Key Required for ECDH-ES JWE Encryption Setup")

        # epk is either an Elliptic curve key instance or a JWK description of
        # one. This key belongs to the entity on the other side.
        try:
            _epk = kwargs['epk']
        except KeyError:
            _epk = ec.generate_private_key(NIST2SEC[as_unicode(key.crv)],
                                           default_backend())
            epk = ECKey().load_key(_epk.public_key())
        else:
            if isinstance(_epk, ec.EllipticCurvePublicKey):
                epk = ECKey().load_key(_epk)
            elif isinstance(_epk, ECKey):
                epk = _epk
            else:
                raise ValueError("epk of a type I can't handle")

        params = {
            "apu": b64e(apu),
            "apv": b64e(apv),
            "epk": epk.serialize(False)
        }

        cek = iv = None
        if 'cek' in kwargs and kwargs['cek']:
            cek = kwargs['cek']
        if 'iv' in kwargs and kwargs['iv']:
            iv = kwargs['iv']

        iv = self._generate_iv(self.enc, iv=iv)

        if self.alg == "ECDH-ES":
            try:
                dk_len = KEY_LEN[self.enc]
            except KeyError:
                raise Exception(
                    "Unknown key length for algorithm %s" % self.enc)

            cek = ecdh_derive_key(_epk, key.key, apu, apv,
                                  str(self.enc).encode(), dk_len)
        elif self.alg in ["ECDH-ES+A128KW", "ECDH-ES+A192KW", "ECDH-ES+A256KW"]:
            _pre, _post = self.alg.split("+")
            klen = int(_post[1:4])
            kek = ecdh_derive_key(_epk, key.key, apu, apv,
                                  str(_post).encode(), klen)
            cek = self._generate_key(self.enc, cek=cek)
            encrypted_key = aes_key_wrap(kek, cek, default_backend())
        else:
            raise Exception("Unsupported algorithm %s" % self.alg)

        return cek, encrypted_key, iv, params, epk
Ejemplo n.º 8
0
    def process_request(self, request=None, cookie=None, **kwargs):
        """
        Perform user logout

        :param request:
        :param cookie:
        :param kwargs:
        :return:
        """
        _sdb = self.endpoint_context.sdb

        try:
            part = self.endpoint_context.cookie_dealer.get_cookie_value(
                cookie, cookie_name='oidc_op')
        except IndexError:
            raise InvalidRequest('Cookie error')

        if part:
            # value is a base64 encoded JSON document
            _cookie_info = json.loads(as_unicode(b64d(as_bytes(part[0]))))
            _sid = _cookie_info['sid']
        else:
            _sid = ''

        if 'id_token_hint' in request:
            _ith_sid = _sdb.sso_db.get_sids_by_sub(
                request[verified_claim_name("id_token_hint")]['sub'])[0]
            if _ith_sid != _sid:  # someone's messing with me
                raise ValueError('Wrong ID Token hint')

        session = _sdb[_sid]

        client_id = session['authn_req']['client_id']

        _cinfo = self.endpoint_context.cdb[client_id]

        # verify that the post_logout_redirect_uri if present are among the ones
        # registered

        try:
            _url_q = splitquery(request['post_logout_redirect_uri'])
        except KeyError:
            pass
        else:
            if not _url_q in _cinfo['post_logout_redirect_uris']:
                raise ValueError('Unregistered post_logout_redirect_uri')

        # Kill the session
        _sdb.revoke_session(sid=_sid)

        if 'post_logout_redirect_uri' in request:
            _ruri = request["post_logout_redirect_uri"]
            if 'state' in request:
                _ruri = '{}?{}'.format(_ruri,
                                       urlencode({'state': request['state']}))
        else:  # To  my own logout-done page
            try:
                _ruri = self.endpoint_context.conf['post_logout_page']
            except KeyError:
                _ruri = self.endpoint_context.issuer

        return {'response_args': _ruri}
Ejemplo n.º 9
0
def cookie_value(b64):
    return json.loads(as_unicode(b64d(as_bytes(b64))))
Ejemplo n.º 10
0
 def _decrypt_sid(self, enc_msg):
     _msg = b64d(as_bytes(enc_msg))
     encrypter = AES_GCMEncrypter(key=as_bytes(self.server_get("endpoint_context").symkey))
     ctx, tag = split_ctx_and_tag(_msg)
     return as_unicode(encrypter.decrypt(as_bytes(ctx), iv=self.iv, tag=as_bytes(tag)))
Ejemplo n.º 11
0
 def _decode(self, payload):
     _msg = b64d(bytes(payload))
     if "cty" in self:
         if self["cty"] == "JWT":
             _msg = json.loads(_msg)
     return _msg