Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Importing OpenSSL OCSP tests #181

Merged
merged 12 commits into from
Jun 29, 2021
1 change: 1 addition & 0 deletions crypto/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -574,6 +574,7 @@ if(BUILD_TESTING)
lhash/lhash_test.cc
obj/obj_test.cc
ocsp/ocsp_test.cc
ocsp/ocsp_openssl_test.cc
pem/pem_test.cc
pkcs7/pkcs7_test.cc
pkcs8/pkcs8_test.cc
Expand Down
206 changes: 206 additions & 0 deletions crypto/ocsp/ocsp_openssl_test.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,206 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
#include <gtest/gtest.h>

#include "openssl/ocsp.h"
#include "openssl/pem.h"

#include "../test/test_util.h"

struct OpenSSL_OCSPTestVector{
std::string ocsp_response;
std::string cafile;
std::string untrusted;
int expected_ocsp_verify_status;
};

// Test vectors from OpenSSL OCSP's tests
// https://github.com/openssl/openssl/blob/OpenSSL_1_1_1-stable/test/recipes/80-test_ocsp.t
static const OpenSSL_OCSPTestVector kTestVectors[] = {
// === VALID OCSP RESPONSES ===
{"ND1","ND1_Issuer_ICA","",1},
{"ND2","ND2_Issuer_Root","",1},
{"ND3","ND3_Issuer_Root","",1},
{"ND1","ND1_Cross_Root","ND1_Issuer_ICA-Cross",1},
{"D1","D1_Issuer_ICA","",1},
{"D2","D2_Issuer_Root","",1},
{"D3","D3_Issuer_Root","",1},
// === INVALID SIGNATURE on the OCSP RESPONSE ===
{"ISOP_ND1","ND1_Issuer_ICA","",0},
{"ISOP_ND2","ND2_Issuer_Root","",0},
{"ISOP_ND3","ND3_Issuer_Root","",0},
{"ISOP_D1","D1_Issuer_ICA","",0},
{"ISOP_D2","D2_Issuer_Root","",0},
{"ISOP_D3","D3_Issuer_Root","",0},
// === WRONG RESPONDERID in the OCSP RESPONSE ===
{"WRID_ND1","ND1_Issuer_ICA","",0},
{"WRID_ND2","ND2_Issuer_Root","",0},
{"WRID_ND3","ND3_Issuer_Root","",0},
{"WRID_D1","D1_Issuer_ICA","",0},
{"WRID_D2","D2_Issuer_Root","",0},
{"WRID_D3","D3_Issuer_Root","",0},
// === WRONG ISSUERNAMEHASH in the OCSP RESPONSE ===
{"WINH_ND1","ND1_Issuer_ICA","",0},
{"WINH_ND2","ND2_Issuer_Root","",0},
{"WINH_ND3","ND3_Issuer_Root","",0},
{"WINH_D1","D1_Issuer_ICA","",0},
{"WINH_D2","D2_Issuer_Root","",0},
{"WINH_D3","D3_Issuer_Root","",0},
// === WRONG ISSUERKEYHASH in the OCSP RESPONSE ===
{"WIKH_ND1","ND1_Issuer_ICA","",0},
{"WIKH_ND2","ND2_Issuer_Root","",0},
{"WIKH_ND3","ND3_Issuer_Root","",0},
{"WIKH_D1","D1_Issuer_ICA","",0},
{"WIKH_D2","D2_Issuer_Root","",0},
{"WIKH_D3","D3_Issuer_Root","",0},
// === WRONG KEY in the DELEGATED OCSP SIGNING CERTIFICATE ===
{"WKDOSC_D1","D1_Issuer_ICA","",0},
{"WKDOSC_D2","D2_Issuer_Root","",0},
{"WKDOSC_D3","D3_Issuer_Root","",0},
// === INVALID SIGNATURE on the DELEGATED OCSP SIGNING CERTIFICATE ===
{"ISDOSC_D1","D1_Issuer_ICA","",0},
{"ISDOSC_D1","D2_Issuer_Root","",0},
{"ISDOSC_D1","D3_Issuer_Root","",0},
// === WRONG SUBJECT NAME in the ISSUER CERTIFICATE ===
{"ND1","WSNIC_ND1_Issuer_ICA","",0},
{"ND2","WSNIC_ND2_Issuer_Root","",0},
{"ND3","WSNIC_ND3_Issuer_Root","",0},
{"D1","WSNIC_D1_Issuer_ICA","",0},
{"D2","WSNIC_D2_Issuer_Root","",0},
{"D3","WSNIC_D3_Issuer_Root","",0},
// === WRONG KEY in the ISSUER CERTIFICATE ===
{"ND1","WKIC_ND1_Issuer_ICA","",0},
{"ND2","WKIC_ND2_Issuer_Root","",0},
{"ND3","WKIC_ND3_Issuer_Root","",0},
{"D1","WKIC_D1_Issuer_ICA","",0},
{"D2","WKIC_D2_Issuer_Root","",0},
{"D3","WKIC_D3_Issuer_Root","",0},
// === 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","",1},
{"ND2","ISIC_ND2_Issuer_Root","",1},
{"ND3","ISIC_ND3_Issuer_Root","",1},
{"D1","ISIC_D1_Issuer_ICA","",1},
{"D2","ISIC_D2_Issuer_Root","",1},
{"D3","ISIC_D3_Issuer_Root","",1},
};


class OpenSSL_OCSPTest : public testing::TestWithParam<OpenSSL_OCSPTestVector> {};

INSTANTIATE_TEST_SUITE_P(All, OpenSSL_OCSPTest, testing::ValuesIn(kTestVectors));


std::string GetTestData(const char *path);

static bool DecodeBase64(std::vector<uint8_t> *out, const char *in) {
size_t len;
if (!EVP_DecodedLength(&len, strlen(in))) {
fprintf(stderr, "EVP_DecodedLength failed\n");
return false;
}

out->resize(len);
if (!EVP_DecodeBase64(out->data(), &len, len, (const uint8_t *)in,
strlen(in))) {
fprintf(stderr, "EVP_DecodeBase64 failed\n");
return false;
}
out->resize(len);
return true;
}

// CertFromPEM parses the given, NUL-terminated pem block and returns an |X509*|.
static bssl::UniquePtr<X509> CertFromPEM(const char *pem) {
bssl::UniquePtr<BIO> bio(BIO_new_mem_buf(pem, strlen(pem)));
return bssl::UniquePtr<X509>(
PEM_read_bio_X509(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<STACK_OF(X509)> CertsToStack(
const std::vector<X509 *> &certs) {
bssl::UniquePtr<STACK_OF(X509)> 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<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()));
}

TEST_P(OpenSSL_OCSPTest, VerifyOpenSSL_OCSP_response) {
const OpenSSL_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/openssl-ocsp/" + t.ocsp_response + ".ors").c_str());
data.erase(std::remove(data.begin(), data.end(), '\n'), data.end());
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);
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_cert(CertFromPEM(
GetTestData(std::string("crypto/ocsp/test/openssl-ocsp/" + t.cafile + ".pem").c_str()).c_str()));
X509_STORE_add_cert(trust_store.get(),ca_cert.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(t.untrusted.compare("") == 0){
server_cert_chain = CertsToStack({ca_cert.get()});
}
else {
server_cert_chain = CertChainFromPEM(GetTestData(std::string("crypto/ocsp/test/openssl-ocsp/" + 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);
}
24 changes: 15 additions & 9 deletions crypto/ocsp/ocsp_verify.c
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ static X509 *ocsp_find_signer_sk(STACK_OF(X509) *certs, OCSP_RESPID *id) {
X509 *cert;
for (size_t i = 0; i < sk_X509_num(certs); i++) {
cert = sk_X509_value(certs, i);
if (!X509_pubkey_digest(cert, EVP_sha1(), tmphash, NULL)) {
if (X509_pubkey_digest(cert, EVP_sha1(), tmphash, NULL)) {
if (memcmp(keyhash, tmphash, SHA_DIGEST_LENGTH) == 0) {
return cert;
}
Expand Down Expand Up @@ -104,17 +104,19 @@ static int ocsp_setup_untrusted(OCSP_BASICRESP *bs,
OPENSSL_PUT_ERROR(OCSP, ERR_R_PASSED_NULL_PARAMETER);
return -1;
}

if (!IS_OCSP_FLAG_SET(flags, OCSP_NOCHAIN)) {
if (IS_OCSP_FLAG_SET(flags, OCSP_NOCHAIN)) {
*untrusted = NULL;
} else if (bs->certs && certs) {
*untrusted = sk_X509_dup(bs->certs);
if (*untrusted == NULL) {
return -1;
}
for (size_t i = 0; i < sk_X509_num(certs); i++) {
if (!sk_X509_push(*untrusted, sk_X509_value(certs, i))) {
return -1;
}
}
} else if (certs != NULL) {
*untrusted = certs;
} else {
*untrusted = bs->certs;
}
return 1;
}
Expand Down Expand Up @@ -148,8 +150,10 @@ static int ocsp_verify_signer(X509 *signer, X509_STORE *st,
// Verify |X509_STORE_CTX| and return certificate chain.
ret = X509_verify_cert(ctx);
if (ret <= 0) {
ret = X509_STORE_CTX_get_error(ctx);
int err = X509_STORE_CTX_get_error(ctx);
OPENSSL_PUT_ERROR(OCSP, OCSP_R_CERTIFICATE_VERIFY_ERROR);
ERR_add_error_data(2, "Verify error:",
X509_verify_cert_error_string(err));
goto end;
}
if (chain != NULL) {
Expand Down Expand Up @@ -322,7 +326,7 @@ static int ocsp_check_issuer(OCSP_BASICRESP *bs, STACK_OF(X509) *chain) {

int OCSP_basic_verify(OCSP_BASICRESP *bs, STACK_OF(X509) *certs,
X509_STORE *st, unsigned long flags) {
if (bs == NULL || certs == NULL || st == NULL) {
if (bs == NULL || st == NULL) {
OPENSSL_PUT_ERROR(OCSP, ERR_R_PASSED_NULL_PARAMETER);
return -1;
}
Expand Down Expand Up @@ -377,6 +381,8 @@ int OCSP_basic_verify(OCSP_BASICRESP *bs, STACK_OF(X509) *certs,

end:
sk_X509_pop_free(chain, X509_free);
sk_X509_free(untrusted);
if (bs->certs && certs) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't the condition be untrusted != NULL? untrusted goes out-of-scope here.

I get you use the condition under the if somewhere else, but that could change in the future (unlikely, but could).

Can't sk_X509_free handle NULL inputs? Then we don't even need a condition.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, that makes way more sense. The condition used to fail when I put in untrusted != NULL, but when you pointed that out, I realized OpenSSL had written untrusted to point to a reference of bs->certs or certs instead of duplicating it.

Changed the behavior of untrusted to point to a copy instead of a pointer reference, and removed the condition since sk_X509_free takes NULL inputs.

sk_X509_free(untrusted);
}
return ret;
}
1 change: 1 addition & 0 deletions crypto/ocsp/test/aws/OCSP-TEST.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ The files in this directory represent a cert hierarchy to test OCSP response sta
Issuer for all of the other certs in the directory.
This certificate can be used to generate certificates for authorized OCSP responders designated by the CA.
The authorized OCSP responder may then use the generated certificate to sign OCSP responses for the specific CA.

## OCSP
* ocsp_cert.pem
* ocsp_key.pem
Expand Down
32 changes: 32 additions & 0 deletions crypto/ocsp/test/openssl-ocsp/D1.ors
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
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
4J2l
27 changes: 27 additions & 0 deletions crypto/ocsp/test/openssl-ocsp/D1_Issuer_ICA.pem
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
-----BEGIN CERTIFICATE-----
MIIEhjCCA26gAwIBAgILBAAAAAABL07hXdQwDQYJKoZIhvcNAQEFBQAwTDEgMB4G
A1UECxMXR2xvYmFsU2lnbiBSb290IENBIC0gUjIxEzARBgNVBAoTCkdsb2JhbFNp
Z24xEzARBgNVBAMTCkdsb2JhbFNpZ24wHhcNMTEwNDEzMTAwMDAwWhcNMjIwNDEz
MTAwMDAwWjBZMQswCQYDVQQGEwJCRTEZMBcGA1UEChMQR2xvYmFsU2lnbiBudi1z
YTEvMC0GA1UEAxMmR2xvYmFsU2lnbiBFeHRlbmRlZCBWYWxpZGF0aW9uIENBIC0g
RzIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDNoUbMUpq4pbR/WNnN
2EugcgyXW6aIIMO5PUbc0FxSMPb6WU+FX7DbiLSpXysjSKyr9ZJ4FLYyD/tcaoVb
AJDgu2X1WvlPZ37HbCnsk8ArysRe2LDb1r4/mwvAj6ldrvcAAqT8umYROHf+IyAl
VRDFvYK5TLFoxuJwe4NcE2fBofN8C6iZmtDimyUxyCuNQPZSY7GgrVou9Xk2bTUs
Dt0F5NDiB0i3KF4r1VjVbNAMoQFGAVqPxq9kx1UBXeHRxmxQJaAFrQCrDI1la93r
wnJUyQ88ABeHIu/buYZ4FlGud9mmKE3zWI2DZ7k0JZscUYBR84OSaqOuR5rW5Isb
wO2xAgMBAAGjggFaMIIBVjAOBgNVHQ8BAf8EBAMCAQYwEgYDVR0TAQH/BAgwBgEB
/wIBADAdBgNVHQ4EFgQUsLBK/Rx1KPgcYaoT9vrBkD1rFqMwRwYDVR0gBEAwPjA8
BgRVHSAAMDQwMgYIKwYBBQUHAgEWJmh0dHBzOi8vd3d3Lmdsb2JhbHNpZ24uY29t
L3JlcG9zaXRvcnkvMDYGA1UdHwQvMC0wK6ApoCeGJWh0dHA6Ly9jcmwuZ2xvYmFs
c2lnbi5uZXQvcm9vdC1yMi5jcmwwRAYIKwYBBQUHAQEEODA2MDQGCCsGAQUFBzAB
hihodHRwOi8vb2NzcC5nbG9iYWxzaWduLmNvbS9FeHRlbmRlZFNTTENBMCkGA1Ud
JQQiMCAGCCsGAQUFBwMBBggrBgEFBQcDAgYKKwYBBAGCNwoDAzAfBgNVHSMEGDAW
gBSb4gdXZxwewGoG3lm0mi3f3BmGLjANBgkqhkiG9w0BAQUFAAOCAQEAL0m28rZa
pJWrnlrpK4KbzJBrfHRFIOde2Mcj7ig1sTVlKqVR4FU/9oNntOQ2KbDa7JeVqYoF
o0X+Iy5SiLQfEICt0oufo1+oxetz3nmIQZgz7qdgGLFGyUAQB5yPClLJExoGbqCb
LTr2rk/no1E1KlsYBRLlUdy2NmLz4aQP++TPw5S/EauhWTEB8MxT7I9j12yW00gq
iiPtRVaoZkHqAblH7qFHDBTxI+Egc8p9UHxkOFejj0qcm+ltRc9Ea01gIEBxJbVG
qmwIft/I+shWKpLLg7h5CZctXqEBzgbttJfJBNxB7+BPNk3kQHNG7BESfIhbNCYl
TercGL7FG81kwA==
-----END CERTIFICATE-----
32 changes: 32 additions & 0 deletions crypto/ocsp/test/openssl-ocsp/D2.ors
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
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
fBIRKjnK621vAWvc7UR+0hqnZ+U=
Loading