Example #1
0
def test_decode_jwt_invalid_key(private_key_pem):
    # Encode with the test private key.
    token = jwt.encode(_token_data("aud", "subject", "someissuer"), private_key_pem, "RS256")

    # Try to decode with a different public key.
    another_public_key = (
        rsa.generate_private_key(
            public_exponent=65537,
            key_size=2048,
        )
        .public_key()
        .public_bytes(
            encoding=serialization.Encoding.PEM,
            format=serialization.PublicFormat.SubjectPublicKeyInfo,
        )
    )
    with pytest.raises(InvalidTokenError) as ite:
        max_exp = exp_max_s_option(3600)
        decode(
            token,
            another_public_key,
            algorithms=["RS256"],
            audience="aud",
            issuer="someissuer",
            options=max_exp,
            leeway=60,
        )
    assert ite.match("Signature verification failed")
Example #2
0
def test_decode_jwt_validation(
    aud, iss, nbf, iat, exp, expected_exception, private_key_pem, public_key
):
    token = jwt.encode(_token_data(aud, "subject", iss, iat, exp, nbf), private_key_pem, "RS256")

    if expected_exception is not None:
        with pytest.raises(InvalidTokenError) as ite:
            max_exp = exp_max_s_option(3600)
            decode(
                token,
                public_key,
                algorithms=["RS256"],
                audience="someaudience",
                issuer="someissuer",
                options=max_exp,
                leeway=60,
            )
        assert ite.match(expected_exception)
    else:
        max_exp = exp_max_s_option(3600)
        decode(
            token,
            public_key,
            algorithms=["RS256"],
            audience="someaudience",
            issuer="someissuer",
            options=max_exp,
            leeway=60,
        )
Example #3
0
def test_decode_jwt_invalid_algorithm(private_key_pem, public_key):
  # Encode with the test private key.
  token = jwt.encode(_token_data('aud', 'subject', 'someissuer'), private_key_pem, 'RS256')

  # Attempt to decode but only with a different algorithm than that used.
  with pytest.raises(InvalidAlgorithmError) as ite:
    max_exp = exp_max_s_option(3600)
    decode(token, public_key, algorithms=['ES256'], audience='aud',
           issuer='someissuer', options=max_exp, leeway=60)
  assert ite.match('are not whitelisted')
Example #4
0
    def decode_user_jwt(self, token):
        """ Decodes the given JWT under the given provider and returns it. Raises an InvalidTokenError
        exception on an invalid token or a PublicKeyLoadException if the public key could not be
        loaded for decoding.
    """
        # Find the key to use.
        headers = jwt.get_unverified_header(token)
        kid = headers.get('kid', None)
        if kid is None:
            raise InvalidTokenError('Missing `kid` header')

        logger.debug(
            'Using key `%s`, attempting to decode token `%s` with aud `%s` and iss `%s`',
            kid, token, self.client_id(), self._issuer)
        try:
            return decode(token,
                          self._get_public_key(kid),
                          algorithms=ALLOWED_ALGORITHMS,
                          audience=self.client_id(),
                          issuer=self._issuer,
                          leeway=JWT_CLOCK_SKEW_SECONDS,
                          options=dict(require_nbf=False))
        except InvalidTokenError as ite:
            logger.warning(
                'Could not decode token `%s` for OIDC: %s. Will attempt again after '
                + 'retrieving public keys.', token, ite)

            # Public key may have expired. Try to retrieve an updated public key and use it to decode.
            try:
                return decode(token,
                              self._get_public_key(kid, force_refresh=True),
                              algorithms=ALLOWED_ALGORITHMS,
                              audience=self.client_id(),
                              issuer=self._issuer,
                              leeway=JWT_CLOCK_SKEW_SECONDS,
                              options=dict(require_nbf=False))
            except InvalidTokenError as ite:
                logger.warning(
                    'Could not decode token `%s` for OIDC: %s. Attempted again after '
                    + 'retrieving public keys.', token, ite)

                # Decode again with verify=False, and log the decoded token to allow for easier debugging.
                nonverified = decode(token,
                                     self._get_public_key(kid,
                                                          force_refresh=True),
                                     algorithms=ALLOWED_ALGORITHMS,
                                     audience=self.client_id(),
                                     issuer=self._issuer,
                                     leeway=JWT_CLOCK_SKEW_SECONDS,
                                     options=dict(require_nbf=False,
                                                  verify=False))
                logger.debug('Got an error when trying to verify OIDC JWT: %s',
                             nonverified)
                raise ite
Example #5
0
def test_decode_jwt_invalid_key(private_key_pem):
  # Encode with the test private key.
  token = jwt.encode(_token_data('aud', 'subject', 'someissuer'), private_key_pem, 'RS256')

  # Try to decode with a different public key.
  another_public_key = RSA.generate(2048).publickey().exportKey('PEM')
  with pytest.raises(InvalidTokenError) as ite:
    max_exp = exp_max_s_option(3600)
    decode(token, another_public_key, algorithms=['RS256'], audience='aud',
           issuer='someissuer', options=max_exp, leeway=60)
  assert ite.match('Signature verification failed')
Example #6
0
def _validate_jwt(encoded_jwt, jwk, service):
    public_key = jwtutil.jwk_dict_to_public_key(jwk)

    try:
        jwtutil.decode(encoded_jwt,
                       public_key,
                       algorithms=['RS256'],
                       audience=JWT_AUDIENCE,
                       issuer=service)
    except jwtutil.InvalidTokenError:
        logger.exception('JWT validation failure')
        abort(400)
Example #7
0
def test_jwk_dict_to_public_key(private_key, private_key_pem):
  public_key = private_key.publickey()
  jwk = RSAKey(key=private_key.publickey()).serialize()
  converted = jwk_dict_to_public_key(jwk)

  # Encode with the test private key.
  token = jwt.encode(_token_data('aud', 'subject', 'someissuer'), private_key_pem, 'RS256')

  # Decode with the converted key.
  max_exp = exp_max_s_option(3600)
  decode(token, converted, algorithms=['RS256'], audience='aud',
         issuer='someissuer', options=max_exp, leeway=60)
Example #8
0
    def _execute_call(self, url, aud, auth=None, params=None):
        """ Executes a call to the external JWT auth provider. """
        result = self.client.get(url, timeout=2, auth=auth, params=params)
        if result.status_code != 200:
            return (None, result.text or 'Could not make JWT auth call')

        try:
            result_data = json.loads(result.text)
        except ValueError:
            raise Exception(
                'Returned JWT body for url %s does not contain JSON', url)

        # Load the JWT returned.
        encoded = result_data.get('token', '')
        exp_limit_options = jwtutil.exp_max_s_option(self.max_fresh_s)
        try:
            payload = jwtutil.decode(encoded,
                                     self.public_key,
                                     algorithms=['RS256'],
                                     audience=aud,
                                     issuer=self.issuer,
                                     options=exp_limit_options)
            return (payload, None)
        except jwtutil.InvalidTokenError:
            logger.exception('Exception when decoding returned JWT for url %s',
                             url)
            return (None, 'Exception when decoding returned JWT')
Example #9
0
def test_decode_jwt_invalid_algorithm(private_key_pem, public_key):
    # Encode with the test private key.
    token = jwt.encode(_token_data("aud", "subject", "someissuer"), private_key_pem, "RS256")

    # Attempt to decode but only with a different algorithm than that used.
    with pytest.raises(InvalidAlgorithmError) as ite:
        max_exp = exp_max_s_option(3600)
        decode(
            token,
            public_key,
            algorithms=["ES256"],
            audience="aud",
            issuer="someissuer",
            options=max_exp,
            leeway=60,
        )
    assert ite.match("are not whitelisted")
Example #10
0
def verify_build_token(token, aud, token_type, instance_keys):
    """Verify the JWT build token."""
    try:
        headers = jwt.get_unverified_header(token)
    except jwtutil.InvalidTokenError as ite:
        logger.error("Invalid token reason: %s", ite)
        raise InvalidBuildTokenException(ite)

    kid = headers.get("kid", None)
    if kid is None:
        logger.error("Missing kid header on encoded JWT: %s", token)
        raise InvalidBuildTokenException("Missing kid header")

    public_key = instance_keys.get_service_key_public_key(kid)
    if public_key is None:
        logger.error(
            "Could not find requested service key %s with encoded JWT: %s",
            kid, token)
        raise InvalidBuildTokenException("Unknown service key")

    try:
        payload = jwtutil.decode(
            token,
            public_key,
            verify=True,
            algorithms=[ALGORITHM],
            audience=aud,
            issuer=instance_keys.service_name,
            leeway=JWT_CLOCK_SKEW_SECONDS,
        )
    except jwtutil.InvalidTokenError as ite:
        logger.error("Invalid token reason: %s", ite)
        raise InvalidBuildTokenException(ite)

    if "sub" not in payload:
        raise InvalidBuildTokenException("Missing sub field in JWT")

    if payload["sub"] != ANONYMOUS_SUB:
        raise InvalidBuildTokenException("Wrong sub field in JWT")

    if ("context" not in payload or not payload["context"]["token_type"]
            or not payload["context"]["build_id"]
            or not payload["context"]["job_id"]
            or not payload["context"]["expiration"]):
        raise InvalidBuildTokenException("Missing context field in JWT")

    try:
        jsonschema.validate(payload["context"], BUILD_TOKEN_CONTEXT_SCHEMA)
    except jsonschema.ValidationError:
        raise InvalidBuildTokenException(
            "Unable to validate build token context schema: malformed context")

    if payload["context"]["token_type"] != token_type:
        raise InvalidBuildTokenException(
            "Build token type in JWT does not match expected type: %s" %
            token_type)

    return payload
Example #11
0
def decode_bearer_token(bearer_token,
                        instance_keys,
                        config,
                        metric_queue=None):
    """ decode_bearer_token decodes the given bearer token that contains both a Key ID as well as the
      encoded JWT and returns the decoded and validated JWT. On any error, raises an
      InvalidBearerTokenException with the reason for failure.
  """
    # Decode the key ID.
    try:
        headers = jwt.get_unverified_header(bearer_token)
    except jwtutil.InvalidTokenError as ite:
        logger.exception("Invalid token reason: %s", ite)
        raise InvalidBearerTokenException(ite)

    kid = headers.get("kid", None)
    if kid is None:
        logger.error("Missing kid header on encoded JWT: %s", bearer_token)
        raise InvalidBearerTokenException("Missing kid header")

    # Find the matching public key.
    public_key = instance_keys.get_service_key_public_key(kid)
    if public_key is None:
        if metric_queue is not None:
            metric_queue.invalid_instance_key_count.Inc(labelvalues=[kid])

        logger.error(
            "Could not find requested service key %s with encoded JWT: %s",
            kid, bearer_token)
        raise InvalidBearerTokenException("Unknown service key")

    # Load the JWT returned.
    try:
        expected_issuer = instance_keys.service_name
        audience = config["SERVER_HOSTNAME"]
        max_signed_s = config.get("REGISTRY_JWT_AUTH_MAX_FRESH_S", 3660)
        max_exp = jwtutil.exp_max_s_option(max_signed_s)
        payload = jwtutil.decode(
            bearer_token,
            public_key,
            algorithms=[ALGORITHM],
            audience=audience,
            issuer=expected_issuer,
            options=max_exp,
            leeway=JWT_CLOCK_SKEW_SECONDS,
        )
    except jwtutil.InvalidTokenError as ite:
        logger.exception("Invalid token reason: %s", ite)
        raise InvalidBearerTokenException(ite)

    if not "sub" in payload:
        raise InvalidBearerTokenException("Missing sub field in JWT")

    return payload
Example #12
0
File: secscan.py Project: syed/quay
def secscan_notification():
    # If Quay is configured with a Clair V4 PSK we assume
    # Clair will also sign JWT's with this PSK. Therefore,
    # attempt jwt verification.
    key = app.config.get("SECURITY_SCANNER_V4_PSK", None)
    if key:
        key = base64.b64decode(key)
        jwt_header = request.headers.get(JWT_HEADER_NAME, "")
        match = TOKEN_REGEX.match(jwt_header)
        if match is None:
            logger.error("Could not find matching bearer token")
            abort(401)
        token = match.group(1)
        try:
            decode(token, key=key, algorithms=["HS256"])
        except jwt.exceptions.InvalidTokenError as e:
            logger.error("Could not verify jwt {}".format(e))
            abort(401)
        logger.debug("Successfully verified jwt")

    data = request.get_json()
    if data is None:
        logger.error("expected json request")
        abort(400)

    logger.debug("Got notification from V4 Security Scanner: %s", data)
    if "notification_id" not in data or "callback" not in data:
        abort(400)

    notification_id = data["notification_id"]
    name = ["with_id", notification_id]
    if not secscan_notification_queue.alive(name):
        secscan_notification_queue.put(
            name,
            json.dumps({"notification_id": notification_id}),
        )

    return make_response("Okay")
Example #13
0
def test_jwk_dict_to_public_key(private_key, private_key_pem):
    public_key = private_key.public_key()
    key_dict = jwk.dumps(
        public_key.public_bytes(
            encoding=serialization.Encoding.PEM,
            format=serialization.PublicFormat.SubjectPublicKeyInfo,
        )
    )
    converted = jwk_dict_to_public_key(key_dict)

    # Encode with the test private key.
    token = jwt.encode(_token_data("aud", "subject", "someissuer"), private_key_pem, "RS256")

    # Decode with the converted key.
    max_exp = exp_max_s_option(3600)
    decode(
        token,
        converted,
        algorithms=["RS256"],
        audience="aud",
        issuer="someissuer",
        options=max_exp,
        leeway=60,
    )