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
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))
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}
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}
def deserialize(self): self.key = b64d(bytes(self.k))
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
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}
def cookie_value(b64): return json.loads(as_unicode(b64d(as_bytes(b64))))
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)))
def _decode(self, payload): _msg = b64d(bytes(payload)) if "cty" in self: if self["cty"] == "JWT": _msg = json.loads(_msg) return _msg