Пример #1
0
    def build(self, json_web_key=None, ensure_ascii=True):
        """
        Builds a DockerSchema1Manifest object, with optional signature.
        """
        payload = OrderedDict(self._base_payload)
        payload.update({
            DOCKER_SCHEMA1_HISTORY_KEY: self._history,
            DOCKER_SCHEMA1_FS_LAYERS_KEY: self._fs_layer_digests,
        })

        payload_str = json.dumps(payload, indent=3, ensure_ascii=ensure_ascii)
        if json_web_key is None:
            return DockerSchema1Manifest(
                Bytes.for_string_or_unicode(payload_str))

        payload_str = Bytes.for_string_or_unicode(payload_str).as_encoded_str()
        split_point = payload_str.rfind(b"\n}")

        protected_payload = {
            "formatTail":
            base64url_encode(payload_str[split_point:]).decode("ascii"),
            "formatLength":
            split_point,
            "time":
            datetime.utcnow().strftime(_ISO_DATETIME_FORMAT_ZULU),
        }
        protected = base64url_encode(
            json.dumps(protected_payload,
                       ensure_ascii=ensure_ascii).encode("utf-8"))
        logger.debug("Generated protected block: %s", protected)

        bytes_to_sign = b"%s.%s" % (protected, base64url_encode(payload_str))

        signer = SIGNER_ALGS[_JWS_SIGNING_ALGORITHM]
        signature = base64url_encode(
            signer.sign(bytes_to_sign, json_web_key.get_key()))
        logger.debug("Generated signature: %s", signature)

        public_members = set(json_web_key.public_members)
        public_key = {
            comp: value
            for comp, value in list(json_web_key.to_dict().items())
            if comp in public_members
        }

        signature_block = {
            DOCKER_SCHEMA1_HEADER_KEY: {
                "jwk": public_key,
                "alg": _JWS_SIGNING_ALGORITHM
            },
            DOCKER_SCHEMA1_SIGNATURE_KEY: signature.decode("ascii"),
            DOCKER_SCHEMA1_PROTECTED_KEY: protected.decode("ascii"),
        }

        logger.debug("Encoded signature block: %s",
                     json.dumps(signature_block))
        payload.update({DOCKER_SCHEMA1_SIGNATURES_KEY: [signature_block]})

        json_str = json.dumps(payload, indent=3, ensure_ascii=ensure_ascii)
        return DockerSchema1Manifest(Bytes.for_string_or_unicode(json_str))
    def encode(self, payload, signing_key):
        if not isinstance(payload, Mapping):
            raise TypeError('Expecting a mapping object, as only '
                            'JSON objects can be used as payloads.')

        token_segments = []

        signing_key = load_signing_key(signing_key, self.crypto_backend)

        # prepare header
        header = {'typ': self.token_type, 'alg': self.signing_algorithm}
        token_segments.append(base64url_encode(json_encode(header)))

        # prepare payload
        token_segments.append(base64url_encode(json_encode(payload)))

        # prepare signature
        signing_input = b'.'.join(token_segments)
        signer = self._get_signer(signing_key)
        signer.update(signing_input)
        signature = signer.finalize()
        raw_signature = der_to_raw_signature(signature, signing_key.curve)
        token_segments.append(base64url_encode(raw_signature))

        # combine the header, payload, and signature into a token and return it
        token = b'.'.join(token_segments)
        return token
Пример #3
0
    def _validate(self):
        """
        Reference: https://docs.docker.com/registry/spec/manifest-v2-1/#signed-manifests
        """
        if not self._signatures:
            return

        payload_str = self._payload
        for signature in self._signatures:
            protected = signature[DOCKER_SCHEMA1_PROTECTED_KEY]
            sig = signature[DOCKER_SCHEMA1_SIGNATURE_KEY]

            jwk = JsonWebKey.import_key(
                signature[DOCKER_SCHEMA1_HEADER_KEY]["jwk"])
            jws = JsonWebSignature(
                algorithms=[signature[DOCKER_SCHEMA1_HEADER_KEY]["alg"]])

            obj_to_verify = {
                DOCKER_SCHEMA1_PROTECTED_KEY: protected,
                DOCKER_SCHEMA1_SIGNATURE_KEY: sig,
                DOCKER_SCHEMA1_HEADER_KEY: {
                    "alg": signature[DOCKER_SCHEMA1_HEADER_KEY]["alg"]
                },
                "payload": base64url_encode(payload_str),
            }

            try:
                data = jws.deserialize_json(obj_to_verify,
                                            jwk.get_public_key())
            except (BadSignatureError, UnsupportedAlgorithmError):
                raise InvalidSchema1Signature()

            if not data:
                raise InvalidSchema1Signature()
Пример #4
0
    def encode(self, payload, signing_key=None):
        if not isinstance(payload, Mapping):
            raise TypeError('Expecting a mapping object, as only '
                            'JSON objects can be used as payloads.')

        if not signing_key:
            # create unsecured token
            header = {'typ': self.token_type, 'alg': 'none'}
            return self._create_signing_input(payload, header) + b'.'

        signing_key = load_signing_key(
            BitcoinPrivateKey(signing_key).to_pem(), self.crypto_backend)

        # prepare header
        header = {'typ': self.token_type, 'alg': self.signing_algorithm}

        # get token signing_input
        signing_input = self._create_signing_input(payload, header)

        # prepare signature
        signer = self._get_signer(signing_key)
        signer.update(signing_input)
        signature = signer.finalize()
        raw_signature = der_to_raw_signature(signature, signing_key.curve)

        # combine the header, payload, and signature into a token and return it
        return signing_input + b'.' + base64url_encode(raw_signature)
 def decode(cls, token):
     header, payload, raw_signature, signing_input = cls._unpack(token)
     token = { 
         "header": header,
         "payload": payload,
         "signature": base64url_encode(raw_signature)
     }
     return token
Пример #6
0
 def test_invalid_jku_in_token_header(self):
     uaa_config = uaa_configs.VALID['uaa']
     header = get_unverified_header(jwt_tokens.CORRECT_END_USER_TOKEN)
     header['jku'] = 'http://ana.ondemandh.com\\\\\\\\\\\\\\\\@' + uaa_config['uaadomain']
     token_parts = jwt_tokens.CORRECT_END_USER_TOKEN.split(".")
     token_parts[0] = base64url_encode(json.dumps(header).encode('utf-8')).decode()
     token = '.'.join(token_parts)
     with self.assertRaises(RuntimeError) as e:
         xssec.create_security_context(token, uaa_config)
     self.assertEqual("JKU of token is not trusted", str(e.exception),)
Пример #7
0
def forged_user_header(jwt_generator):
    """Return JWT token with a forged UID claim"""
    token = jwt_generator(uid='bozydar')

    # Decode token:
    header_bytes, payload_bytes, signature_bytes = [
        base64url_decode(_.encode('ascii')) for _ in token.split(".")]
    payload_dict = json.loads(payload_bytes.decode('ascii'))

    # Rewrite uid and invert token decode procedure.
    payload_dict['uid'] = 'fafok'
    payload_bytes = json.dumps(payload_dict).encode('utf-8')
    forged_token = '.'.join(
        base64url_encode(_).decode('ascii') for _ in (
            header_bytes, payload_bytes, signature_bytes)
        )

    header = {'Authorization': 'token={}'.format(forged_token)}
    return header
Пример #8
0
def calculate_at_hash(access_token, hash_alg):
	"""Helper method for calculating an access token
	hash, as described in http://openid.net/specs/openid-connect-core-1_0.html#CodeIDToken
	Its value is the base64url encoding of the left-most half of the hash of the octets
	of the ASCII representation of the access_token value, where the hash algorithm
	used is the hash algorithm used in the alg Header Parameter of the ID Token's JOSE
	Header. For instance, if the alg is RS256, hash the access_token value with SHA-256,
	then take the left-most 128 bits and base64url encode them. The at_hash value is a
	case sensitive string.
	Args:
		access_token (str): An access token string.
		hash_alg (callable): A callable returning a hash object, e.g. hashlib.sha256
	"""
	hash_digest = hash_alg(access_token.encode('utf-8')).digest()
	cut_at = int(len(hash_digest) / 2)
	truncated = hash_digest[:cut_at]
	from jwt.utils import base64url_encode
	at_hash = base64url_encode(truncated)
	return at_hash.decode('utf-8')
Пример #9
0
def calculate_at_hash(access_token, hash_alg):
    """Helper method for calculating an access token
	hash, as described in http://openid.net/specs/openid-connect-core-1_0.html#CodeIDToken
	Its value is the base64url encoding of the left-most half of the hash of the octets
	of the ASCII representation of the access_token value, where the hash algorithm
	used is the hash algorithm used in the alg Header Parameter of the ID Token's JOSE
	Header. For instance, if the alg is RS256, hash the access_token value with SHA-256,
	then take the left-most 128 bits and base64url encode them. The at_hash value is a
	case sensitive string.
	Args:
		access_token (str): An access token string.
		hash_alg (callable): A callable returning a hash object, e.g. hashlib.sha256
	"""
    hash_digest = hash_alg(access_token.encode('utf-8')).digest()
    cut_at = int(len(hash_digest) / 2)
    truncated = hash_digest[:cut_at]
    from jwt.utils import base64url_encode
    at_hash = base64url_encode(truncated)
    return at_hash.decode('utf-8')
Пример #10
0
def forged_user_header(jwt_generator):
    """Return JWT token with a forged UID claim"""
    token = jwt_generator(uid='bozydar')

    # Decode token:
    header_bytes, payload_bytes, signature_bytes = [
        base64url_decode(_.encode('ascii')) for _ in token.split(".")
    ]
    payload_dict = json.loads(payload_bytes.decode('ascii'))

    # Rewrite uid and invert token decode procedure.
    payload_dict['uid'] = 'fafok'
    payload_bytes = json.dumps(payload_dict).encode('utf-8')
    forged_token = '.'.join(
        base64url_encode(_).decode('ascii')
        for _ in (header_bytes, payload_bytes, signature_bytes))

    header = {'Authorization': 'token={}'.format(forged_token)}
    return header
Пример #11
0
    def _validate(self):
        if not self._signatures:
            return

        payload_str = self._payload
        for signature in self._signatures:
            bytes_to_verify = "{0}.{1}".format(signature["protected"],
                                               base64url_encode(payload_str))
            signer = SIGNER_ALGS[signature["header"]["alg"]]
            key = keyrep(signature["header"]["jwk"])
            gk = key.get_key()
            sig = base64url_decode(signature["signature"].encode("utf-8"))

            try:
                verified = signer.verify(bytes_to_verify, sig, gk)
            except BadSignature:
                raise InvalidSchema1Signature()

            if not verified:
                raise InvalidSchema1Signature()
Пример #12
0
    def _validate(self):
        if not self._signatures:
            return

        payload_str = self._payload
        for signature in self._signatures:
            bytes_to_verify = '{0}.{1}'.format(signature['protected'],
                                               base64url_encode(payload_str))
            signer = SIGNER_ALGS[signature['header']['alg']]
            key = keyrep(signature['header']['jwk'])
            gk = key.get_key()
            sig = base64url_decode(signature['signature'].encode('utf-8'))

            try:
                verified = signer.verify(bytes_to_verify, sig, gk)
            except BadSignature:
                raise InvalidSchema1Signature()

            if not verified:
                raise InvalidSchema1Signature()
    def test_login_fails_bad_signature(self, dex):

        # This token is not expired, but has a bad signature.
        id_token = self._get_id_token(dex, '*****@*****.**', 'password')

        header_bytes, payload_bytes, signature_bytes = [
            base64url_decode(_.encode('ascii')) for _ in id_token.split(".")
        ]
        payload_dict = json.loads(payload_bytes.decode('ascii'))

        # Change `email` and invert token decode procedure.
        forged_payload_dict = payload_dict.copy()
        forged_payload_dict['email'] = '*****@*****.**'
        forged_payload_bytes = json.dumps(forged_payload_dict).encode('utf-8')

        forged_token = '.'.join(
            base64url_encode(_).decode('ascii')
            for _ in (header_bytes, forged_payload_bytes, signature_bytes))

        r = requests.post(Url('/auth/login'), json={'token': forged_token})
        assert r.status_code == 401
        assert 'bad token' in r.json()['description']
Пример #14
0
    def build(self, json_web_key=None, ensure_ascii=True):
        """
        Builds a DockerSchema1Manifest object, with optional signature.

        NOTE: For backward compatibility, "JWS JSON Serialization" is used instead of "JWS Compact Serialization", since the latter **requires** that the
        "alg" headers be carried in the **protected** headers, which was never done before migrating to authlib (One shouldn't be using schema1 anyways)

        References:
            - https://tools.ietf.org/html/rfc7515#section-10.7
            - https://docs.docker.com/registry/spec/manifest-v2-1/#signed-manifests
        """
        payload = OrderedDict(self._base_payload)
        payload.update({
            DOCKER_SCHEMA1_HISTORY_KEY: self._history,
            DOCKER_SCHEMA1_FS_LAYERS_KEY: self._fs_layer_digests,
        })

        payload_str = json.dumps(payload, indent=3, ensure_ascii=ensure_ascii)
        if json_web_key is None:
            return DockerSchema1Manifest(
                Bytes.for_string_or_unicode(payload_str))

        payload_str = Bytes.for_string_or_unicode(payload_str).as_encoded_str()
        split_point = payload_str.rfind(b"\n}")

        protected_payload = {
            DOCKER_SCHEMA1_FORMAT_TAIL_KEY:
            base64url_encode(payload_str[split_point:]).decode("ascii"),
            DOCKER_SCHEMA1_FORMAT_LENGTH_KEY:
            split_point,
            "time":
            datetime.utcnow().strftime(_ISO_DATETIME_FORMAT_ZULU),
        }

        # Flattened JSON serialization header
        jws = JsonWebSignature(algorithms=[_JWS_SIGNING_ALGORITHM])
        headers = {
            "protected": protected_payload,
            "header": {
                "alg": _JWS_SIGNING_ALGORITHM
            },
        }

        signed = jws.serialize_json(headers, payload_str,
                                    json_web_key.get_private_key())
        protected = signed["protected"]
        signature = signed["signature"]
        logger.debug("Generated signature: %s", signature)
        logger.debug("Generated protected block: %s", protected)

        public_members = set(json_web_key.REQUIRED_JSON_FIELDS +
                             json_web_key.ALLOWED_PARAMS)
        public_key = {
            comp: value
            for comp, value in list(json_web_key.as_dict().items())
            if comp in public_members
        }
        public_key["kty"] = json_web_key.kty

        # Signed Docker schema 1 manifests require the kid to be in a specific format
        # https://docs.docker.com/registry/spec/auth/jwt/
        pub_key = json_web_key.get_public_key()
        # Take the DER encoded public key which the JWT token was signed against
        key_der = pub_key.public_bytes(
            encoding=Encoding.DER, format=PublicFormat.SubjectPublicKeyInfo)
        # Create a SHA256 hash out of it and truncate to 240bits
        hash256 = sha256()
        hash256.update(key_der)
        digest = hash256.digest()
        digest_first_240_bits = digest[:30]
        # Split the result into 12 base32 encoded groups with : as delimiter
        base32 = base64.b32encode(digest_first_240_bits).decode("ascii")
        kid = ""
        i = 0
        for i in range(0, int(len(base32) / 4) - 1):
            start = i * 4
            end = start + 4
            kid += base32[start:end] + ":"
        kid += base32[(i + 1) * 4:]  # Add the last group without the delimiter
        public_key["kid"] = kid

        signature_block = {
            DOCKER_SCHEMA1_HEADER_KEY: {
                "jwk": public_key,
                "alg": _JWS_SIGNING_ALGORITHM
            },
            DOCKER_SCHEMA1_SIGNATURE_KEY: signature,
            DOCKER_SCHEMA1_PROTECTED_KEY: protected,
        }

        logger.debug("Encoded signature block: %s",
                     json.dumps(signature_block))
        payload.update({DOCKER_SCHEMA1_SIGNATURES_KEY: [signature_block]})

        json_str = json.dumps(payload, indent=3, ensure_ascii=ensure_ascii)
        return DockerSchema1Manifest(Bytes.for_string_or_unicode(json_str))
Пример #15
0
    def build(self, json_web_key=None, ensure_ascii=True):
        """
        Builds a DockerSchema1Manifest object, with optional signature.

        NOTE: For backward compatibility, "JWS JSON Serialization" is used instead of "JWS Compact Serialization", since the latter **requires** that the
        "alg" headers be carried in the **protected** headers, which was never done before migrating to authlib (One shouldn't be using schema1 anyways)

        References:
            - https://tools.ietf.org/html/rfc7515#section-10.7
            - https://docs.docker.com/registry/spec/manifest-v2-1/#signed-manifests
        """
        payload = OrderedDict(self._base_payload)
        payload.update({
            DOCKER_SCHEMA1_HISTORY_KEY: self._history,
            DOCKER_SCHEMA1_FS_LAYERS_KEY: self._fs_layer_digests,
        })

        payload_str = json.dumps(payload, indent=3, ensure_ascii=ensure_ascii)
        if json_web_key is None:
            return DockerSchema1Manifest(
                Bytes.for_string_or_unicode(payload_str))

        payload_str = Bytes.for_string_or_unicode(payload_str).as_encoded_str()
        split_point = payload_str.rfind(b"\n}")

        protected_payload = {
            DOCKER_SCHEMA1_FORMAT_TAIL_KEY:
            base64url_encode(payload_str[split_point:]).decode("ascii"),
            DOCKER_SCHEMA1_FORMAT_LENGTH_KEY:
            split_point,
            "time":
            datetime.utcnow().strftime(_ISO_DATETIME_FORMAT_ZULU),
        }

        # Flattened JSON serialization header
        jws = JsonWebSignature(algorithms=[_JWS_SIGNING_ALGORITHM])
        headers = {
            "protected": protected_payload,
            "header": {
                "alg": _JWS_SIGNING_ALGORITHM
            },
        }

        signed = jws.serialize_json(headers, payload_str,
                                    json_web_key.get_private_key())
        protected = signed["protected"]
        signature = signed["signature"]
        logger.debug("Generated signature: %s", signature)
        logger.debug("Generated protected block: %s", protected)

        public_members = set(json_web_key.REQUIRED_JSON_FIELDS +
                             json_web_key.ALLOWED_PARAMS)
        public_key = {
            comp: value
            for comp, value in list(json_web_key.as_dict().items())
            if comp in public_members
        }
        public_key["kty"] = json_web_key.kty

        signature_block = {
            DOCKER_SCHEMA1_HEADER_KEY: {
                "jwk": public_key,
                "alg": _JWS_SIGNING_ALGORITHM
            },
            DOCKER_SCHEMA1_SIGNATURE_KEY: signature,
            DOCKER_SCHEMA1_PROTECTED_KEY: protected,
        }

        logger.debug("Encoded signature block: %s",
                     json.dumps(signature_block))
        payload.update({DOCKER_SCHEMA1_SIGNATURES_KEY: [signature_block]})

        json_str = json.dumps(payload, indent=3, ensure_ascii=ensure_ascii)
        return DockerSchema1Manifest(Bytes.for_string_or_unicode(json_str))
 def decode(self, token):
     token_parts = self._unpack_token(token)
     header, payload, raw_signature, signing_input = token_parts
     token = {"header": header, "payload": payload, "signature": base64url_encode(raw_signature)}
     return token
Пример #17
0
 def generate_token(self) -> bytes:
     return base64url_encode(bytes(signer.sign(self.id), "utf-8"))
Пример #18
0
 def _create_signing_input(payload, header):
     return b'.'.join([
         base64url_encode(json_encode(header)),
         base64url_encode(json_encode(payload))
     ])