Esempio n. 1
0
    def _validate_json_jws(self, payload_segment, payload, header_obj, key):
        protected_segment = header_obj.get('protected')
        if not protected_segment:
            raise DecodeError('Missing "protected" value')

        signature_segment = header_obj.get('signature')
        if not signature_segment:
            raise DecodeError('Missing "signature" value')

        protected_segment = to_bytes(protected_segment)
        protected = _extract_header(protected_segment)
        header = header_obj.get('header')
        if header and not isinstance(header, dict):
            raise DecodeError('Invalid "header" value')
        jws_header = JWSHeader(protected, header)

        self._validate_header(jws_header)

        algorithm, key = prepare_algorithm_key(self._algorithms, jws_header,
                                               payload, key)
        signing_input = b'.'.join([protected_segment, payload_segment])
        signature = _extract_signature(to_bytes(signature_segment))
        if algorithm.verify(signing_input, key, signature):
            return jws_header, True
        return jws_header, False
Esempio n. 2
0
def decode_payload(bytes_payload):
    try:
        payload = json.loads(to_unicode(bytes_payload))
    except ValueError:
        raise DecodeError('Invalid payload value')
    if not isinstance(payload, dict):
        raise DecodeError('Invalid payload type')
    return payload
Esempio n. 3
0
def _ensure_dict(s):
    if not isinstance(s, dict):
        try:
            s = json.loads(to_unicode(s))
        except (ValueError, TypeError):
            raise DecodeError('Invalid JWS')

    if not isinstance(s, dict):
        raise DecodeError('Invalid JWS')

    return s
Esempio n. 4
0
def ensure_dict(s, structure_name):
    if not isinstance(s, dict):
        try:
            s = json_loads(to_unicode(s))
        except (ValueError, TypeError):
            raise DecodeError('Invalid {}'.format(structure_name))

    if not isinstance(s, dict):
        raise DecodeError('Invalid {}'.format(structure_name))

    return s
Esempio n. 5
0
    def deserialize_compact(self, s, key, decode=None):
        """Exact JWS Compact Serialization, and validate with the given key.
        If key is not provided, the returned dict will contain the signature,
        and signing input values. Via `Section 7.1`_.

        :param s: text of JWS Compact Serialization
        :param key: key used to verify the signature
        :param decode: a function to decode payload data
        :return: JWSObject
        :raise: BadSignatureError

        .. _`Section 7.1`: https://tools.ietf.org/html/rfc7515#section-7.1
        """
        try:
            s = to_bytes(s)
            signing_input, signature_segment = s.rsplit(b'.', 1)
            protected_segment, payload_segment = signing_input.split(b'.', 1)
        except ValueError:
            raise DecodeError('Not enough segments')

        protected = _extract_header(protected_segment)
        jws_header = JWSHeader(protected, None)

        payload = _extract_payload(payload_segment)
        if decode:
            payload = decode(payload)

        signature = _extract_signature(signature_segment)
        rv = JWSObject(jws_header, payload, 'compact')
        algorithm, key = self._prepare_algorithm_key(jws_header, payload, key)
        if algorithm.verify(signing_input, signature, key):
            return rv
        raise BadSignatureError(rv)
Esempio n. 6
0
    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}
Esempio n. 7
0
    def decode(self, s, key, claims_cls=None,
               claims_options=None, claims_params=None):
        """Decode the JWS with the given key. This is similar with
        :meth:`verify`, except that it will raise BadSignatureError when
        signature doesn't match.

        :param s: text of JWT
        :param key: key used to verify the signature
        :param claims_cls: class to be used for JWT claims
        :param claims_options: `options` parameters for claims_cls
        :param claims_params: `params` parameters for claims_cls
        :return: claims_cls instance
        :raise: BadSignatureError
        """
        if claims_cls is None:
            claims_cls = JWTClaims

        key_func = create_key_func(key)

        s = to_bytes(s)
        dot_count = s.count(b'.')
        if dot_count == 2:
            data = self._jws.deserialize_compact(s, key_func, decode_payload)
        elif dot_count == 4:
            data = self._jwe.deserialize_compact(s, key_func, decode_payload)
        else:
            raise DecodeError('Invalid input segments length')
        return claims_cls(
            data['payload'], data['header'],
            options=claims_options,
            params=claims_params,
        )
Esempio n. 8
0
    def deserialize_json(self, obj, key, decode=None):
        """Exact JWS JSON Serialization, and validate with the given key.
        If key is not provided, it will return a dict without signature
        verification. Header will still be validated. Via `Section 7.2`_.

        :param obj: text of JWS JSON Serialization
        :param key: key used to verify the signature
        :param decode: a function to decode payload data
        :return: JWSObject
        :raise: BadSignatureError

        .. _`Section 7.2`: https://tools.ietf.org/html/rfc7515#section-7.2
        """
        obj = ensure_dict(obj, 'JWS')

        payload_segment = obj.get('payload')
        if not payload_segment:
            raise DecodeError('Missing "payload" value')

        payload_segment = to_bytes(payload_segment)
        payload = _extract_payload(payload_segment)
        if decode:
            payload = decode(payload)

        if 'signatures' not in obj:
            # flattened JSON JWS
            jws_header, valid = self._validate_json_jws(
                payload_segment, payload, obj, key)

            rv = JWSObject(jws_header, payload, 'flat')
            if valid:
                return rv
            raise BadSignatureError(rv)

        headers = []
        is_valid = True
        for header_obj in obj['signatures']:
            jws_header, valid = self._validate_json_jws(
                payload_segment, payload, header_obj, key)
            headers.append(jws_header)
            if not valid:
                is_valid = False

        rv = JWSObject(headers, payload, 'json')
        if is_valid:
            return rv
        raise BadSignatureError(rv)
Esempio n. 9
0
    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}