Files
cli/vendor/aws-lc-sys/aws-lc/crypto/ocsp/ocsp_verify.c

503 lines
15 KiB
C

// 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;
}