예제 #1
0
파일: s_client.c 프로젝트: 1310701102/sl4a
static int ocsp_resp_cb(SSL *s, void *arg)
	{
	const unsigned char *p;
	int len;
	OCSP_RESPONSE *rsp;
	len = SSL_get_tlsext_status_ocsp_resp(s, &p);
	BIO_puts(arg, "OCSP response: ");
	if (!p)
		{
		BIO_puts(arg, "no response sent\n");
		return 1;
		}
	rsp = d2i_OCSP_RESPONSE(NULL, &p, len);
	if (!rsp)
		{
		BIO_puts(arg, "response parse error\n");
		BIO_dump_indent(arg, (char *)p, len, 4);
		return 0;
		}
	BIO_puts(arg, "\n======================================\n");
	OCSP_RESPONSE_print(arg, rsp, 0);
	BIO_puts(arg, "======================================\n");
	OCSP_RESPONSE_free(rsp);
	return 1;
	}
예제 #2
0
//--------------------------------------------------
// Helper function to read OCSP_RESPONSE from binary input data
// ppResp - address of newly allocated OCSP_RESPONSE object
// pMBufInData - input data
// returns error code or ERR_OK
//--------------------------------------------------
int ddocOcspReadOcspResp(OCSP_RESPONSE** ppResp, DigiDocMemBuf* pMBufInData)
{
  int err = ERR_OK;
  unsigned char* p1;

  RETURN_IF_NULL_PARAM(ppResp);
  RETURN_IF_NULL_PARAM(pMBufInData);
  RETURN_IF_NULL_PARAM(pMBufInData->pMem);
  ddocDebug(4, "ddocOcspReadOcspResp", "converting: %d bytes to OCSP_RESPONSE", pMBufInData->nLen);
  p1 = (unsigned char*)pMBufInData->pMem;
  d2i_OCSP_RESPONSE(ppResp, (const unsigned char**)&p1, pMBufInData->nLen);
  ddocDebug(4, "ddocOcspReadOcspResp", "OCSP_RESPONSE: %s", (*ppResp ? "OK" : "ERR"));
  return err;
}
예제 #3
0
파일: ocsp.c 프로젝트: laggyluke/bud
int bud_client_staple_json(bud_client_t* client, JSON_Value* json) {
  JSON_Object* obj;
  const char* b64_body;
  size_t b64_body_len;
  char* body;
  const unsigned char* pbody;
  size_t body_len;
  OCSP_RESPONSE* resp;
  int status;
  int r;

  r = -1;
  body = NULL;

  obj = json_value_get_object(json);
  b64_body = json_object_get_string(obj, "response");
  if (b64_body == NULL)
    goto done;

  b64_body_len = strlen(b64_body);
  body_len = bud_base64_decoded_size_fast(b64_body_len);
  body = malloc(body_len);
  if (body == NULL)
    goto done;

  body_len = bud_base64_decode(body, body_len, b64_body, b64_body_len);
  pbody = (const unsigned char*) body;
  resp = d2i_OCSP_RESPONSE(NULL, &pbody, body_len);
  if (resp == NULL)
    goto done;

  /* Not successful response, do not waste bandwidth on it */
  status = OCSP_response_status(resp);
  OCSP_RESPONSE_free(resp);
  if (status != OCSP_RESPONSE_STATUS_SUCCESSFUL)
    goto done;

  /* Set stapling! */
  client->stapling_ocsp_resp = body;
  client->stapling_ocsp_resp_len = body_len;
  body = NULL;
  r = 0;

done:
  free(body);
  return r;
}
예제 #4
0
static VALUE
ossl_ocspres_initialize(int argc, VALUE *argv, VALUE self)
{
    VALUE arg;
    unsigned char *p;

    rb_scan_args(argc, argv, "01", &arg);
    if(!NIL_P(arg)) {
        arg = ossl_to_der_if_possible(arg);
        StringValue(arg);
        p = RSTRING_PTR(arg);
        if(!d2i_OCSP_RESPONSE((OCSP_RESPONSE**)&DATA_PTR(self), &p,
                              RSTRING_LEN(arg))) {
            ossl_raise(eOCSPError, "cannot load DER encoded response");
        }
    }

    return self;
}
예제 #5
0
static VALUE
ossl_ocspres_initialize(VALUE self, SEL sel, int argc, VALUE *argv)
{
    VALUE arg;
    const unsigned char *p;

    rb_scan_args(argc, argv, "01", &arg);
    if(!NIL_P(arg)){
	OCSP_RESPONSE *res = DATA_PTR(self), *x;
	arg = ossl_to_der_if_possible(arg);
	StringValue(arg);
	p = (unsigned char *)RSTRING_PTR(arg);
	x = d2i_OCSP_RESPONSE(&res, &p, RSTRING_LEN(arg));
	DATA_PTR(self) = res;
	if(!x){
	    ossl_raise(eOCSPError, "cannot load DER encoded response");
	}
    }

    return self;
}
예제 #6
0
int main(int argc, char *argv[]) {
	int sd, ocsp_status;
	const unsigned char *p;
	long len;
	OCSP_RESPONSE *rsp = NULL;
	OCSP_BASICRESP *br = NULL;
	X509_STORE     *st = NULL;
	STACK_OF(X509) *ch = NULL;
	char *host, *port;
#ifdef _PATH_SSL_CA_FILE
	char *cafile = _PATH_SSL_CA_FILE;
#else
	char *cafile = "/etc/ssl/cert.pem";
#endif

	SSL *ssl;
	SSL_CTX *ctx;

	SSL_library_init();
	SSL_load_error_strings();

	ctx = SSL_CTX_new(SSLv23_client_method());

	if (!SSL_CTX_load_verify_locations(ctx, cafile, NULL)) {
		printf("failed to load %s\n", cafile);
		exit(-1);
	}

	if (argc != 3)
		errx(-1, "need a host and port to connect to");
	else {
		host = argv[1];
		port = argv[2];
	}

	sd = tcp_connect(host, port);

	ssl = SSL_new(ctx);

	SSL_set_fd(ssl, (int) sd);
	SSL_set_tlsext_status_type(ssl, TLSEXT_STATUSTYPE_ocsp);

	if (SSL_connect(ssl) <= 0) {
		printf("SSL connect error\n");
		exit(-1);
	}

	if (SSL_get_verify_result(ssl) != X509_V_OK) {
		printf("Certificate doesn't verify from host %s port %s\n", host, port);
		exit(-1);
	}

	/* ==== VERIFY OCSP RESPONSE ==== */


	len = SSL_get_tlsext_status_ocsp_resp(ssl, &p);

	if (!p) {
		printf("No OCSP response received for %s port %s\n", host, port);
		exit(-1);
	}

	rsp = d2i_OCSP_RESPONSE(NULL, &p, len);
	if (!rsp) {
		puts("Invalid OCSP response");
		exit(-1);
	}

	ocsp_status = OCSP_response_status(rsp);
	if (ocsp_status != OCSP_RESPONSE_STATUS_SUCCESSFUL) {
		printf("Invalid OCSP response status: %s (%d)",
		       OCSP_response_status_str(ocsp_status), ocsp_status);
		exit(-1);
	}

	br = OCSP_response_get1_basic(rsp);
	if (!br) {
		puts("Invalid OCSP response");
		exit(-1);
	}

	ch = SSL_get_peer_cert_chain(ssl);
	st = SSL_CTX_get_cert_store(ctx);

	if (OCSP_basic_verify(br, ch, st, 0) <= 0) {
		puts("OCSP response verification failed");
		exit(-1);
	}

	printf("OCSP validated from %s %s\n", host, port);

	return 0;
}
static void
ngx_ssl_stapling_ocsp_handler(ngx_ssl_ocsp_ctx_t *ctx)
{
#if OPENSSL_VERSION_NUMBER >= 0x0090707fL
    const
#endif
    u_char                *p;
    int                    n;
    size_t                 len;
    time_t                 now, valid;
    ngx_str_t              response;
    X509_STORE            *store;
    STACK_OF(X509)        *chain;
    OCSP_CERTID           *id;
    OCSP_RESPONSE         *ocsp;
    OCSP_BASICRESP        *basic;
    ngx_ssl_stapling_t    *staple;
    ASN1_GENERALIZEDTIME  *thisupdate, *nextupdate;

    staple = ctx->data;
    now = ngx_time();
    ocsp = NULL;
    basic = NULL;
    id = NULL;

    if (ctx->code != 200) {
        goto error;
    }

    /* check the response */

    len = ctx->response->last - ctx->response->pos;
    p = ctx->response->pos;

    ocsp = d2i_OCSP_RESPONSE(NULL, &p, len);
    if (ocsp == NULL) {
        ngx_ssl_error(NGX_LOG_ERR, ctx->log, 0,
                      "d2i_OCSP_RESPONSE() failed");
        goto error;
    }

    n = OCSP_response_status(ocsp);

    if (n != OCSP_RESPONSE_STATUS_SUCCESSFUL) {
        ngx_log_error(NGX_LOG_ERR, ctx->log, 0,
                      "OCSP response not successful (%d: %s)",
                      n, OCSP_response_status_str(n));
        goto error;
    }

    basic = OCSP_response_get1_basic(ocsp);
    if (basic == NULL) {
        ngx_ssl_error(NGX_LOG_ERR, ctx->log, 0,
                      "OCSP_response_get1_basic() failed");
        goto error;
    }

    store = SSL_CTX_get_cert_store(staple->ssl_ctx);
    if (store == NULL) {
        ngx_ssl_error(NGX_LOG_CRIT, ctx->log, 0,
                      "SSL_CTX_get_cert_store() failed");
        goto error;
    }

#if OPENSSL_VERSION_NUMBER >= 0x10001000L
    SSL_CTX_get_extra_chain_certs(staple->ssl_ctx, &chain);
#else
    chain = staple->ssl_ctx->extra_certs;
#endif

    if (OCSP_basic_verify(basic, chain, store,
                          staple->verify ? OCSP_TRUSTOTHER : OCSP_NOVERIFY)
        != 1)
    {
        ngx_ssl_error(NGX_LOG_ERR, ctx->log, 0,
                      "OCSP_basic_verify() failed");
        goto error;
    }

    id = OCSP_cert_to_id(NULL, ctx->cert, ctx->issuer);
    if (id == NULL) {
        ngx_ssl_error(NGX_LOG_CRIT, ctx->log, 0,
                      "OCSP_cert_to_id() failed");
        goto error;
    }

    if (OCSP_resp_find_status(basic, id, &n, NULL, NULL,
                              &thisupdate, &nextupdate)
        != 1)
    {
        ngx_log_error(NGX_LOG_ERR, ctx->log, 0,
                      "certificate status not found in the OCSP response");
        goto error;
    }

    if (n != V_OCSP_CERTSTATUS_GOOD) {
        ngx_log_error(NGX_LOG_ERR, ctx->log, 0,
                      "certificate status \"%s\" in the OCSP response",
                      OCSP_cert_status_str(n));
        goto error;
    }

    if (OCSP_check_validity(thisupdate, nextupdate, 300, -1) != 1) {
        ngx_ssl_error(NGX_LOG_ERR, ctx->log, 0,
                      "OCSP_check_validity() failed");
        goto error;
    }

    if (nextupdate) {
        valid = ngx_ssl_stapling_time(nextupdate);
        if (valid == (time_t) NGX_ERROR) {
            ngx_log_error(NGX_LOG_ERR, ctx->log, 0,
                          "invalid nextUpdate time in certificate status");
            goto error;
        }

    } else {
        valid = NGX_MAX_TIME_T_VALUE;
    }

    OCSP_CERTID_free(id);
    OCSP_BASICRESP_free(basic);
    OCSP_RESPONSE_free(ocsp);

    id = NULL;
    basic = NULL;
    ocsp = NULL;

    /* copy the response to memory not in ctx->pool */

    response.len = len;
    response.data = ngx_alloc(response.len, ctx->log);

    if (response.data == NULL) {
        goto error;
    }

    ngx_memcpy(response.data, ctx->response->pos, response.len);

    ngx_log_debug2(NGX_LOG_DEBUG_EVENT, ctx->log, 0,
                   "ssl ocsp response, %s, %uz",
                   OCSP_cert_status_str(n), response.len);

    if (staple->staple.data) {
        ngx_free(staple->staple.data);
    }

    staple->staple = response;
    staple->valid = valid;

    /*
     * refresh before the response expires,
     * but not earlier than in 5 minutes, and at least in an hour
     */

    staple->loading = 0;
    staple->refresh = ngx_max(ngx_min(valid - 300, now + 3600), now + 300);

    ngx_ssl_ocsp_done(ctx);
    return;

error:

    staple->loading = 0;
    staple->refresh = now + 300;

    if (id) {
        OCSP_CERTID_free(id);
    }

    if (basic) {
        OCSP_BASICRESP_free(basic);
    }

    if (ocsp) {
        OCSP_RESPONSE_free(ocsp);
    }

    ngx_ssl_ocsp_done(ctx);
}
예제 #8
0
int OCSP_sendreq_nbio(OCSP_RESPONSE **presp, OCSP_REQ_CTX *rctx)
	{
	int i, n;
	const unsigned char *p;
	next_io:
	if (!(rctx->state & OHS_NOREAD))
		{
		n = BIO_read(rctx->io, rctx->iobuf, rctx->iobuflen);

		if (n <= 0)
			{
			if (BIO_should_retry(rctx->io))
				return -1;
			return 0;
			}

		/* Write data to memory BIO */

		if (BIO_write(rctx->mem, rctx->iobuf, n) != n)
			return 0;
		}

	switch(rctx->state)
		{

		case OHS_ASN1_WRITE:
		n = BIO_get_mem_data(rctx->mem, &p);

		i = BIO_write(rctx->io,
			p + (n - rctx->asn1_len), rctx->asn1_len);

		if (i <= 0)
			{
			if (BIO_should_retry(rctx->io))
				return -1;
			rctx->state = OHS_ERROR;
			return 0;
			}

		rctx->asn1_len -= i;

		if (rctx->asn1_len > 0)
			goto next_io;

		rctx->state = OHS_ASN1_FLUSH;

		(void)BIO_reset(rctx->mem);

		case OHS_ASN1_FLUSH:

		i = BIO_flush(rctx->io);

		if (i > 0)
			{
			rctx->state = OHS_FIRSTLINE;
			goto next_io;
			}

		if (BIO_should_retry(rctx->io))
			return -1;

		rctx->state = OHS_ERROR;
		return 0;

		case OHS_ERROR:
		return 0;

		case OHS_FIRSTLINE:
		case OHS_HEADERS:

		/* Attempt to read a line in */

		next_line:
		/* Due to &%^*$" memory BIO behaviour with BIO_gets we
		 * have to check there's a complete line in there before
		 * calling BIO_gets or we'll just get a partial read.
		 */
		n = BIO_get_mem_data(rctx->mem, &p);
		if ((n <= 0) || !TINYCLR_SSL_MEMCHR(p, '\n', n))
			{
			if (n >= rctx->iobuflen)
				{
				rctx->state = OHS_ERROR;
				return 0;
				}
			goto next_io;
			}
		n = BIO_gets(rctx->mem, (char *)rctx->iobuf, rctx->iobuflen);

		if (n <= 0)
			{
			if (BIO_should_retry(rctx->mem))
				goto next_io;
			rctx->state = OHS_ERROR;
			return 0;
			}

		/* Don't allow excessive lines */
		if (n == rctx->iobuflen)
			{
			rctx->state = OHS_ERROR;
			return 0;
			}

		/* First line */
		if (rctx->state == OHS_FIRSTLINE)
			{
			if (parse_http_line1((char *)rctx->iobuf))
				{
				rctx->state = OHS_HEADERS;
				goto next_line;
				}
			else
				{
				rctx->state = OHS_ERROR;
				return 0;
				}
			}
		else
			{
			/* Look for blank line: end of headers */
			for (p = rctx->iobuf; *p; p++)
				{
				if ((*p != '\r') && (*p != '\n'))
					break;
				}
			if (*p)
				goto next_line;

			rctx->state = OHS_ASN1_HEADER;

			}
 
		/* Fall thru */


		case OHS_ASN1_HEADER:
		/* Now reading ASN1 header: can read at least 6 bytes which
		 * is more than enough for any valid ASN1 SEQUENCE header
		 */
		n = BIO_get_mem_data(rctx->mem, &p);
		if (n < 6)
			goto next_io;

		/* Check it is an ASN1 SEQUENCE */
		if (*p++ != (V_ASN1_SEQUENCE|V_ASN1_CONSTRUCTED))
			{
			rctx->state = OHS_ERROR;
			return 0;
			}

		/* Check out length field */
		if (*p & 0x80)
			{
			n = *p & 0x7F;
			/* Not NDEF or excessive length */
			if (!n || (n > 4))
				{
				rctx->state = OHS_ERROR;
				return 0;
				}
			p++;
			rctx->asn1_len = 0;
			for (i = 0; i < n; i++)
				{
				rctx->asn1_len <<= 8;
				rctx->asn1_len |= *p++;
				}

			if (rctx->asn1_len > OCSP_MAX_REQUEST_LENGTH)
				{
				rctx->state = OHS_ERROR;
				return 0;
				}

			rctx->asn1_len += n + 2;
			}
		else
			rctx->asn1_len = *p + 2;

		rctx->state = OHS_ASN1_CONTENT;

		/* Fall thru */
		
		case OHS_ASN1_CONTENT:
		n = BIO_get_mem_data(rctx->mem, &p);
		if (n < (int)rctx->asn1_len)
			goto next_io;


		*presp = d2i_OCSP_RESPONSE(NULL, &p, rctx->asn1_len);
		if (*presp)
			{
			rctx->state = OHS_DONE;
			return 1;
			}

		rctx->state = OHS_ERROR;
		return 0;

		break;

		case OHS_DONE:
		return 1;

		}



	return 0;


	}
예제 #9
0
s2n_cert_validation_code s2n_x509_validator_validate_cert_stapled_ocsp_response(struct s2n_x509_validator *validator,
                                                                                struct s2n_connection *conn,
                                                                                const uint8_t *ocsp_response_raw,
                                                                                uint32_t ocsp_response_length) {

    if (validator->skip_cert_validation || !validator->check_stapled_ocsp) {
        return S2N_CERT_OK;
    }

#if !S2N_OCSP_STAPLING_SUPPORTED
    /* Default to safety */
    return S2N_CERT_ERR_UNTRUSTED;
#else

    OCSP_RESPONSE *ocsp_response = NULL;
    OCSP_BASICRESP *basic_response = NULL;

    s2n_cert_validation_code ret_val = S2N_CERT_ERR_INVALID;

    if (!ocsp_response_raw) {
        return ret_val;
    }

    ocsp_response = d2i_OCSP_RESPONSE(NULL, &ocsp_response_raw, ocsp_response_length);

    if (!ocsp_response) {
        goto clean_up;
    }

    int ocsp_status = OCSP_response_status(ocsp_response);

    if (ocsp_status != OCSP_RESPONSE_STATUS_SUCCESSFUL) {
        goto clean_up;
    }

    basic_response = OCSP_response_get1_basic(ocsp_response);
    if (!basic_response) {
        goto clean_up;
    }

    int i;

    int certs_in_chain = sk_X509_num(validator->cert_chain);
    int certs_in_ocsp = sk_X509_num(OCSP_GET_CERTS(basic_response));

    if (certs_in_chain >= 2 && certs_in_ocsp >= 1) {
        X509 *responder = sk_X509_value(OCSP_GET_CERTS(basic_response), certs_in_ocsp - 1);

        /*check to see if one of the certs in the chain is an issuer of the cert in the ocsp response.*/
        /*if so it needs to be added to the OCSP verification chain.*/
        for (i = 0; i < certs_in_chain; i++) {
            X509 *issuer = sk_X509_value(validator->cert_chain, i);
            int issuer_value = X509_check_issued(issuer, responder);

            if (issuer_value == X509_V_OK) {
                if (!OCSP_basic_add1_cert(basic_response, issuer)) {
                    goto clean_up;
                }
            }
        }
    }

    int ocsp_verify_err = OCSP_basic_verify(basic_response, validator->cert_chain, validator->trust_store->trust_store, 0);
    /* do the crypto checks on the response.*/
    if (!ocsp_verify_err) {
        ret_val = S2N_CERT_ERR_EXPIRED;
        goto clean_up;
    }

    /* for each response check the timestamps and the status. */
    for (i = 0; i < OCSP_resp_count(basic_response); i++) {
        int status_reason;
        ASN1_GENERALIZEDTIME *revtime, *thisupd, *nextupd;

        OCSP_SINGLERESP *single_response = OCSP_resp_get0(basic_response, i);
        if (!single_response) {
            goto clean_up;
        }

        ocsp_status = OCSP_single_get0_status(single_response, &status_reason, &revtime,
                                              &thisupd, &nextupd);

        uint64_t this_update = 0;
        int thisupd_err = s2n_asn1_time_to_nano_since_epoch_ticks((const char *) thisupd->data,
                                                                  (uint32_t) thisupd->length, &this_update);

        uint64_t next_update = 0;
        int nextupd_err = s2n_asn1_time_to_nano_since_epoch_ticks((const char *) nextupd->data,
                                                                  (uint32_t) nextupd->length, &next_update);

        uint64_t current_time = 0;
        int current_time_err = conn->config->wall_clock(conn->config->sys_clock_ctx, &current_time);

        if (thisupd_err || nextupd_err || current_time_err) {
            ret_val = S2N_CERT_ERR_UNTRUSTED;
            goto clean_up;
        }

        if (current_time < this_update || current_time > next_update) {
            ret_val = S2N_CERT_ERR_EXPIRED;
            goto clean_up;
        }

        switch (ocsp_status) {
            case V_OCSP_CERTSTATUS_GOOD:
                break;

            case V_OCSP_CERTSTATUS_REVOKED:
                ret_val = S2N_CERT_ERR_REVOKED;
                goto clean_up;

            case V_OCSP_CERTSTATUS_UNKNOWN:
                goto clean_up;
            default:
                goto clean_up;
        }
    }

    ret_val = S2N_CERT_OK;

    clean_up:
    if (basic_response) {
        OCSP_BASICRESP_free(basic_response);
    }

    if (ocsp_response) {
        OCSP_RESPONSE_free(ocsp_response);
    }

    return ret_val;
#endif /* S2N_OCSP_STAPLING_SUPPORTED */
}
void OCSPResponseVerifier_impl::ProcessL()
{
	if (!m_request || !m_response_der || m_response_length == 0 ||
	    !m_certificate_chain || !m_ca_storage)
			LEAVE(OpStatus::ERR_OUT_OF_RANGE);

	// Default value. Will be checked in the end of the function.
	OP_STATUS status = OpStatus::ERR;

	// Default verify status.
	m_verify_status  = CryptoCertificateChain::VERIFY_STATUS_UNKNOWN;

	OCSP_RESPONSE*  ocsp_response  = 0;
	OCSP_BASICRESP* ocsp_basicresp = 0;

	do
	{
		const OCSPRequest_impl* request_impl =
			static_cast <const OCSPRequest_impl*> (m_request);
		const CryptoCertificateChain_impl* chain_impl =
			static_cast <const CryptoCertificateChain_impl*> (m_certificate_chain);
		const CryptoCertificateStorage_impl* storage_impl =
			static_cast <const CryptoCertificateStorage_impl*> (m_ca_storage);

		if (!request_impl || !chain_impl || !storage_impl)
		{
			status = OpStatus::ERR_OUT_OF_RANGE;
			break;
		}

		OCSP_REQUEST*   ocsp_request = request_impl->GetOCSP_REQUEST();
		STACK_OF(X509)* x509_chain   = chain_impl->GetStackOfX509();
		X509_STORE*     x509_store   = storage_impl->GetX509Store();
		OP_ASSERT(ocsp_request && x509_chain && x509_store);
		// Both ocsp_request, x509_chain and x509_store are owned
		// by their container objects.

		// Temporary variable, according to the documentation.
		const unsigned char* response_tmp = m_response_der;
		ocsp_response = d2i_OCSP_RESPONSE(0, &response_tmp, m_response_length);
		OPENSSL_VERIFY_OR_BREAK2(ocsp_response, status);

		// Check that the OCSP responder was able to respond correctly.
		int ocsp_response_status = OCSP_response_status(ocsp_response);
		OPENSSL_BREAK2_IF(ocsp_response_status != OCSP_RESPONSE_STATUS_SUCCESSFUL, status);

		ocsp_basicresp = OCSP_response_get1_basic(ocsp_response);
		OPENSSL_VERIFY_OR_BREAK2(ocsp_basicresp, status);

		// Verify that the response is correctly signed.
		int response_valid_status =
			OCSP_basic_verify(ocsp_basicresp, x509_chain, x509_store, /* flags = */ 0);
		OPENSSL_BREAK2_IF(response_valid_status != 1, status);

		OP_ASSERT(ocsp_request->tbsRequest && ocsp_request->tbsRequest->requestList);
		OCSP_ONEREQ* ocsp_onereq =
			sk_OCSP_ONEREQ_value(ocsp_request->tbsRequest->requestList, 0);
		OPENSSL_VERIFY_OR_BREAK(ocsp_onereq);
		// ocsp_request owns ocsp_onereq.

		OCSP_CERTID* ocsp_certid = ocsp_onereq->reqCert;
		OPENSSL_VERIFY_OR_BREAK(ocsp_certid);
		// ocsp_request owns ocsp_certid.

		int revocation_code = -1, response_code = -1;
		ASN1_GENERALIZEDTIME* thisupd = 0;
		ASN1_GENERALIZEDTIME* nextupd = 0;
		int err = OCSP_resp_find_status(
			ocsp_basicresp, ocsp_certid, &response_code, &revocation_code,
			0, &thisupd, &nextupd);
		OPENSSL_BREAK2_IF(err != 1 || response_code < 0, status);

		// Allow some difference in client and server clocks.
		const long int CLOCK_SHARPNESS = 3600;
		// Default age limit for responses without nextUpdate field.
		const long int DEFAULT_MAXAGE  = 100 * 24 * 3600;

		err = OCSP_check_validity(thisupd, nextupd, CLOCK_SHARPNESS, DEFAULT_MAXAGE);
		OPENSSL_BREAK2_IF(err != 1, status);

		switch (response_code)
		{
			case V_OCSP_CERTSTATUS_GOOD:
				m_verify_status = CryptoCertificateChain::OK_CHECKED_WITH_OCSP;
				status = OpStatus::OK;
				break;

			case V_OCSP_CERTSTATUS_REVOKED:
				m_verify_status = CryptoCertificateChain::CERTIFICATE_REVOKED;
				status = OpStatus::OK;
				break;

			default:
				OP_ASSERT(!"Unexpected OCSP response code!");
				// fall-through

			case V_OCSP_CERTSTATUS_UNKNOWN:
				OP_ASSERT(m_verify_status == CryptoCertificateChain::VERIFY_STATUS_UNKNOWN);
				break;
		}

	} while(0);


	if(ocsp_basicresp)
		OCSP_BASICRESP_free(ocsp_basicresp);

	if(ocsp_response)
		OCSP_RESPONSE_free(ocsp_response);

	// There shouldn't be any errors.
	OP_ASSERT(ERR_peek_error() == 0);

	LEAVE_IF_ERROR(status);
}