def is_valid_time(self, cert_id, ocsp_response):
        res = OCSPResponse.load(ocsp_response)

        if res['response_status'].native != 'successful':
            raise RevocationCheckError(msg="Invalid Status: {0}".format(
                res['response_status'].native),
                                       errno=ER_INVALID_OCSP_RESPONSE)

        basic_ocsp_response = res.basic_ocsp_response
        if basic_ocsp_response['certs'].native:
            logger.debug("Certificate is attached in Basic OCSP Response")
            ocsp_cert = basic_ocsp_response['certs'][0]
            logger.debug("Verifying the attached certificate is signed by "
                         "the issuer")
            logger.debug(
                "Valid Not After: %s",
                ocsp_cert['tbs_certificate']['validity']['not_after'].native)

            cur_time = datetime.now(timezone.utc)
            """
            Note:
            We purposefully do not verify certificate signature here.
            The OCSP Response is extracted from the OCSP Response Cache
            which is expected to have OCSP Responses with verified
            attached signature. Moreover this OCSP Response is eventually
            going to be processed by the driver before being consumed by
            the driver.
            This step ensures that the OCSP Response cache does not have
            any invalid entries.
            """
            cert_valid, debug_msg = self.check_cert_time_validity(
                cur_time, ocsp_cert)
            if not cert_valid:
                logger.debug(debug_msg)
                return False

        tbs_response_data = basic_ocsp_response['tbs_response_data']

        single_response = tbs_response_data['responses'][0]
        cert_status = single_response['cert_status'].name

        try:
            if cert_status == 'good':
                self._process_good_status(single_response, cert_id,
                                          ocsp_response)
        except Exception as ex:
            logger.debug("Failed to validate ocsp response %s", ex)
            return False

        return True
Exemplo n.º 2
0
    def process_ocsp_response(self, issuer, cert_id, ocsp_response):
        try:
            res = OCSPResponse.load(ocsp_response)
        except Exception:
            raise OperationalError(msg='Invalid OCSP Response',
                                   errno=ER_INVALID_OCSP_RESPONSE)
        if res['response_status'].native != 'successful':
            raise OperationalError(msg="Invalid Status: {0}".format(
                res['response_status'].native),
                                   errno=ER_INVALID_OCSP_RESPONSE)

        basic_ocsp_response = res.basic_ocsp_response
        if basic_ocsp_response['certs'].native:
            logger.debug("Certificate is attached in Basic OCSP Response")
            ocsp_cert = basic_ocsp_response['certs'][0]
            logger.debug("Verifying the attached certificate is signed by "
                         "the issuer")
            logger.debug(
                "Valid Not After: %s",
                ocsp_cert['tbs_certificate']['validity']['not_after'].native)
            self.verify_signature(ocsp_cert.hash_algo, ocsp_cert.signature,
                                  issuer, ocsp_cert['tbs_certificate'])
        else:
            logger.debug("Certificate is NOT attached in Basic OCSP Response. "
                         "Using issuer's certificate")
            ocsp_cert = issuer

        tbs_response_data = basic_ocsp_response['tbs_response_data']

        logger.debug("Verifying the OCSP response is signed by the issuer.")
        self.verify_signature(
            basic_ocsp_response['signature_algorithm'].hash_algo,
            basic_ocsp_response['signature'].native, ocsp_cert,
            tbs_response_data)

        single_response = tbs_response_data['responses'][0]
        cert_status = single_response['cert_status'].name
        if cert_status == 'good':
            self._process_good_status(single_response, cert_id, ocsp_response)
            SnowflakeOCSP.OCSP_CACHE.update_cache(self, cert_id, ocsp_response)
        elif cert_status == 'revoked':
            self._process_revoked_status(single_response, cert_id)
        elif cert_status == 'unknown':
            self._process_unknown_status(cert_id)
        else:
            raise OperationalError(
                msg="Unknown revocation status was returned. OCSP response "
                "may be malformed: {0}".format(cert_status),
                errno=ER_INVALID_OCSP_RESPONSE_CODE)
Exemplo n.º 3
0
def http_ocsp_response(ocsp_response: OCSPResponse,
                       max_age: int = 86400) -> dict:
    ocsp_response_bytes = ocsp_response.dump()

    # calculate sha1 digest of ocsp_response
    h = hashlib.sha1()
    h.update(ocsp_response_bytes)

    # create a http response
    response = {
        "statusCode": 200,
        "headers": {
            "Content-Type": "application/ocsp-response",
            "ETag": h.hexdigest(),
        },
        "body": base64.b64encode(ocsp_response_bytes).decode('utf8'),
        "isBase64Encoded": True,
    }

    # conform http response to RFC 5019
    if ocsp_response[
            'response_status'].native == ResponseStatus.successful.value:
        response_headers = response['headers']
        ocsp_response_data = ocsp_response.response_data
        if ocsp_response_data['produced_at']:
            ts = mktime(ocsp_response_data['produced_at'].native.timetuple())
            response_headers["Last-Modified"] = formatdate(timeval=ts,
                                                           localtime=False,
                                                           usegmt=True)

            for single_response in ocsp_response_data['responses']:
                if single_response['next_update']:
                    ts = mktime(
                        single_response['next_update'].native.timetuple())
                    response_headers["Expires"] = formatdate(timeval=ts,
                                                             localtime=False,
                                                             usegmt=True)
                    response_headers[
                        "Cache-Control"] = "max-age={},public,no-transform,must-revalidate".format(
                            max_age)
                    break

    return response
    def is_valid_time(self, cert_id, ocsp_response):
        res = OCSPResponse.load(ocsp_response)

        if res['response_status'].native != 'successful':
            raise RevocationCheckError(msg="Invalid Status: {0}".format(
                res['response_status'].native),
                                       errno=ER_INVALID_OCSP_RESPONSE)

        basic_ocsp_response = res.basic_ocsp_response
        tbs_response_data = basic_ocsp_response['tbs_response_data']

        single_response = tbs_response_data['responses'][0]
        cert_status = single_response['cert_status'].name

        try:
            if cert_status == 'good':
                self._process_good_status(single_response, cert_id,
                                          ocsp_response)
        except Exception as ex:
            logger.debug("Failed to validate ocsp response %s", ex)
            return False

        return True
    def process_ocsp_response(self, issuer, cert_id, ocsp_response):
        try:
            res = OCSPResponse.load(ocsp_response)
            if self.test_mode is not None:
                ocsp_load_failure = getenv("SF_TEST_OCSP_FORCE_BAD_OCSP_RESPONSE")
                if ocsp_load_failure is not None:
                    raise RevocationCheckError("Force fail")
        except Exception:
            raise RevocationCheckError(
                msg='Invalid OCSP Response',
                errno=ER_INVALID_OCSP_RESPONSE
            )

        if res['response_status'].native != 'successful':
            raise RevocationCheckError(
                msg="Invalid Status: {}".format(res['response_status'].native),
                errno=ER_INVALID_OCSP_RESPONSE)

        basic_ocsp_response = res.basic_ocsp_response
        if basic_ocsp_response['certs'].native:
            logger.debug("Certificate is attached in Basic OCSP Response")
            ocsp_cert = basic_ocsp_response['certs'][0]
            logger.debug("Verifying the attached certificate is signed by "
                         "the issuer")
            logger.debug(
                "Valid Not After: %s",
                 ocsp_cert['tbs_certificate']['validity']['not_after'].native)

            cur_time = datetime.now(timezone.utc)

            """
            Signature verification should happen before any kind of
            validation
            """
            self.verify_signature(
                ocsp_cert.hash_algo,
                ocsp_cert.signature,
                issuer,
                ocsp_cert['tbs_certificate'])

            cert_valid, debug_msg = self.check_cert_time_validity(cur_time, ocsp_cert)

            if not cert_valid:
                raise RevocationCheckError(
                    msg=debug_msg,
                    errno=ER_INVALID_OCSP_RESPONSE_CODE)

        else:
            logger.debug("Certificate is NOT attached in Basic OCSP Response. "
                         "Using issuer's certificate")
            ocsp_cert = issuer

        tbs_response_data = basic_ocsp_response['tbs_response_data']

        logger.debug("Verifying the OCSP response is signed by the issuer.")
        self.verify_signature(
            basic_ocsp_response['signature_algorithm'].hash_algo,
            basic_ocsp_response['signature'].native,
            ocsp_cert,
            tbs_response_data)

        single_response = tbs_response_data['responses'][0]
        cert_status = single_response['cert_status'].name
        if self.test_mode is not None:
            test_cert_status = getenv("SF_TEST_OCSP_CERT_STATUS")
            if test_cert_status == 'revoked':
                cert_status = 'revoked'
            elif test_cert_status == 'unknown':
                cert_status = 'unknown'
            elif test_cert_status == 'good':
                cert_status = 'good'

        try:
            if cert_status == 'good':
                self._process_good_status(single_response, cert_id, ocsp_response)
                SnowflakeOCSP.OCSP_CACHE.update_cache(self, cert_id, ocsp_response)
            elif cert_status == 'revoked':
                self._process_revoked_status(single_response, cert_id)
            elif cert_status == 'unknown':
                self._process_unknown_status(cert_id)
            else:
                debug_msg = "Unknown revocation status was returned." \
                            "OCSP response may be malformed: {}.".\
                    format(cert_status)
                raise RevocationCheckError(
                    msg=debug_msg,
                    errno=ER_INVALID_OCSP_RESPONSE_CODE
                )
        except RevocationCheckError as op_er:
            debug_msg = "{} Consider running curl -o ocsp.der {}".\
                format(op_er.msg,
                           self.debug_ocsp_failure_url)
            raise RevocationCheckError(msg=debug_msg, errno=op_er.errno)
    def process_ocsp_response(self, issuer, cert_id, ocsp_response):
        try:
            res = OCSPResponse.load(ocsp_response)
        except Exception:
            raise RevocationCheckError(msg='Invalid OCSP Response',
                                       errno=ER_INVALID_OCSP_RESPONSE)
        if res['response_status'].native != 'successful':
            raise RevocationCheckError(msg="Invalid Status: {0}".format(
                res['response_status'].native),
                                       errno=ER_INVALID_OCSP_RESPONSE)

        basic_ocsp_response = res.basic_ocsp_response
        if basic_ocsp_response['certs'].native:
            logger.debug("Certificate is attached in Basic OCSP Response")
            ocsp_cert = basic_ocsp_response['certs'][0]
            logger.debug("Verifying the attached certificate is signed by "
                         "the issuer")
            logger.debug(
                "Valid Not After: %s",
                ocsp_cert['tbs_certificate']['validity']['not_after'].native)

            cur_time = datetime.now(timezone.utc)

            if cur_time > ocsp_cert['tbs_certificate']['validity']['not_after'].native or \
                    cur_time < ocsp_cert['tbs_certificate']['validity']['not_before'].native:
                debug_msg = "Certificate attached to OCSP response is invalid. OCSP response "\
                            "current time - {0} certificate not before time - {1} certificate "\
                            "not after time - {2}. Consider running curl -o ocsp.der {3}".\
                    format(cur_time,
                           ocsp_cert['tbs_certificate']['validity']['not_before'].native,
                           ocsp_cert['tbs_certificate']['validity']['not_after'].native,
                           super(SnowflakeOCSPAsn1Crypto, self).debug_ocsp_failure_url)

                raise RevocationCheckError(msg=debug_msg,
                                           errno=ER_INVALID_OCSP_RESPONSE_CODE)

            self.verify_signature(ocsp_cert.hash_algo, ocsp_cert.signature,
                                  issuer, ocsp_cert['tbs_certificate'])
        else:
            logger.debug("Certificate is NOT attached in Basic OCSP Response. "
                         "Using issuer's certificate")
            ocsp_cert = issuer

        tbs_response_data = basic_ocsp_response['tbs_response_data']

        logger.debug("Verifying the OCSP response is signed by the issuer.")
        self.verify_signature(
            basic_ocsp_response['signature_algorithm'].hash_algo,
            basic_ocsp_response['signature'].native, ocsp_cert,
            tbs_response_data)

        single_response = tbs_response_data['responses'][0]
        cert_status = single_response['cert_status'].name
        try:
            if cert_status == 'good':
                self._process_good_status(single_response, cert_id,
                                          ocsp_response)
                SnowflakeOCSP.OCSP_CACHE.update_cache(self, cert_id,
                                                      ocsp_response)
            elif cert_status == 'revoked':
                self._process_revoked_status(single_response, cert_id)
            elif cert_status == 'unknown':
                self._process_unknown_status(cert_id)
            else:
                debug_msg = "Unknown revocation status was returned." \
                            "OCSP response may be malformed: {0}.".\
                    format(cert_status)
                raise RevocationCheckError(msg=debug_msg,
                                           errno=ER_INVALID_OCSP_RESPONSE_CODE)
        except RevocationCheckError as op_er:
            debug_msg = "{0} Consider running curl -o ocsp.der {1}".\
                format(op_er.msg,
                           self.debug_ocsp_failure_url)
            raise RevocationCheckError(msg=debug_msg, errno=op_er.errno)
Exemplo n.º 7
0
    def process_ocsp_response(self, issuer, cert_id, ocsp_response):
        try:
            res = OCSPResponse.load(ocsp_response)
            if self.test_mode is not None:
                ocsp_load_failure = getenv(
                    "SF_TEST_OCSP_FORCE_BAD_OCSP_RESPONSE")
                if ocsp_load_failure is not None:
                    raise RevocationCheckError(
                        "Force fail", errno=ER_OCSP_RESPONSE_LOAD_FAILURE)
        except Exception:
            raise RevocationCheckError(msg="Invalid OCSP Response",
                                       errno=ER_OCSP_RESPONSE_LOAD_FAILURE)

        if res["response_status"].native != "successful":
            raise RevocationCheckError(
                msg="Invalid Status: {}".format(res["response_status"].native),
                errno=ER_OCSP_RESPONSE_STATUS_UNSUCCESSFUL,
            )

        basic_ocsp_response = res.basic_ocsp_response
        if basic_ocsp_response["certs"].native:
            logger.debug("Certificate is attached in Basic OCSP Response")
            ocsp_cert = basic_ocsp_response["certs"][0]
            logger.debug("Verifying the attached certificate is signed by "
                         "the issuer")
            logger.debug(
                "Valid Not After: %s",
                ocsp_cert["tbs_certificate"]["validity"]["not_after"].native,
            )

            cur_time = datetime.now(timezone.utc)

            try:
                """
                Signature verification should happen before any kind of
                validation
                """
                self.verify_signature(
                    ocsp_cert.hash_algo,
                    ocsp_cert.signature,
                    issuer,
                    ocsp_cert["tbs_certificate"],
                )
            except RevocationCheckError as rce:
                raise RevocationCheckError(
                    msg=rce.msg, errno=ER_OCSP_RESPONSE_ATTACHED_CERT_INVALID)
            cert_valid, debug_msg = self.check_cert_time_validity(
                cur_time, ocsp_cert)

            if not cert_valid:
                raise RevocationCheckError(
                    msg=debug_msg,
                    errno=ER_OCSP_RESPONSE_ATTACHED_CERT_EXPIRED)

        else:
            logger.debug("Certificate is NOT attached in Basic OCSP Response. "
                         "Using issuer's certificate")
            ocsp_cert = issuer

        tbs_response_data = basic_ocsp_response["tbs_response_data"]

        logger.debug("Verifying the OCSP response is signed by the issuer.")
        try:
            self.verify_signature(
                basic_ocsp_response["signature_algorithm"].hash_algo,
                basic_ocsp_response["signature"].native,
                ocsp_cert,
                tbs_response_data,
            )
        except RevocationCheckError as rce:
            raise RevocationCheckError(
                msg=rce.msg, errno=ER_OCSP_RESPONSE_INVALID_SIGNATURE)

        single_response = tbs_response_data["responses"][0]
        cert_status = single_response["cert_status"].name
        if self.test_mode is not None:
            test_cert_status = getenv("SF_TEST_OCSP_CERT_STATUS")
            if test_cert_status == "revoked":
                cert_status = "revoked"
            elif test_cert_status == "unknown":
                cert_status = "unknown"
            elif test_cert_status == "good":
                cert_status = "good"

        try:
            if cert_status == "good":
                self._process_good_status(single_response, cert_id,
                                          ocsp_response)
                SnowflakeOCSP.OCSP_CACHE.update_cache(self, cert_id,
                                                      ocsp_response)
            elif cert_status == "revoked":
                self._process_revoked_status(single_response, cert_id)
            elif cert_status == "unknown":
                self._process_unknown_status(cert_id)
            else:
                debug_msg = (
                    "Unknown revocation status was returned."
                    "OCSP response may be malformed: {}.".format(cert_status))
                raise RevocationCheckError(
                    msg=debug_msg, errno=ER_OCSP_RESPONSE_CERT_STATUS_INVALID)
        except RevocationCheckError as op_er:
            debug_msg = "{} Consider running curl -o ocsp.der {}".format(
                op_er.msg, self.debug_ocsp_failure_url)
            raise RevocationCheckError(msg=debug_msg, errno=op_er.errno)