Skip to content

Commit

Permalink
add support and tests for OCSP_basic_sign (aws#1742)
Browse files Browse the repository at this point in the history
`OCSP_basic_sign` is mandatory for OCSP responders as the RFC specifies
that all OCSP response SHALL be digitally signed. This is different from
`OCSP_request_sign` since signing the OCSP request was optional.
`OCSP_basic_sign` isn't too complex however, the API does a basic ASN.1
sign along with setting some fields. The slight complications involved
are with the additional flag customizations exposed by OpenSSL. Ruby
also exposes these flags, so I've taken them in and documented them. I
also took the chance to rephrase the documentation around
`OCSP_request_sign`.

### Testing:
Reused the certs and keys used for testing `OCSP_request_sign`. What's
different from `OCSP_request_sign` is that we create our own
`OCSP_BASICRESP` instead of loading it from a file. Creating one from
scratch makes more sense as all OCSP response files already have an
existing signature.

By submitting this pull request, I confirm that my contribution is made
under the terms of the Apache 2.0 license and the ISC license.
  • Loading branch information
samuel40791765 authored Aug 19, 2024
1 parent 013240b commit 8080ce3
Show file tree
Hide file tree
Showing 3 changed files with 355 additions and 39 deletions.
122 changes: 122 additions & 0 deletions crypto/ocsp/ocsp_server.c
Original file line number Diff line number Diff line change
Expand Up @@ -70,3 +70,125 @@ int OCSP_basic_add1_cert(OCSP_BASICRESP *resp, X509 *cert) {
X509_up_ref(cert);
return 1;
}

static int OCSP_RESPID_set_by_name(OCSP_RESPID *respid, X509 *cert) {
GUARD_PTR(respid);
GUARD_PTR(cert);
if (!X509_NAME_set(&respid->value.byName, X509_get_subject_name(cert))) {
return 0;
}

respid->type = V_OCSP_RESPID_NAME;
return 1;
}

static int OCSP_RESPID_set_by_key(OCSP_RESPID *respid, X509 *cert) {
GUARD_PTR(respid);
GUARD_PTR(cert);

// RFC2560 requires SHA1.
unsigned char digest[SHA_DIGEST_LENGTH];
if (!X509_pubkey_digest(cert, EVP_sha1(), digest, NULL)) {
return 0;
}

ASN1_OCTET_STRING *byKey = ASN1_OCTET_STRING_new();
if (byKey == NULL) {
return 0;
}

if (!ASN1_OCTET_STRING_set(byKey, digest, SHA_DIGEST_LENGTH)) {
ASN1_OCTET_STRING_free(byKey);
return 0;
}
respid->type = V_OCSP_RESPID_KEY;
respid->value.byKey = byKey;

return 1;
}


// OCSP_basic_sign_ctx does the actual signing operation for |OCSP_basic_sign|,
// but with an initialized |EVP_MD_CTX|.
static int OCSP_basic_sign_ctx(OCSP_BASICRESP *resp, X509 *signer,
EVP_MD_CTX *ctx, STACK_OF(X509) *certs,
unsigned long flags) {
GUARD_PTR(resp);
GUARD_PTR(signer);

if (ctx == NULL || ctx->pctx == NULL) {
OPENSSL_PUT_ERROR(OCSP, OCSP_R_NO_SIGNER_KEY);
return 0;
}

EVP_PKEY *pkey = EVP_PKEY_CTX_get0_pkey(ctx->pctx);
if (pkey == NULL || !X509_check_private_key(signer, pkey)) {
OPENSSL_PUT_ERROR(OCSP, OCSP_R_PRIVATE_KEY_DOES_NOT_MATCH_CERTIFICATE);
return 0;
}

// Add relevant certificates to the response. This is optional according to
// the RFC.
if (!IS_OCSP_FLAG_SET(flags, OCSP_NOCERTS)) {
if (!OCSP_basic_add1_cert(resp, signer)) {
return 0;
}
for (size_t i = 0; i < sk_X509_num(certs); i++) {
X509 *tmpcert = sk_X509_value(certs, i);
if (!OCSP_basic_add1_cert(resp, tmpcert)) {
return 0;
}
}
}

// Set |responderId| of response.
OCSP_RESPID *rid = resp->tbsResponseData->responderId;
if (IS_OCSP_FLAG_SET(flags, OCSP_RESPID_KEY)) {
if (!OCSP_RESPID_set_by_key(rid, signer)) {
return 0;
}
} else if (!OCSP_RESPID_set_by_name(rid, signer)) {
// The OCSP responder is identified by name by default.
return 0;
}

// Set |producedAt| time field of response. Although this can be
// excluded with |OCSP_NOTIME|, a definitive response should include
// the generation time.
if (!IS_OCSP_FLAG_SET(flags, OCSP_NOTIME)) {
if (!X509_gmtime_adj(resp->tbsResponseData->producedAt, 0)) {
return 0;
}
}

// Do the actual signing. This is mandatory according to the RFC.
if (!ASN1_item_sign_ctx(ASN1_ITEM_rptr(OCSP_RESPDATA),
resp->signatureAlgorithm, NULL, resp->signature,
resp->tbsResponseData, ctx)) {
return 0;
}

return 1;
}

int OCSP_basic_sign(OCSP_BASICRESP *resp, X509 *signer, EVP_PKEY *key,
const EVP_MD *dgst, STACK_OF(X509) *certs,
unsigned long flags) {
GUARD_PTR(resp);
GUARD_PTR(signer);
GUARD_PTR(key);
GUARD_PTR(dgst);

EVP_MD_CTX *ctx = EVP_MD_CTX_new();
if (ctx == NULL) {
return 0;
}

if (!EVP_DigestSignInit(ctx, NULL, dgst, NULL, key)) {
EVP_MD_CTX_free(ctx);
return 0;
}
int ret = OCSP_basic_sign_ctx(resp, signer, ctx, certs, flags);
EVP_MD_CTX_free(ctx);
return ret;
}
Loading

0 comments on commit 8080ce3

Please sign in to comment.