chore: checkpoint before Python removal

This commit is contained in:
2026-03-26 22:33:59 +00:00
parent 683cec9307
commit e568ddf82a
29972 changed files with 11269302 additions and 2 deletions

View File

@@ -0,0 +1,283 @@
// Copyright 2015-2021 The OpenSSL Project Authors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
#include "openssl/ocsp.h"
#include "openssl/x509.h"
#if defined(__cplusplus)
extern "C" {
#endif
// CRLReason does not have a status assigned to the value 7.
//
// See Reason Code RFC: https://www.rfc-editor.org/rfc/rfc5280#section-5.3.1.
#define OCSP_UNASSIGNED_REVOKED_STATUS 7
// OCSPResponseStatus does not have a status assigned to the value 4.
//
// See Reason Code RFC:
// https://datatracker.ietf.org/doc/html/rfc6960#section-4.2.1
#define OCSP_UNASSIGNED_RESPONSE_STATUS 4
// OCSP Request ASN.1 specification:
// https://datatracker.ietf.org/doc/html/rfc6960#section-4.1.1
//
// OCSP Response ASN.1 specification:
// https://datatracker.ietf.org/doc/html/rfc6960#section-4.2.1
// CertID ::= SEQUENCE {
// hashAlgorithm AlgorithmIdentifier,
// issuerNameHash OCTET STRING, --Hash of Issuer's DN
// issuerKeyHash OCTET STRING, --Hash of Issuers public key (excluding
// the tag & length fields)
// serialNumber CertificateSerialNumber }
//
struct ocsp_cert_id_st {
X509_ALGOR *hashAlgorithm;
ASN1_OCTET_STRING *issuerNameHash;
ASN1_OCTET_STRING *issuerKeyHash;
ASN1_INTEGER *serialNumber;
};
// Request ::= SEQUENCE {
// reqCert CertID,
// singleRequestExtensions [0] EXPLICIT Extensions OPTIONAL }
//
struct ocsp_one_request_st {
OCSP_CERTID *reqCert;
STACK_OF(X509_EXTENSION) *singleRequestExtensions;
};
// TBSRequest ::= SEQUENCE {
// version [0] EXPLICIT Version DEFAULT v1,
// requestorName [1] EXPLICIT GeneralName OPTIONAL,
// requestList SEQUENCE OF Request,
// requestExtensions [2] EXPLICIT Extensions OPTIONAL }
//
struct ocsp_req_info_st {
ASN1_INTEGER *version;
GENERAL_NAME *requestorName;
STACK_OF(OCSP_ONEREQ) *requestList;
STACK_OF(X509_EXTENSION) *requestExtensions;
};
// Signature ::= SEQUENCE {
// signatureAlgorithm AlgorithmIdentifier,
// signature BIT STRING,
// certs [0] EXPLICIT SEQUENCE OF Certificate OPTIONAL }
//
struct ocsp_signature_st {
X509_ALGOR *signatureAlgorithm;
ASN1_BIT_STRING *signature;
STACK_OF(X509) *certs;
};
// OCSPRequest ::= SEQUENCE {
// tbsRequest TBSRequest,
// optionalSignature [0] EXPLICIT Signature OPTIONAL }
//
struct ocsp_request_st {
OCSP_REQINFO *tbsRequest;
OCSP_SIGNATURE *optionalSignature;
};
// Opaque OCSP request status structure
struct ocsp_req_ctx_st {
int state; // Current I/O state
unsigned char *iobuf; // Line buffer. Should only be modified during
// http exchange in OCSP_REQ_CTX_nbio.
int iobuflen; // Line buffer length
BIO *io; // BIO to perform I/O with
BIO *mem; // Memory BIO response is built into
unsigned long asn1_len; // ASN1 length of response
unsigned long max_resp_len; // Maximum length of response
};
// OCSPResponseStatus ::= ENUMERATED {
// successful (0), --Response has valid confirmations
// malformedRequest (1), --Illegal confirmation request
// internalError (2), --Internal error in issuer
// tryLater (3), --Try again later
// --(4) is not used
// sigRequired (5), --Must sign the request
// unauthorized (6) --Request unauthorized
// }
//
// ResponseBytes ::= SEQUENCE {
// responseType OBJECT IDENTIFIER,
// response OCTET STRING }
//
struct ocsp_resp_bytes_st {
ASN1_OBJECT *responseType;
ASN1_OCTET_STRING *response;
};
// OCSPResponse ::= SEQUENCE {
// responseStatus OCSPResponseStatus,
// responseBytes [0] EXPLICIT ResponseBytes OPTIONAL }
//
struct ocsp_response_st {
ASN1_ENUMERATED *responseStatus;
OCSP_RESPBYTES *responseBytes;
};
// ResponderID ::= CHOICE {
// byName [1] Name,
// byKey [2] KeyHash }
//
// KeyHash ::= OCTET STRING --SHA-1 hash of responder's public key
// --(excluding the tag and length fields)
//
// The RFC requires that the KeyHash value be of a SHA-1 hash. Even though this
// is not being used cryptographically, there is the possibility of a response
// being returned with a forced Responder KeyHash when using SHA-1 (assuming a
// preimage attack, which is beyond the scope of how SHA-1 is currently
// vulnerable). However, our hand are tied with what the RFC mandates.
//
// RFC 6960: https://datatracker.ietf.org/doc/html/rfc6960#appendix-B.2
struct ocsp_responder_id_st {
int type;
union {
X509_NAME *byName;
ASN1_OCTET_STRING *byKey;
} value;
};
// RevokedInfo ::= SEQUENCE {
// revocationTime GeneralizedTime,
// revocationReason [0] EXPLICIT CRLReason OPTIONAL }
//
struct ocsp_revoked_info_st {
ASN1_GENERALIZEDTIME *revocationTime;
ASN1_ENUMERATED *revocationReason;
};
// CertStatus ::= CHOICE {
// good [0] IMPLICIT NULL,
// revoked [1] IMPLICIT RevokedInfo,
// unknown [2] IMPLICIT UnknownInfo }
//
struct ocsp_cert_status_st {
int type;
union {
ASN1_NULL *good;
OCSP_REVOKEDINFO *revoked;
ASN1_NULL *unknown;
} value;
};
// SingleResponse ::= SEQUENCE {
// certID CertID,
// certStatus CertStatus,
// thisUpdate GeneralizedTime,
// nextUpdate [0] EXPLICIT GeneralizedTime OPTIONAL,
// singleExtensions [1] EXPLICIT Extensions OPTIONAL }
//
struct ocsp_single_response_st {
OCSP_CERTID *certId;
OCSP_CERTSTATUS *certStatus;
ASN1_GENERALIZEDTIME *thisUpdate;
ASN1_GENERALIZEDTIME *nextUpdate;
STACK_OF(X509_EXTENSION) *singleExtensions;
};
// ResponseData ::= SEQUENCE {
// version [0] EXPLICIT Version DEFAULT v1,
// responderID ResponderID,
// producedAt GeneralizedTime,
// responses SEQUENCE OF SingleResponse,
// responseExtensions [1] EXPLICIT Extensions OPTIONAL }
//
struct ocsp_response_data_st {
ASN1_INTEGER *version;
OCSP_RESPID *responderId;
ASN1_GENERALIZEDTIME *producedAt;
STACK_OF(OCSP_SINGLERESP) *responses;
STACK_OF(X509_EXTENSION) *responseExtensions;
};
// BasicOCSPResponse ::= SEQUENCE {
// tbsResponseData ResponseData,
// signatureAlgorithm AlgorithmIdentifier,
// signature BIT STRING,
// certs [0] EXPLICIT SEQUENCE OF Certificate OPTIONAL }
//
//
// Note 1: The value for "signature" is specified in the OCSP rfc2560 as
// follows: "The value for the signature SHALL be computed on the hash of
// the DER encoding ResponseData." This means that you must hash the
// DER-encoded tbsResponseData, and then run it through a crypto-signing
// function, which will (at least w/RSA) do a hash-'n'-private-encrypt
// operation. This seems a bit odd, but that's the spec. Also note that
// the data structures do not leave anywhere to independently specify the
// algorithm used for the initial hash. So, we look at the
// signature-specification algorithm, and try to do something intelligent.
// -- Kathy Weinhold, CertCo
//
// Note 2: It seems that the mentioned passage from RFC 2560 (section
// 4.2.1) is open for interpretation. I've done tests against another
// responder, and found that it doesn't do the double hashing that the RFC
// seems to say one should. Therefore, all relevant functions take a flag
// saying which variant should be used. -- Richard Levitte, OpenSSL team
// and CeloCom
struct ocsp_basic_response_st {
OCSP_RESPDATA *tbsResponseData;
X509_ALGOR *signatureAlgorithm;
ASN1_BIT_STRING *signature;
STACK_OF(X509) *certs;
};
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)
// OCSP_get_default_digest sets the default digest according to |signer|.
// This exists because OpenSSL sets the default to |EVP_sha256| when passing
// NULL for |type| in |EVP_DigestSignInit| when using certain key types. We wish
// to avoid this general behavior for all |EVP_DigestSign*| operations, so we
// only set the default digest from the OCSP layer. |dgst| represents the user's
// self-defined digest type, if it's non-NULL, |dgst| is directly returned.
const EVP_MD *OCSP_get_default_digest(const EVP_MD *dgst, EVP_PKEY *signer);
// Try exchanging request and response via HTTP on (non-)blocking BIO in rctx.
OPENSSL_EXPORT int OCSP_REQ_CTX_nbio(OCSP_REQ_CTX *rctx);
// Tries to exchange the request and response with |OCSP_REQ_CTX_nbio|, but on
// success, it additionally parses the response, which must be a
// DER-encoded ASN.1 structure.
int OCSP_REQ_CTX_nbio_d2i(OCSP_REQ_CTX *rctx, ASN1_VALUE **pval,
const ASN1_ITEM *it);
// Returns the internal memory BIO of the |OCSP_REQ_CTX|. For AWS-LC, this is
// only used for testing if contents of |OCSP_REQ_CTX| have been written
// correctly.
OPENSSL_EXPORT BIO *OCSP_REQ_CTX_get0_mem_bio(OCSP_REQ_CTX *rctx);
// OCSP extension functions
// OCSP_REQUEST_get_ext_by_NID returns the index of an extension from an
// |OCSP_REQUEST| by its NID. Returns -1 if not found.
OPENSSL_EXPORT int OCSP_REQUEST_get_ext_by_NID(OCSP_REQUEST *req, int nid,
int lastpos);
// OCSP_REQUEST_get_ext retrieves an |X509_EXTENSION| from an |OCSP_REQUEST|
// by its position in the extension list.
OPENSSL_EXPORT X509_EXTENSION *OCSP_REQUEST_get_ext(OCSP_REQUEST *req, int loc);
// OCSP_BASICRESP_add_ext adds a copy of |ex| to the extension list in
// |*bs|. It returns 1 on success and 0 on error. The new extension is
// inserted at index |loc|, shifting extensions to the right. If |loc| is -1 or
// out of bounds, the new extension is appended to the list.
int OCSP_BASICRESP_add_ext(OCSP_BASICRESP *bs, X509_EXTENSION *ex, int loc);
#define IS_OCSP_FLAG_SET(flags, query) (flags & query)
#define OCSP_MAX_RESP_LENGTH (100 * 1024)
#if defined(__cplusplus)
} // extern C
#endif

View File

@@ -0,0 +1,116 @@
// Copyright 2015-2021 The OpenSSL Project Authors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
// OCSP ASN1 structure definitions can be found in RFC link below
// https://tools.ietf.org/html/rfc6960#section-4.2.1
#include "internal.h"
#include "../x509/internal.h"
ASN1_SEQUENCE(OCSP_SIGNATURE) = {
ASN1_SIMPLE(OCSP_SIGNATURE, signatureAlgorithm, X509_ALGOR),
ASN1_SIMPLE(OCSP_SIGNATURE, signature, ASN1_BIT_STRING),
ASN1_EXP_SEQUENCE_OF_OPT(OCSP_SIGNATURE, certs, X509,
0)} ASN1_SEQUENCE_END(OCSP_SIGNATURE)
ASN1_SEQUENCE(OCSP_CERTID) = {
ASN1_SIMPLE(OCSP_CERTID, hashAlgorithm, X509_ALGOR),
ASN1_SIMPLE(OCSP_CERTID, issuerNameHash, ASN1_OCTET_STRING),
ASN1_SIMPLE(OCSP_CERTID, issuerKeyHash, ASN1_OCTET_STRING),
ASN1_SIMPLE(OCSP_CERTID, serialNumber,
ASN1_INTEGER)} ASN1_SEQUENCE_END(OCSP_CERTID)
ASN1_SEQUENCE(OCSP_ONEREQ) = {
ASN1_SIMPLE(OCSP_ONEREQ, reqCert, OCSP_CERTID),
ASN1_EXP_SEQUENCE_OF_OPT(OCSP_ONEREQ, singleRequestExtensions,
X509_EXTENSION, 0)} ASN1_SEQUENCE_END(OCSP_ONEREQ)
ASN1_SEQUENCE(OCSP_REQINFO) = {
ASN1_EXP_OPT(OCSP_REQINFO, version, ASN1_INTEGER, 0),
ASN1_EXP_OPT(OCSP_REQINFO, requestorName, GENERAL_NAME, 1),
ASN1_SEQUENCE_OF(OCSP_REQINFO, requestList, OCSP_ONEREQ),
ASN1_EXP_SEQUENCE_OF_OPT(OCSP_REQINFO, requestExtensions, X509_EXTENSION,
2)} ASN1_SEQUENCE_END(OCSP_REQINFO)
ASN1_SEQUENCE(OCSP_REQUEST) = {
ASN1_SIMPLE(OCSP_REQUEST, tbsRequest, OCSP_REQINFO),
ASN1_EXP_OPT(OCSP_REQUEST, optionalSignature, OCSP_SIGNATURE,
0)} ASN1_SEQUENCE_END(OCSP_REQUEST)
ASN1_SEQUENCE(OCSP_RESPBYTES) = {
ASN1_SIMPLE(OCSP_RESPBYTES, responseType, ASN1_OBJECT),
ASN1_SIMPLE(OCSP_RESPBYTES, response,
ASN1_OCTET_STRING)} ASN1_SEQUENCE_END(OCSP_RESPBYTES)
ASN1_SEQUENCE(OCSP_RESPONSE) = {
ASN1_SIMPLE(OCSP_RESPONSE, responseStatus, ASN1_ENUMERATED),
ASN1_EXP_OPT(OCSP_RESPONSE, responseBytes, OCSP_RESPBYTES,
0)} ASN1_SEQUENCE_END(OCSP_RESPONSE)
ASN1_CHOICE(OCSP_RESPID) = {
ASN1_EXP(OCSP_RESPID, value.byName, X509_NAME, 1),
ASN1_EXP(OCSP_RESPID, value.byKey, ASN1_OCTET_STRING,
2)} ASN1_CHOICE_END(OCSP_RESPID)
ASN1_SEQUENCE(OCSP_REVOKEDINFO) = {
ASN1_SIMPLE(OCSP_REVOKEDINFO, revocationTime, ASN1_GENERALIZEDTIME),
ASN1_EXP_OPT(OCSP_REVOKEDINFO, revocationReason, ASN1_ENUMERATED,
0)} ASN1_SEQUENCE_END(OCSP_REVOKEDINFO)
ASN1_CHOICE(OCSP_CERTSTATUS) = {
ASN1_IMP(OCSP_CERTSTATUS, value.good, ASN1_NULL, 0),
ASN1_IMP(OCSP_CERTSTATUS, value.revoked, OCSP_REVOKEDINFO, 1),
ASN1_IMP(OCSP_CERTSTATUS, value.unknown, ASN1_NULL,
2)} ASN1_CHOICE_END(OCSP_CERTSTATUS)
ASN1_SEQUENCE(OCSP_SINGLERESP) = {
ASN1_SIMPLE(OCSP_SINGLERESP, certId, OCSP_CERTID),
ASN1_SIMPLE(OCSP_SINGLERESP, certStatus, OCSP_CERTSTATUS),
ASN1_SIMPLE(OCSP_SINGLERESP, thisUpdate, ASN1_GENERALIZEDTIME),
ASN1_EXP_OPT(OCSP_SINGLERESP, nextUpdate, ASN1_GENERALIZEDTIME, 0),
ASN1_EXP_SEQUENCE_OF_OPT(OCSP_SINGLERESP, singleExtensions, X509_EXTENSION,
1)} ASN1_SEQUENCE_END(OCSP_SINGLERESP)
ASN1_SEQUENCE(OCSP_RESPDATA) = {
ASN1_EXP_OPT(OCSP_RESPDATA, version, ASN1_INTEGER, 0),
ASN1_SIMPLE(OCSP_RESPDATA, responderId, OCSP_RESPID),
ASN1_SIMPLE(OCSP_RESPDATA, producedAt, ASN1_GENERALIZEDTIME),
ASN1_SEQUENCE_OF(OCSP_RESPDATA, responses, OCSP_SINGLERESP),
ASN1_EXP_SEQUENCE_OF_OPT(OCSP_RESPDATA, responseExtensions, X509_EXTENSION,
1)} ASN1_SEQUENCE_END(OCSP_RESPDATA)
ASN1_SEQUENCE(OCSP_BASICRESP) = {
ASN1_SIMPLE(OCSP_BASICRESP, tbsResponseData, OCSP_RESPDATA),
ASN1_SIMPLE(OCSP_BASICRESP, signatureAlgorithm, X509_ALGOR),
ASN1_SIMPLE(OCSP_BASICRESP, signature, ASN1_BIT_STRING),
ASN1_EXP_SEQUENCE_OF_OPT(OCSP_BASICRESP, certs, X509,
0)} ASN1_SEQUENCE_END(OCSP_BASICRESP)
IMPLEMENT_ASN1_FUNCTIONS(OCSP_SIGNATURE)
IMPLEMENT_ASN1_FUNCTIONS(OCSP_CERTID)
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)
OCSP_RESPONSE *d2i_OCSP_RESPONSE_bio(BIO *bp, OCSP_RESPONSE **presp) {
return ASN1_item_d2i_bio(ASN1_ITEM_rptr(OCSP_RESPONSE), bp, presp);
}
int i2d_OCSP_RESPONSE_bio(BIO *bp, OCSP_RESPONSE *presp) {
return ASN1_item_i2d_bio(ASN1_ITEM_rptr(OCSP_RESPONSE), bp, presp);
}
OCSP_REQUEST *d2i_OCSP_REQUEST_bio(BIO *bp, OCSP_REQUEST **preq) {
return ASN1_item_d2i_bio(ASN1_ITEM_rptr(OCSP_REQUEST), bp, preq);
}
int i2d_OCSP_REQUEST_bio(BIO *bp, OCSP_REQUEST *preq) {
return ASN1_item_i2d_bio(ASN1_ITEM_rptr(OCSP_REQUEST), bp, preq);
}

View File

@@ -0,0 +1,325 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0 OR ISC
#include "../asn1/internal.h"
#include "internal.h"
OCSP_ONEREQ *OCSP_request_add0_id(OCSP_REQUEST *req, OCSP_CERTID *cid) {
OCSP_ONEREQ *one = OCSP_ONEREQ_new();
if (one == NULL) {
return NULL;
}
// Reassign |OCSP_CERTID| allocated by OCSP_ONEREQ_new().
OCSP_CERTID_free(one->reqCert);
one->reqCert = cid;
if (req != NULL && !sk_OCSP_ONEREQ_push(req->tbsRequest->requestList, one)) {
one->reqCert = NULL; // do not free on error
OCSP_ONEREQ_free(one);
return NULL;
}
return one;
}
int OCSP_request_set1_name(OCSP_REQUEST *req, X509_NAME *nm) {
GENERAL_NAME *gen = GENERAL_NAME_new();
if (gen == NULL) {
return 0;
}
if (!X509_NAME_set(&gen->d.directoryName, nm)) {
GENERAL_NAME_free(gen);
return 0;
}
gen->type = GEN_DIRNAME;
GENERAL_NAME_free(req->tbsRequest->requestorName);
req->tbsRequest->requestorName = gen;
return 1;
}
int OCSP_request_add1_cert(OCSP_REQUEST *req, X509 *cert) {
if (req->optionalSignature == NULL) {
req->optionalSignature = OCSP_SIGNATURE_new();
}
OCSP_SIGNATURE *sig = req->optionalSignature;
if (sig == NULL) {
return 0;
}
if (cert == NULL) {
return 1;
}
if (sig->certs == NULL && (sig->certs = sk_X509_new_null()) == NULL) {
return 0;
}
if (!sk_X509_push(sig->certs, cert)) {
return 0;
}
// sk_X509_push takes ownership.
X509_up_ref(cert);
return 1;
}
int OCSP_request_sign(OCSP_REQUEST *req, X509 *signer, EVP_PKEY *key,
const EVP_MD *dgst, STACK_OF(X509) *certs,
unsigned long flags) {
if (req->optionalSignature != NULL) {
// OpenSSL lets an |OCSP_REQUEST| be signed twice, regardless of whether
// an |optionalSignature| exists. Signing an OCSP Request twice creates
// a dangling |OCSP_SIGNATURE| pointer with no clear way of recovering
// it. We disallow this behavior and only allow the |OCSP_REQUEST| to be
// signed once. There's no indication or use case detailed in the RFC
// that the OCSP request can or should be signed twice.
// https://datatracker.ietf.org/doc/html/rfc6960#section-4.1.2
OPENSSL_PUT_ERROR(OCSP, OCSP_R_OCSP_REQUEST_DUPLICATE_SIGNATURE);
goto err;
}
if (!OCSP_request_set1_name(req, X509_get_subject_name(signer))) {
goto err;
}
req->optionalSignature = OCSP_SIGNATURE_new();
if (req->optionalSignature == NULL) {
goto err;
}
if (key != NULL) {
if (!X509_check_private_key(signer, key)) {
OPENSSL_PUT_ERROR(OCSP, OCSP_R_PRIVATE_KEY_DOES_NOT_MATCH_CERTIFICATE);
goto err;
}
const EVP_MD *init_dgst = OCSP_get_default_digest(dgst, key);
if (init_dgst == NULL) {
OPENSSL_PUT_ERROR(OCSP, EVP_R_NO_DEFAULT_DIGEST);
goto err;
}
if (!ASN1_item_sign(ASN1_ITEM_rptr(OCSP_REQINFO),
req->optionalSignature->signatureAlgorithm, NULL,
req->optionalSignature->signature, req->tbsRequest, key,
init_dgst)) {
goto err;
}
}
if (!IS_OCSP_FLAG_SET(flags, OCSP_NOCERTS)) {
if (!OCSP_request_add1_cert(req, signer)) {
goto err;
}
for (size_t i = 0; i < sk_X509_num(certs); i++) {
if (!OCSP_request_add1_cert(req, sk_X509_value(certs, i))) {
goto err;
}
}
}
return 1;
err:
OCSP_SIGNATURE_free(req->optionalSignature);
req->optionalSignature = NULL;
return 0;
}
int OCSP_response_status(OCSP_RESPONSE *resp) {
if (resp == NULL) {
OPENSSL_PUT_ERROR(OCSP, ERR_R_PASSED_NULL_PARAMETER);
return -1;
}
return ASN1_ENUMERATED_get(resp->responseStatus);
}
OCSP_BASICRESP *OCSP_response_get1_basic(OCSP_RESPONSE *resp) {
if (resp == NULL) {
OPENSSL_PUT_ERROR(OCSP, ERR_R_PASSED_NULL_PARAMETER);
return NULL;
}
OCSP_RESPBYTES *rb = resp->responseBytes;
if (rb == NULL) {
OPENSSL_PUT_ERROR(OCSP, OCSP_R_NO_RESPONSE_DATA);
return NULL;
}
if (OBJ_obj2nid(rb->responseType) != NID_id_pkix_OCSP_basic) {
OPENSSL_PUT_ERROR(OCSP, OCSP_R_NOT_BASIC_RESPONSE);
return NULL;
}
return ASN1_item_unpack(rb->response, ASN1_ITEM_rptr(OCSP_BASICRESP));
}
OCSP_SINGLERESP *OCSP_resp_get0(OCSP_BASICRESP *bs, size_t idx) {
if (bs == NULL) {
OPENSSL_PUT_ERROR(OCSP, ERR_R_PASSED_NULL_PARAMETER);
return NULL;
}
if (bs->tbsResponseData == NULL) {
OPENSSL_PUT_ERROR(OCSP, OCSP_R_NO_RESPONSE_DATA);
return NULL;
}
return sk_OCSP_SINGLERESP_value(bs->tbsResponseData->responses, idx);
}
int OCSP_resp_find(OCSP_BASICRESP *bs, OCSP_CERTID *id, int last) {
if (bs == NULL || id == NULL) {
OPENSSL_PUT_ERROR(OCSP, ERR_R_PASSED_NULL_PARAMETER);
return -1;
}
if (bs->tbsResponseData == NULL) {
OPENSSL_PUT_ERROR(OCSP, OCSP_R_NO_RESPONSE_DATA);
return -1;
}
// |OCSP_SINGLERESP| stack is in |OCSP_BASICRESP| responseData, we look for
// |OCSP_CERTID| in here.
STACK_OF(OCSP_SINGLERESP) *sresp = bs->tbsResponseData->responses;
OCSP_SINGLERESP *single;
if (last < 0) {
last = 0;
} else {
last++;
}
for (size_t i = last; i < sk_OCSP_SINGLERESP_num(sresp); i++) {
single = sk_OCSP_SINGLERESP_value(sresp, i);
if (!OCSP_id_cmp(id, single->certId)) {
return i;
}
}
return -1;
}
int OCSP_single_get0_status(OCSP_SINGLERESP *single, int *reason,
ASN1_GENERALIZEDTIME **revtime,
ASN1_GENERALIZEDTIME **thisupd,
ASN1_GENERALIZEDTIME **nextupd) {
if (single == NULL) {
OPENSSL_PUT_ERROR(OCSP, ERR_R_PASSED_NULL_PARAMETER);
return -1;
}
OCSP_CERTSTATUS *cst = single->certStatus;
if (cst == NULL) {
OPENSSL_PUT_ERROR(OCSP, ERR_R_PASSED_NULL_PARAMETER);
return -1;
}
int status = cst->type;
// If certificate status is revoked, we look up certificate revocation time
// and reason.
if (status == V_OCSP_CERTSTATUS_REVOKED) {
OCSP_REVOKEDINFO *rev = cst->value.revoked;
if (rev != NULL) {
if (revtime != NULL) {
*revtime = rev->revocationTime;
}
if (reason != NULL) {
if (rev->revocationReason) {
*reason = ASN1_ENUMERATED_get(rev->revocationReason);
} else {
*reason = -1;
}
}
}
}
// Send back when certificate was last updated and when is the next update
// time.
if (thisupd != NULL) {
*thisupd = single->thisUpdate;
}
if (nextupd != NULL) {
*nextupd = single->nextUpdate;
}
return status;
}
int OCSP_resp_find_status(OCSP_BASICRESP *bs, OCSP_CERTID *id, int *status,
int *reason, ASN1_GENERALIZEDTIME **revtime,
ASN1_GENERALIZEDTIME **thisupd,
ASN1_GENERALIZEDTIME **nextupd) {
if (bs == NULL || id == NULL) {
OPENSSL_PUT_ERROR(OCSP, ERR_R_PASSED_NULL_PARAMETER);
return -1;
}
// we look for index of equivalent |OCSP_CERTID| of issuer certificate in
// |OCSP_BASICRESP|
int single_idx = OCSP_resp_find(bs, id, -1);
if (single_idx < 0) {
return 0;
}
OCSP_SINGLERESP *single = OCSP_resp_get0(bs, single_idx);
// Extract the update time and revocation status of certificate sent back
// from OCSP responder
int single_status =
OCSP_single_get0_status(single, reason, revtime, thisupd, nextupd);
if (status != NULL) {
*status = single_status;
}
return 1;
}
int OCSP_check_validity(ASN1_GENERALIZEDTIME *thisUpdate,
ASN1_GENERALIZEDTIME *nextUpdate,
long drift_num_seconds, long max_age_seconds) {
int ret = 1;
int64_t t_tmp;
int64_t t_now = time(NULL);
// Check |thisUpdate| is valid and not more than |drift_num_seconds| in the
// future.
if (!ASN1_GENERALIZEDTIME_check(thisUpdate)) {
OPENSSL_PUT_ERROR(OCSP, OCSP_R_ERROR_IN_THISUPDATE_FIELD);
ret = 0;
} else {
t_tmp = t_now + drift_num_seconds;
if (X509_cmp_time_posix(thisUpdate, t_tmp) > 0) {
OPENSSL_PUT_ERROR(OCSP, OCSP_R_STATUS_NOT_YET_VALID);
ret = 0;
}
// If |max_num_seconds| is specified, check that |thisUpdate| is not more
// than |max_num_seconds| in the past.
if (max_age_seconds >= 0) {
t_tmp = t_now - max_age_seconds;
if (X509_cmp_time_posix(thisUpdate, t_tmp) < 0) {
OPENSSL_PUT_ERROR(OCSP, OCSP_R_STATUS_TOO_OLD);
ret = 0;
}
}
}
// If |nextUpdate| field is empty, we have validated everything we can at
// this point.
if (nextUpdate == NULL) {
return ret;
}
// Check |nextUpdate| is valid and not more than |drift_num_seconds| in the
// past.
if (!ASN1_GENERALIZEDTIME_check(nextUpdate)) {
OPENSSL_PUT_ERROR(OCSP, OCSP_R_ERROR_IN_NEXTUPDATE_FIELD);
ret = 0;
} else {
t_tmp = t_now - drift_num_seconds;
if (X509_cmp_time_posix(nextUpdate, t_tmp) < 0) {
OPENSSL_PUT_ERROR(OCSP, OCSP_R_STATUS_EXPIRED);
ret = 0;
}
}
// Also don't allow |nextUpdate| to precede |thisUpdate|.
if (ASN1_STRING_cmp(nextUpdate, thisUpdate) < 0) {
OPENSSL_PUT_ERROR(OCSP, OCSP_R_NEXTUPDATE_BEFORE_THISUPDATE);
ret = 0;
}
return ret;
}
int OCSP_resp_count(OCSP_BASICRESP *bs) {
if (bs == NULL) {
return -1;
}
return (int)sk_OCSP_SINGLERESP_num(bs->tbsResponseData->responses);
}
const OCSP_CERTID *OCSP_SINGLERESP_get0_id(const OCSP_SINGLERESP *single) {
return single->certId;
}

View File

@@ -0,0 +1,175 @@
// Copyright 2015-2021 The OpenSSL Project Authors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
// Modifications Copyright Amazon.com, Inc. or its affiliates.
// SPDX-License-Identifier: Apache-2.0 OR ISC
#include <openssl/mem.h>
#include <openssl/rand.h>
#include <string.h>
#include "../internal.h"
#include "internal.h"
#define OCSP_DEFAULT_NONCE_LENGTH 16
int OCSP_REQUEST_get_ext_by_NID(OCSP_REQUEST *req, int nid, int lastpos) {
return X509v3_get_ext_by_NID(req->tbsRequest->requestExtensions, nid,
lastpos);
}
X509_EXTENSION *OCSP_REQUEST_get_ext(OCSP_REQUEST *req, int loc) {
return X509v3_get_ext(req->tbsRequest->requestExtensions, loc);
}
int OCSP_BASICRESP_add_ext(OCSP_BASICRESP *bs, X509_EXTENSION *ex, int loc) {
return (X509v3_add_ext(&bs->tbsResponseData->responseExtensions, ex, loc) !=
NULL);
}
int OCSP_BASICRESP_get_ext_by_NID(OCSP_BASICRESP *bs, int nid, int lastpos) {
return X509v3_get_ext_by_NID(bs->tbsResponseData->responseExtensions, nid,
lastpos);
}
X509_EXTENSION *OCSP_BASICRESP_get_ext(OCSP_BASICRESP *bs, int loc) {
return X509v3_get_ext(bs->tbsResponseData->responseExtensions, loc);
}
X509_EXTENSION *OCSP_BASICRESP_delete_ext(OCSP_BASICRESP *x, int loc) {
return X509v3_delete_ext(x->tbsResponseData->responseExtensions, loc);
}
int OCSP_SINGLERESP_add_ext(OCSP_SINGLERESP *sresp, X509_EXTENSION *ex,
int loc) {
GUARD_PTR(sresp);
return (X509v3_add_ext(&sresp->singleExtensions, ex, loc) != NULL);
}
int OCSP_SINGLERESP_get_ext_count(OCSP_SINGLERESP *sresp) {
GUARD_PTR(sresp);
return X509v3_get_ext_count(sresp->singleExtensions);
}
X509_EXTENSION *OCSP_SINGLERESP_get_ext(OCSP_SINGLERESP *sresp, int loc) {
GUARD_PTR(sresp);
return X509v3_get_ext(sresp->singleExtensions, loc);
}
static int ocsp_add_nonce(STACK_OF(X509_EXTENSION) **exts, unsigned char *val,
int len) {
unsigned char *tmpval;
ASN1_OCTET_STRING os;
int ret = 0;
if (len <= 0) {
len = OCSP_DEFAULT_NONCE_LENGTH;
}
// Create the OCTET STRING manually by writing out the header and
// appending the content octets. This avoids an extra memory allocation
// operation in some cases. Applications should *NOT* do this because it
// relies on library internals.
os.length = ASN1_object_size(0, len, V_ASN1_OCTET_STRING);
if (os.length < 0) {
return 0;
}
os.data = OPENSSL_malloc(os.length);
if (os.data == NULL) {
goto err;
}
tmpval = os.data;
ASN1_put_object(&tmpval, 0, len, V_ASN1_OCTET_STRING, V_ASN1_UNIVERSAL);
if (val != NULL) {
OPENSSL_memcpy(tmpval, val, len);
} else {
AWSLC_ABORT_IF_NOT_ONE(RAND_bytes(tmpval, len));
}
if (X509V3_add1_i2d(exts, NID_id_pkix_OCSP_Nonce, &os, 0,
X509V3_ADD_REPLACE) <= 0) {
goto err;
}
ret = 1;
err:
OPENSSL_free(os.data);
return ret;
}
int OCSP_request_add1_nonce(OCSP_REQUEST *req, unsigned char *val, int len) {
if (req == NULL) {
OPENSSL_PUT_ERROR(OCSP, ERR_R_PASSED_NULL_PARAMETER);
return 0;
}
if (val != NULL && len <= 0) {
OPENSSL_PUT_ERROR(OCSP, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
return 0;
}
return ocsp_add_nonce(&req->tbsRequest->requestExtensions, val, len);
}
int OCSP_basic_add1_nonce(OCSP_BASICRESP *resp, unsigned char *val, int len) {
if (resp == NULL) {
OPENSSL_PUT_ERROR(OCSP, ERR_R_PASSED_NULL_PARAMETER);
return 0;
}
if (val != NULL && len <= 0) {
OPENSSL_PUT_ERROR(OCSP, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
return 0;
}
return ocsp_add_nonce(&resp->tbsResponseData->responseExtensions, val, len);
}
int OCSP_check_nonce(OCSP_REQUEST *req, OCSP_BASICRESP *bs) {
if (req == NULL || bs == NULL) {
OPENSSL_PUT_ERROR(OCSP, ERR_R_PASSED_NULL_PARAMETER);
return OCSP_NONCE_NOT_EQUAL;
}
// Since we are only interested in the presence or absence of
// the nonce and comparing its value there is no need to use
// the X509V3 routines: this way we can avoid them allocating an
// ASN1_OCTET_STRING structure for the value which would be
// freed immediately anyway.
int req_idx, resp_idx;
X509_EXTENSION *req_ext, *resp_ext;
req_idx = OCSP_REQUEST_get_ext_by_NID(req, NID_id_pkix_OCSP_Nonce, -1);
resp_idx = OCSP_BASICRESP_get_ext_by_NID(bs, NID_id_pkix_OCSP_Nonce, -1);
// Check that both are absent.
if ((req_idx < 0) && (resp_idx < 0)) {
return OCSP_NONCE_BOTH_ABSENT;
}
// Check in request only.
if ((req_idx >= 0) && (resp_idx < 0)) {
return OCSP_NONCE_REQUEST_ONLY;
}
// Check in response, but not request.
if ((req_idx < 0) && (resp_idx >= 0)) {
return OCSP_NONCE_RESPONSE_ONLY;
}
// Otherwise, there is a nonce in both the request and response, so retrieve
// the extensions.
req_ext = OCSP_REQUEST_get_ext(req, req_idx);
resp_ext = OCSP_BASICRESP_get_ext(bs, resp_idx);
if (ASN1_OCTET_STRING_cmp(X509_EXTENSION_get_data(req_ext),
X509_EXTENSION_get_data(resp_ext))) {
return OCSP_NONCE_NOT_EQUAL;
}
return OCSP_NONCE_EQUAL;
}
int OCSP_copy_nonce(OCSP_BASICRESP *resp, OCSP_REQUEST *req) {
GUARD_PTR(resp);
GUARD_PTR(req);
// Check for nonce in request.
int req_idx = OCSP_REQUEST_get_ext_by_NID(req, NID_id_pkix_OCSP_Nonce, -1);
// If no nonce, that's OK. We return 2 in this case.
if (req_idx < 0) {
return 2;
}
X509_EXTENSION *req_ext = OCSP_REQUEST_get_ext(req, req_idx);
// Nonce found, but no entry at the index.
// This shouldn't happen under normal circumstances.
GUARD_PTR(req_ext);
// Append the nonce.
return OCSP_BASICRESP_add_ext(resp, req_ext, -1);
}

View File

@@ -0,0 +1,530 @@
// Copyright 2001-2017 The OpenSSL Project Authors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
// Modifications Copyright Amazon.com, Inc. or its affiliates.
// SPDX-License-Identifier: Apache-2.0 OR ISC
#include <ctype.h>
#include <string.h>
#include "openssl/mem.h"
#include "../internal.h"
#include "internal.h"
#define OCSP_MAX_LINE_LEN 4096;
// OCSP_REQ_CTX states
// If set no reading should be performed
#define OHS_NOREAD 0x1000
// Error condition
#define OHS_ERROR (0 | OHS_NOREAD)
// First line being read
#define OHS_FIRSTLINE 1
// MIME headers being read
#define OHS_HEADERS 2
// OCSP initial header (tag + length) being read
#define OHS_ASN1_HEADER 3
// OCSP content octets being read
#define OHS_ASN1_CONTENT 4
// First call: ready to start I/O
#define OHS_ASN1_WRITE_INIT (5 | OHS_NOREAD)
// Request being sent
#define OHS_ASN1_WRITE (6 | OHS_NOREAD)
// Request being flushed
#define OHS_ASN1_FLUSH (7 | OHS_NOREAD)
// Completed
#define OHS_DONE (8 | OHS_NOREAD)
// Headers set, no final \r\n included
#define OHS_HTTP_HEADER (9 | OHS_NOREAD)
static int check_protocol(char *line) {
if (strlen(line) >= 4 && strncmp(line, "HTTP", 4) == 0) {
return 1;
}
return 0;
}
// Parse the HTTP response. This will look like this: "HTTP/1.0 200 OK". We
// need to obtain the numeric code and (optional) informational message.
static int parse_http_line(char *line) {
int http_code;
char *code, *reason, *end;
if (!check_protocol(line)) {
OPENSSL_PUT_ERROR(OCSP, OCSP_R_SERVER_RESPONSE_PARSE_ERROR);
return 0;
}
// Skip to first white space (passed protocol info)
for (code = line; *code != '\0' && !OPENSSL_isspace(*code); code++) {
continue;
}
if (*code == '\0') {
OPENSSL_PUT_ERROR(OCSP, OCSP_R_SERVER_RESPONSE_PARSE_ERROR);
return 0;
}
// Skip past white space to start of response code.
while (*code != '\0' && OPENSSL_isspace(*code)) {
code++;
}
if (*code == '\0') {
OPENSSL_PUT_ERROR(OCSP, OCSP_R_SERVER_RESPONSE_PARSE_ERROR);
return 0;
}
// Find end of response code: first whitespace after start of code.
for (reason = code; *reason != '\0' && !OPENSSL_isspace(*reason); reason++) {
continue;
}
if (*reason == '\0') {
OPENSSL_PUT_ERROR(OCSP, OCSP_R_SERVER_RESPONSE_PARSE_ERROR);
return 0;
}
// Set end of response code and start of message.
*reason++ = '\0';
// Attempt to parse numeric code
http_code = (int)strtoul(code, &end, 10);
if (*end != '\0') {
return 0;
}
// Skip over any leading white space in message.
while (*reason != '\0' && OPENSSL_isspace(*reason)) {
reason++;
}
if (*reason != '\0') {
// Finally, zap any trailing white space in message (include CRLF).
// We know reason has a non-white space character so this is OK.
for (end = reason + strlen(reason) - 1; OPENSSL_isspace(*end); end--) {
*end = '\0';
}
}
if (http_code != 200) {
OPENSSL_PUT_ERROR(OCSP, OCSP_R_SERVER_RESPONSE_PARSE_ERROR);
if (*reason == '\0') {
ERR_add_error_data(2, "Code=", code);
} else {
ERR_add_error_data(4, "Code=", code, ",Reason=", reason);
}
return 0;
}
return 1;
}
// |OCSP_REQUEST| sending:
// OHS_HTTP_HEADER should be initial state when first calling
// |OCSP_REQ_CTX_nbio|. OHS_HTTP_HEADER represents the state where OCSP request
// headers have finished being written, and we want to add the final "\r\n" to
// the ASN.1 OCSP request. This falls through to OHS_ASN1_WRITE_INIT and
// OHS_ASN1_WRITE, where we start writing the OCSP request from |rctx->mem| to
// |rctx->io|. |OHS_ASN1_WRITE| will continue writing the ASN.1 contents until
// all |OCSP_REQUEST| ASN.1 contents have been written. When OHS_ASN1_WRITE
// finishes writing, we will reset the BIO contents of |rctx->mem| and set the
// state to OHS_ASN1_FLUSH. OHS_ASN1_FLUSH will flush any buffered output that
// had been in |rctx->io|.
//
// |OCSP_RESPONSE| awaiting:
// Once OHS_ASN1_FLUSH has finished, we'll turn into a state of OHS_FIRSTLINE,
// and start expecting to read the first line of the HTTP OCSP response written
// back by the OCSP responder in |rctx->mem|. OHS_FIRSTLINE expects to parse the
// first line of the HTTP response, which contains the numeric code and
// (optional) informational message. The numeric code is parsed and verified
// with |parse_http_line|. Once the numeric code is parsed, OHS_FIRSTLINE will
// transtion to OHS_HEADERS. OHS_HEADERS parses any additional subsequent HTTP
// content headers in the OCSP HTTP response. Once a blank line is detected, we
// fallthrough to the state OHS_ASN1_HEADER and start expecting the ASN.1
// contents of the OCSP response. OHS_ASN1_HEADER first checks the ASN1 header
// contents, which should contain the length field. This then falls through to
// |OHS_ASN1_CONTENT| where we start reading in the actual contents of the
// ASN.1 OCSP response. Once all ASN.1 contents up to the length field have been
// read, |OCSP_REQ_CTX_nbio| will finish in the state of OHS_DONE.
// |OCSP_REQ_CTX_nbio| will not return 1 until we reach OHS_DONE.
int OCSP_REQ_CTX_nbio(OCSP_REQ_CTX *rctx) {
int ret, tmp_data_len;
size_t data_len;
const unsigned char *data;
next_io:
if (!(rctx->state & OHS_NOREAD)) {
tmp_data_len = BIO_read(rctx->io, rctx->iobuf, rctx->iobuflen);
if (tmp_data_len <= 0) {
if (BIO_should_retry(rctx->io)) {
return -1;
}
return 0;
}
// Write data to memory BIO.
if (BIO_write(rctx->mem, rctx->iobuf, tmp_data_len) != tmp_data_len) {
return 0;
}
data_len = (size_t)tmp_data_len;
}
switch (rctx->state) {
case OHS_HTTP_HEADER:
// Last operation was adding headers: need a final "\r\n".
if (BIO_write(rctx->mem, "\r\n", 2) != 2) {
rctx->state = OHS_ERROR;
return 0;
}
rctx->state = OHS_ASN1_WRITE_INIT;
OPENSSL_FALLTHROUGH;
case OHS_ASN1_WRITE_INIT:
if(!BIO_mem_contents(rctx->mem, NULL, &data_len)) {
rctx->state = OHS_ERROR;
return 0;
}
rctx->asn1_len = data_len;
rctx->state = OHS_ASN1_WRITE;
OPENSSL_FALLTHROUGH;
case OHS_ASN1_WRITE:
if(!BIO_mem_contents(rctx->mem, &data, &data_len)) {
rctx->state = OHS_ERROR;
return 0;
}
int write_len = BIO_write(rctx->io, data + (data_len - rctx->asn1_len),
(int)rctx->asn1_len);
if (write_len <= 0) {
if (BIO_should_retry(rctx->io)) {
return -1;
}
rctx->state = OHS_ERROR;
return 0;
}
rctx->asn1_len -= write_len;
if (rctx->asn1_len > 0) {
goto next_io;
}
rctx->state = OHS_ASN1_FLUSH;
if(!BIO_reset(rctx->mem)) {
return 0;
}
OPENSSL_FALLTHROUGH;
case OHS_ASN1_FLUSH:
ret = BIO_flush(rctx->io);
if (ret > 0) {
rctx->state = OHS_FIRSTLINE;
goto next_io;
}
if (BIO_should_retry(rctx->io)) {
return -1;
}
rctx->state = OHS_ERROR;
return 0;
case OHS_ERROR:
return 0;
case OHS_FIRSTLINE:
case OHS_HEADERS:
// Attempt to read a line in.
next_line:
// Due to strange memory BIO behaviour with BIO_gets we have to
// check there's a complete line in there before calling BIO_gets
// or we'll just get a partial read.
if(!BIO_mem_contents(rctx->mem, &data, &data_len)) {
rctx->state = OHS_ERROR;
return 0;
}
if ((data_len <= 0) || !memchr(data, '\n', data_len)) {
if (data_len >= (size_t)rctx->iobuflen) {
rctx->state = OHS_ERROR;
return 0;
}
goto next_io;
}
tmp_data_len = BIO_gets(rctx->mem, (char *)rctx->iobuf, rctx->iobuflen);
if (tmp_data_len <= 0) {
if (BIO_should_retry(rctx->mem)) {
goto next_io;
}
rctx->state = OHS_ERROR;
return 0;
}
// Don't allow excessive lines.
if (tmp_data_len >= rctx->iobuflen) {
rctx->state = OHS_ERROR;
return 0;
}
data_len = (size_t)tmp_data_len;
// First line.
if (rctx->state == OHS_FIRSTLINE) {
if (parse_http_line((char *)rctx->iobuf)) {
rctx->state = OHS_HEADERS;
goto next_line;
} else {
rctx->state = OHS_ERROR;
return 0;
}
} else {
// Look for blank line: end of headers.
for (data = rctx->iobuf; *data; data++) {
if ((*data != '\r') && (*data != '\n')) {
break;
}
}
if (*data != '\0') {
goto next_line;
}
rctx->state = OHS_ASN1_HEADER;
}
OPENSSL_FALLTHROUGH;
case OHS_ASN1_HEADER:
// Now reading ASN1 header: can read at least 2 bytes which is
// enough for ASN1 SEQUENCE header and either length field or at
// least the length of the length field.
if(!BIO_mem_contents(rctx->mem, &data, &data_len)) {
rctx->state = OHS_ERROR;
return 0;
}
if (data_len < 2) {
goto next_io;
}
// Check it is an ASN1 SEQUENCE.
if (*data++ != (V_ASN1_SEQUENCE | V_ASN1_CONSTRUCTED)) {
rctx->state = OHS_ERROR;
return 0;
}
// Check out length field. This is checking to see if the length is
// encoded as long-form (multiple bytes) versus being a length that
// can be encoded into 7 bits. "0x80" implies "0x80 + N", where N is
// the number of length bytes to follow.
if ((*data & 0x80) != 0) {
// If MSB set on initial length octet we can now always read 6
// octets: make sure we have them.
if (data_len < 6) {
goto next_io;
}
data_len = *data & 0x7F;
// Not NDEF or excessive length.
if (!data_len || (data_len > 4)) {
rctx->state = OHS_ERROR;
return 0;
}
data++;
rctx->asn1_len = 0;
for (size_t i = 0; i < data_len; i++) {
rctx->asn1_len <<= 8;
rctx->asn1_len |= *data++;
}
if (rctx->asn1_len > rctx->max_resp_len) {
rctx->state = OHS_ERROR;
return 0;
}
rctx->asn1_len += data_len + 2;
} else {
rctx->asn1_len = *data + 2;
}
rctx->state = OHS_ASN1_CONTENT;
OPENSSL_FALLTHROUGH;
case OHS_ASN1_CONTENT:
if(!BIO_mem_contents(rctx->mem, NULL, &data_len)) {
rctx->state = OHS_ERROR;
return 0;
}
if (data_len < rctx->asn1_len) {
goto next_io;
}
rctx->state = OHS_DONE;
return 1;
case OHS_DONE:
return 1;
}
return 0;
}
int OCSP_sendreq_nbio(OCSP_RESPONSE **presp, OCSP_REQ_CTX *rctx) {
return OCSP_REQ_CTX_nbio_d2i(rctx, (ASN1_VALUE **)presp,
ASN1_ITEM_rptr(OCSP_RESPONSE));
}
OCSP_RESPONSE *OCSP_sendreq_bio(BIO *b, const char *path, OCSP_REQUEST *req) {
OCSP_RESPONSE *resp = NULL;
OCSP_REQ_CTX *ctx;
int rv;
ctx = OCSP_sendreq_new(b, path, req, -1);
if (ctx == NULL) {
return NULL;
}
// This waits indefinitely on a response, if |BIO_should_retry| is on and
// the BIO persists.
do {
rv = OCSP_sendreq_nbio(&resp, ctx);
} while ((rv == -1) && BIO_should_retry(b));
OCSP_REQ_CTX_free(ctx);
if (rv) {
return resp;
}
return NULL;
}
int OCSP_REQ_CTX_nbio_d2i(OCSP_REQ_CTX *rctx, ASN1_VALUE **pval,
const ASN1_ITEM *it) {
int rv;
size_t len;
const unsigned char *p;
rv = OCSP_REQ_CTX_nbio(rctx);
if (rv != 1) {
return rv;
}
if (!BIO_mem_contents(rctx->mem, &p, &len)) {
goto err;
}
*pval = ASN1_item_d2i(NULL, &p, (long)len, it);
if (*pval == NULL) {
goto err;
}
return 1;
err:
rctx->state = OHS_ERROR;
return 0;
}
OCSP_REQ_CTX *OCSP_REQ_CTX_new(BIO *io, int maxline) {
OCSP_REQ_CTX *rctx = OPENSSL_malloc(sizeof(OCSP_REQ_CTX));
if (rctx == NULL) {
return NULL;
}
rctx->state = OHS_ERROR;
rctx->max_resp_len = OCSP_MAX_RESP_LENGTH;
rctx->mem = BIO_new(BIO_s_mem());
rctx->io = io;
if (maxline > 0) {
rctx->iobuflen = maxline;
} else {
rctx->iobuflen = OCSP_MAX_LINE_LEN;
}
rctx->iobuf = OPENSSL_malloc(rctx->iobuflen);
if (rctx->iobuf == NULL || rctx->mem == NULL) {
OCSP_REQ_CTX_free(rctx);
return NULL;
}
return rctx;
}
void OCSP_REQ_CTX_free(OCSP_REQ_CTX *rctx) {
if (rctx == NULL) {
return;
}
BIO_free(rctx->mem);
OPENSSL_free(rctx->iobuf);
OPENSSL_free(rctx);
}
OCSP_REQ_CTX *OCSP_sendreq_new(BIO *io, const char *path, OCSP_REQUEST *req,
int maxline) {
OCSP_REQ_CTX *rctx = NULL;
rctx = OCSP_REQ_CTX_new(io, maxline);
if (rctx == NULL) {
return NULL;
}
if (!OCSP_REQ_CTX_http(rctx, "POST", path)) {
goto err;
}
if (req != NULL && !OCSP_REQ_CTX_set1_req(rctx, req)) {
goto err;
}
return rctx;
err:
OCSP_REQ_CTX_free(rctx);
return NULL;
}
int OCSP_REQ_CTX_http(OCSP_REQ_CTX *rctx, const char *op, const char *path) {
static const char http_hdr[] = "%s %s HTTP/1.0\r\n";
// Set to a default path, if path is NULL.
if (path == NULL) {
path = "/";
}
if (BIO_printf(rctx->mem, http_hdr, op, path) <= 0) {
return 0;
}
rctx->state = OHS_HTTP_HEADER;
return 1;
}
int OCSP_REQ_CTX_set1_req(OCSP_REQ_CTX *rctx, OCSP_REQUEST *req) {
return OCSP_REQ_CTX_i2d(rctx, ASN1_ITEM_rptr(OCSP_REQUEST),
(ASN1_VALUE *)req);
}
int OCSP_REQ_CTX_add1_header(OCSP_REQ_CTX *rctx, const char *name,
const char *value) {
if (name == NULL) {
return 0;
}
// The following being written conforms to the message-header field
// specification in https://www.rfc-editor.org/rfc/rfc2616#section-4.2.
// message-header = field-name ":" [ field-value ]
if (BIO_puts(rctx->mem, name) <= 0) {
return 0;
}
if (value != NULL) {
if (BIO_write(rctx->mem, ": ", 2) != 2) {
return 0;
}
if (BIO_puts(rctx->mem, value) <= 0) {
return 0;
}
}
if (BIO_write(rctx->mem, "\r\n", 2) != 2) {
return 0;
}
rctx->state = OHS_HTTP_HEADER;
return 1;
}
int OCSP_REQ_CTX_i2d(OCSP_REQ_CTX *rctx, const ASN1_ITEM *it, ASN1_VALUE *val) {
static const char req_hdr[] =
"Content-Type: application/ocsp-request\r\n"
"Content-Length: %d\r\n\r\n";
int reqlen = ASN1_item_i2d(val, NULL, it);
if (BIO_printf(rctx->mem, req_hdr, reqlen) <= 0) {
return 0;
}
if (ASN1_item_i2d_bio(it, rctx->mem, val) <= 0) {
return 0;
}
rctx->state = OHS_ASN1_WRITE_INIT;
return 1;
}
BIO *OCSP_REQ_CTX_get0_mem_bio(OCSP_REQ_CTX *rctx) { return rctx->mem; }
void OCSP_set_max_response_length(OCSP_REQ_CTX *rctx, unsigned long len) {
if (len == 0) {
rctx->max_resp_len = OCSP_MAX_RESP_LENGTH;
} else {
rctx->max_resp_len = len;
}
}

View File

@@ -0,0 +1,542 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0 OR ISC
#include <gtest/gtest.h>
#include <ctime>
#include <fstream>
#include <openssl/ocsp.h>
#include <openssl/pem.h>
#include <openssl/ssl.h>
#include "../../tool/transport_common.h"
#include "../internal.h"
#include "../test/test_util.h"
#include "internal.h"
#define OCSP_VERIFYSTATUS_SUCCESS 1
#define OCSP_VERIFYSTATUS_ERROR 0
#define OCSP_VERIFYSTATUS_FATALERROR -1
std::string GetTestData(const char *path);
// Certificates always expire and that applies to the root certificates we're
// using in our trust store too.
static void isCertificateExpiring(X509 *cert) {
// Get the current time and add 1 year to it.
int64_t warning_time = time(nullptr) + 31536000;
if (X509_cmp_time_posix(X509_get_notAfter(cert), warning_time) < 0) {
fprintf(stdout, "\n\n\n\n");
X509_NAME_print_ex_fp(stdout, X509_get_subject_name(cert), 0, 0);
fprintf(stdout,
" WILL EXPIRE IN A YEAR, PLEASE REPLACE ME WITH THE NEW"
" EXPECTED ROOT CERTIFICATE FROM THE ENDPOINT.\n\n\n\n");
}
}
static X509_STORE *SetupTrustStore() {
X509_STORE *trust_store = X509_STORE_new();
for (int i = 1; i <= 3; i++) {
std::ostringstream oss;
oss << "crypto/ocsp/test/integration-tests/AmazonRootCA" << i << ".pem";
bssl::UniquePtr<X509> ca_cert(
CertFromPEM(GetTestData(oss.str().c_str()).c_str()));
EXPECT_TRUE(X509_STORE_add_cert(trust_store, ca_cert.get()));
isCertificateExpiring(ca_cert.get());
}
return trust_store;
}
static OCSP_RESPONSE *GetOCSPResponse(const char *ocsp_responder_host,
X509 *certificate,
OCSP_REQUEST *request) {
// Connect to OCSP Host. Connect to the defined OCSP responder if specified.
// Otherwise, we connect to the OCSP responder specified in the cert.
char *host = nullptr;
bssl::UniquePtr<BIO> http_bio;
if (ocsp_responder_host) {
http_bio = bssl::UniquePtr<BIO>(BIO_new_connect(ocsp_responder_host));
} else {
bssl::UniquePtr<STACK_OF(OPENSSL_STRING)> responder(
X509_get1_ocsp(certificate));
char *path, *port;
int is_ssl;
EXPECT_TRUE(OCSP_parse_url(sk_OPENSSL_STRING_value(responder.get(), 0),
&host, &port, &path, &is_ssl));
http_bio = bssl::UniquePtr<BIO>(BIO_new_connect(host));
OPENSSL_free(path);
OPENSSL_free(port);
}
EXPECT_TRUE(http_bio);
BIO_set_conn_port(http_bio.get(), "80");
BIO_set_retry_read(http_bio.get());
EXPECT_TRUE(BIO_do_connect(http_bio.get()));
// Set up an |OCSP_REQ_CTX| to be sent out.
bssl::UniquePtr<OCSP_REQ_CTX> ocsp_ctx(
OCSP_sendreq_new(http_bio.get(), "/", nullptr, -1));
OCSP_REQ_CTX_add1_header(ocsp_ctx.get(), "Host",
ocsp_responder_host ? ocsp_responder_host : host);
OCSP_REQ_CTX_set1_req(ocsp_ctx.get(), request);
// Try connecting to the OCSP responder endpoint with a timeout of 8 seconds.
// Sends out an OCSP request and expects an OCSP response.
//
// Note: The OCSP responder times out occsasionally, which results in
// arbitrary test failues. We can adjust |timeout| accordingly when these
// happen too often.
OCSP_RESPONSE *resp = nullptr;
time_t start_time = time(nullptr);
double timeout = 8;
int rv;
do {
rv = OCSP_sendreq_nbio(&resp, ocsp_ctx.get());
} while (rv == -1 && difftime(time(nullptr), start_time) < timeout);
OPENSSL_free(host);
return resp;
}
static void ValidateOCSPResponse(OCSP_RESPONSE *response,
bool authorized_responder, X509 *certificate,
X509 *issuer, X509_STORE *trust_store,
OCSP_CERTID *cert_id,
int expected_verify_status,
int expected_cert_status) {
if (authorized_responder) {
ASSERT_EQ(OCSP_response_status(response), OCSP_RESPONSE_STATUS_SUCCESSFUL);
} else {
ASSERT_NE(OCSP_response_status(response), OCSP_RESPONSE_STATUS_SUCCESSFUL);
}
bssl::UniquePtr<OCSP_BASICRESP> basic_response(
OCSP_response_get1_basic(response));
// The OCSP response should not have any additional data to parse if it's an
// unauthorized responder.
if (!authorized_responder) {
ASSERT_FALSE(basic_response);
return;
}
ASSERT_TRUE(basic_response);
bssl::UniquePtr<STACK_OF(X509)> untrusted_chain(sk_X509_new_null());
EXPECT_TRUE(sk_X509_push(untrusted_chain.get(), X509_dup(certificate)));
EXPECT_TRUE(sk_X509_push(untrusted_chain.get(), X509_dup(issuer)));
// Verifies the OCSP responder's signature on the OCSP response data.
EXPECT_EQ(OCSP_basic_verify(basic_response.get(), untrusted_chain.get(),
trust_store, 0),
expected_verify_status);
// Checks revocation status of the response.
int status;
ASN1_GENERALIZEDTIME *thisupdate, *nextupdate;
EXPECT_TRUE(OCSP_resp_find_status(basic_response.get(), cert_id, &status,
nullptr, nullptr, &thisupdate,
&nextupdate));
EXPECT_EQ(status, expected_cert_status);
// We just got an OCSP response, so this should never be out of date.
EXPECT_TRUE(OCSP_check_validity(thisupdate, nextupdate, 0, -1));
}
struct IntegrationTestVector {
const char *url_host;
// Connect to the OCSP responder specified in the cert if NULL.
const char *ocsp_responder_host;
// This is true if we successfully connect to the OCSP responder.
bool expected_conn_status;
// This is true if the OCSP responder is authorized to validate the cert.
bool authorized_responder;
int expected_verify_status;
int expected_cert_status;
};
// We connect to "ocsp.sca[0-4]a.amazontrust.com" OCSP responder endpoints.
// The following URLs in the test suite were taken from Amazon Trust
// Services: https://www.amazontrust.com/repository/.
// To avoid having timebomb failures when hard-coded certs expire, we do an
// SSL connection with a specified URL and retrieve the certificate chain the
// server uses. "valid.*.amazontrust.com" endpoints rotate their cert
// periodically, so we can trust that doing OCSP verification with their
// certificate chain will not result in any timebomb failures further down the
// line. We also connect against the OCSP responder URI specified in the
// cert, so that our tests aren't sensitive to changes in the endpoint's OCSP
// responder URI.
static const IntegrationTestVector kIntegrationTestVectors[] = {
{"valid.rootca1.demo.amazontrust.com", nullptr, true, true,
OCSP_VERIFYSTATUS_SUCCESS, V_OCSP_CERTSTATUS_GOOD},
{"valid.rootca2.demo.amazontrust.com", nullptr, true, true,
OCSP_VERIFYSTATUS_SUCCESS, V_OCSP_CERTSTATUS_GOOD},
{"valid.rootca3.demo.amazontrust.com", nullptr, true, true,
OCSP_VERIFYSTATUS_SUCCESS, V_OCSP_CERTSTATUS_GOOD},
// A revoked cert will succeed OCSP cert verification, but the OCSP
// responder will indicate it has been revoked.
{"revoked.rootca1.demo.amazontrust.com", nullptr, true, true,
OCSP_VERIFYSTATUS_SUCCESS, V_OCSP_CERTSTATUS_REVOKED},
{"revoked.rootca2.demo.amazontrust.com", nullptr, true, true,
OCSP_VERIFYSTATUS_SUCCESS, V_OCSP_CERTSTATUS_REVOKED},
{"revoked.rootca3.demo.amazontrust.com", nullptr, true, true,
OCSP_VERIFYSTATUS_SUCCESS, V_OCSP_CERTSTATUS_REVOKED},
// Connect to an unauthorized OCSP responder endpoint. This will
// successfully get an OCSP response, but the OCSP response will not be
// valid.
// Site not available {"valid.rootca1.demo.amazontrust.com", "ocsp.sectigo.com", true, false, 0, 0},
// Connect to a non-OCSP responder endpoint. These should fail to get an
// OCSP response, unless these hosts decide to become OCSP responders.
{"valid.rootca1.demo.amazontrust.com", "amazon.com", false, false, 0, 0},
{"valid.rootca2.demo.amazontrust.com", "www.example.com", false, false, 0,
0},
};
class OCSPIntegrationTest
: public testing::TestWithParam<IntegrationTestVector> {};
INSTANTIATE_TEST_SUITE_P(All, OCSPIntegrationTest,
testing::ValuesIn(kIntegrationTestVectors));
TEST_P(OCSPIntegrationTest, AmazonTrustServices) {
const IntegrationTestVector &t = GetParam();
// Connect to the specified endpoint. We do this to retrieve the
// certificate chain the endpoint uses.
int sock = -1;
ASSERT_TRUE(InitSocketLibrary());
ASSERT_TRUE(Connect(&sock, t.url_host, false));
ASSERT_TRUE(SSL_library_init());
bssl::UniquePtr<SSL_CTX> ssl_ctx(SSL_CTX_new(TLS_method()));
ASSERT_TRUE(ssl_ctx);
// Set up trust store.
bssl::UniquePtr<X509_STORE> trust_store(SetupTrustStore());
ASSERT_TRUE(SSL_CTX_set1_verify_cert_store(ssl_ctx.get(), trust_store.get()));
bssl::UniquePtr<BIO> bio(BIO_new_socket(sock, BIO_CLOSE));
bssl::UniquePtr<SSL> ssl(SSL_new(ssl_ctx.get()));
// Let the endpoint know what we're trying to connect to since multiple
// websites can be served on one server.
SSL_set_tlsext_host_name(ssl.get(), t.url_host);
SSL_set_bio(ssl.get(), bio.get(), bio.get());
ASSERT_TRUE(SSL_connect(ssl.get()));
STACK_OF(X509) *chain = SSL_get_peer_full_cert_chain(ssl.get());
EXPECT_EQ(SSL_get_verify_result(ssl.get()), X509_V_OK);
// Leaf to be verified should be the first one in the chain.
X509 *certificate = sk_X509_value(chain, 0);
ASSERT_TRUE(certificate);
// find the issuer in the chain.
X509 *issuer = nullptr;
for (size_t i = 0; i < sk_X509_num(chain); ++i) {
X509 *issuer_candidate = sk_X509_value(chain, i);
const int issuer_value = X509_check_issued(issuer_candidate, certificate);
if (issuer_value == X509_V_OK) {
issuer = issuer_candidate;
break;
}
}
ASSERT_TRUE(issuer);
bssl::UniquePtr<OCSP_REQUEST> request(OCSP_REQUEST_new());
bssl::UniquePtr<OCSP_CERTID> cert_id(
OCSP_cert_to_id(EVP_sha1(), certificate, issuer));
ASSERT_TRUE(OCSP_request_add0_id(request.get(), cert_id.get()));
bssl::UniquePtr<OCSP_RESPONSE> resp(
GetOCSPResponse(t.ocsp_responder_host, certificate, request.get()));
if (t.expected_conn_status) {
EXPECT_TRUE(resp);
ValidateOCSPResponse(resp.get(), t.authorized_responder, certificate,
issuer, trust_store.get(), cert_id.get(),
t.expected_verify_status, t.expected_cert_status);
} else {
EXPECT_FALSE(resp);
}
bio.release();
cert_id.release();
}
#define VALID 'V'
#define REVOKED 'R'
struct Certificate {
char statusFlag; // Certificate status flag (V, R, E)
std::string expirationDate; // Expiration date in [YY]YYMMDDHHMMSSZ format
std::string revocationDate; // Revocation date in [YY]YYMMDDHHMMSSZ format
// (empty if not revoked)
std::string serialNumber; // Serial number in hex
std::string fileName; // Certificate filename or 'unknown'
std::string subjectDN; // Certificate subject DN
};
static std::vector<const Certificate *> parse_ca_database(
const std::string &filename) {
std::vector<const Certificate *> certificates;
std::string db = GetTestData(filename.c_str());
std::istringstream stream(db);
std::string line;
while (std::getline(stream, line)) {
std::istringstream iss(line);
auto *cert = new Certificate;
// Parsing status flag
iss >> cert->statusFlag;
iss.ignore(); // Ignore the space after the flag
// Parsing expiration date
iss >> cert->expirationDate;
iss.ignore();
// Parsing revocation date. This only exists if the cert is revoked.
if (cert->statusFlag == REVOKED) {
iss >> cert->revocationDate;
iss.ignore();
}
// Parsing serial number
iss >> cert->serialNumber;
iss.ignore();
iss >> cert->fileName;
iss.ignore();
// Parsing the subject DN (remaining part of the line)
std::getline(iss, cert->subjectDN);
certificates.push_back(cert);
}
return certificates;
}
static const Certificate *lookup_serial_number(
const std::vector<const Certificate *> &certificates,
ASN1_INTEGER *serial_number) {
bssl::UniquePtr<BIGNUM> bnser(ASN1_INTEGER_to_BN(serial_number, nullptr));
bssl::UniquePtr<char> asciiHex(BN_bn2hex(bnser.get()));
for (auto &cert : certificates) {
if (cert->serialNumber == asciiHex.get()) {
return cert;
}
}
return nullptr;
}
// Replicate basic functionalities of an OCSP responder.
static bool LocalMockOCSPResponder(BIO *bio, const X509 *ca_cert,
const std::string &ca_txt_db) {
std::vector<const Certificate *> certificates =
parse_ca_database("crypto/ocsp/test/aws/" + ca_txt_db);
// Set up OCSP responder server credentials
bssl::UniquePtr<X509> signer_cert(CertFromPEM(
GetTestData(std::string("crypto/ocsp/test/aws/ocsp_cert.pem").c_str())
.c_str()));
EXPECT_TRUE(signer_cert);
bssl::UniquePtr<EVP_PKEY> signer_key(EVP_PKEY_new());
bssl::UniquePtr<RSA> rsa(RSAFromPEM(
GetTestData(std::string("crypto/ocsp/test/aws/ocsp_key.pem").c_str())
.c_str()));
EXPECT_TRUE(rsa);
EXPECT_TRUE(EVP_PKEY_set1_RSA(signer_key.get(), rsa.get()));
// Read request from |bio|.
bssl::UniquePtr<OCSP_REQUEST> request(d2i_OCSP_REQUEST_bio(bio, nullptr));
EXPECT_TRUE(request);
// Set up an |OCSP_BASICRESP| to send back to the requester.
bssl::UniquePtr<OCSP_BASICRESP> basic_response(OCSP_BASICRESP_new());
EXPECT_TRUE(basic_response);
bssl::UniquePtr<ASN1_TIME> this_update(
ASN1_GENERALIZEDTIME_set(nullptr, std::time(nullptr)));
EXPECT_TRUE(this_update);
bssl::UniquePtr<ASN1_TIME> next_update(
ASN1_GENERALIZEDTIME_set(nullptr, std::time(nullptr) + 3600));
EXPECT_TRUE(next_update);
int onereq_count = OCSP_request_onereq_count(request.get());
// Examine each certificate id in the request.
for (int i = 0; i < onereq_count; i++) {
ASN1_OBJECT *cid_md_oid;
ASN1_INTEGER *serial;
OCSP_ONEREQ *one = OCSP_request_onereq_get0(request.get(), i);
OCSP_CERTID *cid = OCSP_onereq_get0_id(one);
EXPECT_TRUE(OCSP_id_get0_info(nullptr, &cid_md_oid, nullptr, &serial, cid));
const EVP_MD *cid_md = EVP_get_digestbyobj(cid_md_oid);
EXPECT_TRUE(cid_md);
// In a typical OCSP responder, this would loop over every |ca_cert|
// available to compare, but we only use 1 here for testing simplicity.
bssl::UniquePtr<OCSP_CERTID> ca_id(
OCSP_cert_to_id(cid_md, nullptr, ca_cert));
EXPECT_TRUE(ca_id);
if (OCSP_id_issuer_cmp(ca_id.get(), cid) != 0) {
EXPECT_TRUE(OCSP_basic_add1_status(basic_response.get(), cid,
V_OCSP_CERTSTATUS_UNKNOWN, 0, nullptr,
this_update.get(), next_update.get()));
continue;
}
// Look for the certificate in our certs database.
const Certificate *cert_info = lookup_serial_number(certificates, serial);
if (cert_info == nullptr) {
EXPECT_TRUE(OCSP_basic_add1_status(basic_response.get(), cid,
V_OCSP_CERTSTATUS_UNKNOWN, 0, nullptr,
this_update.get(), next_update.get()));
} else if (cert_info->statusFlag == VALID) {
EXPECT_TRUE(OCSP_basic_add1_status(basic_response.get(), cid,
V_OCSP_CERTSTATUS_GOOD, 0, nullptr,
this_update.get(), next_update.get()));
} else if (cert_info->statusFlag == REVOKED) {
bssl::UniquePtr<ASN1_TIME> revoked_time(ASN1_UTCTIME_new());
EXPECT_TRUE(ASN1_UTCTIME_set_string(revoked_time.get(),
cert_info->revocationDate.c_str()));
EXPECT_TRUE(OCSP_basic_add1_status(
basic_response.get(), cid, V_OCSP_CERTSTATUS_REVOKED,
OCSP_REVOKED_STATUS_UNSPECIFIED, revoked_time.get(),
this_update.get(), next_update.get()));
}
}
// This can return 1 or 2 depending on the existence of the request's nonce.
// Either are correct.
int nonce_copy_status = OCSP_copy_nonce(basic_response.get(), request.get());
EXPECT_TRUE(nonce_copy_status == 1 || nonce_copy_status == 2);
// Do the actual sign.
EXPECT_TRUE(OCSP_basic_sign(basic_response.get(), signer_cert.get(),
signer_key.get(), EVP_sha256(), nullptr, 0));
// Write response to |bio| to send back to client.
bssl::UniquePtr<OCSP_RESPONSE> response(OCSP_response_create(
OCSP_RESPONSE_STATUS_SUCCESSFUL, basic_response.get()));
EXPECT_TRUE(response);
EXPECT_TRUE(i2d_OCSP_RESPONSE_bio(bio, response.get()));
for (auto &cert : certificates) {
delete cert;
}
return true;
}
struct RoundTripTestVector {
const char *ca_txt_db;
// cert that we're checking the revocation status for.
const char *ocsp_request_cert;
// OCSP nonces are optional, so we test both scenarios.
bool add_nonce;
int expected_cert_status;
};
static const RoundTripTestVector kRoundTripTestVectors[] = {
{"certs.txt", "rsa_cert", true, V_OCSP_CERTSTATUS_GOOD},
{"certs_revoked.txt", "rsa_cert", true, V_OCSP_CERTSTATUS_REVOKED},
{"certs_unknown.txt", "rsa_cert", true, V_OCSP_CERTSTATUS_UNKNOWN},
{"certs.txt", "ecdsa_cert", true, V_OCSP_CERTSTATUS_GOOD},
{"certs_revoked.txt", "ecdsa_cert", true, V_OCSP_CERTSTATUS_REVOKED},
{"certs_unknown.txt", "ecdsa_cert", true, V_OCSP_CERTSTATUS_UNKNOWN},
{"certs.txt", "rsa_cert", false, V_OCSP_CERTSTATUS_GOOD},
{"certs_revoked.txt", "rsa_cert", false, V_OCSP_CERTSTATUS_REVOKED},
{"certs_unknown.txt", "rsa_cert", false, V_OCSP_CERTSTATUS_UNKNOWN},
{"certs.txt", "ecdsa_cert", false, V_OCSP_CERTSTATUS_GOOD},
{"certs_revoked.txt", "ecdsa_cert", false, V_OCSP_CERTSTATUS_REVOKED},
{"certs_unknown.txt", "ecdsa_cert", false, V_OCSP_CERTSTATUS_UNKNOWN},
};
class OCSPRoundTripTest : public testing::TestWithParam<RoundTripTestVector> {};
INSTANTIATE_TEST_SUITE_P(All, OCSPRoundTripTest,
testing::ValuesIn(kRoundTripTestVectors));
// Test a round trip of an OCSP request from an OCSP client <-> OCSP response
// from an OCSP responder. The contents of this test replicate how an OCSP
// client would communicate against an OCSP server.
TEST_P(OCSPRoundTripTest, OCSPRoundTrip) {
const RoundTripTestVector &t = GetParam();
// Set up a |BIO| to communicate with the OCSP responder.
bssl::UniquePtr<BIO> communication(BIO_new(BIO_s_mem()));
bssl::UniquePtr<X509> ca_cert(CertFromPEM(
GetTestData(std::string("crypto/ocsp/test/aws/ca_cert.pem").c_str())
.c_str()));
// Create OCSP request and request revocation status of |certificate|.
bssl::UniquePtr<OCSP_REQUEST> request(OCSP_REQUEST_new());
ASSERT_TRUE(request);
bssl::UniquePtr<X509> certificate = CertFromPEM(
GetTestData(std::string("crypto/ocsp/test/aws/" +
std::string(t.ocsp_request_cert) + ".pem")
.c_str())
.c_str());
ASSERT_TRUE(certificate);
OCSP_CERTID *cert_id =
OCSP_cert_to_id(EVP_sha1(), certificate.get(), ca_cert.get());
EXPECT_TRUE(cert_id);
ASSERT_TRUE(OCSP_request_add0_id(request.get(), cert_id));
if (t.add_nonce) {
ASSERT_TRUE(OCSP_request_add1_nonce(request.get(), nullptr, 0));
}
EXPECT_TRUE(i2d_OCSP_REQUEST_bio(communication.get(), request.get()));
// Send OCSP request to local mock OCSP responder.
ASSERT_TRUE(
LocalMockOCSPResponder(communication.get(), ca_cert.get(), t.ca_txt_db));
// Parse |response| from local mock OCSP responder.
bssl::UniquePtr<OCSP_RESPONSE> response(
d2i_OCSP_RESPONSE_bio(communication.get(), nullptr));
ASSERT_TRUE(response);
// Do verifications and check revocation status if the response was
// successful.
if (OCSP_response_status(response.get()) == OCSP_RESPONSE_STATUS_SUCCESSFUL) {
bssl::UniquePtr<OCSP_BASICRESP> basic_response(
OCSP_response_get1_basic(response.get()));
ASSERT_TRUE(basic_response);
// Verifies the OCSP responder's signature on the OCSP response data.
bssl::UniquePtr<X509_STORE> trust_store(X509_STORE_new());
X509_STORE_add_cert(trust_store.get(), ca_cert.get());
EXPECT_EQ(OCSP_basic_verify(basic_response.get(),
CertsToStack({certificate.get()}).get(),
trust_store.get(), 0),
1);
if (t.add_nonce) {
EXPECT_EQ(OCSP_check_nonce(request.get(), basic_response.get()),
OCSP_NONCE_EQUAL);
} else {
EXPECT_EQ(OCSP_check_nonce(request.get(), basic_response.get()),
OCSP_NONCE_BOTH_ABSENT);
}
// Checks revocation status of the response.
int status = -1, revocation_reason = -1;
ASN1_GENERALIZEDTIME *this_update = nullptr, *next_update = nullptr,
*revocation_time = nullptr;
EXPECT_TRUE(OCSP_resp_find_status(basic_response.get(), cert_id, &status,
&revocation_reason, &revocation_time,
&this_update, &next_update));
EXPECT_EQ(status, t.expected_cert_status);
// We just got an OCSP response, so this should never be out of date.
EXPECT_TRUE(OCSP_check_validity(this_update, next_update, 0, -1));
if (status == V_OCSP_CERTSTATUS_REVOKED) {
EXPECT_TRUE(revocation_time);
EXPECT_GE(revocation_reason, OCSP_REVOKED_STATUS_UNSPECIFIED);
EXPECT_LE(revocation_reason, OCSP_REVOKED_STATUS_AACOMPROMISE);
EXPECT_NE(revocation_reason, OCSP_UNASSIGNED_REVOKED_STATUS);
} else {
EXPECT_FALSE(revocation_time);
EXPECT_EQ(revocation_reason, -1);
}
}
}

View File

@@ -0,0 +1,240 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0 OR ISC
#include <openssl/mem.h>
#include <string.h>
#include "internal.h"
OCSP_CERTID *OCSP_cert_to_id(const EVP_MD *dgst, const X509 *subject,
const X509 *issuer) {
if (issuer == NULL) {
OPENSSL_PUT_ERROR(OCSP, ERR_R_PASSED_NULL_PARAMETER);
return NULL;
}
const X509_NAME *iname;
const ASN1_INTEGER *serial;
ASN1_BIT_STRING *ikey;
if (dgst == NULL) {
dgst = EVP_sha1();
}
if (subject != NULL) {
iname = X509_get_issuer_name(subject);
serial = X509_get0_serialNumber(subject);
} else {
iname = X509_get_subject_name(issuer);
serial = NULL;
}
ikey = X509_get0_pubkey_bitstr(issuer);
return OCSP_cert_id_new(dgst, iname, ikey, serial);
}
OCSP_CERTID *OCSP_cert_id_new(const EVP_MD *dgst, const X509_NAME *issuerName,
const ASN1_BIT_STRING *issuerKey,
const ASN1_INTEGER *serialNumber) {
if (dgst == NULL || issuerName == NULL || issuerKey == NULL) {
OPENSSL_PUT_ERROR(OCSP, ERR_R_PASSED_NULL_PARAMETER);
return NULL;
}
int nid;
unsigned int i;
X509_ALGOR *alg;
unsigned char md[EVP_MAX_MD_SIZE];
OCSP_CERTID *cid = OCSP_CERTID_new();
if (cid == NULL) {
return NULL;
}
alg = cid->hashAlgorithm;
ASN1_OBJECT_free(alg->algorithm);
nid = EVP_MD_type(dgst);
if (nid == NID_undef) {
OPENSSL_PUT_ERROR(OCSP, OCSP_R_UNKNOWN_NID);
goto err;
}
alg->algorithm = OBJ_nid2obj(nid);
if (alg->algorithm == NULL) {
goto err;
}
alg->parameter = ASN1_TYPE_new();
if (alg->parameter == NULL) {
goto err;
}
alg->parameter->type = V_ASN1_NULL;
if (!X509_NAME_digest(issuerName, dgst, md, &i)) {
OPENSSL_PUT_ERROR(OCSP, OCSP_R_DIGEST_ERR);
goto err;
}
if (!(ASN1_OCTET_STRING_set(cid->issuerNameHash, md, i))) {
goto err;
}
// Calculate the issuerKey hash, excluding tag and length
if (!EVP_Digest(issuerKey->data, issuerKey->length, md, &i, dgst, NULL)) {
goto err;
}
if (!(ASN1_OCTET_STRING_set(cid->issuerKeyHash, md, i))) {
goto err;
}
// Only copy |serialNumber| if available. This may be empty.
if (serialNumber != NULL) {
if (ASN1_STRING_copy(cid->serialNumber, serialNumber) == 0) {
goto err;
}
}
return cid;
err:
OCSP_CERTID_free(cid);
return NULL;
}
int OCSP_id_issuer_cmp(const OCSP_CERTID *a, const OCSP_CERTID *b) {
if (a == NULL || b == NULL) {
OPENSSL_PUT_ERROR(OCSP, ERR_R_PASSED_NULL_PARAMETER);
return -1;
}
if (a->hashAlgorithm == NULL || b->hashAlgorithm == NULL) {
OPENSSL_PUT_ERROR(OCSP, ERR_R_PASSED_NULL_PARAMETER);
return -1;
}
int ret = OBJ_cmp(a->hashAlgorithm->algorithm, b->hashAlgorithm->algorithm);
if (ret != 0) {
return ret;
}
ret = ASN1_OCTET_STRING_cmp(a->issuerNameHash, b->issuerNameHash);
if (ret != 0) {
return ret;
}
ret = ASN1_OCTET_STRING_cmp(a->issuerKeyHash, b->issuerKeyHash);
return ret;
}
int OCSP_id_cmp(const OCSP_CERTID *a, const OCSP_CERTID *b) {
if (a == NULL || b == NULL) {
OPENSSL_PUT_ERROR(OCSP, ERR_R_PASSED_NULL_PARAMETER);
return -1;
}
// Compare OCSP issuer name and key
int ret = OCSP_id_issuer_cmp(a, b);
if (ret != 0) {
return ret;
}
// Compare certificate serialNumber
ret = ASN1_INTEGER_cmp(a->serialNumber, b->serialNumber);
return ret;
}
int OCSP_parse_url(const char *url, char **phost, char **pport, char **ppath,
int *pssl) {
if (url == NULL || phost == NULL || pport == NULL || ppath == NULL ||
pssl == NULL) {
OPENSSL_PUT_ERROR(OCSP, ERR_R_PASSED_NULL_PARAMETER);
return 0;
}
char *parser, *buffer;
char *host = NULL;
char *port = NULL;
*phost = NULL;
*pport = NULL;
*ppath = NULL;
// Duplicate into the buffer since the contents are going to be changed.
buffer = OPENSSL_strdup(url);
if (buffer == NULL) {
goto mem_err;
}
// Check for initial colon
parser = strchr(buffer, ':');
if (parser == NULL) {
goto parse_err;
}
*(parser++) = '\0';
// Set default ports for http and https. If a port is specified later, this
// will be overwritten. |pssl| will be set to true, if https is being used.
if (strncmp(buffer, "https", 5) == 0) {
*pssl = 1;
port = (char *)"443";
} else if (strncmp(buffer, "http", 4) == 0) {
*pssl = 0;
port = (char *)"80";
} else {
goto parse_err;
}
// Check for double slash.
if ((parser[0] != '/') || (parser[1] != '/')) {
goto parse_err;
}
parser += 2;
host = parser;
// Check for trailing part of path.
parser = strchr(parser, '/');
if (parser == NULL) {
// Default is "/" if there is no trailing path in the URL.
*ppath = OPENSSL_strdup("/");
} else {
*ppath = OPENSSL_strdup(parser);
// Set start of path to 0 so hostname is valid.
*parser = '\0';
}
if (*ppath == NULL) {
goto mem_err;
}
parser = host;
// Checks if the host is an ipv6 host address.
if (host[0] == '[') {
host++;
parser = strchr(host, ']');
if (parser == NULL) {
goto parse_err;
}
*parser = '\0';
parser++;
}
// Look for optional ':' for port number.
if ((parser = strchr(parser, ':'))) {
*parser = 0;
port = parser + 1;
}
*pport = OPENSSL_strdup(port);
if (*pport == NULL) {
goto mem_err;
}
*phost = OPENSSL_strdup(host);
if (*phost == NULL) {
goto mem_err;
}
OPENSSL_free(buffer);
return 1;
mem_err:
OPENSSL_PUT_ERROR(OCSP, ERR_R_MALLOC_FAILURE);
goto err;
parse_err:
OPENSSL_PUT_ERROR(OCSP, OCSP_R_ERROR_PARSING_URL);
err:
OPENSSL_free(buffer);
OPENSSL_free(*ppath);
*ppath = NULL;
OPENSSL_free(*pport);
*pport = NULL;
OPENSSL_free(*phost);
*phost = NULL;
return 0;
}

View File

@@ -0,0 +1,269 @@
// Copyright 2000-2016 The OpenSSL Project Authors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
#include <openssl/ocsp.h>
#include <openssl/pem.h>
#include "internal.h"
static int ocsp_certid_print(BIO *bp, OCSP_CERTID *certid, int indent) {
BIO_printf(bp, "%*sCertificate ID:\n", indent, "");
indent += 2;
BIO_printf(bp, "%*sHash Algorithm: ", indent, "");
i2a_ASN1_OBJECT(bp, certid->hashAlgorithm->algorithm);
BIO_printf(bp, "\n%*sIssuer Name Hash: ", indent, "");
i2a_ASN1_STRING(bp, certid->issuerNameHash, 0);
BIO_printf(bp, "\n%*sIssuer Key Hash: ", indent, "");
i2a_ASN1_STRING(bp, certid->issuerKeyHash, 0);
BIO_printf(bp, "\n%*sSerial Number: ", indent, "");
i2a_ASN1_INTEGER(bp, certid->serialNumber);
BIO_printf(bp, "\n");
return 1;
}
typedef struct {
long t;
const char *m;
} OCSP_TBLSTR;
static const char *do_table2string(long s, const OCSP_TBLSTR *ts, size_t len) {
size_t i;
for (i = 0; i < len; i++) {
if (ts[i].t == s) {
return ts[i].m;
}
}
return "(UNKNOWN)";
}
const char *OCSP_response_status_str(long status_code) {
static const OCSP_TBLSTR rstat_tbl[] = {
{OCSP_RESPONSE_STATUS_SUCCESSFUL, "successful"},
{OCSP_RESPONSE_STATUS_MALFORMEDREQUEST, "malformedrequest"},
{OCSP_RESPONSE_STATUS_INTERNALERROR, "internalerror"},
{OCSP_RESPONSE_STATUS_TRYLATER, "trylater"},
{OCSP_RESPONSE_STATUS_SIGREQUIRED, "sigrequired"},
{OCSP_RESPONSE_STATUS_UNAUTHORIZED, "unauthorized"}};
size_t tbl_size = (sizeof(rstat_tbl) / sizeof((rstat_tbl)[0]));
return do_table2string(status_code, rstat_tbl, tbl_size);
}
const char *OCSP_cert_status_str(long status_code) {
static const OCSP_TBLSTR cstat_tbl[] = {
{V_OCSP_CERTSTATUS_GOOD, "good"},
{V_OCSP_CERTSTATUS_REVOKED, "revoked"},
{V_OCSP_CERTSTATUS_UNKNOWN, "unknown"}};
size_t tbl_size = (sizeof(cstat_tbl) / sizeof((cstat_tbl)[0]));
return do_table2string(status_code, cstat_tbl, tbl_size);
}
const char *OCSP_crl_reason_str(long s) {
static const OCSP_TBLSTR reason_tbl[] = {
{OCSP_REVOKED_STATUS_UNSPECIFIED, "unspecified"},
{OCSP_REVOKED_STATUS_KEYCOMPROMISE, "keyCompromise"},
{OCSP_REVOKED_STATUS_CACOMPROMISE, "cACompromise"},
{OCSP_REVOKED_STATUS_AFFILIATIONCHANGED, "affiliationChanged"},
{OCSP_REVOKED_STATUS_SUPERSEDED, "superseded"},
{OCSP_REVOKED_STATUS_CESSATIONOFOPERATION, "cessationOfOperation"},
{OCSP_REVOKED_STATUS_CERTIFICATEHOLD, "certificateHold"},
{OCSP_REVOKED_STATUS_REMOVEFROMCRL, "removeFromCRL"},
{OCSP_REVOKED_STATUS_PRIVILEGEWITHDRAWN, "privilegeWithdrawn"},
{OCSP_REVOKED_STATUS_AACOMPROMISE, "aACompromise"}};
size_t tbl_size = (sizeof(reason_tbl) / sizeof((reason_tbl)[0]));
return do_table2string(s, reason_tbl, tbl_size);
}
int OCSP_REQUEST_print(BIO *bp, OCSP_REQUEST *req, unsigned long flags) {
if(bp == NULL|| req ==NULL) {
OPENSSL_PUT_ERROR(OCSP, ERR_R_PASSED_NULL_PARAMETER);
return 0;
}
long l;
OCSP_CERTID *cid = NULL;
OCSP_ONEREQ *one = NULL;
OCSP_REQINFO *inf = req->tbsRequest;
OCSP_SIGNATURE *sig = req->optionalSignature;
if (BIO_puts(bp, "OCSP Request Data:\n") <= 0) {
return 0;
}
l = ASN1_INTEGER_get(inf->version);
if (BIO_printf(bp, " Version: %ld (0x%ld)", l + 1, l) <= 0) {
return 0;
}
if (inf->requestorName != NULL) {
if (BIO_puts(bp, "\n Requestor Name: ") <= 0) {
return 0;
}
GENERAL_NAME_print(bp, inf->requestorName);
}
if (BIO_puts(bp, "\n Requestor List:\n") <= 0) {
return 0;
}
for (size_t i = 0; i < sk_OCSP_ONEREQ_num(inf->requestList); i++) {
one = sk_OCSP_ONEREQ_value(inf->requestList, i);
cid = one->reqCert;
ocsp_certid_print(bp, cid, 8);
if (!X509V3_extensions_print(bp, "Request Single Extensions",
one->singleRequestExtensions, flags, 8)) {
return 0;
}
}
if (!X509V3_extensions_print(bp, "Request Extensions", inf->requestExtensions,
flags, 4)) {
return 0;
}
if (sig != NULL) {
X509_signature_print(bp, sig->signatureAlgorithm, sig->signature);
for (size_t i = 0; i < sk_X509_num(sig->certs); i++) {
X509_print(bp, sk_X509_value(sig->certs, i));
PEM_write_bio_X509(bp, sk_X509_value(sig->certs, i));
}
}
return 1;
}
int OCSP_RESPONSE_print(BIO *bp, OCSP_RESPONSE *resp, unsigned long flags) {
if(bp == NULL|| resp ==NULL) {
OPENSSL_PUT_ERROR(OCSP, ERR_R_PASSED_NULL_PARAMETER);
return 0;
}
int ret = 0;
long l;
OCSP_CERTID *cid = NULL;
OCSP_BASICRESP *br = NULL;
OCSP_RESPID *rid = NULL;
OCSP_RESPDATA *rd = NULL;
OCSP_CERTSTATUS *cst = NULL;
OCSP_REVOKEDINFO *rev = NULL;
OCSP_SINGLERESP *single = NULL;
OCSP_RESPBYTES *rb = resp->responseBytes;
if (BIO_puts(bp, "OCSP Response Data:\n") <= 0) {
goto err;
}
l = ASN1_ENUMERATED_get(resp->responseStatus);
if (BIO_printf(bp, " OCSP Response Status: %s (0x%ld)\n",
OCSP_response_status_str(l), l) <= 0) {
goto err;
}
if (rb == NULL) {
return 1;
}
if (BIO_puts(bp, " Response Type: ") <= 0) {
goto err;
}
if (i2a_ASN1_OBJECT(bp, rb->responseType) <= 0) {
goto err;
}
if (OBJ_obj2nid(rb->responseType) != NID_id_pkix_OCSP_basic) {
BIO_puts(bp, " (unknown response type)\n");
return 1;
}
if ((br = OCSP_response_get1_basic(resp)) == NULL) {
goto err;
}
rd = br->tbsResponseData;
l = ASN1_INTEGER_get(rd->version);
if (BIO_printf(bp, "\n Version: %ld (0x%ld)\n", l + 1, l) <= 0) {
goto err;
}
if (BIO_puts(bp, " Responder Id: ") <= 0) {
goto err;
}
rid = rd->responderId;
switch (rid->type) {
case V_OCSP_RESPID_NAME:
X509_NAME_print_ex(bp, rid->value.byName, 0, XN_FLAG_ONELINE);
break;
case V_OCSP_RESPID_KEY:
i2a_ASN1_STRING(bp, rid->value.byKey, 0);
break;
}
if (BIO_printf(bp, "\n Produced At: ") <= 0) {
goto err;
}
if (!ASN1_GENERALIZEDTIME_print(bp, rd->producedAt)) {
goto err;
}
if (BIO_printf(bp, "\n Responses:\n") <= 0) {
goto err;
}
for (size_t i = 0; i < sk_OCSP_SINGLERESP_num(rd->responses); i++) {
if (!sk_OCSP_SINGLERESP_value(rd->responses, i)) {
continue;
}
single = sk_OCSP_SINGLERESP_value(rd->responses, i);
cid = single->certId;
if (ocsp_certid_print(bp, cid, 4) <= 0) {
goto err;
}
cst = single->certStatus;
if (BIO_printf(bp, " Cert Status: %s",
OCSP_cert_status_str(cst->type)) <= 0) {
goto err;
}
if (cst->type == V_OCSP_CERTSTATUS_REVOKED) {
rev = cst->value.revoked;
if (BIO_printf(bp, "\n Revocation Time: ") <= 0) {
goto err;
}
if (!ASN1_GENERALIZEDTIME_print(bp, rev->revocationTime)) {
goto err;
}
if (rev->revocationReason) {
l = ASN1_ENUMERATED_get(rev->revocationReason);
if (BIO_printf(bp, "\n Revocation Reason: %s (0x%ld)",
OCSP_crl_reason_str(l), l) <= 0) {
goto err;
}
}
}
if (BIO_printf(bp, "\n This Update: ") <= 0) {
goto err;
}
if (!ASN1_GENERALIZEDTIME_print(bp, single->thisUpdate)) {
goto err;
}
if (single->nextUpdate) {
if (BIO_printf(bp, "\n Next Update: ") <= 0) {
goto err;
}
if (!ASN1_GENERALIZEDTIME_print(bp, single->nextUpdate)) {
goto err;
}
}
if (BIO_puts(bp, "\n") <= 0) {
goto err;
}
if (!X509V3_extensions_print(bp, "Response Single Extensions",
single->singleExtensions, flags, 8)) {
goto err;
}
if (BIO_puts(bp, "\n") <= 0) {
goto err;
}
}
if (!X509V3_extensions_print(bp, "Response Extensions",
rd->responseExtensions, flags, 4)) {
goto err;
}
if (X509_signature_print(bp, br->signatureAlgorithm, br->signature) <= 0) {
goto err;
}
for (size_t i = 0; i < sk_X509_num(br->certs); i++) {
X509_print(bp, sk_X509_value(br->certs, i));
PEM_write_bio_X509(bp, sk_X509_value(br->certs, i));
}
ret = 1;
err:
OCSP_BASICRESP_free(br);
return ret;
}

View File

@@ -0,0 +1,358 @@
// Copyright 2001-2017 The OpenSSL Project Authors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
// Modifications Copyright Amazon.com, Inc. or its affiliates.
// SPDX-License-Identifier: Apache-2.0 OR ISC
#include "../internal.h"
#include "internal.h"
int OCSP_request_onereq_count(OCSP_REQUEST *req) {
GUARD_PTR(req);
GUARD_PTR(req->tbsRequest);
return (int)sk_OCSP_ONEREQ_num(req->tbsRequest->requestList);
}
OCSP_ONEREQ *OCSP_request_onereq_get0(OCSP_REQUEST *req, int i) {
GUARD_PTR(req);
GUARD_PTR(req->tbsRequest);
return sk_OCSP_ONEREQ_value(req->tbsRequest->requestList, i);
}
int OCSP_id_get0_info(ASN1_OCTET_STRING **nameHash, ASN1_OBJECT **algor,
ASN1_OCTET_STRING **keyHash, ASN1_INTEGER **serial,
OCSP_CERTID *cid) {
if (cid == NULL) {
return 0;
}
if (algor != NULL) {
*algor = cid->hashAlgorithm->algorithm;
}
if (nameHash != NULL) {
*nameHash = cid->issuerNameHash;
}
if (keyHash != NULL) {
*keyHash = cid->issuerKeyHash;
}
if (serial != NULL) {
*serial = cid->serialNumber;
}
return 1;
}
int OCSP_request_is_signed(OCSP_REQUEST *req) {
GUARD_PTR(req);
if (req->optionalSignature != NULL) {
return 1;
}
return 0;
}
OCSP_CERTID *OCSP_onereq_get0_id(OCSP_ONEREQ *one) {
GUARD_PTR(one);
return one->reqCert;
}
int OCSP_basic_add1_cert(OCSP_BASICRESP *resp, X509 *cert) {
if (resp->certs == NULL && (resp->certs = sk_X509_new_null()) == NULL) {
return 0;
}
if (!sk_X509_push(resp->certs, cert)) {
return 0;
}
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 (!ASN1_GENERALIZEDTIME_set(resp->tbsResponseData->producedAt,
time(NULL))) {
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;
}
const EVP_MD *OCSP_get_default_digest(const EVP_MD *dgst, EVP_PKEY *signer) {
if (dgst != NULL) {
return dgst;
}
int pkey_nid = EVP_PKEY_id(signer);
if (pkey_nid == EVP_PKEY_EC || pkey_nid == EVP_PKEY_RSA ||
pkey_nid == EVP_PKEY_DSA) {
return EVP_sha256();
}
return NULL;
}
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);
const EVP_MD *init_dgst = OCSP_get_default_digest(dgst, key);
if (init_dgst == NULL) {
OPENSSL_PUT_ERROR(OCSP, EVP_R_NO_DEFAULT_DIGEST);
return 0;
}
EVP_MD_CTX *ctx = EVP_MD_CTX_new();
if (ctx == NULL) {
return 0;
}
if (!EVP_DigestSignInit(ctx, NULL, init_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;
}
OCSP_SINGLERESP *OCSP_basic_add1_status(OCSP_BASICRESP *resp, OCSP_CERTID *cid,
int status, int revoked_reason,
ASN1_TIME *revoked_time,
ASN1_TIME *this_update,
ASN1_TIME *next_update) {
if (resp == NULL || resp->tbsResponseData == NULL || cid == NULL ||
this_update == NULL) {
OPENSSL_PUT_ERROR(OCSP, ERR_R_PASSED_NULL_PARAMETER);
return NULL;
}
// 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 (revoked_reason < OCSP_REVOKED_STATUS_UNSPECIFIED ||
revoked_reason > OCSP_REVOKED_STATUS_AACOMPROMISE ||
revoked_reason == OCSP_UNASSIGNED_REVOKED_STATUS) {
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, revoked_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 == OCSP_UNASSIGNED_RESPONSE_STATUS) {
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;
}
// responseType will be id-pkix-ocsp-basic according to RFC6960.
//
// https://datatracker.ietf.org/doc/html/rfc6960#section-4.2.1
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;
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,502 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0 OR ISC
#include <string.h>
#include "../internal.h"
#include "internal.h"
#define SIGNER_IN_PROVIDED_CERTS 2
#define SIGNER_IN_OCSP_CERTS 1
#define SIGNER_NOT_FOUND 0
// Set up |X509_STORE_CTX| to verify signer and returns cert chain if verify is
// OK. A |OCSP_RESPID| can be identified either by name or its keyhash.
// https://datatracker.ietf.org/doc/html/rfc6960#section-4.2.2.3
static X509 *ocsp_find_signer_sk(STACK_OF(X509) *certs, OCSP_RESPID *id) {
if (certs == NULL || id == NULL) {
return NULL;
}
// Easy if lookup by name.
if (id->type == V_OCSP_RESPID_NAME) {
return X509_find_by_subject(certs, id->value.byName);
}
// Lookup by key hash.
unsigned char tmphash[SHA_DIGEST_LENGTH], *keyhash;
// If key hash isn't SHA1 length then forget it.
if (id->value.byKey == NULL || id->value.byKey->length != SHA_DIGEST_LENGTH) {
return NULL;
}
keyhash = id->value.byKey->data;
// Calculate hash of each key and compare.
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 (OPENSSL_memcmp(keyhash, tmphash, SHA_DIGEST_LENGTH) == 0) {
return cert;
}
}
}
return NULL;
}
// Find signer in cert stack or |OCSP_BASICRESP|'s cert stack.
static int ocsp_find_signer(X509 **psigner, OCSP_BASICRESP *bs,
STACK_OF(X509) *certs, unsigned long flags) {
if (psigner == NULL) {
OPENSSL_PUT_ERROR(OCSP, ERR_R_PASSED_NULL_PARAMETER);
return -1;
}
X509 *signer;
OCSP_RESPID *rid = bs->tbsResponseData->responderId;
// look for signer in certs stack.
signer = ocsp_find_signer_sk(certs, rid);
if (signer != NULL) {
*psigner = signer;
return SIGNER_IN_PROVIDED_CERTS;
}
// look in certs stack the responder may have included in |OCSP_BASICRESP|,
// unless the flags contain |OCSP_NOINTERN|.
signer = ocsp_find_signer_sk(bs->certs, rid);
if (signer != NULL && !IS_OCSP_FLAG_SET(flags, OCSP_NOINTERN)) {
*psigner = signer;
return SIGNER_IN_OCSP_CERTS;
}
// Maybe lookup from store if by subject name.
*psigner = NULL;
return SIGNER_NOT_FOUND;
}
// check if public key in signer matches key in |OCSP_BASICRESP|.
static int ocsp_verify_key(OCSP_BASICRESP *bs, X509 *signer) {
if (signer == NULL) {
OPENSSL_PUT_ERROR(OCSP, ERR_R_PASSED_NULL_PARAMETER);
return -1;
}
EVP_PKEY *skey = X509_get_pubkey(signer);
if (skey == NULL) {
OPENSSL_PUT_ERROR(OCSP, OCSP_R_NO_SIGNER_KEY);
return -1;
}
int ret =
ASN1_item_verify(ASN1_ITEM_rptr(OCSP_RESPDATA), bs->signatureAlgorithm,
bs->signature, bs->tbsResponseData, skey);
EVP_PKEY_free(skey);
if (ret <= 0) {
OPENSSL_PUT_ERROR(OCSP, OCSP_R_SIGNATURE_FAILURE);
}
return ret;
}
// Set untrusted certificate stack from |OCSP_BASICRESP|.
static int ocsp_setup_untrusted(OCSP_BASICRESP *bs, STACK_OF(X509) *certs,
STACK_OF(X509) **untrusted,
unsigned long flags) {
if (untrusted == NULL) {
OPENSSL_PUT_ERROR(OCSP, ERR_R_PASSED_NULL_PARAMETER);
return -1;
}
if (IS_OCSP_FLAG_SET(flags, OCSP_NOCHAIN)) {
*untrusted = NULL;
} else if (bs->certs && certs) {
*untrusted = sk_X509_dup(bs->certs);
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 = sk_X509_dup(certs);
} else {
*untrusted = sk_X509_dup(bs->certs);
}
return 1;
}
static int ocsp_verify_signer(X509 *signer, X509_STORE *st,
STACK_OF(X509) *untrusted,
STACK_OF(X509) **chain) {
if (signer == NULL) {
OPENSSL_PUT_ERROR(OCSP, ERR_R_PASSED_NULL_PARAMETER);
return -1;
}
// Set up |X509_STORE_CTX| with |*signer|, |*st|, and |*untrusted|.
X509_STORE_CTX *ctx = X509_STORE_CTX_new();
int ret = -1;
if (ctx == NULL) {
goto end;
}
if (!X509_STORE_CTX_init(ctx, st, signer, untrusted)) {
OPENSSL_PUT_ERROR(OCSP, ERR_R_X509_LIB);
goto end;
}
if (!X509_STORE_CTX_set_purpose(ctx, X509_PURPOSE_OCSP_HELPER)) {
OPENSSL_PUT_ERROR(OCSP, ERR_R_X509_LIB);
goto end;
}
// Verify |X509_STORE_CTX| and return certificate chain.
ret = X509_verify_cert(ctx);
if (ret <= 0) {
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) {
*chain = X509_STORE_CTX_get1_chain(ctx);
}
end:
X509_STORE_CTX_free(ctx);
return ret;
}
// Check the issuer certificate IDs for equality. If the issuer IDs all match
// then we just need to check equality against one of them. If there is a
// mismatch with the same algorithm then there's no point trying to match any
// certificates against the issuer, and |*ret| will be set to NULL.
static int ocsp_check_ids(STACK_OF(OCSP_SINGLERESP) *sresp, OCSP_CERTID **ret) {
if (sresp == NULL || ret == NULL) {
OPENSSL_PUT_ERROR(OCSP, ERR_R_PASSED_NULL_PARAMETER);
return -1;
}
OCSP_CERTID *tmpid, *cid;
size_t idcount = sk_OCSP_SINGLERESP_num(sresp);
if (idcount == 0) {
OPENSSL_PUT_ERROR(OCSP, OCSP_R_RESPONSE_CONTAINS_NO_REVOCATION_DATA);
return -1;
}
cid = sk_OCSP_SINGLERESP_value(sresp, 0)->certId;
*ret = NULL;
for (size_t i = 1; i < idcount; i++) {
tmpid = sk_OCSP_SINGLERESP_value(sresp, i)->certId;
// Check to see if IDs match.
if (OCSP_id_issuer_cmp(cid, tmpid) != 0) {
// If algorithm mismatch, let caller deal with it instead.
if (OBJ_cmp(tmpid->hashAlgorithm->algorithm,
cid->hashAlgorithm->algorithm) != 0) {
return 1;
}
return 0;
}
}
// All IDs match: only need to check one ID.
*ret = cid;
return 1;
}
// Returns -1 on fatal error, 0 if there is no match and 1 if there is a
// match.
static int ocsp_match_issuerid(X509 *cert, OCSP_CERTID *cid,
STACK_OF(OCSP_SINGLERESP) *sresp) {
if (cert == NULL) {
OPENSSL_PUT_ERROR(OCSP, ERR_R_PASSED_NULL_PARAMETER);
return -1;
}
// If only one ID to match then do it.
if (cid) {
const EVP_MD *dgst;
X509_NAME *iname;
unsigned char md[EVP_MAX_MD_SIZE];
// Set up message digest for comparison.
dgst = EVP_get_digestbyobj(cid->hashAlgorithm->algorithm);
if (dgst == NULL) {
OPENSSL_PUT_ERROR(OCSP, OCSP_R_UNKNOWN_MESSAGE_DIGEST);
return -1;
}
size_t mdlen = EVP_MD_size(dgst);
iname = X509_get_subject_name(cert);
if (!X509_NAME_digest(iname, dgst, md, NULL)) {
return -1;
}
// Compare message digest with |OCSP_CERTID|
if (cid->issuerNameHash->length >= 0 && cid->issuerKeyHash->length >= 0) {
if (((size_t)cid->issuerNameHash->length != mdlen) ||
(size_t)cid->issuerKeyHash->length != mdlen) {
return 0;
}
}
if (0 != OPENSSL_memcmp(md, cid->issuerNameHash->data, mdlen)) {
return 0;
}
if (1 != X509_pubkey_digest(cert, dgst, md, NULL)) {
return -1;
}
if (0 != OPENSSL_memcmp(md, cid->issuerKeyHash->data, mdlen)) {
return 0;
}
return 1;
}
// We have to match the whole stack recursively, if |cid| is NULL. This
// means that the issuer certificate's hash algorithm did not match with
// the rest of the |certId|s in the |OCSP_SINGLERESP| stack. (Issuer
// certificate is taken from the root of the |OCSP_SINGLERESP| stack).
// We'll have to find a match from the signer or responder CA certificate
// (both are certificates in the certificate chain) in the
// |OCSP_SINGLERESP| stack instead.
else {
int ret;
OCSP_CERTID *tmpid;
for (size_t i = 0; i < sk_OCSP_SINGLERESP_num(sresp); i++) {
tmpid = sk_OCSP_SINGLERESP_value(sresp, i)->certId;
ret = ocsp_match_issuerid(cert, tmpid, NULL);
if (ret <= 0) {
return ret;
}
}
return 1;
}
}
static int ocsp_check_delegated(X509 *x) {
if (x == NULL) {
OPENSSL_PUT_ERROR(OCSP, ERR_R_PASSED_NULL_PARAMETER);
return -1;
}
if ((X509_get_extension_flags(x) & EXFLAG_XKUSAGE) &&
(X509_get_extended_key_usage(x) & XKU_OCSP_SIGN)) {
return 1;
}
OPENSSL_PUT_ERROR(OCSP, OCSP_R_MISSING_OCSPSIGNING_USAGE);
return 0;
}
// check OCSP responder issuer and ids
static int ocsp_check_issuer(OCSP_BASICRESP *bs, STACK_OF(X509) *chain) {
if (chain == NULL) {
OPENSSL_PUT_ERROR(OCSP, ERR_R_PASSED_NULL_PARAMETER);
return -1;
}
STACK_OF(OCSP_SINGLERESP) *sresp = bs->tbsResponseData->responses;
OCSP_CERTID *caid = NULL;
int ret;
if (sk_X509_num(chain) <= 0) {
OPENSSL_PUT_ERROR(OCSP, OCSP_R_NO_CERTIFICATES_IN_CHAIN);
return -1;
}
// See if the issuer IDs match.
ret = ocsp_check_ids(sresp, &caid);
// If ID mismatch or other error then return.
if (ret <= 0) {
return ret;
}
X509 *signer, *sca;
signer = sk_X509_value(chain, 0);
// Check to see if OCSP responder CA matches request CA.
if (sk_X509_num(chain) > 1) {
sca = sk_X509_value(chain, 1);
ret = ocsp_match_issuerid(sca, caid, sresp);
if (ret < 0) {
return ret;
}
if (ret != 0) {
// If matches, then check extension flags.
if (ocsp_check_delegated(signer)) {
return 1;
}
return 0;
}
}
// Otherwise check if OCSP request signed directly by request CA.
return ocsp_match_issuerid(signer, caid, sresp);
}
int OCSP_basic_verify(OCSP_BASICRESP *bs, STACK_OF(X509) *certs, X509_STORE *st,
unsigned long flags) {
if (bs == NULL || st == NULL) {
OPENSSL_PUT_ERROR(OCSP, ERR_R_PASSED_NULL_PARAMETER);
return -1;
}
X509 *signer;
STACK_OF(X509) *chain = NULL;
STACK_OF(X509) *untrusted = NULL;
// Look for signer certificate.
int ret = ocsp_find_signer(&signer, bs, certs, flags);
if (ret <= SIGNER_NOT_FOUND) {
OPENSSL_PUT_ERROR(OCSP, OCSP_R_SIGNER_CERTIFICATE_NOT_FOUND);
goto end;
}
if ((ret == SIGNER_IN_PROVIDED_CERTS) &&
IS_OCSP_FLAG_SET(flags, OCSP_TRUSTOTHER)) {
// We skip verification if the flag to trust |certs| is set and the signer
// is found within that stack.
flags |= OCSP_NOVERIFY;
}
// Check if public key in signer matches key in |OCSP_BASICRESP|.
ret = ocsp_verify_key(bs, signer);
if (ret <= 0) {
goto end;
}
if (!IS_OCSP_FLAG_SET(flags, OCSP_NOVERIFY)) {
// Verify signer and if valid, check the certificate chain.
ret = ocsp_setup_untrusted(bs, certs, &untrusted, flags);
if (ret <= 0) {
goto end;
}
ret = ocsp_verify_signer(signer, st, untrusted, &chain);
if (ret <= 0) {
goto end;
}
// At this point we have a valid certificate chain, need to verify it
// against the OCSP issuer criteria.
ret = ocsp_check_issuer(bs, chain);
// If a certificate chain is not verifiable against the OCSP issuer
// criteria, we try to check for explicit trust.
if (ret == 0) {
// Easy case: explicitly trusted. Get root CA and check for explicit
// trust.
if (IS_OCSP_FLAG_SET(flags, OCSP_NOEXPLICIT)) {
goto end;
}
X509 *root_cert = sk_X509_value(chain, sk_X509_num(chain) - 1);
if (X509_check_trust(root_cert, NID_OCSP_sign, 0) != X509_TRUST_TRUSTED) {
OPENSSL_PUT_ERROR(OCSP, OCSP_R_ROOT_CA_NOT_TRUSTED);
ret = 0;
goto end;
}
ret = 1;
}
}
end:
sk_X509_pop_free(chain, X509_free);
sk_X509_free(untrusted);
return ret;
}
// ocsp_req_find_signer assigns |*psigner| to the signing certificate and
// returns |SIGNER_IN_OCSP_CERTS| if it was found within |req| or returns
// |SIGNER_IN_TRUSTED_CERTS| if it was found within |certs|. It returns
// |SIGNER_NOT_FOUND| if the signing certificate is not found.
static int ocsp_req_find_signer(X509 **psigner, OCSP_REQUEST *req,
X509_NAME *nm, STACK_OF(X509) *certs,
unsigned long flags) {
X509 *signer = NULL;
if (!IS_OCSP_FLAG_SET(flags, OCSP_NOINTERN)) {
signer = X509_find_by_subject(req->optionalSignature->certs, nm);
if (signer != NULL) {
*psigner = signer;
return SIGNER_IN_OCSP_CERTS;
}
}
signer = X509_find_by_subject(certs, nm);
if (signer != NULL) {
*psigner = signer;
return SIGNER_IN_PROVIDED_CERTS;
}
return SIGNER_NOT_FOUND;
}
int OCSP_request_verify(OCSP_REQUEST *req, STACK_OF(X509) *certs,
X509_STORE *store, unsigned long flags) {
GUARD_PTR(req);
GUARD_PTR(req->tbsRequest);
GUARD_PTR(store);
// Check if |req| has signature to check against.
if (req->optionalSignature == NULL) {
OPENSSL_PUT_ERROR(OCSP, OCSP_R_REQUEST_NOT_SIGNED);
return 0;
}
GENERAL_NAME *gen = req->tbsRequest->requestorName;
if (gen == NULL || gen->type != GEN_DIRNAME) {
OPENSSL_PUT_ERROR(OCSP, OCSP_R_UNSUPPORTED_REQUESTORNAME_TYPE);
return 0;
}
// Find |signer| from |certs| or |req->optionalSignature->certs| against criteria.
X509 *signer = NULL;
int signer_status =
ocsp_req_find_signer(&signer, req, gen->d.directoryName, certs, flags);
if (signer_status <= SIGNER_NOT_FOUND || signer == NULL) {
OPENSSL_PUT_ERROR(OCSP, OCSP_R_SIGNER_CERTIFICATE_NOT_FOUND);
return 0;
}
if (signer_status == SIGNER_IN_PROVIDED_CERTS &&
IS_OCSP_FLAG_SET(flags, OCSP_TRUSTOTHER)) {
// We skip certificate verification if the flag to trust |certs| is set and
// the signer is found within that stack.
flags |= OCSP_NOVERIFY;
}
// Validate |req|'s signature.
EVP_PKEY *skey = X509_get0_pubkey(signer);
if (skey == NULL) {
OPENSSL_PUT_ERROR(OCSP, OCSP_R_NO_SIGNER_KEY);
return 0;
}
if (ASN1_item_verify(ASN1_ITEM_rptr(OCSP_REQINFO),
req->optionalSignature->signatureAlgorithm,
req->optionalSignature->signature, req->tbsRequest,
skey) <= 0) {
OPENSSL_PUT_ERROR(OCSP, OCSP_R_SIGNATURE_FAILURE);
return 0;
}
// Set up |ctx| and start doing the actual verification.
X509_STORE_CTX *ctx = X509_STORE_CTX_new();
if (ctx == NULL) {
return 0;
}
// Validate the signing certificate.
int ret = 0;
if (!IS_OCSP_FLAG_SET(flags, OCSP_NOVERIFY)) {
// Initialize and set purpose of |ctx| for verification.
if (1 != X509_STORE_CTX_init(ctx, store, signer, NULL) ||
1 != X509_STORE_CTX_set_purpose(ctx, X509_PURPOSE_OCSP_HELPER)) {
OPENSSL_PUT_ERROR(OCSP, ERR_R_X509_LIB);
goto end;
}
if (!IS_OCSP_FLAG_SET(flags, OCSP_NOCHAIN)) {
X509_STORE_CTX_set_chain(ctx, req->optionalSignature->certs);
}
// Do the verification.
if (X509_verify_cert(ctx) <= 0) {
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;
}
}
ret = 1;
end:
X509_STORE_CTX_free(ctx);
return ret;
}