Skip to content

Commit

Permalink
add OCSP_response_create and OCSP_basic_add1_status
Browse files Browse the repository at this point in the history
  • Loading branch information
samuel40791765 committed Aug 1, 2024
1 parent fb0cba5 commit b53cd54
Show file tree
Hide file tree
Showing 5 changed files with 244 additions and 17 deletions.
2 changes: 2 additions & 0 deletions crypto/ocsp/internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -254,6 +254,8 @@ DECLARE_ASN1_FUNCTIONS(OCSP_ONEREQ)
DECLARE_ASN1_FUNCTIONS(OCSP_RESPDATA)
DECLARE_ASN1_FUNCTIONS(OCSP_REQINFO)
DECLARE_ASN1_FUNCTIONS(OCSP_SIGNATURE)
DECLARE_ASN1_FUNCTIONS(OCSP_RESPBYTES)
DECLARE_ASN1_FUNCTIONS(OCSP_REVOKEDINFO)

// Try exchanging request and response via HTTP on (non-)blocking BIO in rctx.
OPENSSL_EXPORT int OCSP_REQ_CTX_nbio(OCSP_REQ_CTX *rctx);
Expand Down
2 changes: 2 additions & 0 deletions crypto/ocsp/ocsp_asn.c
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,9 @@ IMPLEMENT_ASN1_FUNCTIONS(OCSP_ONEREQ)
IMPLEMENT_ASN1_FUNCTIONS(OCSP_REQINFO)
IMPLEMENT_ASN1_FUNCTIONS(OCSP_REQUEST)
IMPLEMENT_ASN1_FUNCTIONS(OCSP_RESPONSE)
IMPLEMENT_ASN1_FUNCTIONS(OCSP_RESPBYTES)
IMPLEMENT_ASN1_FUNCTIONS(OCSP_RESPDATA)
IMPLEMENT_ASN1_FUNCTIONS(OCSP_REVOKEDINFO)
IMPLEMENT_ASN1_FUNCTIONS(OCSP_BASICRESP)
IMPLEMENT_ASN1_DUP_FUNCTION(OCSP_CERTID)
IMPLEMENT_ASN1_FUNCTIONS(OCSP_SINGLERESP)
Expand Down
144 changes: 144 additions & 0 deletions crypto/ocsp/ocsp_server.c
Original file line number Diff line number Diff line change
Expand Up @@ -70,3 +70,147 @@ int OCSP_basic_add1_cert(OCSP_BASICRESP *resp, X509 *cert) {
X509_up_ref(cert);
return 1;
}

OCSP_SINGLERESP *OCSP_basic_add1_status(OCSP_BASICRESP *resp, OCSP_CERTID *cid,
int status, int reason,
ASN1_TIME *revoked_time,
ASN1_TIME *this_update,
ASN1_TIME *next_update) {
GUARD_PTR(resp);
GUARD_PTR(cid);
GUARD_PTR(this_update);
// Ambiguous status values are not allowed.
if (status < V_OCSP_CERTSTATUS_GOOD || status > V_OCSP_CERTSTATUS_UNKNOWN) {
OPENSSL_PUT_ERROR(OCSP, OCSP_R_UNKNOWN_FIELD_VALUE);
return NULL;
}

OCSP_SINGLERESP *single = OCSP_SINGLERESP_new();
if (single == NULL) {
goto err;
}

// Init |resp->tbsResponseData->responses| if NULL.
if (resp->tbsResponseData->responses == NULL) {
resp->tbsResponseData->responses = sk_OCSP_SINGLERESP_new_null();
if (resp->tbsResponseData->responses == NULL) {
goto err;
}
}

if (!ASN1_TIME_to_generalizedtime(this_update, &single->thisUpdate)) {
goto err;
}
if (next_update != NULL) {
// |next_update| is allowed to be NULL. Only set |single->nextUpdate| if
// |next_update| is non-NULL.
if (!ASN1_TIME_to_generalizedtime(next_update, &single->nextUpdate)) {
goto err;
}
}

// Reset |single->certId|.
OCSP_CERTID_free(single->certId);
single->certId = OCSP_CERTID_dup(cid);
if (single->certId == NULL) {
goto err;
}

single->certStatus->type = status;
switch (single->certStatus->type) {
case V_OCSP_CERTSTATUS_REVOKED:
if (revoked_time == NULL) {
OPENSSL_PUT_ERROR(OCSP, OCSP_R_NO_REVOKED_TIME);
goto err;
}

single->certStatus->value.revoked = OCSP_REVOKEDINFO_new();
if (single->certStatus->value.revoked == NULL) {
goto err;
}

// Start assigning values to |info| once initialized successfully.
OCSP_REVOKEDINFO *info = single->certStatus->value.revoked;
if (!ASN1_TIME_to_generalizedtime(revoked_time, &info->revocationTime)) {
goto err;
}
// https://www.rfc-editor.org/rfc/rfc5280#section-5.3.1 specifies the only
// valid reason codes are 0-10. Value 7 is not used.
if (reason < OCSP_REVOKED_STATUS_UNSPECIFIED ||
reason > OCSP_REVOKED_STATUS_AACOMPROMISE || reason == 7) {
OPENSSL_PUT_ERROR(OCSP, OCSP_R_UNKNOWN_FIELD_VALUE);
goto err;
}
info->revocationReason = ASN1_ENUMERATED_new();
if (info->revocationReason == NULL ||
!ASN1_ENUMERATED_set(info->revocationReason, reason)) {
goto err;
}

break;

case V_OCSP_CERTSTATUS_GOOD:
single->certStatus->value.good = ASN1_NULL_new();
if (single->certStatus->value.good == NULL) {
goto err;
}
break;

case V_OCSP_CERTSTATUS_UNKNOWN:
single->certStatus->value.unknown = ASN1_NULL_new();
if (single->certStatus->value.unknown == NULL) {
goto err;
}
break;

default:
goto err;
}

// Finally add the |OCSP_SINGLERESP| we were working with to |resp|.
if (!sk_OCSP_SINGLERESP_push(resp->tbsResponseData->responses, single)) {
goto err;
}
return single;

err:
OCSP_SINGLERESP_free(single);
return NULL;
}

OCSP_RESPONSE *OCSP_response_create(int status, OCSP_BASICRESP *bs) {
if (status < OCSP_RESPONSE_STATUS_SUCCESSFUL ||
status > OCSP_RESPONSE_STATUS_UNAUTHORIZED ||
// 4 is not a valid response status code.
status == 4) {
OPENSSL_PUT_ERROR(OCSP, OCSP_R_UNKNOWN_FIELD_VALUE);
return NULL;
}

OCSP_RESPONSE *rsp = OCSP_RESPONSE_new();
if (rsp == NULL) {
goto err;
}
if (!ASN1_ENUMERATED_set(rsp->responseStatus, status)) {
goto err;
}
if (bs == NULL) {
// |bs| is allowed to be NULL.
return rsp;
}

rsp->responseBytes = OCSP_RESPBYTES_new();
if (rsp->responseBytes == NULL) {
goto err;
}
rsp->responseBytes->responseType = OBJ_nid2obj(NID_id_pkix_OCSP_basic);
if (!ASN1_item_pack(bs, ASN1_ITEM_rptr(OCSP_BASICRESP),
&rsp->responseBytes->response)) {
goto err;
}
return rsp;

err:
OCSP_RESPONSE_free(rsp);
return NULL;
}
103 changes: 86 additions & 17 deletions crypto/ocsp/ocsp_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,17 @@ static bssl::UniquePtr<OCSP_REQUEST> LoadOCSP_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/server_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,
Expand Down Expand Up @@ -599,16 +610,8 @@ TEST_P(OCSPVerifyFlagTest, OCSPVerifyFlagTest) {
}

TEST(OCSPTest, GetInfo) {
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/server_cert.pem").c_str())
.c_str()));

// Create a sample |OCSP_CERTID| structure.
bssl::UniquePtr<OCSP_CERTID> cert_id(
OCSP_cert_to_id(EVP_sha256(), subject.get(), issuer.get()));
bssl::UniquePtr<OCSP_CERTID> cert_id(LoadTestOCSP_CERTID());
ASSERT_TRUE(cert_id);

ASN1_OCTET_STRING *nameHash = nullptr;
Expand Down Expand Up @@ -644,6 +647,79 @@ TEST(OCSPTest, BasicAddCert) {
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()));

EXPECT_TRUE(OCSP_basic_add1_status(basicResponse.get(), certId.get(),
V_OCSP_CERTSTATUS_UNKNOWN, 0, nullptr,
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, 7,
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));

// |OCSP_basic_add1_status| has succeeded 3 times at this point, so the
// |basicResponse| should have 3 |OCSP_SINGLERESP|s in the internal stack.
EXPECT_EQ((int)sk_OCSP_SINGLERESP_num(
basicResponse.get()->tbsResponseData->responses),
3);
}

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(4, 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
Expand Down Expand Up @@ -1004,14 +1080,7 @@ TEST(OCSPRequestTest, AddCert) {
ASSERT_TRUE(ocspRequest);

// Construct |OCSP_CERTID| from certs.
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/server_cert.pem").c_str())
.c_str()));
OCSP_CERTID *certId =
OCSP_cert_to_id(nullptr, ca_cert.get(), server_cert.get());
OCSP_CERTID *certId = LoadTestOCSP_CERTID();
ASSERT_TRUE(certId);

OCSP_ONEREQ *oneRequest = OCSP_request_add0_id(ocspRequest.get(), certId);
Expand Down
10 changes: 10 additions & 0 deletions include/openssl/ocsp.h
Original file line number Diff line number Diff line change
Expand Up @@ -369,6 +369,14 @@ OPENSSL_EXPORT int OCSP_id_get0_info(ASN1_OCTET_STRING **nameHash,
// OCSP_basic_add1_cert adds |cert| to the |resp|.
OPENSSL_EXPORT int OCSP_basic_add1_cert(OCSP_BASICRESP *resp, X509 *cert);

// OCSP_basic_add1_status
OPENSSL_EXPORT OCSP_SINGLERESP *OCSP_basic_add1_status(
OCSP_BASICRESP *resp, OCSP_CERTID *cid, int status, int reason,
ASN1_TIME *revoked_time, ASN1_TIME *this_update, ASN1_TIME *next_update);

// OCSP_response_create creates an |OCSP_RESPONSE| and encodes an optional |bs| within it.
OPENSSL_EXPORT OCSP_RESPONSE *OCSP_response_create(int status, OCSP_BASICRESP *bs);

// OCSP_SINGLERESP_get0_id returns the |OCSP_CERTID| within |x|.
OPENSSL_EXPORT const OCSP_CERTID *OCSP_SINGLERESP_get0_id(
const OCSP_SINGLERESP *x);
Expand Down Expand Up @@ -477,6 +485,7 @@ BSSL_NAMESPACE_END
#define OCSP_R_NOT_BASIC_RESPONSE 104
#define OCSP_R_NO_CERTIFICATES_IN_CHAIN 105
#define OCSP_R_NO_RESPONSE_DATA 108
#define OCSP_R_NO_REVOKED_TIME 109
#define OCSP_R_PRIVATE_KEY_DOES_NOT_MATCH_CERTIFICATE 110
#define OCSP_R_RESPONSE_CONTAINS_NO_REVOCATION_DATA 111
#define OCSP_R_ROOT_CA_NOT_TRUSTED 112
Expand All @@ -494,5 +503,6 @@ BSSL_NAMESPACE_END
#define OCSP_R_STATUS_TOO_OLD 127
#define OCSP_R_NO_SIGNER_KEY 130
#define OCSP_R_OCSP_REQUEST_DUPLICATE_SIGNATURE 131
#define OCSP_R_UNKNOWN_FIELD_VALUE 132

#endif // AWSLC_OCSP_H

0 comments on commit b53cd54

Please sign in to comment.