// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 OR ISC #include #include #include #include "openssl/ocsp.h" #include "openssl/pem.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_REQUEST_SIGN_SUCCESS 1 #define OCSP_REQUEST_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 *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 RSAFromPEM(const char *pem) { bssl::UniquePtr bio(BIO_new_mem_buf(pem, strlen(pem))); return bssl::UniquePtr( PEM_read_bio_RSAPrivateKey(bio.get(), nullptr, nullptr, nullptr)); } static bssl::UniquePtr ECDSAFromPEM(const char *pem) { bssl::UniquePtr bio(BIO_new_mem_buf(pem, strlen(pem))); return bssl::UniquePtr( PEM_read_bio_ECPrivateKey(bio.get(), nullptr, nullptr, nullptr)); } static bssl::UniquePtr CertChainFromPEM(const char *pem) { bssl::UniquePtr stack(sk_X509_new_null()); if (!stack) { return nullptr; } bssl::UniquePtr bio(BIO_new_mem_buf(pem, strlen(pem))); for (;;) { bssl::UniquePtr cert = bssl::UniquePtr( 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 CertsToStack( const std::vector &certs) { bssl::UniquePtr stack(sk_X509_new_null()); if (!stack) { return nullptr; } for (auto cert : certs) { if (!bssl::PushToStack(stack.get(), bssl::UpRef(cert))) { return nullptr; } } return stack; } static bssl::UniquePtr LoadOCSP_RESPONSE( bssl::Span der) { const uint8_t *ptr = der.data(); return bssl::UniquePtr( d2i_OCSP_RESPONSE(nullptr, &ptr, der.size())); } static bssl::UniquePtr LoadOCSP_REQUEST( bssl::Span der) { const uint8_t *ptr = der.data(); return bssl::UniquePtr( d2i_OCSP_REQUEST(nullptr, &ptr, der.size())); } static void ExtractAndVerifyBasicOCSP( bssl::Span der, const std::string ca_cert_file, const std::string server_cert_file, int expected_ocsp_verify_status, bssl::UniquePtr *basic_response, bssl::UniquePtr *server_cert_chain) { bssl::UniquePtr 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_response_get1_basic(ocsp_response.get())); ASSERT_TRUE(*basic_response); // Set up trust store and certificate chain. bssl::UniquePtr ca_cert(CertFromPEM( GetTestData( std::string("crypto/ocsp/test/aws/" + ca_cert_file + ".pem").c_str()) .c_str())); bssl::UniquePtr server_cert( CertFromPEM(GetTestData(std::string("crypto/ocsp/test/aws/" + server_cert_file + ".pem") .c_str()) .c_str())); bssl::UniquePtr 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 *basic_response, bssl::UniquePtr *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 cert_id = bssl::UniquePtr(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", "server_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", "server_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", "server_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", "server_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", "server_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", "server_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", "server_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", "server_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", "server_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", "server_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", "server_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", "server_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", "server_cert", EVP_sha256(), OCSP_VERIFYSTATUS_ERROR, OCSP_RESPFINDSTATUS_UNDEFINED, -1, nullptr}, }; class OCSPTestAWS : public testing::TestWithParam {}; INSTANTIATE_TEST_SUITE_P(All, OCSPTestAWS, testing::ValuesIn(nTestVectors)); TEST_P(OCSPTestAWS, VerifyOCSPResponseExtended) { const OCSPAWSTestVector &t = GetParam(); std::string data = GetTestData(std::string("crypto/ocsp/test/aws/" + std::string(t.ocsp_response) + ".der") .c_str()); std::vector ocsp_reponse_data(data.begin(), data.end()); // OCSP response parsing and verification step. bssl::UniquePtr basic_response; bssl::UniquePtr 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 {}; INSTANTIATE_TEST_SUITE_P(All, OCSPResponseStatusTest, testing::ValuesIn(respTestVectors)); TEST_P(OCSPResponseStatusTest, VerifyOCSPResponseExtended) { const OCSPResponseStatusTestVector &t = GetParam(); std::string data = GetTestData(std::string("crypto/ocsp/test/aws/" + std::string(t.ocsp_response) + ".der") .c_str()); std::vector ocsp_reponse_data(data.begin(), data.end()); bssl::UniquePtr 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 ocsp_reponse_data(data.begin(), data.end()); bssl::UniquePtr basic_response; bssl::UniquePtr server_cert_chain; ExtractAndVerifyBasicOCSP(ocsp_reponse_data, "ca_cert", "server_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]". 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 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 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 basic_response; bssl::UniquePtr server_cert_chain; ExtractAndVerifyBasicOCSP(ocsp_reponse_data, "ca_cert", "server_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 ocsp_reponse_data(data.begin(), data.end()); bssl::UniquePtr basic_response; bssl::UniquePtr server_cert_chain; ExtractAndVerifyBasicOCSP(ocsp_reponse_data, "ca_cert", "server_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 OCSPVerifyFlagTestVector { const char *ocsp_response; unsigned long ocsp_verify_flag; bool include_expired_signer_cert; int expected_ocsp_verify_status; }; static const OCSPVerifyFlagTestVector OCSPVerifyFlagTestVectors[] = { {"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 {}; INSTANTIATE_TEST_SUITE_P(All, OCSPVerifyFlagTest, testing::ValuesIn(OCSPVerifyFlagTestVectors)); TEST_P(OCSPVerifyFlagTest, OCSPVerifyFlagTest) { const OCSPVerifyFlagTestVector &t = GetParam(); std::string respData = GetTestData(std::string("crypto/ocsp/test/aws/" + std::string(t.ocsp_response) + ".der") .c_str()); std::vector ocsp_response_data(respData.begin(), respData.end()); bssl::UniquePtr 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 basic_response = bssl::UniquePtr( OCSP_response_get1_basic(ocsp_response.get())); ASSERT_TRUE(basic_response); // Set up trust store and certificate chain. bssl::UniquePtr ca_cert(CertFromPEM( GetTestData(std::string("crypto/ocsp/test/aws/ca_cert.pem").c_str()) .c_str())); bssl::UniquePtr server_cert(CertFromPEM( GetTestData(std::string("crypto/ocsp/test/aws/server_cert.pem").c_str()) .c_str())); bssl::UniquePtr trust_store(X509_STORE_new()); X509_STORE_add_cert(trust_store.get(), ca_cert.get()); bssl::UniquePtr server_cert_chain( CertsToStack({server_cert.get(), ca_cert.get()})); ASSERT_TRUE(server_cert_chain); if (t.include_expired_signer_cert) { bssl::UniquePtr 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); } TEST(OCSPTest, GetInfo) { bssl::UniquePtr issuer(CertFromPEM( GetTestData(std::string("crypto/ocsp/test/aws/ca_cert.pem").c_str()) .c_str())); bssl::UniquePtr subject(CertFromPEM( GetTestData(std::string("crypto/ocsp/test/aws/server_cert.pem").c_str()) .c_str())); // Create a sample |OCSP_CERTID| structure. bssl::UniquePtr cert_id( OCSP_cert_to_id(EVP_sha256(), subject.get(), issuer.get())); 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 ocsp_response_data(respData.begin(), respData.end()); bssl::UniquePtr ocspResponse = LoadOCSP_RESPONSE(ocsp_response_data); bssl::UniquePtr basicResponse( OCSP_response_get1_basic(ocspResponse.get())); ASSERT_TRUE(basicResponse); bssl::UniquePtr 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()); } // === 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 {}; INSTANTIATE_TEST_SUITE_P(All, OCSPTest, testing::ValuesIn(kTestVectors)); TEST_P(OCSPTest, VerifyOCSPResponse) { const OCSPTestVector &t = GetParam(); bssl::UniquePtr ocsp_response; bssl::UniquePtr 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 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_response_get1_basic(ocsp_response.get())); ASSERT_TRUE(basic_response); // Set up OpenSSL OCSP tests trust store parameters and cert chain. bssl::UniquePtr server_cert_chain; bssl::UniquePtr trust_store(X509_STORE_new()); bssl::UniquePtr 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 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 OCSPReqTestVector { const char *ocsp_request; int expected_parse_status; int expected_sign_status; const char *signer_cert; const char *private_key; const EVP_MD *dgst; }; static const OCSPReqTestVector kRequestTestVectors[] = { {"ocsp_request", OCSP_REQUEST_PARSE_SUCCESS, OCSP_REQUEST_SIGN_SUCCESS, "server_cert", "server_key", EVP_sha1()}, {"ocsp_request", OCSP_REQUEST_PARSE_SUCCESS, OCSP_REQUEST_SIGN_SUCCESS, "server_cert", "server_key", EVP_sha256()}, {"ocsp_request_attached_cert", OCSP_REQUEST_PARSE_SUCCESS, OCSP_REQUEST_SIGN_ERROR, "server_cert", "server_key", nullptr}, {"ocsp_request_no_nonce", OCSP_REQUEST_PARSE_SUCCESS, OCSP_REQUEST_SIGN_SUCCESS, "server_cert", "server_key", EVP_sha512()}, {"ocsp_request_signed", OCSP_REQUEST_PARSE_SUCCESS, OCSP_REQUEST_SIGN_ERROR, "server_cert", "server_key", nullptr}, {"ocsp_request_signed_sha256", OCSP_REQUEST_PARSE_SUCCESS, OCSP_REQUEST_SIGN_ERROR, "server_cert", "server_key", nullptr}, {"ocsp_response", OCSP_REQUEST_PARSE_ERROR, OCSP_REQUEST_SIGN_ERROR, "server_cert", "server_key", nullptr}, // Test signing with ECDSA certs and keys. {"ocsp_request", OCSP_REQUEST_PARSE_SUCCESS, OCSP_REQUEST_SIGN_SUCCESS, "server_ecdsa_cert", "server_ecdsa_key", EVP_sha1()}, {"ocsp_request", OCSP_REQUEST_PARSE_SUCCESS, OCSP_REQUEST_SIGN_SUCCESS, "server_ecdsa_cert", "server_ecdsa_key", EVP_sha256()}, {"ocsp_request_no_nonce", OCSP_REQUEST_PARSE_SUCCESS, OCSP_REQUEST_SIGN_SUCCESS, "server_cert", "server_key", EVP_sha256()}, {"ocsp_request_no_nonce", OCSP_REQUEST_PARSE_SUCCESS, OCSP_REQUEST_SIGN_SUCCESS, "server_ecdsa_cert", "server_ecdsa_key", EVP_sha256()}, // Test certificate type mismatch. {"ocsp_request", OCSP_REQUEST_PARSE_SUCCESS, OCSP_REQUEST_SIGN_ERROR, "server_cert", "server_ecdsa_key", EVP_sha256()}, {"ocsp_request", OCSP_REQUEST_PARSE_SUCCESS, OCSP_REQUEST_SIGN_ERROR, "server_ecdsa_cert", "server_key", EVP_sha256()}, // Test certificate key and cert mismatch. {"ocsp_request", OCSP_REQUEST_PARSE_SUCCESS, OCSP_REQUEST_SIGN_ERROR, "ca_cert", "server_key", EVP_sha256()}, }; class OCSPRequestTest : public testing::TestWithParam {}; 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 OCSPReqTestVector &t = GetParam(); std::string data = GetTestData(std::string("crypto/ocsp/test/aws/" + std::string(t.ocsp_request) + ".der") .c_str()); std::vector ocsp_request_data(data.begin(), data.end()); bssl::UniquePtr 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_new(BIO_s_mem())); const uint8_t *out; size_t outlen; bssl::UniquePtr 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 bio2(BIO_new(BIO_s_mem())); const uint8_t *out2; size_t outlen2; bssl::UniquePtr 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(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 OCSPReqTestVector &t = GetParam(); std::string data = GetTestData(std::string("crypto/ocsp/test/aws/" + std::string(t.ocsp_request) + ".der") .c_str()); std::vector ocsp_request_data(data.begin(), data.end()); bssl::UniquePtr ocspRequest = LoadOCSP_REQUEST(ocsp_request_data); bssl::UniquePtr server_cert( CertFromPEM(GetTestData(std::string("crypto/ocsp/test/aws/" + std::string(t.signer_cert) + ".pem") .c_str()) .c_str())); ASSERT_TRUE(server_cert); bssl::UniquePtr additional_cert(CertChainFromPEM( GetTestData(std::string("crypto/ocsp/test/aws/ca_cert.pem").c_str()) .c_str())); ASSERT_TRUE(additional_cert); if (t.expected_parse_status == OCSP_REQUEST_PARSE_SUCCESS) { bssl::UniquePtr pkey(EVP_PKEY_new()); if (std::string(t.private_key) == "server_key") { bssl::UniquePtr rsa(RSAFromPEM( GetTestData(std::string("crypto/ocsp/test/aws/" + std::string(t.private_key) + ".pem") .c_str()) .c_str())); ASSERT_TRUE(rsa); ASSERT_TRUE(EVP_PKEY_set1_RSA(pkey.get(), rsa.get())); } else { bssl::UniquePtr ecdsa(ECDSAFromPEM( GetTestData(std::string("crypto/ocsp/test/aws/" + std::string(t.private_key) + ".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(), server_cert.get(), pkey.get(), t.dgst, additional_cert.get(), 0); if (t.expected_sign_status == OCSP_REQUEST_SIGN_SUCCESS) { ASSERT_TRUE(ret); } else { ASSERT_FALSE(ret); } } } 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 ocsp_request_data(data.begin(), data.end()); bssl::UniquePtr ocspRequest = LoadOCSP_REQUEST(ocsp_request_data); bssl::UniquePtr bio(BIO_new(BIO_s_mem())); const uint8_t *out; size_t outlen; bssl::UniquePtr 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(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 ocsp_request_data(data.begin(), data.end()); bssl::UniquePtr ocspRequest = LoadOCSP_REQUEST(ocsp_request_data); ASSERT_TRUE(ocspRequest); // Construct |OCSP_CERTID| from certs. bssl::UniquePtr ca_cert(CertFromPEM( GetTestData(std::string("crypto/ocsp/test/aws/ca_cert.pem").c_str()) .c_str())); bssl::UniquePtr server_cert(CertFromPEM( GetTestData(std::string("crypto/ocsp/test/aws/server_cert.pem").c_str()) .c_str())); OCSP_CERTID *certId = OCSP_cert_to_id(nullptr, ca_cert.get(), server_cert.get()); 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 ocsp_request_data(data.begin(), data.end()); bssl::UniquePtr ocspRequest = LoadOCSP_REQUEST(ocsp_request_data); ASSERT_TRUE(ocspRequest); bssl::UniquePtr bio(BIO_new(BIO_s_mem())); bssl::UniquePtr 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 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 {}; 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 ocsp_request_data(data.begin(), data.end()); std::string respData = GetTestData( std::string("crypto/ocsp/test/aws/ocsp_response.der").c_str()); std::vector ocsp_response_data(respData.begin(), respData.end()); // Write an HTTP OCSP response into the BIO. bssl::UniquePtr 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 ocspRequest = LoadOCSP_REQUEST(ocsp_request_data); ASSERT_TRUE(ocspRequest); bssl::UniquePtr 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 {}; 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 {}; 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 ocsp_request_data(data.begin(), data.end()); bssl::UniquePtr 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 ocsp_response_data(respData.begin(), respData.end()); bssl::UniquePtr ocspResponse = LoadOCSP_RESPONSE(ocsp_response_data); bssl::UniquePtr 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); } TEST(OCSPTest, OCSPNonce) { std::string data = GetTestData( std::string("crypto/ocsp/test/aws/ocsp_request_no_nonce.der").c_str()); std::vector ocsp_request_data(data.begin(), data.end()); bssl::UniquePtr 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); } 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 ocsp_request_data(reqData.begin(), reqData.end()); std::string respData = GetTestData( std::string("crypto/ocsp/test/aws/ocsp_response.der").c_str()); std::vector ocsp_response_data(respData.begin(), respData.end()); // Write an OCSP response into the BIO. bssl::UniquePtr 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 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|. 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 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|. 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 issuer(CertFromPEM( GetTestData(std::string("crypto/ocsp/test/aws/ca_cert.pem").c_str()) .c_str())); bssl::UniquePtr subject(CertFromPEM( GetTestData(std::string("crypto/ocsp/test/aws/server_cert.pem").c_str()) .c_str())); // Create a sample OCSP_CERTID structure bssl::UniquePtr orig_id( OCSP_cert_to_id(EVP_sha256(), subject.get(), issuer.get())); ASSERT_TRUE(orig_id); // Duplicate the OCSP_CERTID structure bssl::UniquePtr 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 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 ocsp_response_data(respData.begin(), respData.end()); bssl::UniquePtr ocspResponse = LoadOCSP_RESPONSE(ocsp_response_data); bssl::UniquePtr 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 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 ocsp_request_data(data.begin(), data.end()); bssl::UniquePtr ocspRequest = LoadOCSP_REQUEST(ocsp_request_data); bssl::UniquePtr 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); } }