def parse(self, msg, name): """Parses the message. We check that the message is properly formatted. :param msg: a json-encoded value containing a JWS or JWE+JWS token :raises InvalidMessage: if the message cannot be parsed or validated :returns: A verified payload """ try: jtok = JWT(jwt=msg) except Exception as e: raise InvalidMessage('Failed to parse message: %s' % str(e)) try: token = jtok.token if isinstance(token, JWE): token.decrypt(self.kkstore.server_keys[KEY_USAGE_ENC]) # If an encrypted payload is received then there must be # a nested signed payload to verify the provenance. payload = token.payload.decode('utf-8') token = JWS() token.deserialize(payload) elif isinstance(token, JWS): pass else: raise TypeError("Invalid Token type: %s" % type(jtok)) # Retrieve client keys for later use self.client_keys = [ JWK(**self._get_key(token.jose_header, KEY_USAGE_SIG)), JWK(**self._get_key(token.jose_header, KEY_USAGE_ENC)) ] # verify token and get payload token.verify(self.client_keys[KEY_USAGE_SIG]) claims = json_decode(token.payload) except Exception as e: logger.debug('Failed to validate message', exc_info=True) raise InvalidMessage('Failed to validate message: %s' % str(e)) check_kem_claims(claims, name) self.name = name self.payload = claims.get('value') self.msg_type = 'kem' return { 'type': self.msg_type, 'value': { 'kid': self.client_keys[KEY_USAGE_ENC].key_id, 'claims': claims } }
def verify(sjws, pub_pem): sjws = json_decode(sjws) pub_pem = json_decode(pub_pem.replace('\n', '\\n')) if pub_pem.startswith("-----BEGIN"): pub_key = JWK.from_pem(to_bytes_2and3(pub_pem)) else: pub_key = JWK(kty='oct', k=base64url_encode(pub_pem)) sig = JWS() sig.deserialize(sjws, pub_key) sys.stdout.write(base64url_decode(json_decode(sig.serialize())['payload']))
def verify(sjws, pub_pem): sjws = json_decode(sjws) pub_pem = json_decode(pub_pem.replace('\n', '\\n')) if pub_pem.startswith("-----BEGIN"): pub_key = JWK.from_pem(to_bytes_2and3(pub_pem)) else: pub_key = JWK(kty='oct', k=base64url_encode(pub_pem)) sig = JWS() sig.deserialize(sjws, pub_key) sys.stdout.write(base64url_decode(json_decode(sig.serialize())['payload']))
def resolve(self, next, root, info, **args): headers = info.context.headers if "Authorization" in headers: token = JWS() token.deserialize(headers["Authorization"].removeprefix("Bearer ")) byte_payload = token.objects.pop("payload") json_payload = byte_payload.decode("UTF-8") payload = json.loads(json_payload) info.context.token = payload return next(root, info, **args)
def parse(self, msg, name): """Parses the message. We check that the message is properly formatted. :param msg: a json-encoded value containing a JWS or JWE+JWS token :raises InvalidMessage: if the message cannot be parsed or validated :returns: A verified payload """ try: jtok = JWT(jwt=msg) except Exception as e: raise InvalidMessage('Failed to parse message: %s' % str(e)) try: token = jtok.token if isinstance(token, JWE): token.decrypt(self.kkstore.server_keys[KEY_USAGE_ENC]) # If an encrypted payload is received then there must be # a nested signed payload to verify the provenance. payload = token.payload.decode('utf-8') token = JWS() token.deserialize(payload) elif isinstance(token, JWS): pass else: raise TypeError("Invalid Token type: %s" % type(jtok)) # Retrieve client keys for later use self.client_keys = [ JWK(**self._get_key(token.jose_header, KEY_USAGE_SIG)), JWK(**self._get_key(token.jose_header, KEY_USAGE_ENC))] # verify token and get payload token.verify(self.client_keys[KEY_USAGE_SIG]) claims = json_decode(token.payload) except Exception as e: logger.debug('Failed to validate message', exc_info=True) raise InvalidMessage('Failed to validate message: %s' % str(e)) check_kem_claims(claims, name) self.name = name self.payload = claims.get('value') self.msg_type = 'kem' return {'type': self.msg_type, 'value': {'kid': self.client_keys[KEY_USAGE_ENC].key_id, 'claims': claims}}
def _validateSignature(self, cvs): if cvs.antecedent is None: # Special cased to bootstrap data structures cvs.ratchet(self) jws = JWS() jws.deserialize(self.serialize()) tprint = jws.jose_header["kid"] if (cvs.antecedent.pkt == tprint): key = keystore()[tprint] jws.verify(key) else: # XXX: This case is only revavent on the GodBlock # TODO: support cases where block isn't signed by preceding key # (key recovery, issuer tombstone) raise NotImplementedError("TODO") # pragma: no cover
def _validateSignature(self, cvs): if cvs.antecedent is None: # Special cased to bootstrap data structures cvs.ratchet(self) jws = JWS() jws.deserialize(self.serialize()) tprint = jws.jose_header["kid"] idchain = chainstore()[self.creator] if self.creator not in cvs._recent_thumbprints: raise ChainValidationError("No grants for creator: " + self.creator) creator_print = cvs._recent_thumbprints[self.creator] if idchain.isSameOrSubsequent(tprint, creator_print): key = keystore()[tprint] jws.verify(key) else: raise ChainValidationError("Out of date key.")
def verify_jwt(jwt, pub_key=None, allowed_algs=None, iat_skew=timedelta(), checks_optional=False, ignore_not_implemented=False): """ Verify a JSON Web Token. :param jwt: The JSON Web Token to verify. :type jwt: str or unicode :param pub_key: The public key to be used to verify the token. Note: if you pass ``None`` and **allowed_algs** contains ``none`` then the token's signature will not be verified. :type pub_key: `jwcrypto.jwk.JWK <https://jwcrypto.readthedocs.io/en/latest/jwk.html>`_ :param allowed_algs: Algorithms expected to be used to sign the token. The ``in`` operator is used to test membership. :type allowed_algs: list or NoneType (meaning an empty list) :param iat_skew: The amount of leeway to allow between the issuer's clock and the verifier's clock when verifiying that the token was generated in the past. Defaults to no leeway. :type iat_skew: datetime.timedelta :param checks_optional: If ``False``, then the token must contain the **typ** header property and the **iat**, **nbf** and **exp** claim properties. :type checks_optional: bool :param ignore_not_implemented: If ``False``, then the token must *not* contain the **jku**, **jwk**, **x5u**, **x5c** or **x5t** header properties. :type ignore_not_implemented: bool :rtype: tuple :returns: ``(header, claims)`` if the token was verified successfully. The token must pass the following tests: - Its header must contain a property **alg** with a value in **allowed_algs**. - Its signature must verify using **pub_key** (unless its algorithm is ``none`` and ``none`` is in **allowed_algs**). - If the corresponding property is present or **checks_optional** is ``False``: - Its header must contain a property **typ** with the value ``JWT``. - Its claims must contain a property **iat** which represents a date in the past (taking into account :obj:`iat_skew`). - Its claims must contain a property **nbf** which represents a date in the past. - Its claims must contain a property **exp** which represents a date in the future. :raises: If the token failed to verify. """ if allowed_algs is None: allowed_algs = [] if not isinstance(allowed_algs, list): # jwcrypto only supports list of allowed algorithms raise _JWTError('allowed_algs must be a list') header, claims, _ = jwt.split('.') parsed_header = json_decode(base64url_decode(header)) alg = parsed_header.get('alg') if alg is None: raise _JWTError('alg header not present') if alg not in allowed_algs: raise _JWTError('algorithm not allowed: ' + alg) if not ignore_not_implemented: for k in parsed_header: if k not in JWSHeaderRegistry: raise _JWTError('unknown header: ' + k) if not JWSHeaderRegistry[k].supported: raise _JWTError('header not implemented: ' + k) if pub_key: token = JWS() token.allowed_algs = allowed_algs token.deserialize(jwt, pub_key) elif 'none' not in allowed_algs: raise _JWTError('no key but none alg not allowed') parsed_claims = json_decode(base64url_decode(claims)) utcnow = datetime.utcnow() now = timegm(utcnow.utctimetuple()) typ = parsed_header.get('typ') if typ is None: if not checks_optional: raise _JWTError('typ header not present') elif typ != 'JWT': raise _JWTError('typ header is not JWT') iat = parsed_claims.get('iat') if iat is None: if not checks_optional: raise _JWTError('iat claim not present') elif iat > timegm((utcnow + iat_skew).utctimetuple()): raise _JWTError('issued in the future') nbf = parsed_claims.get('nbf') if nbf is None: if not checks_optional: raise _JWTError('nbf claim not present') elif nbf > now: raise _JWTError('not yet valid') exp = parsed_claims.get('exp') if exp is None: if not checks_optional: raise _JWTError('exp claim not present') elif exp <= now: raise _JWTError('expired') return parsed_header, parsed_claims
def unwrap_signature(message: str, signing_certificate: JWK) -> str: """Validate and strip a signature from a response from DCS""" jwstoken = JWS() jwstoken.deserialize(raw_jws=message, key=signing_certificate) return jwstoken.payload.decode("utf-8")
def verify_jwt(jwt, pub_key=None, allowed_algs=None, iat_skew=timedelta(), checks_optional=False, ignore_not_implemented=False): """ Verify a JSON Web Token. :param jwt: The JSON Web Token to verify. :type jwt: str or unicode :param pub_key: The public key to be used to verify the token. Note: if you pass ``None`` and **allowed_algs** contains ``none`` then the token's signature will not be verified. :type pub_key: `jwcrypto.jwk.JWK <https://jwcrypto.readthedocs.io/en/latest/jwk.html>`_ :param allowed_algs: Algorithms expected to be used to sign the token. The ``in`` operator is used to test membership. :type allowed_algs: list or NoneType (meaning an empty list) :param iat_skew: The amount of leeway to allow between the issuer's clock and the verifier's clock when verifiying that the token was generated in the past. Defaults to no leeway. :type iat_skew: datetime.timedelta :param checks_optional: If ``False``, then the token must contain the **typ** header property and the **iat**, **nbf** and **exp** claim properties. :type checks_optional: bool :param ignore_not_implemented: If ``False``, then the token must *not* contain the **jku**, **jwk**, **x5u**, **x5c** or **x5t** header properties. :type ignore_not_implemented: bool :rtype: tuple :returns: ``(header, claims)`` if the token was verified successfully. The token must pass the following tests: - Its header must contain a property **alg** with a value in **allowed_algs**. - Its signature must verify using **pub_key** (unless its algorithm is ``none`` and ``none`` is in **allowed_algs**). - If the corresponding property is present or **checks_optional** is ``False``: - Its header must contain a property **typ** with the value ``JWT``. - Its claims must contain a property **iat** which represents a date in the past (taking into account :obj:`iat_skew`). - Its claims must contain a property **nbf** which represents a date in the past. - Its claims must contain a property **exp** which represents a date in the future. :raises: If the token failed to verify. """ if allowed_algs is None: allowed_algs = [] if not isinstance(allowed_algs, list): # jwcrypto only supports list of allowed algorithms raise _JWTError('allowed_algs must be a list') header, claims, _ = jwt.split('.') parsed_header = json_decode(base64url_decode(header)) alg = parsed_header.get('alg') if alg is None: raise _JWTError('alg header not present') if alg not in allowed_algs: raise _JWTError('algorithm not allowed: ' + alg) if not ignore_not_implemented: for k in parsed_header: if k not in JWSHeaderRegistry: raise _JWTError('unknown header: ' + k) if not JWSHeaderRegistry[k].supported: raise _JWTError('header not implemented: ' + k) if pub_key: token = JWS() token.allowed_algs = allowed_algs token.deserialize(jwt, pub_key) elif 'none' not in allowed_algs: raise _JWTError('no key but none alg not allowed') parsed_claims = json_decode(base64url_decode(claims)) utcnow = datetime.utcnow() now = timegm(utcnow.utctimetuple()) typ = parsed_header.get('typ') if typ is None: if not checks_optional: raise _JWTError('typ header not present') elif typ != 'JWT': raise _JWTError('typ header is not JWT') iat = parsed_claims.get('iat') if iat is None: if not checks_optional: raise _JWTError('iat claim not present') elif iat > timegm((utcnow + iat_skew).utctimetuple()): raise _JWTError('issued in the future') nbf = parsed_claims.get('nbf') if nbf is None: if not checks_optional: raise _JWTError('nbf claim not present') elif nbf > now: raise _JWTError('not yet valid') exp = parsed_claims.get('exp') if exp is None: if not checks_optional: raise _JWTError('exp claim not present') elif exp <= now: raise _JWTError('expired') return parsed_header, parsed_claims