Files
cli/vendor/aws-lc-sys/aws-lc/crypto/ocsp/ocsp_test.cc

2012 lines
84 KiB
C++

// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0 OR ISC
#include <gtest/gtest.h>
#include <array>
#include <ctime>
#include "openssl/ocsp.h"
#include "openssl/pem.h"
#include "openssl/bio.h"
#include "../internal.h"
#include "../test/test_util.h"
#include "internal.h"
static const time_t invalid_before_ocsp_update_time = 1621988613;
static const time_t valid_after_ocsp_update_time = 1621988615;
static const time_t valid_before_ocsp_expire_time = 1937348613;
static const time_t invalid_after_ocsp_expire_time = 1937348615;
static const time_t invalid_before_ocsp_update_time_sha256 = 1622145762;
static const time_t valid_after_ocsp_update_time_sha256 = 1622145764;
static const time_t valid_before_ocsp_expire_time_sha256 = 1937505762;
static const time_t invalid_after_ocsp_expire_time_sha256 = 1937505764;
#define OCSP_VERIFYSTATUS_SUCCESS 1
#define OCSP_VERIFYSTATUS_ERROR 0
#define OCSP_VERIFYSTATUS_FATALERROR -1
#define OCSP_RESPFINDSTATUS_SUCCESS 1
#define OCSP_RESPFINDSTATUS_ERROR 0
#define OCSP_RESPFINDSTATUS_UNDEFINED -1
#define OCSP_REQUEST_PARSE_SUCCESS 1
#define OCSP_REQUEST_PARSE_ERROR 0
#define OCSP_HTTP_PARSE_SUCCESS 1
#define OCSP_HTTP_PARSE_ERROR 0
#define OCSP_SIGN_SUCCESS 1
#define OCSP_SIGN_ERROR 0
#define OCSP_URL_PARSE_SUCCESS 1
#define OCSP_URL_PARSE_ERROR 0
std::string GetTestData(const char *path);
static bool DecodeBase64(std::vector<uint8_t> *out, const char *in) {
int temp_length = 0;
size_t total_length = 0;
EVP_ENCODE_CTX ctx;
EVP_DecodeInit(&ctx);
out->resize(strlen(in));
int decode_update_result = EVP_DecodeUpdate(&ctx, out->data(), &temp_length,
(const uint8_t *)in, strlen(in));
if (decode_update_result != 1 && decode_update_result != 0) {
fprintf(stderr, "EVP_DecodeUpdate failed\n");
return false;
}
total_length += temp_length;
temp_length = 0;
if (1 != EVP_DecodeFinal(&ctx, &out->data()[total_length], &temp_length)) {
fprintf(stderr, "EVP_DecodeFinal failed\n");
return false;
}
total_length += temp_length;
out->resize(total_length);
return true;
}
static bssl::UniquePtr<EC_KEY> ECDSAFromPEM(const char *pem) {
bssl::UniquePtr<BIO> bio(BIO_new_mem_buf(pem, strlen(pem)));
return bssl::UniquePtr<EC_KEY>(
PEM_read_bio_ECPrivateKey(bio.get(), nullptr, nullptr, nullptr));
}
static bssl::UniquePtr<STACK_OF(X509)> CertChainFromPEM(const char *pem) {
bssl::UniquePtr<STACK_OF(X509)> stack(sk_X509_new_null());
if (!stack) {
return nullptr;
}
bssl::UniquePtr<BIO> bio(BIO_new_mem_buf(pem, strlen(pem)));
for (;;) {
bssl::UniquePtr<X509> cert = bssl::UniquePtr<X509>(
PEM_read_bio_X509(bio.get(), nullptr, nullptr, nullptr));
if (cert == nullptr) {
break;
}
if (!bssl::PushToStack(stack.get(), bssl::UpRef(cert.get()))) {
return nullptr;
}
}
return stack;
}
static bssl::UniquePtr<OCSP_RESPONSE> LoadOCSP_RESPONSE(
bssl::Span<const uint8_t> der) {
const uint8_t *ptr = der.data();
return bssl::UniquePtr<OCSP_RESPONSE>(
d2i_OCSP_RESPONSE(nullptr, &ptr, der.size()));
}
static bssl::UniquePtr<OCSP_REQUEST> LoadOCSP_REQUEST(
bssl::Span<const uint8_t> der) {
const uint8_t *ptr = der.data();
return bssl::UniquePtr<OCSP_REQUEST>(
d2i_OCSP_REQUEST(nullptr, &ptr, der.size()));
}
// Load a generic |OCSP_CERTID| for testing.
static OCSP_CERTID *LoadTestOCSP_CERTID() {
bssl::UniquePtr<X509> ca_cert(CertFromPEM(
GetTestData(std::string("crypto/ocsp/test/aws/ca_cert.pem").c_str())
.c_str()));
bssl::UniquePtr<X509> server_cert(CertFromPEM(
GetTestData(std::string("crypto/ocsp/test/aws/rsa_cert.pem").c_str())
.c_str()));
return OCSP_cert_to_id(nullptr, ca_cert.get(), server_cert.get());
}
static void ExtractAndVerifyBasicOCSP(
bssl::Span<const uint8_t> der, const std::string ca_cert_file,
const std::string server_cert_file, int expected_ocsp_verify_status,
bssl::UniquePtr<OCSP_BASICRESP> *basic_response,
bssl::UniquePtr<STACK_OF(X509)> *server_cert_chain) {
bssl::UniquePtr<OCSP_RESPONSE> ocsp_response;
ocsp_response = LoadOCSP_RESPONSE(der);
ASSERT_TRUE(ocsp_response);
int ret = OCSP_response_status(ocsp_response.get());
if (ret != OCSP_RESPONSE_STATUS_SUCCESSFUL) {
return;
}
*basic_response = bssl::UniquePtr<OCSP_BASICRESP>(
OCSP_response_get1_basic(ocsp_response.get()));
ASSERT_TRUE(*basic_response);
// Set up trust store and certificate chain.
bssl::UniquePtr<X509> ca_cert(CertFromPEM(
GetTestData(
std::string("crypto/ocsp/test/aws/" + ca_cert_file + ".pem").c_str())
.c_str()));
bssl::UniquePtr<X509> server_cert(
CertFromPEM(GetTestData(std::string("crypto/ocsp/test/aws/" +
server_cert_file + ".pem")
.c_str())
.c_str()));
bssl::UniquePtr<X509_STORE> trust_store(X509_STORE_new());
X509_STORE_add_cert(trust_store.get(), ca_cert.get());
*server_cert_chain = CertsToStack({server_cert.get(), ca_cert.get()});
ASSERT_TRUE(*server_cert_chain);
// Verifies the OCSP responder's signature on the OCSP response data.
const int ocsp_verify_err = OCSP_basic_verify(
basic_response->get(), server_cert_chain->get(), trust_store.get(), 0);
ASSERT_EQ(expected_ocsp_verify_status, ocsp_verify_err);
}
static void CheckOCSP_CERTSTATUS(
bssl::UniquePtr<OCSP_BASICRESP> *basic_response,
bssl::UniquePtr<STACK_OF(X509)> *server_cert_chain, const EVP_MD *dgst,
int expected_resp_find_status, int *status, ASN1_GENERALIZEDTIME **thisupd,
ASN1_GENERALIZEDTIME **nextupd) {
X509 *subject = sk_X509_value(server_cert_chain->get(), 0);
X509 *issuer = sk_X509_value(server_cert_chain->get(), 1);
// Convert issuer certificate to |OCSP_CERTID|.
bssl::UniquePtr<OCSP_CERTID> cert_id =
bssl::UniquePtr<OCSP_CERTID>(OCSP_cert_to_id(dgst, subject, issuer));
ASSERT_TRUE(cert_id);
int reason = 0;
ASN1_GENERALIZEDTIME *revtime;
// Checks revocation status of the response.
const int ocsp_resp_find_status_res =
OCSP_resp_find_status(basic_response->get(), cert_id.get(), status,
&reason, &revtime, thisupd, nextupd);
ASSERT_EQ(expected_resp_find_status, ocsp_resp_find_status_res);
}
// Test data below are taken from s2n's ocsp test files:
// https://github.com/aws/s2n-tls/blob/main/tests/pems/ocsp
// OCSP testing methods were taken from s2n's validation tests:
// https://github.com/aws/s2n-tls/blob/main/tests/unit/s2n_x509_validator_test.c
struct OCSPAWSTestVector {
const char *ocsp_response;
const char *cafile;
const char *server_cert;
const EVP_MD *dgst;
int expected_ocsp_verify_status;
int expected_ocsp_resp_find_status;
int expected_ocsp_cert_status;
const char *expected_ocsp_cert_status_string;
};
static const OCSPAWSTestVector nTestVectors[] = {
// === SHA1 OCSP RESPONSES ===
// Test valid OCSP response signed by an OCSP responder.
{"ocsp_response", "ca_cert", "rsa_cert", EVP_sha1(),
OCSP_VERIFYSTATUS_SUCCESS, OCSP_RESPFINDSTATUS_SUCCESS,
V_OCSP_CERTSTATUS_GOOD, "good"},
// Test against same good OCSP response, but checking behavior of not
// specifying hash algorithm used for |OCSP_cert_to_id| this time (should
// default to sha1). When |*dgst| is set to NULL, the default hash algorithm
// should automatically be set to sha1. The revocation status check of the
// response should work if hash algorithm of |cert_id| has been set to sha1
// successfully.
{"ocsp_response", "ca_cert", "rsa_cert", nullptr, OCSP_VERIFYSTATUS_SUCCESS,
OCSP_RESPFINDSTATUS_SUCCESS, V_OCSP_CERTSTATUS_GOOD, "good"},
// Test valid OCSP response directly signed by the CA certificate.
{"ocsp_response_ca_signed", "ca_cert", "rsa_cert", EVP_sha1(),
OCSP_VERIFYSTATUS_SUCCESS, OCSP_RESPFINDSTATUS_SUCCESS,
V_OCSP_CERTSTATUS_GOOD, "good"},
// Test OCSP response status is revoked.
{"ocsp_response_revoked", "ca_cert", "rsa_cert", EVP_sha1(),
OCSP_VERIFYSTATUS_SUCCESS, OCSP_RESPFINDSTATUS_SUCCESS,
V_OCSP_CERTSTATUS_REVOKED, "revoked"},
// Test OCSP response status is unknown.
{"ocsp_response_unknown", "ca_cert", "rsa_cert", EVP_sha1(),
OCSP_VERIFYSTATUS_SUCCESS, OCSP_RESPFINDSTATUS_SUCCESS,
V_OCSP_CERTSTATUS_UNKNOWN, "unknown"},
// for the requested certificate. (So this would be a completely valid
// response to a different OCSP request for the other certificate.)
{"ocsp_response", "ca_cert", "ecdsa_cert", EVP_sha1(),
OCSP_VERIFYSTATUS_SUCCESS, OCSP_RESPFINDSTATUS_ERROR, -1, nullptr},
// Test OCSP response where the requested certificate was signed by the OCSP
// responder, but signed by the wrong requested OCSP responder key
// certificate.
// However, this incorrect OCSP responder certificate may be a valid OCSP
// responder for some other case and also chains to a trusted root.
{"ocsp_response_wrong_signer", "ca_cert", "rsa_cert", EVP_sha1(),
OCSP_VERIFYSTATUS_ERROR, OCSP_RESPFINDSTATUS_UNDEFINED, -1, nullptr},
// Test OCSP response where the requested certificate was signed by an OCSP
// responder with an expired certificate.
// However, this incorrect OCSP responder certificate may be a valid OCSP
// responder for some other case and also chains to a trusted root.
{"ocsp_response_expired_signer", "ca_cert", "rsa_cert", EVP_sha1(),
OCSP_VERIFYSTATUS_ERROR, OCSP_RESPFINDSTATUS_UNDEFINED, -1, nullptr},
// === SHA256 OCSP RESPONSES ===
// Test valid OCSP response signed by an OCSP responder.
{"ocsp_response_sha256", "ca_cert", "rsa_cert", EVP_sha256(),
OCSP_VERIFYSTATUS_SUCCESS, OCSP_RESPFINDSTATUS_SUCCESS,
V_OCSP_CERTSTATUS_GOOD, "good"},
// Test a SHA-256 revoked OCSP response status.
{"ocsp_response_revoked_sha256", "ca_cert", "rsa_cert", EVP_sha256(),
OCSP_VERIFYSTATUS_SUCCESS, OCSP_RESPFINDSTATUS_SUCCESS,
V_OCSP_CERTSTATUS_REVOKED, "revoked"},
// Test a SHA-256 unknown OCSP response status.
{"ocsp_response_unknown_sha256", "ca_cert", "rsa_cert", EVP_sha256(),
OCSP_VERIFYSTATUS_SUCCESS, OCSP_RESPFINDSTATUS_SUCCESS,
V_OCSP_CERTSTATUS_UNKNOWN, "unknown"},
// Test a SHA-256 OCSP response signed by the correct responder certificate,
// but not for the requested certificate. (So this would be a completely
// valid response to a different OCSP request for the other certificate.)
{"ocsp_response_sha256", "ca_cert", "ecdsa_cert", EVP_sha256(),
OCSP_VERIFYSTATUS_SUCCESS, OCSP_RESPFINDSTATUS_ERROR, -1, nullptr},
// Test a SHA-256 OCSP response signed by the wrong responder certificate,
// but the requested certificate was signed. (however this incorrect OCSP
// responder certificate is a valid OCSP responder for some other case and
// chains to a trusted root). Thus, this response is not valid for any
// request.
{"ocsp_response_wrong_signer_sha256", "ca_cert", "rsa_cert", EVP_sha256(),
OCSP_VERIFYSTATUS_ERROR, OCSP_RESPFINDSTATUS_UNDEFINED, -1, nullptr},
};
class OCSPTestAWS : public testing::TestWithParam<OCSPAWSTestVector> {};
INSTANTIATE_TEST_SUITE_P(All, OCSPTestAWS, testing::ValuesIn(nTestVectors));
TEST_P(OCSPTestAWS, VerifyOCSPResponse) {
const OCSPAWSTestVector &t = GetParam();
std::string data =
GetTestData(std::string("crypto/ocsp/test/aws/" +
std::string(t.ocsp_response) + ".der")
.c_str());
std::vector<uint8_t> ocsp_reponse_data(data.begin(), data.end());
// OCSP response parsing and verification step.
bssl::UniquePtr<OCSP_BASICRESP> basic_response;
bssl::UniquePtr<STACK_OF(X509)> server_cert_chain;
ExtractAndVerifyBasicOCSP(ocsp_reponse_data, t.cafile, t.server_cert,
t.expected_ocsp_verify_status, &basic_response,
&server_cert_chain);
// If OCSP basic verify is successful, we check the OCSP response status.
if (t.expected_ocsp_verify_status == OCSP_VERIFYSTATUS_SUCCESS) {
int status = 0;
ASN1_GENERALIZEDTIME *thisupd, *nextupd;
CheckOCSP_CERTSTATUS(&basic_response, &server_cert_chain, t.dgst,
t.expected_ocsp_resp_find_status, &status, &thisupd,
&nextupd);
// If OCSP response status retrieving is successful, we check if the cert
// status of the OCSP response is correct.
if (t.expected_ocsp_resp_find_status == OCSP_RESPFINDSTATUS_SUCCESS) {
ASSERT_EQ(t.expected_ocsp_cert_status, status);
ASSERT_EQ(std::string(t.expected_ocsp_cert_status_string),
std::string(OCSP_cert_status_str(status)));
}
}
}
struct OCSPResponseStatusTestVector {
const char *ocsp_response;
int expected_ocsp_status;
const char *expected_ocsp_status_string;
};
static const OCSPResponseStatusTestVector respTestVectors[] = {
// === Invalid OCSP response requests sent back an OCSP responder ===
// https://datatracker.ietf.org/doc/html/rfc6960#section-4.2.1
// OCSPResponseStatus: successful
{"ocsp_response", OCSP_RESPONSE_STATUS_SUCCESSFUL, "successful"},
// OCSPResponseStatus: malformedRequest
{"ocsp_response_malformedrequest", OCSP_RESPONSE_STATUS_MALFORMEDREQUEST,
"malformedrequest"},
// OCSPResponseStatus: internalError
{"ocsp_response_internalerror", OCSP_RESPONSE_STATUS_INTERNALERROR,
"internalerror"},
// OCSPResponseStatus: tryLater
{"ocsp_response_trylater", OCSP_RESPONSE_STATUS_TRYLATER, "trylater"},
// OCSPResponseStatus: sigRequired
{"ocsp_response_sigrequired", OCSP_RESPONSE_STATUS_SIGREQUIRED,
"sigrequired"},
// OCSPResponseStatus: unauthorized
{"ocsp_response_unauthorized", OCSP_RESPONSE_STATUS_UNAUTHORIZED,
"unauthorized"},
};
class OCSPResponseStatusTest
: public testing::TestWithParam<OCSPResponseStatusTestVector> {};
INSTANTIATE_TEST_SUITE_P(All, OCSPResponseStatusTest,
testing::ValuesIn(respTestVectors));
TEST_P(OCSPResponseStatusTest, OCSPResponseStatus) {
const OCSPResponseStatusTestVector &t = GetParam();
std::string data =
GetTestData(std::string("crypto/ocsp/test/aws/" +
std::string(t.ocsp_response) + ".der")
.c_str());
std::vector<uint8_t> ocsp_reponse_data(data.begin(), data.end());
bssl::UniquePtr<OCSP_RESPONSE> ocsp_response(
LoadOCSP_RESPONSE(ocsp_reponse_data));
ASSERT_TRUE(ocsp_response);
int ret = OCSP_response_status(ocsp_response.get());
ASSERT_EQ(t.expected_ocsp_status, ret);
ASSERT_EQ(std::string(t.expected_ocsp_status_string),
std::string(OCSP_response_status_str(ret)));
}
// === Specific test cases ===
// Test valid OCSP response signed by an OCSP responder along with check for
// correct time field parsing.
TEST(OCSPTest, TestGoodOCSP) {
std::string data = GetTestData(
std::string("crypto/ocsp/test/aws/ocsp_response.der").c_str());
std::vector<uint8_t> ocsp_reponse_data(data.begin(), data.end());
bssl::UniquePtr<OCSP_BASICRESP> basic_response;
bssl::UniquePtr<STACK_OF(X509)> server_cert_chain;
ExtractAndVerifyBasicOCSP(ocsp_reponse_data, "ca_cert", "rsa_cert",
OCSP_VERIFYSTATUS_SUCCESS, &basic_response,
&server_cert_chain);
int status = 0;
ASN1_GENERALIZEDTIME *thisupd, *nextupd;
CheckOCSP_CERTSTATUS(&basic_response, &server_cert_chain, EVP_sha1(),
OCSP_RESPFINDSTATUS_SUCCESS, &status, &thisupd,
&nextupd);
ASSERT_EQ(V_OCSP_CERTSTATUS_GOOD, status);
// There is 1 |OCSP_SINGLERESP| in the |basic_response|.
EXPECT_EQ(OCSP_resp_count(basic_response.get()), 1);
// If OCSP response is verifiable and all good, an OCSP client should check
// time fields to see if the response is still valid.
// Check before OCSP was last updated connection timestamp
time_t connection_time = invalid_before_ocsp_update_time;
ASSERT_EQ(1, X509_cmp_time(thisupd, &connection_time));
ASSERT_EQ(1, X509_cmp_time(nextupd, &connection_time));
// Check valid connection timestamp right after OCSP response was validated.
connection_time = valid_after_ocsp_update_time;
ASSERT_EQ(-1, X509_cmp_time(thisupd, &connection_time));
ASSERT_EQ(1, X509_cmp_time(nextupd, &connection_time));
// Check valid connection timestamp right before OCSP response expires.
connection_time = valid_before_ocsp_expire_time;
ASSERT_EQ(-1, X509_cmp_time(thisupd, &connection_time));
ASSERT_EQ(1, X509_cmp_time(nextupd, &connection_time));
// Check expired connection timestamp
connection_time = invalid_after_ocsp_expire_time;
ASSERT_EQ(-1, X509_cmp_time(thisupd, &connection_time));
ASSERT_EQ(-1, X509_cmp_time(nextupd, &connection_time));
// Check that |OCSP_check_validity| sees that |thisupd| is more than 100
// seconds in the past, and disallows it.
EXPECT_FALSE(OCSP_check_validity(thisupd, nextupd, 0, 100));
uint32_t err = ERR_get_error();
EXPECT_EQ(OCSP_R_STATUS_TOO_OLD, ERR_GET_REASON(err));
// We offset "nsec" in |OCSP_check_validity| with a negative timestamp
// equal to the current time. We offset this larger timestamp on purpose to
// check if |OCSP_check_validity| can properly reject improper timestamps.
// This will cause the function to fail in two places, once when checking
// if "(current_time + nsec) > thisupd [Status Not Yet Valid]", and a second
// time when checking if "nextupd > (current_time - nsec) [Status Expired]".
ERR_clear_error();
EXPECT_FALSE(OCSP_check_validity(thisupd, nextupd, -time(nullptr), -1));
err = ERR_get_error();
EXPECT_EQ(OCSP_R_STATUS_NOT_YET_VALID, ERR_GET_REASON(err));
err = ERR_get_error();
EXPECT_EQ(OCSP_R_STATUS_EXPIRED, ERR_GET_REASON(err));
ERR_clear_error();
// Check that "NEXTUPDATE_BEFORE_THISUPDATE" is properly detected. We have to
// use |valid_after_ocsp_update_time| instead of |nextupd| to avoid a
// ticking time-bomb test. |nextupd| will throw an additional
// "STATUS_NOT_YET_VALID" error until it is a valid timestamp.
bssl::UniquePtr<ASN1_GENERALIZEDTIME> after_thisupd(
ASN1_GENERALIZEDTIME_new());
ASN1_GENERALIZEDTIME_set(after_thisupd.get(), valid_after_ocsp_update_time);
EXPECT_FALSE(OCSP_check_validity(after_thisupd.get(), thisupd, 0, -1));
err = ERR_get_error();
EXPECT_EQ(OCSP_R_STATUS_EXPIRED, ERR_GET_REASON(err));
err = ERR_get_error();
EXPECT_EQ(OCSP_R_NEXTUPDATE_BEFORE_THISUPDATE, ERR_GET_REASON(err));
// We set |nextupd| to NULL on purpose to avoid another ticking time-bomb
// expiration in our tests. We only check if |thisupd| is a valid timestamp
// in the past.
EXPECT_TRUE(OCSP_check_validity(thisupd, nullptr, 0, -1));
}
// Test valid OCSP response, but the data itself is untrusted.
TEST(OCSPTest, TestUntrustedDataOCSP) {
std::string data = GetTestData(
std::string("crypto/ocsp/test/aws/ocsp_response.der").c_str());
std::vector<uint8_t> ocsp_reponse_data(data.begin(), data.end());
// Mess up a byte right in the middle of the cert.
ocsp_reponse_data[800] = ocsp_reponse_data[800] + 1;
bssl::UniquePtr<OCSP_BASICRESP> basic_response;
bssl::UniquePtr<STACK_OF(X509)> server_cert_chain;
ExtractAndVerifyBasicOCSP(ocsp_reponse_data, "ca_cert", "rsa_cert",
OCSP_VERIFYSTATUS_ERROR, &basic_response,
&server_cert_chain);
}
// Test valid OCSP response hashed with sha256 along with check for correct time
// field parsing.
TEST(OCSPTest, TestGoodOCSP_SHA256) {
std::string data = GetTestData(
std::string("crypto/ocsp/test/aws/ocsp_response_sha256.der").c_str());
std::vector<uint8_t> ocsp_reponse_data(data.begin(), data.end());
bssl::UniquePtr<OCSP_BASICRESP> basic_response;
bssl::UniquePtr<STACK_OF(X509)> server_cert_chain;
ExtractAndVerifyBasicOCSP(ocsp_reponse_data, "ca_cert", "rsa_cert",
OCSP_VERIFYSTATUS_SUCCESS, &basic_response,
&server_cert_chain);
int status = 0;
ASN1_GENERALIZEDTIME *thisupd, *nextupd;
CheckOCSP_CERTSTATUS(&basic_response, &server_cert_chain, EVP_sha256(),
OCSP_RESPFINDSTATUS_SUCCESS, &status, &thisupd,
&nextupd);
ASSERT_EQ(V_OCSP_CERTSTATUS_GOOD, status);
// There is 1 |OCSP_SINGLERESP| in the |basic_response|.
EXPECT_EQ(OCSP_resp_count(basic_response.get()), 1);
// If OCSP response is verifiable and all good, an OCSP client should check
// time fields to see if the response is still valid.
// Check before OCSP was last updated connection timestamp.
time_t connection_time = invalid_before_ocsp_update_time_sha256;
ASSERT_EQ(1, X509_cmp_time(thisupd, &connection_time));
ASSERT_EQ(1, X509_cmp_time(nextupd, &connection_time));
// Check valid connection timestamp right after OCSP response was validated.
connection_time = valid_after_ocsp_update_time_sha256;
ASSERT_EQ(-1, X509_cmp_time(thisupd, &connection_time));
ASSERT_EQ(1, X509_cmp_time(nextupd, &connection_time));
// Check valid connection timestamp right before OCSP response expires.
connection_time = valid_before_ocsp_expire_time_sha256;
ASSERT_EQ(-1, X509_cmp_time(thisupd, &connection_time));
ASSERT_EQ(1, X509_cmp_time(nextupd, &connection_time));
// Check expired connection timestamp.
connection_time = invalid_after_ocsp_expire_time_sha256;
ASSERT_EQ(-1, X509_cmp_time(thisupd, &connection_time));
ASSERT_EQ(-1, X509_cmp_time(nextupd, &connection_time));
}
struct OCSPResponseVerifyFlagTestVector {
const char *ocsp_response;
unsigned long ocsp_verify_flag;
bool include_expired_signer_cert;
int expected_ocsp_verify_status;
};
static const OCSPResponseVerifyFlagTestVector
OCSPResponseVerifyFlagTestVectors[] = {
{"ocsp_response", OCSP_NOINTERN, false, OCSP_VERIFYSTATUS_ERROR},
{"ocsp_response", OCSP_NOCHAIN, false, OCSP_VERIFYSTATUS_SUCCESS},
{"ocsp_response_expired_signer", OCSP_NOVERIFY, false,
OCSP_VERIFYSTATUS_SUCCESS},
{"ocsp_response_wrong_signer", OCSP_NOVERIFY, false,
OCSP_VERIFYSTATUS_SUCCESS},
{"ocsp_response", OCSP_NOEXPLICIT, false, OCSP_VERIFYSTATUS_SUCCESS},
{"ocsp_response", OCSP_TRUSTOTHER, false, OCSP_VERIFYSTATUS_SUCCESS},
{"ocsp_response_expired_signer", OCSP_TRUSTOTHER, false,
OCSP_VERIFYSTATUS_ERROR},
// |OCSP_TRUSTOTHER| sets |OCSP_NOVERIFY| if the signer cert is included
// within the parameters.
{"ocsp_response_expired_signer", OCSP_TRUSTOTHER, true,
OCSP_VERIFYSTATUS_SUCCESS},
};
class OCSPVerifyFlagTest
: public testing::TestWithParam<OCSPResponseVerifyFlagTestVector> {};
INSTANTIATE_TEST_SUITE_P(All, OCSPVerifyFlagTest,
testing::ValuesIn(OCSPResponseVerifyFlagTestVectors));
TEST_P(OCSPVerifyFlagTest, OCSPVerifyFlagTest) {
const OCSPResponseVerifyFlagTestVector &t = GetParam();
std::string respData =
GetTestData(std::string("crypto/ocsp/test/aws/" +
std::string(t.ocsp_response) + ".der")
.c_str());
std::vector<uint8_t> ocsp_response_data(respData.begin(), respData.end());
bssl::UniquePtr<OCSP_RESPONSE> ocsp_response =
LoadOCSP_RESPONSE(ocsp_response_data);
ASSERT_TRUE(ocsp_response);
int ret = OCSP_response_status(ocsp_response.get());
ASSERT_EQ(OCSP_RESPONSE_STATUS_SUCCESSFUL, ret);
bssl::UniquePtr<OCSP_BASICRESP> basic_response =
bssl::UniquePtr<OCSP_BASICRESP>(
OCSP_response_get1_basic(ocsp_response.get()));
ASSERT_TRUE(basic_response);
// Set up trust store and certificate chain.
bssl::UniquePtr<X509> ca_cert(CertFromPEM(
GetTestData(std::string("crypto/ocsp/test/aws/ca_cert.pem").c_str())
.c_str()));
bssl::UniquePtr<X509> server_cert(CertFromPEM(
GetTestData(std::string("crypto/ocsp/test/aws/rsa_cert.pem").c_str())
.c_str()));
bssl::UniquePtr<X509_STORE> trust_store(X509_STORE_new());
X509_STORE_add_cert(trust_store.get(), ca_cert.get());
bssl::UniquePtr<STACK_OF(X509)> server_cert_chain(
CertsToStack({server_cert.get(), ca_cert.get()}));
ASSERT_TRUE(server_cert_chain);
if (t.include_expired_signer_cert) {
bssl::UniquePtr<X509> signing_cert(CertFromPEM(
GetTestData(
std::string("crypto/ocsp/test/aws/ocsp_expired_cert.pem").c_str())
.c_str()));
ASSERT_TRUE(sk_X509_push(server_cert_chain.get(), signing_cert.get()));
X509_up_ref(signing_cert.get());
}
// Does basic verification on OCSP response.
const int ocsp_verify_status =
OCSP_basic_verify(basic_response.get(), server_cert_chain.get(),
trust_store.get(), t.ocsp_verify_flag);
ASSERT_EQ(t.expected_ocsp_verify_status, ocsp_verify_status);
}
struct OCSPRequestVerifyFlagTestVector {
const char *ocsp_request;
unsigned long ocsp_verify_flag;
bool include_expired_signer_cert;
int expected_ocsp_verify_status;
};
static const OCSPRequestVerifyFlagTestVector
OCSPRequestVerifyFlagTestVectors[] = {
// Although signatures for OCSP requests are optional according to the
// RFC, OCSP requests with absent signatures can't be verified.
{"ocsp_request", 0, false, OCSP_VERIFYSTATUS_ERROR},
// ocsp_request_signed's certificate is embedded in the request.
{"ocsp_request_signed", 0, false, OCSP_VERIFYSTATUS_SUCCESS},
{"ocsp_request_signed", OCSP_NOINTERN, false, OCSP_VERIFYSTATUS_ERROR},
{"ocsp_request_signed", OCSP_NOCHAIN, false, OCSP_VERIFYSTATUS_SUCCESS},
{"ocsp_request_wrong_signer", 0, false, OCSP_VERIFYSTATUS_ERROR},
{"ocsp_request_wrong_signer", OCSP_NOCHAIN, false,
OCSP_VERIFYSTATUS_ERROR},
// OCSP request verification will fail if the cert can't be found or
// when working with an expired cert.
{"ocsp_request_expired_signer", 0, true, OCSP_VERIFYSTATUS_ERROR},
{"ocsp_request_expired_signer", 0, false, OCSP_VERIFYSTATUS_ERROR},
{"ocsp_request_expired_signer_no_certs", 0, true,
OCSP_VERIFYSTATUS_ERROR},
{"ocsp_request_expired_signer_no_certs", 0, false,
OCSP_VERIFYSTATUS_ERROR},
// |OCSP_NOVERIFY| skips certificate verification, but can't succeed if
// the signer cert isn't found.
{"ocsp_request_expired_signer", OCSP_NOVERIFY, true,
OCSP_VERIFYSTATUS_SUCCESS},
{"ocsp_request_expired_signer", OCSP_NOVERIFY, false,
OCSP_VERIFYSTATUS_SUCCESS},
{"ocsp_request_expired_signer_no_certs", OCSP_NOVERIFY, true,
OCSP_VERIFYSTATUS_SUCCESS},
{"ocsp_request_expired_signer_no_certs", OCSP_NOVERIFY, false,
OCSP_VERIFYSTATUS_ERROR},
// |OCSP_TRUSTOTHER| sets |OCSP_NOVERIFY| if the signer cert is included
// within the parameters.
{"ocsp_request_expired_signer", OCSP_TRUSTOTHER, true,
OCSP_VERIFYSTATUS_ERROR},
{"ocsp_request_expired_signer", OCSP_TRUSTOTHER, false,
OCSP_VERIFYSTATUS_ERROR},
{"ocsp_request_expired_signer_no_certs", OCSP_TRUSTOTHER, true,
OCSP_VERIFYSTATUS_SUCCESS},
{"ocsp_request_expired_signer_no_certs", OCSP_TRUSTOTHER, false,
OCSP_VERIFYSTATUS_ERROR},
};
class OCSPRequestVerifyFlagTest
: public testing::TestWithParam<OCSPRequestVerifyFlagTestVector> {};
INSTANTIATE_TEST_SUITE_P(All, OCSPRequestVerifyFlagTest,
testing::ValuesIn(OCSPRequestVerifyFlagTestVectors));
TEST_P(OCSPRequestVerifyFlagTest, OCSPVerifyFlagTest) {
const OCSPRequestVerifyFlagTestVector &t = GetParam();
std::string respData =
GetTestData(std::string("crypto/ocsp/test/aws/" +
std::string(t.ocsp_request) + ".der")
.c_str());
std::vector<uint8_t> ocsp_request_data(respData.begin(), respData.end());
bssl::UniquePtr<OCSP_REQUEST> ocsp_request =
LoadOCSP_REQUEST(ocsp_request_data);
ASSERT_TRUE(ocsp_request);
// Set up trust store and certificate chain.
bssl::UniquePtr<X509> ca_cert(CertFromPEM(
GetTestData(std::string("crypto/ocsp/test/aws/ca_cert.pem").c_str())
.c_str()));
bssl::UniquePtr<X509_STORE> trust_store(X509_STORE_new());
X509_STORE_add_cert(trust_store.get(), ca_cert.get());
bssl::UniquePtr<STACK_OF(X509)> server_cert_chain(sk_X509_new_null());
ASSERT_TRUE(server_cert_chain);
if (t.include_expired_signer_cert) {
bssl::UniquePtr<X509> signing_cert(CertFromPEM(
GetTestData(
std::string("crypto/ocsp/test/aws/ocsp_expired_cert.pem").c_str())
.c_str()));
ASSERT_TRUE(sk_X509_push(server_cert_chain.get(), signing_cert.get()));
X509_up_ref(signing_cert.get());
}
// Does basic verification on OCSP request.
const int ocsp_verify_status =
OCSP_request_verify(ocsp_request.get(), server_cert_chain.get(),
trust_store.get(), t.ocsp_verify_flag);
ASSERT_EQ(t.expected_ocsp_verify_status, ocsp_verify_status);
}
TEST(OCSPTest, GetInfo) {
// Create a sample |OCSP_CERTID| structure.
bssl::UniquePtr<OCSP_CERTID> cert_id(LoadTestOCSP_CERTID());
ASSERT_TRUE(cert_id);
ASN1_OCTET_STRING *nameHash = nullptr;
ASN1_OBJECT *algo = nullptr;
ASN1_OCTET_STRING *keyHash = nullptr;
ASN1_INTEGER *serial = nullptr;
EXPECT_TRUE(
OCSP_id_get0_info(&nameHash, &algo, &keyHash, &serial, cert_id.get()));
EXPECT_EQ(nameHash, cert_id.get()->issuerNameHash);
EXPECT_EQ(algo, cert_id.get()->hashAlgorithm->algorithm);
EXPECT_EQ(keyHash, cert_id.get()->issuerKeyHash);
EXPECT_EQ(serial, cert_id.get()->serialNumber);
}
TEST(OCSPTest, BasicAddCert) {
std::string respData = GetTestData(
std::string("crypto/ocsp/test/aws/ocsp_response.der").c_str());
std::vector<uint8_t> ocsp_response_data(respData.begin(), respData.end());
bssl::UniquePtr<OCSP_RESPONSE> ocspResponse =
LoadOCSP_RESPONSE(ocsp_response_data);
bssl::UniquePtr<OCSP_BASICRESP> basicResponse(
OCSP_response_get1_basic(ocspResponse.get()));
ASSERT_TRUE(basicResponse);
bssl::UniquePtr<X509> cert(CertFromPEM(
GetTestData(std::string("crypto/ocsp/test/aws/ca_cert.pem").c_str())
.c_str()));
EXPECT_TRUE(OCSP_basic_add1_cert(basicResponse.get(), cert.get()));
// Check that the cert is the same as the one added to the stack.
EXPECT_EQ(sk_X509_value(basicResponse.get()->certs,
sk_X509_num(basicResponse.get()->certs) - 1),
cert.get());
}
TEST(OCSPTest, BasicAddStatus) {
bssl::UniquePtr<OCSP_BASICRESP> basicResponse(OCSP_BASICRESP_new());
ASSERT_TRUE(basicResponse);
bssl::UniquePtr<OCSP_CERTID> certId(LoadTestOCSP_CERTID());
ASSERT_TRUE(certId);
bssl::UniquePtr<ASN1_TIME> revoked_time(ASN1_TIME_new()),
this_update(ASN1_TIME_new()), next_update(ASN1_TIME_new());
ASSERT_TRUE(
ASN1_TIME_set(revoked_time.get(), invalid_before_ocsp_update_time));
ASSERT_TRUE(ASN1_TIME_set(this_update.get(), valid_after_ocsp_update_time));
ASSERT_TRUE(ASN1_TIME_set(next_update.get(), valid_before_ocsp_expire_time));
EXPECT_TRUE(OCSP_basic_add1_status(basicResponse.get(), certId.get(),
V_OCSP_CERTSTATUS_GOOD, 0, nullptr,
this_update.get(), next_update.get()));
// |revoked_reason| and |revoked_time| are ignored if |status| is
// |V_OCSP_CERTSTATUS_GOOD|, but will still succeed.
EXPECT_TRUE(OCSP_basic_add1_status(
basicResponse.get(), certId.get(), V_OCSP_CERTSTATUS_GOOD,
OCSP_REVOKED_STATUS_CACOMPROMISE, revoked_time.get(), this_update.get(),
next_update.get()));
EXPECT_TRUE(OCSP_basic_add1_status(basicResponse.get(), certId.get(),
V_OCSP_CERTSTATUS_UNKNOWN, 0, nullptr,
this_update.get(), next_update.get()));
// |revoked_reason| and |revoked_time| are ignored if |status| is
// |V_OCSP_CERTSTATUS_UNKNOWN|, but will still succeed.
EXPECT_TRUE(OCSP_basic_add1_status(
basicResponse.get(), certId.get(), V_OCSP_CERTSTATUS_UNKNOWN,
OCSP_REVOKED_STATUS_SUPERSEDED, revoked_time.get(), this_update.get(),
next_update.get()));
// Try setting a revoked response without an |ASN1_TIME|.
EXPECT_FALSE(OCSP_basic_add1_status(basicResponse.get(), certId.get(),
V_OCSP_CERTSTATUS_REVOKED, 0, nullptr,
this_update.get(), nullptr));
// Try setting a revoked response with an invalid revoked reason number.
EXPECT_FALSE(OCSP_basic_add1_status(
basicResponse.get(), certId.get(), V_OCSP_CERTSTATUS_REVOKED,
OCSP_UNASSIGNED_REVOKED_STATUS, revoked_time.get(), this_update.get(),
nullptr));
EXPECT_TRUE(OCSP_basic_add1_status(
basicResponse.get(), certId.get(), V_OCSP_CERTSTATUS_REVOKED,
OCSP_REVOKED_STATUS_UNSPECIFIED, revoked_time.get(), this_update.get(),
nullptr));
// Try setting an invalid status to the response.
EXPECT_FALSE(OCSP_basic_add1_status(basicResponse.get(), certId.get(), 4, 0,
nullptr, this_update.get(), nullptr));
// |OCSP_basic_add1_status| has succeeded 5 times at this point, so the
// |basicResponse| should have 5 |OCSP_SINGLERESP|s in the internal stack.
EXPECT_EQ((int)sk_OCSP_SINGLERESP_num(
basicResponse.get()->tbsResponseData->responses),
5);
}
TEST(OCSPTest, OCSPResponseRecreate) {
std::string data = GetTestData(
std::string("crypto/ocsp/test/aws/ocsp_response.der").c_str());
std::vector<uint8_t> ocsp_response_data(data.begin(), data.end());
bssl::UniquePtr<OCSP_RESPONSE> ocsp_response(
LoadOCSP_RESPONSE(ocsp_response_data));
ASSERT_TRUE(ocsp_response);
bssl::UniquePtr<OCSP_BASICRESP> basic_response(
OCSP_response_get1_basic(ocsp_response.get()));
ASSERT_TRUE(basic_response);
// Recreate the same |OCSP_RESPONSE| with the same contents.
bssl::UniquePtr<OCSP_RESPONSE> ocsp_response_recreated(OCSP_response_create(
OCSP_response_status(ocsp_response.get()), basic_response.get()));
EXPECT_TRUE(ocsp_response_recreated);
// Write out the bytes from |ocsp_response_recreated| to compare with the
// original.
bssl::UniquePtr<BIO> bio(BIO_new(BIO_s_mem()));
EXPECT_TRUE(i2d_OCSP_RESPONSE_bio(bio.get(), ocsp_response_recreated.get()));
const uint8_t *bio_data;
size_t bio_len;
ASSERT_TRUE(BIO_mem_contents(bio.get(), &bio_data, &bio_len));
EXPECT_EQ(Bytes(bio_data, bio_len),
Bytes(ocsp_response_data.data(), ocsp_response_data.size()));
// Disallow creation of an |OCSP_RESPONSE| with an invalid status number.
EXPECT_FALSE(OCSP_response_create(OCSP_UNASSIGNED_RESPONSE_STATUS,
basic_response.get()));
}
// === Translation of OpenSSL's OCSP tests ===
// https://github.com/openssl/openssl/blob/OpenSSL_1_1_1-stable/test/recipes/80-test_ocsp.t
struct OCSPTestVector {
const char *ocsp_response;
const char *cafile;
const char *untrusted;
int expected_ocsp_verify_status;
};
// Test vectors from OpenSSL OCSP's tests
static const OCSPTestVector kTestVectors[] = {
// === VALID OCSP RESPONSES ===
{"ND1", "ND1_Issuer_ICA", "", OCSP_VERIFYSTATUS_SUCCESS},
{"ND2", "ND2_Issuer_Root", "", OCSP_VERIFYSTATUS_SUCCESS},
{"ND3", "ND3_Issuer_Root", "", OCSP_VERIFYSTATUS_SUCCESS},
{"ND1", "ND1_Cross_Root", "ND1_Issuer_ICA-Cross",
OCSP_VERIFYSTATUS_SUCCESS},
{"D1", "D1_Issuer_ICA", "", OCSP_VERIFYSTATUS_SUCCESS},
{"D2", "D2_Issuer_Root", "", OCSP_VERIFYSTATUS_SUCCESS},
{"D3", "D3_Issuer_Root", "", OCSP_VERIFYSTATUS_SUCCESS},
// === INVALID SIGNATURE on the OCSP RESPONSE ===
{"ISOP_ND1", "ND1_Issuer_ICA", "", OCSP_VERIFYSTATUS_ERROR},
{"ISOP_ND2", "ND2_Issuer_Root", "", OCSP_VERIFYSTATUS_ERROR},
{"ISOP_ND3", "ND3_Issuer_Root", "", OCSP_VERIFYSTATUS_ERROR},
{"ISOP_D1", "D1_Issuer_ICA", "", OCSP_VERIFYSTATUS_ERROR},
{"ISOP_D2", "D2_Issuer_Root", "", OCSP_VERIFYSTATUS_ERROR},
{"ISOP_D3", "D3_Issuer_Root", "", OCSP_VERIFYSTATUS_ERROR},
// === WRONG RESPONDERID in the OCSP RESPONSE ===
{"WRID_ND1", "ND1_Issuer_ICA", "", OCSP_VERIFYSTATUS_ERROR},
{"WRID_ND2", "ND2_Issuer_Root", "", OCSP_VERIFYSTATUS_ERROR},
{"WRID_ND3", "ND3_Issuer_Root", "", OCSP_VERIFYSTATUS_ERROR},
{"WRID_D1", "D1_Issuer_ICA", "", OCSP_VERIFYSTATUS_ERROR},
{"WRID_D2", "D2_Issuer_Root", "", OCSP_VERIFYSTATUS_ERROR},
{"WRID_D3", "D3_Issuer_Root", "", OCSP_VERIFYSTATUS_ERROR},
// === WRONG ISSUERNAMEHASH in the OCSP RESPONSE ===
{"WINH_ND1", "ND1_Issuer_ICA", "", OCSP_VERIFYSTATUS_ERROR},
{"WINH_ND2", "ND2_Issuer_Root", "", OCSP_VERIFYSTATUS_ERROR},
{"WINH_ND3", "ND3_Issuer_Root", "", OCSP_VERIFYSTATUS_ERROR},
{"WINH_D1", "D1_Issuer_ICA", "", OCSP_VERIFYSTATUS_ERROR},
{"WINH_D2", "D2_Issuer_Root", "", OCSP_VERIFYSTATUS_ERROR},
{"WINH_D3", "D3_Issuer_Root", "", OCSP_VERIFYSTATUS_ERROR},
// === WRONG ISSUERKEYHASH in the OCSP RESPONSE ===
{"WIKH_ND1", "ND1_Issuer_ICA", "", OCSP_VERIFYSTATUS_ERROR},
{"WIKH_ND2", "ND2_Issuer_Root", "", OCSP_VERIFYSTATUS_ERROR},
{"WIKH_ND3", "ND3_Issuer_Root", "", OCSP_VERIFYSTATUS_ERROR},
{"WIKH_D1", "D1_Issuer_ICA", "", OCSP_VERIFYSTATUS_ERROR},
{"WIKH_D2", "D2_Issuer_Root", "", OCSP_VERIFYSTATUS_ERROR},
{"WIKH_D3", "D3_Issuer_Root", "", OCSP_VERIFYSTATUS_ERROR},
// === WRONG KEY in the DELEGATED OCSP SIGNING CERTIFICATE ===
{"WKDOSC_D1", "D1_Issuer_ICA", "", OCSP_VERIFYSTATUS_ERROR},
{"WKDOSC_D2", "D2_Issuer_Root", "", OCSP_VERIFYSTATUS_ERROR},
{"WKDOSC_D3", "D3_Issuer_Root", "", OCSP_VERIFYSTATUS_ERROR},
// === INVALID SIGNATURE on the DELEGATED OCSP SIGNING CERTIFICATE ===
{"ISDOSC_D1", "D1_Issuer_ICA", "", OCSP_VERIFYSTATUS_ERROR},
{"ISDOSC_D1", "D2_Issuer_Root", "", OCSP_VERIFYSTATUS_ERROR},
{"ISDOSC_D1", "D3_Issuer_Root", "", OCSP_VERIFYSTATUS_ERROR},
// === WRONG SUBJECT NAME in the ISSUER CERTIFICATE ===
{"ND1", "WSNIC_ND1_Issuer_ICA", "", OCSP_VERIFYSTATUS_ERROR},
{"ND2", "WSNIC_ND2_Issuer_Root", "", OCSP_VERIFYSTATUS_ERROR},
{"ND3", "WSNIC_ND3_Issuer_Root", "", OCSP_VERIFYSTATUS_ERROR},
{"D1", "WSNIC_D1_Issuer_ICA", "", OCSP_VERIFYSTATUS_ERROR},
{"D2", "WSNIC_D2_Issuer_Root", "", OCSP_VERIFYSTATUS_ERROR},
{"D3", "WSNIC_D3_Issuer_Root", "", OCSP_VERIFYSTATUS_ERROR},
// === WRONG KEY in the ISSUER CERTIFICATE ===
{"ND1", "WKIC_ND1_Issuer_ICA", "", OCSP_VERIFYSTATUS_ERROR},
{"ND2", "WKIC_ND2_Issuer_Root", "", OCSP_VERIFYSTATUS_ERROR},
{"ND3", "WKIC_ND3_Issuer_Root", "", OCSP_VERIFYSTATUS_ERROR},
{"D1", "WKIC_D1_Issuer_ICA", "", OCSP_VERIFYSTATUS_ERROR},
{"D2", "WKIC_D2_Issuer_Root", "", OCSP_VERIFYSTATUS_ERROR},
{"D3", "WKIC_D3_Issuer_Root", "", OCSP_VERIFYSTATUS_ERROR},
// === INVALID SIGNATURE on the ISSUER CERTIFICATE ===
// Expect success, because we're explicitly trusting the issuer certificate.
// https://datatracker.ietf.org/doc/html/rfc6960#section-2.6
{"ND1", "ISIC_ND1_Issuer_ICA", "", OCSP_VERIFYSTATUS_SUCCESS},
{"ND2", "ISIC_ND2_Issuer_Root", "", OCSP_VERIFYSTATUS_SUCCESS},
{"ND3", "ISIC_ND3_Issuer_Root", "", OCSP_VERIFYSTATUS_SUCCESS},
{"D1", "ISIC_D1_Issuer_ICA", "", OCSP_VERIFYSTATUS_SUCCESS},
{"D2", "ISIC_D2_Issuer_Root", "", OCSP_VERIFYSTATUS_SUCCESS},
{"D3", "ISIC_D3_Issuer_Root", "", OCSP_VERIFYSTATUS_SUCCESS},
};
class OCSPTest : public testing::TestWithParam<OCSPTestVector> {};
INSTANTIATE_TEST_SUITE_P(All, OCSPTest, testing::ValuesIn(kTestVectors));
TEST_P(OCSPTest, VerifyOCSPResponse) {
const OCSPTestVector &t = GetParam();
bssl::UniquePtr<OCSP_RESPONSE> ocsp_response;
bssl::UniquePtr<OCSP_BASICRESP> basic_response;
// Get OCSP response from path.
std::string data = GetTestData(
std::string("crypto/ocsp/test/" + std::string(t.ocsp_response) + ".ors")
.c_str());
std::vector<uint8_t> input;
ASSERT_TRUE(DecodeBase64(&input, data.c_str()));
ocsp_response = LoadOCSP_RESPONSE(input);
ASSERT_TRUE(ocsp_response);
int ret = OCSP_response_status(ocsp_response.get());
ASSERT_EQ(OCSP_RESPONSE_STATUS_SUCCESSFUL, ret);
basic_response = bssl::UniquePtr<OCSP_BASICRESP>(
OCSP_response_get1_basic(ocsp_response.get()));
ASSERT_TRUE(basic_response);
// Set up OpenSSL OCSP tests trust store parameters and cert chain.
bssl::UniquePtr<STACK_OF(X509)> server_cert_chain;
bssl::UniquePtr<X509_STORE> trust_store(X509_STORE_new());
bssl::UniquePtr<X509_VERIFY_PARAM> vpm(X509_VERIFY_PARAM_new());
X509_VERIFY_PARAM_set_time(vpm.get(), (time_t)1355875200);
// Discussion for whether this flag should be on by default or not:
// https://github.com/openssl/openssl/issues/7871
ASSERT_EQ(X509_VERIFY_PARAM_set_flags(vpm.get(), X509_V_FLAG_PARTIAL_CHAIN),
1);
// Get CA root certificate from path and set up trust store.
bssl::UniquePtr<X509> ca_certificate(
CertFromPEM(GetTestData(std::string("crypto/ocsp/test/" +
std::string(t.cafile) + ".pem")
.c_str())
.c_str()));
X509_STORE_add_cert(trust_store.get(), ca_certificate.get());
ASSERT_EQ(X509_STORE_set1_param(trust_store.get(), vpm.get()), 1);
// If untrusted cert chain isn't available, we only use CA cert as root
// cert.
if (std::string(t.untrusted).empty()) {
server_cert_chain = CertsToStack({ca_certificate.get()});
} else {
server_cert_chain = CertChainFromPEM(
GetTestData(
std::string("crypto/ocsp/test/" + std::string(t.untrusted) + ".pem")
.c_str())
.c_str());
}
ASSERT_TRUE(server_cert_chain);
// Does basic verification on OCSP response.
const int ocsp_verify_status = OCSP_basic_verify(
basic_response.get(), server_cert_chain.get(), trust_store.get(), 0);
ASSERT_EQ(t.expected_ocsp_verify_status, ocsp_verify_status);
}
struct OCSPRequestTestVector {
const char *ocsp_request;
int expected_parse_status;
int expected_sign_status;
const char *signer_cert;
const char *key_type;
const EVP_MD *dgst;
};
static const OCSPRequestTestVector kRequestTestVectors[] = {
{"ocsp_request", OCSP_REQUEST_PARSE_SUCCESS, OCSP_SIGN_SUCCESS, "rsa_cert",
"rsa_key", EVP_sha1()},
{"ocsp_request", OCSP_REQUEST_PARSE_SUCCESS, OCSP_SIGN_SUCCESS, "rsa_cert",
"rsa_key", EVP_sha256()},
{"ocsp_request", OCSP_REQUEST_PARSE_SUCCESS, OCSP_SIGN_SUCCESS, "rsa_cert",
"rsa_key", nullptr},
{"ocsp_request_attached_cert", OCSP_REQUEST_PARSE_SUCCESS, OCSP_SIGN_ERROR,
"rsa_cert", "rsa_key", nullptr},
{"ocsp_request_no_nonce", OCSP_REQUEST_PARSE_SUCCESS, OCSP_SIGN_SUCCESS,
"rsa_cert", "rsa_key", EVP_sha512()},
{"ocsp_request_signed", OCSP_REQUEST_PARSE_SUCCESS, OCSP_SIGN_ERROR,
"rsa_cert", "rsa_key", nullptr},
{"ocsp_request_signed_sha256", OCSP_REQUEST_PARSE_SUCCESS, OCSP_SIGN_ERROR,
"rsa_cert", "rsa_key", nullptr},
{"ocsp_response", OCSP_REQUEST_PARSE_ERROR, OCSP_SIGN_ERROR, "rsa_cert",
"rsa_key", nullptr},
// Test signing with ECDSA certs and keys.
{"ocsp_request", OCSP_REQUEST_PARSE_SUCCESS, OCSP_SIGN_SUCCESS,
"ecdsa_cert", "ecdsa_key", EVP_sha1()},
{"ocsp_request", OCSP_REQUEST_PARSE_SUCCESS, OCSP_SIGN_SUCCESS,
"ecdsa_cert", "ecdsa_key", EVP_sha256()},
{"ocsp_request", OCSP_REQUEST_PARSE_SUCCESS, OCSP_SIGN_SUCCESS,
"ecdsa_cert", "ecdsa_key", nullptr},
{"ocsp_request_no_nonce", OCSP_REQUEST_PARSE_SUCCESS, OCSP_SIGN_SUCCESS,
"rsa_cert", "rsa_key", EVP_sha256()},
{"ocsp_request_no_nonce", OCSP_REQUEST_PARSE_SUCCESS, OCSP_SIGN_SUCCESS,
"ecdsa_cert", "ecdsa_key", EVP_sha256()},
// Test certificate type mismatch.
{"ocsp_request", OCSP_REQUEST_PARSE_SUCCESS, OCSP_SIGN_ERROR, "rsa_cert",
"ecdsa_key", EVP_sha256()},
{"ocsp_request", OCSP_REQUEST_PARSE_SUCCESS, OCSP_SIGN_ERROR, "ecdsa_cert",
"rsa_key", EVP_sha256()},
// Test certificate key and cert mismatch.
{"ocsp_request", OCSP_REQUEST_PARSE_SUCCESS, OCSP_SIGN_ERROR, "ca_cert",
"rsa_key", EVP_sha256()},
};
class OCSPRequestTest : public testing::TestWithParam<OCSPRequestTestVector> {};
INSTANTIATE_TEST_SUITE_P(All, OCSPRequestTest,
testing::ValuesIn(kRequestTestVectors));
static const char good_http_request_hdr[] =
"POST / HTTP/1.0\r\n"
"Content-Type: application/ocsp-request\r\n"
"Content-Length: ";
TEST_P(OCSPRequestTest, OCSPRequestParse) {
const OCSPRequestTestVector &t = GetParam();
std::string data =
GetTestData(std::string("crypto/ocsp/test/aws/" +
std::string(t.ocsp_request) + ".der")
.c_str());
std::vector<uint8_t> ocsp_request_data(data.begin(), data.end());
bssl::UniquePtr<OCSP_REQUEST> ocspRequest =
LoadOCSP_REQUEST(ocsp_request_data);
if (t.expected_parse_status == OCSP_REQUEST_PARSE_SUCCESS) {
ASSERT_TRUE(ocspRequest);
// If request parsing is successful, try setting up a |OCSP_REQ_CTX| with
// default settings.
bssl::UniquePtr<BIO> bio(BIO_new(BIO_s_mem()));
const uint8_t *out;
size_t outlen;
bssl::UniquePtr<OCSP_REQ_CTX> ocspReqCtx(
OCSP_sendreq_new(bio.get(), nullptr, ocspRequest.get(), 0));
ASSERT_TRUE(ocspReqCtx);
// Get internal memory of |OCSP_REQ_CTX| and ensure contents are written.
ASSERT_TRUE(BIO_mem_contents(OCSP_REQ_CTX_get0_mem_bio(ocspReqCtx.get()),
&out, &outlen));
ASSERT_GT(outlen, (size_t)0);
// Set up |OCSP_REQ_CTX| without a |OCSP_REQUEST|. Then finalize the context
// later.
bssl::UniquePtr<BIO> bio2(BIO_new(BIO_s_mem()));
const uint8_t *out2;
size_t outlen2;
bssl::UniquePtr<OCSP_REQ_CTX> ocspReqCtxLater(
OCSP_sendreq_new(bio2.get(), nullptr, nullptr, 0));
ASSERT_TRUE(ocspReqCtxLater);
ASSERT_TRUE(
OCSP_REQ_CTX_set1_req(ocspReqCtxLater.get(), ocspRequest.get()));
ASSERT_TRUE(BIO_mem_contents(
OCSP_REQ_CTX_get0_mem_bio(ocspReqCtxLater.get()), &out2, &outlen2));
// Ensure header contents are written as expected.
EXPECT_EQ(
good_http_request_hdr + std::to_string(ocsp_request_data.size()) + "\r",
std::string(reinterpret_cast<const char *>(out),
sizeof(good_http_request_hdr) +
std::to_string(ocsp_request_data.size()).size()));
// Check |OCSP_REQ_CTX| construction methods write the exact same contents.
ASSERT_EQ(Bytes(out, outlen), Bytes(out2, outlen2));
} else {
ASSERT_FALSE(ocspRequest);
}
}
TEST_P(OCSPRequestTest, OCSPRequestSign) {
const OCSPRequestTestVector &t = GetParam();
std::string data =
GetTestData(std::string("crypto/ocsp/test/aws/" +
std::string(t.ocsp_request) + ".der")
.c_str());
std::vector<uint8_t> ocsp_request_data(data.begin(), data.end());
bssl::UniquePtr<OCSP_REQUEST> ocspRequest =
LoadOCSP_REQUEST(ocsp_request_data);
bssl::UniquePtr<X509> signer_cert(
CertFromPEM(GetTestData(std::string("crypto/ocsp/test/aws/" +
std::string(t.signer_cert) + ".pem")
.c_str())
.c_str()));
ASSERT_TRUE(signer_cert);
bssl::UniquePtr<X509> ca_cert(CertFromPEM(
GetTestData(std::string("crypto/ocsp/test/aws/ca_cert.pem").c_str())
.c_str()));
ASSERT_TRUE(ca_cert);
if (t.expected_parse_status == OCSP_REQUEST_PARSE_SUCCESS) {
bssl::UniquePtr<EVP_PKEY> pkey(EVP_PKEY_new());
if (std::string(t.key_type) == "rsa_key") {
bssl::UniquePtr<RSA> rsa(
RSAFromPEM(GetTestData(std::string("crypto/ocsp/test/aws/" +
std::string(t.key_type) + ".pem")
.c_str())
.c_str()));
ASSERT_TRUE(rsa);
ASSERT_TRUE(EVP_PKEY_set1_RSA(pkey.get(), rsa.get()));
} else {
bssl::UniquePtr<EC_KEY> ecdsa(
ECDSAFromPEM(GetTestData(std::string("crypto/ocsp/test/aws/" +
std::string(t.key_type) + ".pem")
.c_str())
.c_str()));
ASSERT_TRUE(ecdsa);
ASSERT_TRUE(EVP_PKEY_set1_EC_KEY(pkey.get(), ecdsa.get()));
}
int ret =
OCSP_request_sign(ocspRequest.get(), signer_cert.get(), pkey.get(),
t.dgst, CertsToStack({ca_cert.get()}).get(), 0);
if (t.expected_sign_status == OCSP_SIGN_SUCCESS) {
ASSERT_TRUE(ret);
EXPECT_TRUE(OCSP_request_is_signed(ocspRequest.get()));
bssl::UniquePtr<X509_STORE> trust_store(X509_STORE_new());
ASSERT_TRUE(X509_STORE_add_cert(trust_store.get(), ca_cert.get()));
EXPECT_TRUE(OCSP_request_verify(ocspRequest.get(),
CertsToStack({signer_cert.get()}).get(),
trust_store.get(), 0));
} else {
ASSERT_FALSE(ret);
EXPECT_FALSE(OCSP_request_is_signed(ocspRequest.get()));
}
}
}
struct OCSPResponseSignTestVector {
int expected_sign_status;
const char *signer_cert;
const char *key_type;
const EVP_MD *dgst;
};
static const OCSPResponseSignTestVector kOCSPResponseSignTestVectors[] = {
{OCSP_SIGN_SUCCESS, "rsa_cert", "rsa_key", EVP_sha1()},
{OCSP_SIGN_SUCCESS, "rsa_cert", "rsa_key", EVP_sha256()},
{OCSP_SIGN_SUCCESS, "rsa_cert", "rsa_key", EVP_sha512()},
{OCSP_SIGN_SUCCESS, "rsa_cert", "rsa_key", nullptr},
// Test signing with ECDSA certs and keys.
{OCSP_SIGN_SUCCESS, "ecdsa_cert", "ecdsa_key", EVP_sha1()},
{OCSP_SIGN_SUCCESS, "ecdsa_cert", "ecdsa_key", EVP_sha256()},
{OCSP_SIGN_SUCCESS, "ecdsa_cert", "ecdsa_key", EVP_sha512()},
{OCSP_SIGN_SUCCESS, "ecdsa_cert", "ecdsa_key", nullptr},
// Test certificate type mismatch.
{OCSP_SIGN_ERROR, "rsa_cert", "ecdsa_key", EVP_sha256()},
{OCSP_SIGN_ERROR, "ecdsa_cert", "rsa_key", EVP_sha256()},
// Test certificate key and cert mismatch.
{OCSP_SIGN_ERROR, "ca_cert", "rsa_key", EVP_sha256()},
};
class OCSPResponseSignTest
: public testing::TestWithParam<OCSPResponseSignTestVector> {};
INSTANTIATE_TEST_SUITE_P(All, OCSPResponseSignTest,
testing::ValuesIn(kOCSPResponseSignTestVectors));
TEST_P(OCSPResponseSignTest, OCSPResponseSign) {
const OCSPResponseSignTestVector &t = GetParam();
// Set up an empty |OCSP_BASICRESP| for signing.
bssl::UniquePtr<OCSP_BASICRESP> basic_response(OCSP_BASICRESP_new());
ASSERT_TRUE(basic_response);
// Gather certs and key needed for signing.
bssl::UniquePtr<X509> signer_cert(
CertFromPEM(GetTestData(std::string("crypto/ocsp/test/aws/" +
std::string(t.signer_cert) + ".pem")
.c_str())
.c_str()));
ASSERT_TRUE(signer_cert);
bssl::UniquePtr<X509> ca_cert(CertFromPEM(
GetTestData(std::string("crypto/ocsp/test/aws/ca_cert.pem").c_str())
.c_str()));
ASSERT_TRUE(ca_cert);
bssl::UniquePtr<EVP_PKEY> pkey(EVP_PKEY_new());
if (std::string(t.key_type) == "rsa_key") {
bssl::UniquePtr<RSA> rsa(
RSAFromPEM(GetTestData(std::string("crypto/ocsp/test/aws/" +
std::string(t.key_type) + ".pem")
.c_str())
.c_str()));
ASSERT_TRUE(rsa);
ASSERT_TRUE(EVP_PKEY_set1_RSA(pkey.get(), rsa.get()));
} else {
bssl::UniquePtr<EC_KEY> ecdsa(
ECDSAFromPEM(GetTestData(std::string("crypto/ocsp/test/aws/" +
std::string(t.key_type) + ".pem")
.c_str())
.c_str()));
ASSERT_TRUE(ecdsa);
ASSERT_TRUE(EVP_PKEY_set1_EC_KEY(pkey.get(), ecdsa.get()));
}
// Do the actual sign.
EXPECT_EQ(OCSP_basic_sign(basic_response.get(), signer_cert.get(), pkey.get(),
t.dgst, CertsToStack({ca_cert.get()}).get(), 0),
t.expected_sign_status);
if (t.expected_sign_status == OCSP_SIGN_SUCCESS) {
bssl::UniquePtr<X509_STORE> trust_store(X509_STORE_new());
ASSERT_TRUE(X509_STORE_add_cert(trust_store.get(), ca_cert.get()));
EXPECT_TRUE(OCSP_basic_verify(basic_response.get(),
CertsToStack({signer_cert.get()}).get(),
trust_store.get(), 0));
}
}
// Test against various flags for |OCSP_basic_sign|.
TEST(OCSPResponseSignTestExtended, OCSPResponseSign) {
bssl::UniquePtr<OCSP_BASICRESP> basic_response(OCSP_BASICRESP_new());
ASSERT_TRUE(basic_response);
bssl::UniquePtr<X509> signer_cert(CertFromPEM(
GetTestData(std::string("crypto/ocsp/test/aws/rsa_cert.pem").c_str())
.c_str()));
ASSERT_TRUE(signer_cert);
bssl::UniquePtr<X509> ca_cert(CertFromPEM(
GetTestData(std::string("crypto/ocsp/test/aws/ca_cert.pem").c_str())
.c_str()));
ASSERT_TRUE(ca_cert);
bssl::UniquePtr<STACK_OF(X509)> additional_cert =
CertsToStack({ca_cert.get()});
bssl::UniquePtr<EVP_PKEY> pkey(EVP_PKEY_new());
bssl::UniquePtr<RSA> rsa(RSAFromPEM(
GetTestData(std::string("crypto/ocsp/test/aws/rsa_key.pem").c_str())
.c_str()));
ASSERT_TRUE(rsa);
ASSERT_TRUE(EVP_PKEY_set1_RSA(pkey.get(), rsa.get()));
// Call |OCSP_basic_sign| with no flags and check the expected output.
EXPECT_TRUE(OCSP_basic_sign(basic_response.get(), signer_cert.get(),
pkey.get(), EVP_sha256(), additional_cert.get(),
0));
EXPECT_TRUE(
ASN1_TIME_check(basic_response.get()->tbsResponseData->producedAt));
// Allow for time field to be within two hours.
EXPECT_GT(
X509_cmp_time_posix(basic_response.get()->tbsResponseData->producedAt,
time(nullptr) - 3600),
0);
EXPECT_LT(
X509_cmp_time_posix(basic_response.get()->tbsResponseData->producedAt,
time(nullptr) + 3600),
0);
EXPECT_EQ(basic_response.get()->tbsResponseData->responderId->type,
V_OCSP_RESPID_NAME);
EXPECT_EQ(
X509_NAME_cmp(
basic_response.get()->tbsResponseData->responderId->value.byName,
X509_get_subject_name(signer_cert.get())),
0);
EXPECT_EQ((int)sk_X509_num(basic_response.get()->certs), 2);
EXPECT_EQ(X509_cmp(sk_X509_value(basic_response.get()->certs, 0),
signer_cert.get()),
0);
EXPECT_EQ(X509_cmp(sk_X509_value(basic_response.get()->certs, 1),
sk_X509_value(additional_cert.get(), 0)),
0);
// Check expected effects of |OCSP_NOTIME|.
basic_response.reset(OCSP_BASICRESP_new());
ASSERT_TRUE(basic_response);
EXPECT_TRUE(OCSP_basic_sign(basic_response.get(), signer_cert.get(),
pkey.get(), EVP_sha256(), additional_cert.get(),
OCSP_NOTIME));
EXPECT_FALSE(
ASN1_TIME_check(basic_response.get()->tbsResponseData->producedAt));
// Check expected effects of |OCSP_RESPID_KEY|.
basic_response.reset(OCSP_BASICRESP_new());
ASSERT_TRUE(basic_response);
ASSERT_FALSE(basic_response.get()->tbsResponseData->responderId->value.byKey);
EXPECT_TRUE(OCSP_basic_sign(basic_response.get(), signer_cert.get(),
pkey.get(), EVP_sha256(), additional_cert.get(),
OCSP_RESPID_KEY));
EXPECT_EQ(basic_response.get()->tbsResponseData->responderId->type,
V_OCSP_RESPID_KEY);
EXPECT_TRUE(basic_response.get()->tbsResponseData->responderId->value.byKey);
// Check expected effects of |OCSP_NOCERTS|.
basic_response.reset(OCSP_BASICRESP_new());
ASSERT_TRUE(basic_response);
EXPECT_TRUE(OCSP_basic_sign(basic_response.get(), signer_cert.get(),
pkey.get(), EVP_sha256(), additional_cert.get(),
OCSP_NOCERTS));
EXPECT_EQ((int)sk_X509_num(basic_response.get()->certs), 0);
}
static const char extended_good_http_request_hdr[] =
"POST / HTTP/1.0\r\n"
"Accept-Charset: character-set\r\n"
"Content-Type: application/ocsp-request\r\n"
"Content-Length: ";
// Check HTTP header can be written with OCSP_REQ_CTX_add1_header().
TEST(OCSPRequestTest, AddHeader) {
std::string data = GetTestData(std::string("crypto/ocsp/test/aws/"
"ocsp_request.der")
.c_str());
std::vector<uint8_t> ocsp_request_data(data.begin(), data.end());
bssl::UniquePtr<OCSP_REQUEST> ocspRequest =
LoadOCSP_REQUEST(ocsp_request_data);
bssl::UniquePtr<BIO> bio(BIO_new(BIO_s_mem()));
const uint8_t *out;
size_t outlen;
bssl::UniquePtr<OCSP_REQ_CTX> ocspReqCtx(
OCSP_sendreq_new(bio.get(), nullptr, nullptr, 0));
ASSERT_TRUE(ocspReqCtx);
// Set an additional HTTP header.
ASSERT_TRUE(OCSP_REQ_CTX_add1_header(ocspReqCtx.get(), "Accept-Charset",
"character-set"));
ASSERT_TRUE(OCSP_REQ_CTX_set1_req(ocspReqCtx.get(), ocspRequest.get()));
ASSERT_TRUE(BIO_mem_contents(OCSP_REQ_CTX_get0_mem_bio(ocspReqCtx.get()),
&out, &outlen));
// Ensure additional header contents are written as expected.
EXPECT_EQ(
extended_good_http_request_hdr +
std::to_string(ocsp_request_data.size()) + "\r\n",
std::string(reinterpret_cast<const char *>(out),
sizeof(extended_good_http_request_hdr) +
std::to_string(ocsp_request_data.size()).size() + 1));
}
// Check a |OCSP_CERTID| can be added to an |OCSP_REQUEST| with
// OCSP_request_add0_id().
TEST(OCSPRequestTest, AddCert) {
std::string data = GetTestData(std::string("crypto/ocsp/test/aws/"
"ocsp_request.der")
.c_str());
std::vector<uint8_t> ocsp_request_data(data.begin(), data.end());
bssl::UniquePtr<OCSP_REQUEST> ocspRequest =
LoadOCSP_REQUEST(ocsp_request_data);
ASSERT_TRUE(ocspRequest);
// Construct |OCSP_CERTID| from certs.
OCSP_CERTID *certId = LoadTestOCSP_CERTID();
ASSERT_TRUE(certId);
OCSP_ONEREQ *oneRequest = OCSP_request_add0_id(ocspRequest.get(), certId);
ASSERT_TRUE(oneRequest);
// OCSP_request_add0_id() allocates a new |OCSP_ONEREQ| and assigns the
// pointer to |OCSP_CERTID| to it, then adds it to the stack within
// |OCSP_REQUEST|.
// |OCSP_REQUEST| now takes ownership of the pointer to |OCSP_CERTID| and
// still maintains ownership of the pointer |OCSP_ONEREQ|, so we have to set
// the references to NULL to avoid freeing the same pointers twice.
oneRequest = nullptr;
certId = nullptr;
}
static const char good_http_response_hdr[] =
"HTTP/1.1 200 OK\r\n"
"Content-Type: application/ocsp-response\r\n"
"Content-Length: ";
// Test that |OCSP_set_max_response_length| correctly sets the maximum response
// length.
TEST(OCSPRequestTest, SetResponseLength) {
std::string data = GetTestData(std::string("crypto/ocsp/test/aws/"
"ocsp_request.der")
.c_str());
std::vector<uint8_t> ocsp_request_data(data.begin(), data.end());
bssl::UniquePtr<OCSP_REQUEST> ocspRequest =
LoadOCSP_REQUEST(ocsp_request_data);
ASSERT_TRUE(ocspRequest);
bssl::UniquePtr<BIO> bio(BIO_new(BIO_s_mem()));
bssl::UniquePtr<OCSP_REQ_CTX> ocspReqCtx(
OCSP_sendreq_new(bio.get(), nullptr, nullptr, 0));
ASSERT_TRUE(ocspReqCtx);
// Check custom length is set correctly.
int new_len = 40000;
OCSP_set_max_response_length(ocspReqCtx.get(), new_len);
EXPECT_EQ((int)ocspReqCtx.get()->max_resp_len, new_len);
// Check that default length is set correctly.
OCSP_set_max_response_length(ocspReqCtx.get(), 0);
EXPECT_EQ((int)ocspReqCtx.get()->max_resp_len, OCSP_MAX_RESP_LENGTH);
// Write an HTTP OCSP response into the BIO.
std::string respData = GetTestData(
std::string("crypto/ocsp/test/aws/ocsp_response.der").c_str());
std::vector<uint8_t> ocsp_response_data(respData.begin(), respData.end());
const int respLen = (int)ocsp_response_data.size();
ASSERT_GT(BIO_printf(bio.get(), "%s", good_http_response_hdr), 0);
ASSERT_GT(BIO_printf(bio.get(), "%d\r\n\r\n", respLen), 0);
ASSERT_EQ(BIO_write(bio.get(), ocsp_response_data.data(), respLen), respLen);
// Set max response length to a length that is too short on purpose.
OCSP_set_max_response_length(ocspReqCtx.get(), 1);
// Sends out an OCSP request and expects an OCSP response in |BIO| with
// |OCSP_REQ_CTX_nbio|. This should fail if the expected length is too short.
EXPECT_FALSE(OCSP_REQ_CTX_nbio(ocspReqCtx.get()));
}
static const char malformed_http_response_hdr[] =
"HTTP/1.200 OK\r\n"
"Content-Type: application/ocsp-response\r\n"
"Content-Length: ";
// This parses in OpenSSL, but fails in AWS-LC because we check for the HTTP
// protocol characters at the front.
static const char non_http_response_hdr[] =
"HTPT/1.1 200 OK\r\n"
"Content-Type: application/ocsp-response\r\n"
"Content-Length: ";
// Only status code 200 should be accepted.
static const char not_ok_http_response_hdr[] =
"HTTP/1.1 404 OK\r\n"
"Content-Type: application/ocsp-response\r\n"
"Content-Length: ";
// This should parse. Only the status code is mandatory, subsequent lines are
// optional.
static const char no_type_http_response_hdr[] =
"HTTP/1.1 200 OK\r\n"
"Content-Length: ";
// This should parse. Only the status code is mandatory, additional information
// is optional.
static const char no_info_http_response_hdr[] =
"HTTP/1.0 200\r\n"
"Content-Length: ";
// OpenSSL uses isspace() to test for white-space characters, which includes
// the following. ``\t''``\n''``\v''``\f''``\r''`` '
// This should parse. "\n" is not included since it will skip the header to the
// next line and fail the http parsing.
static const char additional_space_http_response_hdr[] =
"HTTP/1.1 \t\v\r\f\t 200 \t\v\f\r\t OK \r\n"
"Content-Type: application/ocsp-response\r\n"
"Content-Length: ";
// This should fail, since the status code is expected on the first line.
static const char next_line_http_response_hdr[] =
"HTTP/1.1 \n 200 \f OK\r\n"
"Content-Type: application/ocsp-response\r\n"
"Content-Length: ";
// This should fail. Protocol info is expected at the front.
static const char only_code_http_response_hdr[] =
"200\r\n"
"Content-Length: ";
// This should fail. We're appending a negative content length.
static const char negative_length_http_response_hdr[] =
"HTTP/1.0 200\r\n"
"Content-Length: -10";
// This should fail. We're appending a non-numeric content length.
static const char nonnumeric_length_http_response_hdr[] =
"HTTP/1.0 200\r\n"
"Content-Length: abcd";
// This should fail. We're appending a non-numeric content length.
static const char format_string_http_response_hdr[] =
"HTTP/1.0 200\r\n"
"Content-Length: %s%s%s%s%s";
// This should fail. We're appending a value that will be beyond the default max
// response length.
static const char max_length_http_response_hdr[] =
"HTTP/1.0 200\r\n"
"Content-Length: 999999";
struct OCSPHTTPTestVector {
const char *http_header;
bool response_attached;
int expected_status;
};
static const OCSPHTTPTestVector kResponseHTTPVectors[] = {
{good_http_response_hdr, true, OCSP_HTTP_PARSE_SUCCESS},
{malformed_http_response_hdr, true, OCSP_HTTP_PARSE_ERROR},
{non_http_response_hdr, true, OCSP_HTTP_PARSE_ERROR},
{not_ok_http_response_hdr, true, OCSP_HTTP_PARSE_ERROR},
{no_type_http_response_hdr, true, OCSP_REQUEST_PARSE_SUCCESS},
{no_info_http_response_hdr, true, OCSP_REQUEST_PARSE_SUCCESS},
{additional_space_http_response_hdr, true, OCSP_REQUEST_PARSE_SUCCESS},
{next_line_http_response_hdr, true, OCSP_HTTP_PARSE_ERROR},
{only_code_http_response_hdr, true, OCSP_HTTP_PARSE_ERROR},
// HTTP Header is OK, but no actual response is attached.
{good_http_response_hdr, false, OCSP_HTTP_PARSE_ERROR},
{no_type_http_response_hdr, false, OCSP_HTTP_PARSE_ERROR},
{no_info_http_response_hdr, false, OCSP_HTTP_PARSE_ERROR},
{negative_length_http_response_hdr, false, OCSP_HTTP_PARSE_ERROR},
{nonnumeric_length_http_response_hdr, false, OCSP_HTTP_PARSE_ERROR},
{format_string_http_response_hdr, false, OCSP_HTTP_PARSE_ERROR},
{max_length_http_response_hdr, false, OCSP_HTTP_PARSE_ERROR}};
class OCSPHTTPTest : public testing::TestWithParam<OCSPHTTPTestVector> {};
INSTANTIATE_TEST_SUITE_P(All, OCSPHTTPTest,
testing::ValuesIn(kResponseHTTPVectors));
// Test if OCSP_sendreq_bio() can properly send out an HTTP request with
// a |OCSP_REQUEST|, and expect an HTTP response with an OCSP response back.
// Always sends the same OCSP request and replies with the same OCSP response.
// This test focuses parsing the OCSP response with HTTP. We have other tests
// to test if the actual OCSP response is parsable and verifiable.
TEST_P(OCSPHTTPTest, OCSPRequestHTTP) {
const OCSPHTTPTestVector &t = GetParam();
std::string data =
GetTestData(std::string("crypto/ocsp/test/aws/ocsp_request.der").c_str());
std::vector<uint8_t> ocsp_request_data(data.begin(), data.end());
std::string respData = GetTestData(
std::string("crypto/ocsp/test/aws/ocsp_response.der").c_str());
std::vector<uint8_t> ocsp_response_data(respData.begin(), respData.end());
// Write an HTTP OCSP response into the BIO.
bssl::UniquePtr<BIO> bio(BIO_new(BIO_s_mem()));
const int respLen = (int)ocsp_response_data.size();
ASSERT_GT(BIO_printf(bio.get(), "%s", t.http_header), 0);
ASSERT_GT(BIO_printf(bio.get(), "%d\r\n\r\n", respLen), 0);
if (t.response_attached) {
ASSERT_EQ(BIO_write(bio.get(), ocsp_response_data.data(), respLen),
respLen);
}
// Sends out an OCSP request and expects an OCSP response in |BIO|.
bssl::UniquePtr<OCSP_REQUEST> ocspRequest =
LoadOCSP_REQUEST(ocsp_request_data);
ASSERT_TRUE(ocspRequest);
bssl::UniquePtr<OCSP_RESPONSE> ocspResponse(
OCSP_sendreq_bio(bio.get(), nullptr, ocspRequest.get()));
if (t.expected_status == OCSP_HTTP_PARSE_SUCCESS && t.response_attached) {
ASSERT_TRUE(ocspResponse);
} else {
ASSERT_FALSE(ocspResponse);
}
}
struct OCSPURLTestVector {
const char *ocsp_url;
const char *expected_host;
const char *expected_port;
const char *expected_path;
int expected_ssl;
int expected_status;
};
static const OCSPURLTestVector kOCSPURLVectors[] = {
// === VALID URLs ===
{"http://ocsp.example.com/", "ocsp.example.com", "80", "/", 0,
OCSP_URL_PARSE_SUCCESS},
// Non-standard port.
{"http://ocsp.example.com:8080/", "ocsp.example.com", "8080", "/", 0,
OCSP_URL_PARSE_SUCCESS},
{"http://ocsp.example.com/ocsp", "ocsp.example.com", "80", "/ocsp", 0,
OCSP_URL_PARSE_SUCCESS},
// Port for HTTPS is 443.
{"https://ocsp.example.com/", "ocsp.example.com", "443", "/", 1,
OCSP_URL_PARSE_SUCCESS},
// Empty path, the default should be "/".
{"https://ocsp.example.com", "ocsp.example.com", "443", "/", 1,
OCSP_URL_PARSE_SUCCESS},
// Parsing an IPv6 host address.
{"http://[2001:db8::1]/ocsp", "2001:db8::1", "80", "/ocsp", 0,
OCSP_URL_PARSE_SUCCESS},
{"https://[2001:db8::1]/ocsp", "2001:db8::1", "443", "/ocsp", 1,
OCSP_URL_PARSE_SUCCESS},
// Parsing a URL with an invalid port, but OpenSSL still parses the string
// where the port is expected.
{"http://ocsp.example.com:invalid/", "ocsp.example.com", "invalid", "/", 0,
OCSP_URL_PARSE_SUCCESS},
// Empty host component.
{"http:///ocsp", "", "80", "/ocsp", 0, OCSP_URL_PARSE_SUCCESS},
// Potential Format String attacks.
{"http://%s%s%s%s%s/ocsp", "%s%s%s%s%s", "80", "/ocsp", 0,
OCSP_URL_PARSE_SUCCESS},
{"http://%s%s%s%s%s, argv[1]/ocsp", "%s%s%s%s%s, argv[1]", "80", "/ocsp", 0,
OCSP_URL_PARSE_SUCCESS},
{"http://ocsp/%s%s%s%s%s", "ocsp", "80", "/%s%s%s%s%s", 0,
OCSP_URL_PARSE_SUCCESS},
// === INVALID URLs ===
// Not http or https in front.
{"htp://ocsp.example.com/", nullptr, nullptr, nullptr, 0,
OCSP_URL_PARSE_ERROR},
{"%s%s%s%s://%s%s%s%s%s/ocsp", nullptr, nullptr, nullptr, 0,
OCSP_URL_PARSE_ERROR},
// Double slash is mandatory.
{"http:/ocsp.example.com/", nullptr, nullptr, nullptr, 0,
OCSP_URL_PARSE_ERROR},
// Malformed.
{"httocsp.example.com/", nullptr, nullptr, nullptr, 0,
OCSP_URL_PARSE_ERROR},
// No closing bracket for ipv6.
{"http://[2001:db8::1/", nullptr, nullptr, nullptr, 0,
OCSP_URL_PARSE_ERROR},
};
class OCSPURLTest : public testing::TestWithParam<OCSPURLTestVector> {};
INSTANTIATE_TEST_SUITE_P(All, OCSPURLTest, testing::ValuesIn(kOCSPURLVectors));
TEST_P(OCSPURLTest, OCSPParseURL) {
const OCSPURLTestVector &t = GetParam();
char *host = nullptr;
char *path = nullptr;
char *port = nullptr;
int is_ssl;
int ret = OCSP_parse_url(t.ocsp_url, &host, &port, &path, &is_ssl);
if (t.expected_status == OCSP_URL_PARSE_SUCCESS) {
EXPECT_EQ(ret, OCSP_URL_PARSE_SUCCESS);
EXPECT_EQ(std::string(host), std::string(t.expected_host));
EXPECT_EQ(std::string(port), std::string(t.expected_port));
EXPECT_EQ(std::string(path), std::string(t.expected_path));
OPENSSL_free(host);
OPENSSL_free(path);
OPENSSL_free(port);
} else {
EXPECT_EQ(ret, OCSP_URL_PARSE_ERROR);
EXPECT_FALSE(host);
EXPECT_FALSE(port);
EXPECT_FALSE(path);
}
}
// Nonce value used in "crypto/ocsp/test/aws/ocsp_response.der".
static unsigned char ocsp_response_nonce[] = {
0xaf, 0xab, 0xd4, 0xec, 0x6a, 0x17, 0x2c, 0x4a,
0x98, 0xfb, 0x1a, 0x6d, 0x22, 0xff, 0x29, 0x28};
struct OCSPNonceTestVector {
const char *ocsp_request;
const char *ocsp_response;
bool add_nonce;
unsigned char *nonce;
int nonce_len;
int nonce_check_status;
};
static const OCSPNonceTestVector kNonceTestVectors[] = {
// Add a nonce to an OCSP request without a nonce.
{"ocsp_request_no_nonce", "ocsp_response", true, ocsp_response_nonce,
sizeof(ocsp_response_nonce), OCSP_NONCE_EQUAL},
// Add a nonce to an OCSP request with a nonce. The original nonce should be
// overwritten.
{"ocsp_request", "ocsp_response", true, ocsp_response_nonce,
sizeof(ocsp_response_nonce), OCSP_NONCE_EQUAL},
// Generate a random nonce, which should be different from the nonce
// available in the OCSP response (unless we have a collision).
{"ocsp_request", "ocsp_response", true, nullptr, 0, OCSP_NONCE_NOT_EQUAL},
// OCSP request with no nonce, but an OCSP response that does have one.
{"ocsp_request_no_nonce", "ocsp_response", false, nullptr, 0,
OCSP_NONCE_RESPONSE_ONLY},
// OCSP request with a nonce, but an OCSP response that does not have one.
{"ocsp_request", "ocsp_response_no_nonce", false, nullptr, 0,
OCSP_NONCE_REQUEST_ONLY},
// An OCSP request and an OCSP response that don't have a nonce to compare.
{"ocsp_request_no_nonce", "ocsp_response_no_nonce", false, nullptr, 0,
OCSP_NONCE_BOTH_ABSENT},
};
class OCSPNonceTest : public testing::TestWithParam<OCSPNonceTestVector> {};
INSTANTIATE_TEST_SUITE_P(All, OCSPNonceTest,
testing::ValuesIn(kNonceTestVectors));
TEST_P(OCSPNonceTest, OCSPNonce) {
const OCSPNonceTestVector &t = GetParam();
std::string data =
GetTestData(std::string("crypto/ocsp/test/aws/" +
std::string(t.ocsp_request) + ".der")
.c_str());
std::vector<uint8_t> ocsp_request_data(data.begin(), data.end());
bssl::UniquePtr<OCSP_REQUEST> ocspRequest =
LoadOCSP_REQUEST(ocsp_request_data);
std::string respData =
GetTestData(std::string("crypto/ocsp/test/aws/" +
std::string(t.ocsp_response) + ".der")
.c_str());
std::vector<uint8_t> ocsp_response_data(respData.begin(), respData.end());
bssl::UniquePtr<OCSP_RESPONSE> ocspResponse =
LoadOCSP_RESPONSE(ocsp_response_data);
bssl::UniquePtr<OCSP_BASICRESP> basicResponse(
OCSP_response_get1_basic(ocspResponse.get()));
ASSERT_TRUE(basicResponse);
if (t.add_nonce) {
// Adding a nonce should succeed. The original nonce will get overwritten
// if one already exists.
EXPECT_TRUE(
OCSP_request_add1_nonce(ocspRequest.get(), t.nonce, t.nonce_len));
// Check if hard coded nonce value has been written correctly.
if (t.nonce) {
int req_idx = OCSP_REQUEST_get_ext_by_NID(ocspRequest.get(),
NID_id_pkix_OCSP_Nonce, -1);
const ASN1_OCTET_STRING *nonce_data = X509_EXTENSION_get_data(
OCSP_REQUEST_get_ext(ocspRequest.get(), req_idx));
// The ASN.1 Octet String data type encoding begins with a Tag byte of
// 0x04. The second byte is the length of the encoded value, so we compare
// the rest of the bytes to ensure the nonce value was written correctly.
// https://www.ietf.org/rfc/rfc6025.html#section-2.1.2
EXPECT_EQ(Bytes(&ASN1_STRING_get0_data(nonce_data)[2],
ASN1_STRING_length(nonce_data) - 2),
Bytes(t.nonce, t.nonce_len));
}
}
EXPECT_EQ(OCSP_check_nonce(ocspRequest.get(), basicResponse.get()),
t.nonce_check_status);
// Check that nonce copying from |req| to |bs| also works as expected.
if (t.nonce_check_status == OCSP_NONCE_RESPONSE_ONLY ||
t.nonce_check_status == OCSP_NONCE_BOTH_ABSENT) {
EXPECT_EQ(OCSP_copy_nonce(basicResponse.get(), ocspRequest.get()), 2);
EXPECT_EQ(OCSP_check_nonce(ocspRequest.get(), basicResponse.get()),
t.nonce_check_status);
} else {
// OpenSSL's implementation of |OCSP_copy_nonce| keeps the original nonce in
// |resp| at the start of the list. We have to remove the old nonce prior to
// copying the new one over.
int resp_idx = OCSP_BASICRESP_get_ext_by_NID(basicResponse.get(),
NID_id_pkix_OCSP_Nonce, -1);
if (resp_idx >= 0) {
bssl::UniquePtr<X509_EXTENSION> old_resp_ext(
OCSP_BASICRESP_delete_ext(basicResponse.get(), resp_idx));
}
EXPECT_EQ(OCSP_copy_nonce(basicResponse.get(), ocspRequest.get()), 1);
EXPECT_EQ(OCSP_check_nonce(ocspRequest.get(), basicResponse.get()),
OCSP_NONCE_EQUAL);
}
}
TEST(OCSPTest, OCSPNonce) {
std::string data = GetTestData(
std::string("crypto/ocsp/test/aws/ocsp_request_no_nonce.der").c_str());
std::vector<uint8_t> ocsp_request_data(data.begin(), data.end());
bssl::UniquePtr<OCSP_REQUEST> ocspRequest =
LoadOCSP_REQUEST(ocsp_request_data);
// Adding a nonce but using 0 to trigger the default specified length
// should fail. A length must be specified when adding a specified nonce.
EXPECT_FALSE(
OCSP_request_add1_nonce(ocspRequest.get(), ocsp_response_nonce, 0));
// Adding a random nonce with the default length should succeed.
// |OCSP_REQUEST_get_ext_by_NID| returns a negative number if a nonce does
// not exist.
EXPECT_LT(OCSP_REQUEST_get_ext_by_NID(ocspRequest.get(),
NID_id_pkix_OCSP_Nonce, -1),
0);
EXPECT_TRUE(OCSP_request_add1_nonce(ocspRequest.get(), nullptr, 0));
EXPECT_GE(OCSP_REQUEST_get_ext_by_NID(ocspRequest.get(),
NID_id_pkix_OCSP_Nonce, -1),
0);
// Same tests as above, but against an |OCSP_BASICRESP|.
data = GetTestData(
std::string("crypto/ocsp/test/aws/ocsp_response_no_nonce.der").c_str());
std::vector<uint8_t> ocsp_response_data(data.begin(), data.end());
bssl::UniquePtr<OCSP_RESPONSE> ocspResponse =
LoadOCSP_RESPONSE(ocsp_response_data);
ASSERT_TRUE(ocspResponse);
bssl::UniquePtr<OCSP_BASICRESP> basicResponse(
OCSP_response_get1_basic(ocspResponse.get()));
ASSERT_TRUE(basicResponse);
EXPECT_FALSE(
OCSP_basic_add1_nonce(basicResponse.get(), ocsp_response_nonce, 0));
// Adding a random nonce with the default length should succeed.
// |OCSP_BASICRESP_get_ext_by_NID| returns a negative number if a nonce does
// not exist.
// Add a random nonce with a specified length this time around.
EXPECT_LT(OCSP_BASICRESP_get_ext_by_NID(basicResponse.get(),
NID_id_pkix_OCSP_Nonce, -1),
0);
EXPECT_TRUE(OCSP_basic_add1_nonce(basicResponse.get(), nullptr, 10));
EXPECT_GE(OCSP_BASICRESP_get_ext_by_NID(basicResponse.get(),
NID_id_pkix_OCSP_Nonce, -1),
0);
}
TEST(OCSPTest, OCSPCRLString) {
for (int reason_code = 0; reason_code < 11; reason_code++) {
if (reason_code == 7) {
// Reason Code 7 is not used.
EXPECT_EQ("(UNKNOWN)", std::string(OCSP_crl_reason_str(7)));
continue;
}
EXPECT_NE("(UNKNOWN)", std::string(OCSP_crl_reason_str(reason_code)));
}
// More unexpected cases.
EXPECT_EQ("(UNKNOWN)", std::string(OCSP_crl_reason_str(100)));
EXPECT_EQ("(UNKNOWN)", std::string(OCSP_crl_reason_str(-1)));
EXPECT_EQ("(UNKNOWN)", std::string(OCSP_crl_reason_str(-100)));
}
TEST(OCSPTest, OCSPBIOTest) {
std::string reqData =
GetTestData(std::string("crypto/ocsp/test/aws/ocsp_request.der").c_str());
std::vector<uint8_t> ocsp_request_data(reqData.begin(), reqData.end());
std::string respData = GetTestData(
std::string("crypto/ocsp/test/aws/ocsp_response.der").c_str());
std::vector<uint8_t> ocsp_response_data(respData.begin(), respData.end());
// Write an OCSP response into the BIO.
bssl::UniquePtr<BIO> bio(BIO_new(BIO_s_mem()));
int respLen = (int)ocsp_response_data.size();
ASSERT_EQ(BIO_write(bio.get(), ocsp_response_data.data(), respLen), respLen);
bssl::UniquePtr<OCSP_RESPONSE> ocspResponse(
d2i_OCSP_RESPONSE_bio(bio.get(), nullptr));
EXPECT_TRUE(ocspResponse);
// Reserialize the OCSP response.
EXPECT_TRUE(i2d_OCSP_RESPONSE_bio(bio.get(), ocspResponse.get()));
const uint8_t *bio_data;
size_t bio_len;
ASSERT_TRUE(BIO_mem_contents(bio.get(), &bio_data, &bio_len));
EXPECT_EQ(Bytes(bio_data, bio_len),
Bytes(ocsp_response_data.data(), respLen));
// Write an OCSP request into the BIO, but it shouldn't successfully parse
// with |d2i_OCSP_RESPONSE_bio|.
ASSERT_TRUE(BIO_reset(bio.get()));
const int reqLen = (int)ocsp_request_data.size();
ASSERT_EQ(BIO_write(bio.get(), ocsp_request_data.data(), reqLen), reqLen);
ocspResponse.reset(d2i_OCSP_RESPONSE_bio(bio.get(), nullptr));
EXPECT_FALSE(ocspResponse);
// Write an OCSP request into the BIO.
bio.reset(BIO_new(BIO_s_mem()));
ASSERT_EQ(BIO_write(bio.get(), ocsp_request_data.data(), reqLen), reqLen);
bssl::UniquePtr<OCSP_REQUEST> ocspRequest(
d2i_OCSP_REQUEST_bio(bio.get(), nullptr));
EXPECT_TRUE(ocspRequest);
// Reserialize the OCSP request.
EXPECT_TRUE(i2d_OCSP_REQUEST_bio(bio.get(), ocspRequest.get()));
ASSERT_TRUE(BIO_mem_contents(bio.get(), &bio_data, &bio_len));
EXPECT_EQ(Bytes(bio_data, bio_len), Bytes(ocsp_request_data.data(), reqLen));
// Write an OCSP response into the BIO, but it shouldn't successfully parse
// with |d2i_OCSP_REQUEST_bio|.
ASSERT_TRUE(BIO_reset(bio.get()));
ASSERT_EQ(BIO_write(bio.get(), ocsp_response_data.data(), respLen), respLen);
ocspRequest.reset(d2i_OCSP_REQUEST_bio(bio.get(), nullptr));
EXPECT_FALSE(ocspRequest);
}
TEST(OCSPTest, CertIDDup) {
bssl::UniquePtr<X509> issuer(CertFromPEM(
GetTestData(std::string("crypto/ocsp/test/aws/ca_cert.pem").c_str())
.c_str()));
bssl::UniquePtr<X509> subject(CertFromPEM(
GetTestData(std::string("crypto/ocsp/test/aws/rsa_cert.pem").c_str())
.c_str()));
// Create a sample OCSP_CERTID structure
bssl::UniquePtr<OCSP_CERTID> orig_id(
OCSP_cert_to_id(EVP_sha256(), subject.get(), issuer.get()));
ASSERT_TRUE(orig_id);
// Duplicate the OCSP_CERTID structure
bssl::UniquePtr<OCSP_CERTID> dup_id(OCSP_CERTID_dup(orig_id.get()));
ASSERT_TRUE(dup_id);
// Check that the duplicated structure has the same values as the original.
EXPECT_EQ(
ASN1_INTEGER_cmp(orig_id.get()->serialNumber, dup_id.get()->serialNumber),
0);
EXPECT_EQ(ASN1_STRING_cmp(orig_id.get()->issuerNameHash,
dup_id.get()->issuerNameHash),
0);
EXPECT_EQ(ASN1_STRING_cmp(orig_id.get()->issuerKeyHash,
dup_id.get()->issuerKeyHash),
0);
EXPECT_EQ(
X509_ALGOR_cmp(orig_id.get()->hashAlgorithm, dup_id.get()->hashAlgorithm),
0);
// Check that the duplicated structure is not just a replicated pointer.
EXPECT_NE(orig_id.get(), dup_id.get());
}
TEST(OCSPTest, OCSPResponsePrint) {
static const std::array<std::string, 19> kExpected{
{"OCSP Response Data:", " OCSP Response Status: successful (0x0)",
" Response Type: Basic OCSP Response", " Version: 1 (0x0)",
" Responder Id: C = US, ST = WA, O = s2n, OU = s2n Test OCSP, CN = "
"ocsp.s2ntest.com",
" Produced At: May 26 00:23:34 2021 GMT",
" Responses:", " Certificate ID:", " Hash Algorithm: sha1",
" Issuer Name Hash: DE7932B3217E48FB4E47AE0B9007A55376AE44CA",
" Issuer Key Hash: 12DF817571CA92D3CE1B2C2B773B9E3377F3F76F",
" Serial Number: 7778", " Cert Status: good",
" This Update: May 26 00:23:34 2021 GMT",
" Next Update: May 24 00:23:34 2031 GMT", "",
" Response Extensions:", " OCSP Nonce: ",
" 0410AFABD4EC6A172C4A98FB1A6D22FF2928"}};
std::string respData = GetTestData(
std::string("crypto/ocsp/test/aws/ocsp_response.der").c_str());
std::vector<uint8_t> ocsp_response_data(respData.begin(), respData.end());
bssl::UniquePtr<OCSP_RESPONSE> ocspResponse =
LoadOCSP_RESPONSE(ocsp_response_data);
bssl::UniquePtr<BIO> bio(BIO_new(BIO_s_mem()));
EXPECT_TRUE(OCSP_RESPONSE_print(bio.get(), ocspResponse.get(), 0));
const uint8_t *out;
size_t outlen;
ASSERT_TRUE(BIO_mem_contents(bio.get(), &out, &outlen));
// Iterate through |kExpected| and verify that |OCSP_RESPONSE_print| has
// the expected format.
std::istringstream iss((std::string((char *)out, outlen)));
std::string line;
for (const auto &expected : kExpected) {
std::getline(iss, line);
EXPECT_EQ(line, expected);
}
}
TEST(OCSPTest, OCSPRequestPrint) {
static const std::array<std::string, 11> kExpected{
{"OCSP Request Data:", " Version: 1 (0x0)", " Requestor List:",
" Certificate ID:", " Hash Algorithm: sha1",
" Issuer Name Hash: DE7932B3217E48FB4E47AE0B9007A55376AE44CA",
" Issuer Key Hash: 12DF817571CA92D3CE1B2C2B773B9E3377F3F76F",
" Serial Number: 7778",
" Request Extensions:", " OCSP Nonce: ",
" 0410303F128CD824A2B465F4C846882B3E1F"}};
std::string data =
GetTestData(std::string("crypto/ocsp/test/aws/ocsp_request.der").c_str());
std::vector<uint8_t> ocsp_request_data(data.begin(), data.end());
bssl::UniquePtr<OCSP_REQUEST> ocspRequest =
LoadOCSP_REQUEST(ocsp_request_data);
bssl::UniquePtr<BIO> bio(BIO_new(BIO_s_mem()));
EXPECT_TRUE(OCSP_REQUEST_print(bio.get(), ocspRequest.get(), 0));
const uint8_t *out;
size_t outlen;
ASSERT_TRUE(BIO_mem_contents(bio.get(), &out, &outlen));
// Iterate through |kExpected| and verify that |OCSP_REQUEST_print| has
// the expected format.
std::istringstream iss((std::string((char *)out, outlen)));
std::string line;
for (const auto &expected : kExpected) {
std::getline(iss, line);
EXPECT_EQ(line, expected);
}
}
TEST(OCSPTest, OCSPUtilityFunctions) {
// Create new OCSP_CERTID
OCSP_CERTID *cert_id = OCSP_CERTID_new();
ASSERT_TRUE(cert_id);
bssl::UniquePtr<OCSP_REQUEST> request(OCSP_REQUEST_new());
ASSERT_TRUE(request);
// Test that an |OCSP_ONEREQ| does not exist yet.
EXPECT_EQ(OCSP_request_onereq_count(request.get()), 0);
EXPECT_FALSE(OCSP_request_onereq_get0(request.get(), 0));
OCSP_ONEREQ *one = OCSP_request_add0_id(request.get(), cert_id);
ASSERT_TRUE(one);
EXPECT_EQ(OCSP_request_onereq_count(request.get()), 1);
EXPECT_TRUE(OCSP_request_onereq_get0(request.get(), 0));
// Call function to get OCSP_CERTID
OCSP_CERTID *returned_id = OCSP_onereq_get0_id(one);
// Verify the returned OCSP_CERTID is same as the one set
ASSERT_EQ(returned_id, cert_id);
}
TEST(OCSPTest, OCSP_SINGLERESP) {
bssl::UniquePtr<OCSP_SINGLERESP> single_resp(OCSP_SINGLERESP_new());
ASSERT_TRUE(single_resp);
// Initialize an |X509_EXTENSION| for testing.
bssl::UniquePtr<ASN1_OCTET_STRING> ext_oct(ASN1_OCTET_STRING_new());
const uint8_t data[] = {0x01, 0x02, 0x03, 0x04, 0x05};
ASSERT_TRUE(ASN1_OCTET_STRING_set(ext_oct.get(), data, sizeof(data)));
bssl::UniquePtr<X509_EXTENSION> ext(X509_EXTENSION_create_by_NID(
nullptr, NID_id_pkix_OCSP_CrlID, 0, ext_oct.get()));
ASSERT_TRUE(ext);
// Test |X509_EXTENSION|s work with |OCSP_SINGLERESP|.
EXPECT_TRUE(OCSP_SINGLERESP_add_ext(single_resp.get(), ext.get(), -1));
EXPECT_EQ(OCSP_SINGLERESP_get_ext_count(single_resp.get()), 1);
X509_EXTENSION *retrieved_ext = OCSP_SINGLERESP_get_ext(single_resp.get(), 0);
ASSERT_EQ(ASN1_OCTET_STRING_cmp(X509_EXTENSION_get_data(retrieved_ext),
X509_EXTENSION_get_data(ext.get())),
0);
}