def deserialize_compact(self, s, key, decode=None): """Exact JWS Compact Serialization, and validate with the given key. :param s: text of JWS Compact Serialization :param key: key used to verify the signature :param decode: a function to decode plaintext data :return: dict """ try: s = to_bytes(s) protected_s, ek_s, iv_s, ciphertext_s, tag_s = s.rsplit(b'.') except ValueError: raise DecodeError('Not enough segments') protected = extract_header(protected_s, DecodeError) ek = extract_segment(ek_s, DecodeError, 'encryption key') iv = extract_segment(iv_s, DecodeError, 'initialization vector') ciphertext = extract_segment(ciphertext_s, DecodeError, 'ciphertext') tag = extract_segment(tag_s, DecodeError, 'authentication tag') self._pre_validate_header(protected) algorithm, enc_alg, key = self._prepare_alg_enc_key(protected, key, private=True) self._post_validate_header(protected, algorithm) cek = algorithm.unwrap(ek, protected, key) aad = to_bytes(protected_s, 'ascii') msg = enc_alg.decrypt(ciphertext, aad, iv, tag, cek) payload = self._zip_decompress(msg, protected) if decode: payload = decode(payload) return {'header': protected, 'payload': payload}
def _get_header(self) -> dict: """ Returns the header of the JSON Web Token :rtype dict :return: token header """ token_header = self._token_string.split('.')[0].encode() try: return extract_header(token_header, DecodeError) except DecodeError: auth_error_token_decode()
def _get_ms_jwk(self, token): try: jwks = requests.get(self.key_url).json() token_header = token.split(".")[0].encode() unverified_header = extract_header(token_header, jwt_errors.DecodeError) for key in jwks["keys"]: if key["kid"] == unverified_header["kid"]: logger.info(f'Identified key {key["kid"]}') return jwk.loads(key) except jwt_errors.DecodeError: logger.exception('Error parsing signing keys') raise AuthenticationError("Unable to parse signing keys")
def _get_ms_jwk(self, token: str) -> JsonWebKey: try: self.logger.info(f'Getting signing keys from {self.key_url}') token_header = token.split(".")[0].encode() jwks = self._request_ms_jwks() unverified_header = extract_header(token_header, jwt_errors.DecodeError) # Authlib 1.0.0 changed behaviour of loads in 1.0.0 if version.parse(authlib_version) >= version.parse('1.0.0'): # This can now raise a ValueError: Invalid JSON Web Key Set if # the key is not found return jwk.loads(jwks, unverified_header['kid']) else: for key in jwks["keys"]: if key["kid"] == unverified_header["kid"]: self.logger.info(f'Identified key {key["kid"]}') return jwk.loads(key) except jwt_errors.DecodeError: self.logger.exception('Error parsing signing keys') except ValueError: self.logger.exception('Error finding key') raise AuthenticationError("Unable to parse signing keys")
def _extract_header(header_segment): return extract_header(header_segment, DecodeError)
def deserialize_json(self, obj, key, decode=None, sender_key=None): """Extract JWE JSON Serialization. :param obj: JWE JSON Serialization as dict or str :param key: Private key used to decrypt payload (optionally can be a tuple of kid and essentially key) :param decode: Function to decode payload data :param sender_key: Sender's public key in case JWEAlgorithmWithTagAwareKeyAgreement is used :return: dict with `header` and `payload` keys where `header` value is a dict containing `protected`, `unprotected`, `recipients` and/or `aad` keys """ obj = ensure_dict(obj, 'JWE') obj = deepcopy(obj) if 'protected' in obj: protected = extract_header(to_bytes(obj['protected']), DecodeError) else: protected = None unprotected = obj.get('unprotected') recipients = obj['recipients'] for recipient in recipients: if 'header' not in recipient: recipient['header'] = {} recipient['encrypted_key'] = extract_segment( to_bytes(recipient['encrypted_key']), DecodeError, 'encrypted key') if 'aad' in obj: jwe_aad = extract_segment(to_bytes(obj['aad']), DecodeError, 'JWE AAD') else: jwe_aad = None iv = extract_segment(to_bytes(obj['iv']), DecodeError, 'initialization vector') ciphertext = extract_segment(to_bytes(obj['ciphertext']), DecodeError, 'ciphertext') tag = extract_segment(to_bytes(obj['tag']), DecodeError, 'authentication tag') shared_header = JWESharedHeader(protected, unprotected) alg = self.get_header_alg(shared_header) enc = self.get_header_enc(shared_header) zip_alg = self.get_header_zip(shared_header) self._validate_sender_key(sender_key, alg) self._validate_private_headers(shared_header, alg) for recipient in recipients: self._validate_private_headers(recipient['header'], alg) kid = None if isinstance(key, tuple) and len(key) == 2: # Extract separately provided kid and essentially key kid = key[0] key = key[1] key = alg.prepare_key(key) if kid is None: # If kid has not been provided separately, try to get it from key itself kid = key.kid if sender_key is not None: sender_key = alg.prepare_key(sender_key) def _unwrap_with_sender_key_and_tag(ek, header): return alg.unwrap(enc, ek, header, key, sender_key, tag) def _unwrap_with_sender_key_and_without_tag(ek, header): return alg.unwrap(enc, ek, header, key, sender_key) def _unwrap_without_sender_key_and_tag(ek, header): return alg.unwrap(enc, ek, header, key) def _unwrap_for_matching_recipient(unwrap_func): if kid is not None: for recipient in recipients: if recipient['header'].get('kid') == kid: header = JWEHeader(protected, unprotected, recipient['header']) return unwrap_func(recipient['encrypted_key'], header) # Since no explicit match has been found, iterate over all the recipients error = None for recipient in recipients: header = JWEHeader(protected, unprotected, recipient['header']) try: return unwrap_func(recipient['encrypted_key'], header) except Exception as e: error = e else: if error is None: raise KeyMismatchError() else: raise error if isinstance(alg, JWEAlgorithmWithTagAwareKeyAgreement): # For a JWE algorithm with tag-aware key agreement: if alg.key_size is not None: # In case key agreement with key wrapping mode is used: # Provide authentication tag to .unwrap method cek = _unwrap_for_matching_recipient(_unwrap_with_sender_key_and_tag) else: # Otherwise, don't provide authentication tag to .unwrap method cek = _unwrap_for_matching_recipient(_unwrap_with_sender_key_and_without_tag) else: # For any other JWE algorithm: # Don't provide authentication tag to .unwrap method cek = _unwrap_for_matching_recipient(_unwrap_without_sender_key_and_tag) aad = to_bytes(obj.get('protected', '')) if 'aad' in obj: aad += b'.' + to_bytes(obj['aad']) aad = to_bytes(aad, 'ascii') msg = enc.decrypt(ciphertext, aad, iv, tag, cek) if zip_alg: payload = zip_alg.decompress(to_bytes(msg)) else: payload = msg if decode: payload = decode(payload) for recipient in recipients: if not recipient['header']: del recipient['header'] for member in set(recipient.keys()): if member != 'header': del recipient[member] header = {} if protected: header['protected'] = protected if unprotected: header['unprotected'] = unprotected header['recipients'] = recipients if jwe_aad is not None: header['aad'] = jwe_aad return { 'header': header, 'payload': payload }
def deserialize_compact(self, s, key, decode=None, sender_key=None): """Extract JWE Compact Serialization. :param s: JWE Compact Serialization as bytes :param key: Private key used to decrypt payload (optionally can be a tuple of kid and essentially key) :param decode: Function to decode payload data :param sender_key: Sender's public key in case JWEAlgorithmWithTagAwareKeyAgreement is used :return: dict with `header` and `payload` keys where `header` value is a dict containing protected header fields """ try: s = to_bytes(s) protected_s, ek_s, iv_s, ciphertext_s, tag_s = s.rsplit(b'.') except ValueError: raise DecodeError('Not enough segments') protected = extract_header(protected_s, DecodeError) ek = extract_segment(ek_s, DecodeError, 'encryption key') iv = extract_segment(iv_s, DecodeError, 'initialization vector') ciphertext = extract_segment(ciphertext_s, DecodeError, 'ciphertext') tag = extract_segment(tag_s, DecodeError, 'authentication tag') alg = self.get_header_alg(protected) enc = self.get_header_enc(protected) zip_alg = self.get_header_zip(protected) self._validate_sender_key(sender_key, alg) self._validate_private_headers(protected, alg) if isinstance(key, tuple) and len(key) == 2: # Ignore separately provided kid, extract essentially key only key = key[1] key = prepare_key(alg, protected, key) if sender_key is not None: sender_key = alg.prepare_key(sender_key) if isinstance(alg, JWEAlgorithmWithTagAwareKeyAgreement): # For a JWE algorithm with tag-aware key agreement: if alg.key_size is not None: # In case key agreement with key wrapping mode is used: # Provide authentication tag to .unwrap method cek = alg.unwrap(enc, ek, protected, key, sender_key, tag) else: # Otherwise, don't provide authentication tag to .unwrap method cek = alg.unwrap(enc, ek, protected, key, sender_key) else: # For any other JWE algorithm: # Don't provide authentication tag to .unwrap method cek = alg.unwrap(enc, ek, protected, key) aad = to_bytes(protected_s, 'ascii') msg = enc.decrypt(ciphertext, aad, iv, tag, cek) if zip_alg: payload = zip_alg.decompress(to_bytes(msg)) else: payload = msg if decode: payload = decode(payload) return {'header': protected, 'payload': payload}