Commit 2d3e267b2b for openssl.org
commit 2d3e267b2b035859b1da38644721afef8d88d792
Author: Jakub Zelenka <jakub.zelenka@openssl.foundation>
Date: Thu Jul 2 13:45:16 2026 +0200
x509: add ocsptest for the OCSP stapled-response verification path
Add test/ocsptest.c, exercising check_cert_ocsp_resp() in x509_vfy.c
through X509_verify_cert() with X509_V_FLAG_OCSP_RESP_CHECK and
responses attached via X509_STORE_CTX_set_ocsp_resp(). This path was
previously only covered indirectly through the TLS multi-stapling
tests in sslapitest.c.
The test builds signed OCSP responses at run time from a flat
root -> leaf PKI (the root is both the trust anchor and the authorized
responder), and covers the good, grace-period, non-successful status,
expired, no-response, and wrong-certificate cases, plus a mfail run
over the success path. The PKI is generated by the test-tools ocsptest
command.
Assisted-by: Claude:claude-opus-4-8
Reviewed-by: Daniel Kubec <kubec@openssl.foundation>
Reviewed-by: Tomas Mraz <tomas@openssl.foundation>
MergeDate: Mon Jul 13 15:04:59 2026
(Merged from https://github.com/openssl/openssl/pull/31828)
diff --git a/test/build.info b/test/build.info
index 94e6d08865..a5dcc7f89d 100644
--- a/test/build.info
+++ b/test/build.info
@@ -65,7 +65,7 @@ IF[{- !$disabled{tests} -}]
x509_time_test x509_dup_cert_test x509_check_cert_pkey_test \
recordlentest drbgtest rand_status_test sslbuffertest \
time_offset_test pemtest ssl_cert_table_internal_test ciphername_test \
- servername_test ocspapitest fatalerrtest tls13ccstest \
+ servername_test ocspapitest ocsptest fatalerrtest tls13ccstest \
sysdefaulttest errtest ssl_ctx_test build_wincrypt_test \
context_internal_test aesgcmtest params_test evp_pkey_dparams_test \
keymgmt_internal_test hexstr_test provider_status_test defltfips_test \
@@ -356,6 +356,10 @@ IF[{- !$disabled{tests} -}]
INCLUDE[crltest]=../include ../apps/include
DEPEND[crltest]=../libcrypto libtestutil.a
+ SOURCE[ocsptest]=ocsptest.c
+ INCLUDE[ocsptest]=../include ../apps/include
+ DEPEND[ocsptest]=../libcrypto libtestutil.a
+
SOURCE[v3ext]=v3ext.c
INCLUDE[v3ext]=../include ../apps/include
DEPEND[v3ext]=../libcrypto libtestutil.a
diff --git a/test/ocsptest.c b/test/ocsptest.c
new file mode 100644
index 0000000000..96dc07423b
--- /dev/null
+++ b/test/ocsptest.c
@@ -0,0 +1,408 @@
+/*
+ * Copyright 2026 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include <time.h>
+#include <openssl/bio.h>
+#include <openssl/crypto.h>
+#include <openssl/err.h>
+#include <openssl/pem.h>
+#include <openssl/x509.h>
+#include <openssl/x509_vfy.h>
+#ifndef OPENSSL_NO_OCSP
+#include <openssl/ocsp.h>
+#endif
+
+#include "testutil.h"
+
+#ifndef OPENSSL_NO_OCSP
+
+/*
+ * Fixtures for the OCSP stapled-response verification path
+ * (check_cert_ocsp_resp() in x509_vfy.c), reached from X509_verify_cert()
+ * when X509_V_FLAG_OCSP_RESP_CHECK is set and responses are attached with
+ * X509_STORE_CTX_set_ocsp_resp().
+ *
+ * Root CA (self-signed trust anchor)
+ * \-- leaf (signed by the Root CA)
+ *
+ * The flat chain makes the root both the trust anchor and the authorized OCSP
+ * responder for the leaf, and having the root key lets each test build its own
+ * signed responses at run time. The certificates have a long validity because
+ * OCSP_check_validity() compares against the wall clock and cannot be pinned
+ * via X509_VERIFY_PARAM_set_time().
+ *
+ * The arrays below are generated by the test-tools ocsptest command.
+ */
+
+static const char *kOcspTestRoot[] = {
+ "-----BEGIN CERTIFICATE-----\n",
+ "MIID+DCCAuCgAwIBAgIBATANBgkqhkiG9w0BAQsFADCBmjELMAkGA1UEBhMCVVMx\n",
+ "EzARBgNVBAgMCkNhbGlmb3JuaWExFjAUBgNVBAcMDVNhbiBGcmFuY2lzY28xFTAT\n",
+ "BgNVBAoMDEV4YW1wbGUgQ29ycDEeMBwGA1UECwwVQ2VydGlmaWNhdGUgQXV0aG9y\n",
+ "aXR5MScwJQYDVQQDDB5FeGFtcGxlIENvcnAgT0NTUCBUZXN0IFJvb3QgQ0EwIBcN\n",
+ "MjYwMTAxMDAwMDAwWhgPMjEyNjAxMDEwMDAwMDBaMIGaMQswCQYDVQQGEwJVUzET\n",
+ "MBEGA1UECAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwNU2FuIEZyYW5jaXNjbzEVMBMG\n",
+ "A1UECgwMRXhhbXBsZSBDb3JwMR4wHAYDVQQLDBVDZXJ0aWZpY2F0ZSBBdXRob3Jp\n",
+ "dHkxJzAlBgNVBAMMHkV4YW1wbGUgQ29ycCBPQ1NQIFRlc3QgUm9vdCBDQTCCASIw\n",
+ "DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKuZRsVUiwQoVlwfXY0nVpvMQiJN\n",
+ "Su5YfrxzKJ9sMfPI2rEg9d8kW2qsfnCB0isj+SK+CkcfVu5aXcUVUnp08VuKpsI9\n",
+ "idkeC7bhXkQgBR1p1mBwChc+UUD6qEindJPhFxYur+7gCmpamSHYr8iYmjVe78l8\n",
+ "vRHVuw8KMsiGegvPsuxIYWdY4mgKSCu3o6DeK4XehXU4Ll5j0fGX5eJgbaI6g8qE\n",
+ "j71J6G8Y4Ur/WqzTp/GZueTY0wzU8k45HS5uWarGYB2PH8DfM2SRQBw5hYd2xIvS\n",
+ "jgFYzCXeIZFnHVCMeB//VaUyYGIaw+Dreh+Wb5zBPh9FyJkrOXYyMeFZUScCAwEA\n",
+ "AaNFMEMwEgYDVR0TAQH/BAgwBgEB/wIBADAOBgNVHQ8BAf8EBAMCAYYwHQYDVR0O\n",
+ "BBYEFGSeLzT7Ur6wrLpFYVDHmNi/TQp5MA0GCSqGSIb3DQEBCwUAA4IBAQAMMukk\n",
+ "I0VRfxY1vZngu3LdnBk5If6TbQ6lmWatT4q3BrAID3L9zG/qev6DlyruvnVqoNdU\n",
+ "WcWXnzFSDInOKM64wU2oxdosMPWuAKKLHgDdCicikJtF+N0qJgOcyD+Gv9t0kwDB\n",
+ "O094nZUgrkCafJiy5y7KtZvqOjcvfEK+oD7fXX/VURv8NMOxpuassPHnHGvHqwKu\n",
+ "7jBZMjkOoZH4hJSo5EzZxRZHz6ay7Q8e+6C6PG8CIYXpIqukjVNq9jBb2yog67mW\n",
+ "lHlfnEENCyQDSmnEXAVNpJS67jmcItoD1utnDINVzu87DY9WiLZB06Y1xSp1ie7s\n",
+ "FIfS7bZ1cxVkt5wS\n",
+ "-----END CERTIFICATE-----\n",
+ NULL
+};
+
+static const char *kOcspTestRootKey[] = {
+ "-----BEGIN RSA PRIVATE KEY-----\n",
+ "MIIEowIBAAKCAQEAq5lGxVSLBChWXB9djSdWm8xCIk1K7lh+vHMon2wx88jasSD1\n",
+ "3yRbaqx+cIHSKyP5Ir4KRx9W7lpdxRVSenTxW4qmwj2J2R4LtuFeRCAFHWnWYHAK\n",
+ "Fz5RQPqoSKd0k+EXFi6v7uAKalqZIdivyJiaNV7vyXy9EdW7DwoyyIZ6C8+y7Ehh\n",
+ "Z1jiaApIK7ejoN4rhd6FdTguXmPR8Zfl4mBtojqDyoSPvUnobxjhSv9arNOn8Zm5\n",
+ "5NjTDNTyTjkdLm5ZqsZgHY8fwN8zZJFAHDmFh3bEi9KOAVjMJd4hkWcdUIx4H/9V\n",
+ "pTJgYhrD4Ot6H5ZvnME+H0XImSs5djIx4VlRJwIDAQABAoIBAAj+6w/d47/JtuVI\n",
+ "xlMSXzRMW/cyYsg7SWxAWT5/oeAW2n1zWJBkdopmv+YkAsw81uBfDWjhwraSHtz9\n",
+ "xioh8U6MO+ZuQB4VY3soi2mPiCpyPvPP9nzLc9+v2ZydcrtsjxTxnkrWjJU7afsK\n",
+ "l1nbw3x4HU1McG5RQbzYcFsaJFHJcVdxIYkDfEgnBok1ALaQuRUGfUVYZDVk6Ztg\n",
+ "fMUh5nfNSHWu6y2i8YCphprIfu/zwadicaYLYljYqgP9J1MOPwuMcoCoWgotBUCZ\n",
+ "+29qIPn1mK3ReBmYdE8IIEiSLHYJjJ7EVu8IUfSPotBGGC5PjC11HcCXx0xB9AfB\n",
+ "I8VcDikCgYEA089WizVCWZMxWnCPLDG036vsug3/rrPr/O3W5VXTdpr00CBRzund\n",
+ "7QnaxUyxw37SOAQUtzcKhC0CF3c7Dbryb8d51koROVlWC8rngoDMxiZkbZZe0kL1\n",
+ "jHkOHEOoi83uY0tFoRhYtrcOYObNT9JDifdIDYCnxbLW4UQLo/9mFmUCgYEAz2ZH\n",
+ "hxoxXJ9y3qmIcc2vZTRPdaB4r+qQi5krUgtYg1uKImrvDnnl/4xCnCG9G1s1M/wE\n",
+ "cvuFG5FWGz6TtS0TsCGUXVWZ9s67JT53l02RIVTog/VRHbKsOnj+d9oXglGcxALT\n",
+ "qG02lKocNHzrPUXEw/EIMETKR6WvATZCzhM9mpsCgYB9RoKfb27A4Cgun6husS+T\n",
+ "o3IuUR1KzSvkux+BIRQjcF8fwh3gzb3u9wcn7satJBNeAjvmaW2U47H7AxAwfMPr\n",
+ "jQXo0oIBc29LJkVrkJaNFCQOFQQcRHJLFUZdPT8xASngHKMgNvAxkW+1rIz+ixRb\n",
+ "Q6CgK9oPOkmRjtd7thFBaQKBgQCMbt0QBhRWe0D0tCbHqFaTWJBVPYt60oF9hQFo\n",
+ "VHZiu6EVHQMx8ihimT6hKdc6ps+nm4YHtXez6v07BWxOyW8DXDlx2XyfOexOk7W2\n",
+ "pbcXsr6eW4XJbipgjX0A+pPgkhJsRt26tfi3QVhH0i4XFx7c7mB1Dp9JVE7jqzIh\n",
+ "B7Y28QKBgCg93v7zP5GFmOa6zCZreIIfi1dy50otxfqYewcXy4Egt90cE67vY55u\n",
+ "3B44ySrLkKDuzX2Inrwp5Vao8b9pDQ/AisRzt617eO1H5JtRF3lzZJukYC6j4urU\n",
+ "24kB2IhZCB6/3pwGiS21ma9E6OF9tO5YaGGYo02ZqkoflN+XgoeZ\n",
+ "-----END RSA PRIVATE KEY-----\n",
+ NULL
+};
+
+static const char *kOcspTestLeaf[] = {
+ "-----BEGIN CERTIFICATE-----\n",
+ "MIIEOzCCAyOgAwIBAgICEAAwDQYJKoZIhvcNAQELBQAwgZoxCzAJBgNVBAYTAlVT\n",
+ "MRMwEQYDVQQIDApDYWxpZm9ybmlhMRYwFAYDVQQHDA1TYW4gRnJhbmNpc2NvMRUw\n",
+ "EwYDVQQKDAxFeGFtcGxlIENvcnAxHjAcBgNVBAsMFUNlcnRpZmljYXRlIEF1dGhv\n",
+ "cml0eTEnMCUGA1UEAwweRXhhbXBsZSBDb3JwIE9DU1AgVGVzdCBSb290IENBMCAX\n",
+ "DTI2MDEwMTAwMDAwMFoYDzIxMjYwMTAxMDAwMDAwWjCBiDELMAkGA1UEBhMCVVMx\n",
+ "EzARBgNVBAgMCkNhbGlmb3JuaWExFjAUBgNVBAcMDVNhbiBGcmFuY2lzY28xFTAT\n",
+ "BgNVBAoMDEV4YW1wbGUgQ29ycDEVMBMGA1UECwwMV2ViIFNlcnZpY2VzMR4wHAYD\n",
+ "VQQDDBVvY3NwLWxlYWYuZXhhbXBsZS5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IB\n",
+ "DwAwggEKAoIBAQCoQbsBGnLjATBzU8eWYMeyzu6vy4scpVDdhGnTWMaSIrqoXXge\n",
+ "JXrMHavT13cv2wNTExA6BNqFZA6YLBXYpDJ5oXyOlY1SYjbapX4M0kry4tBZdGWu\n",
+ "vnh04Q07SI8JvjtScB3lAIIhGr1WrQNLfEz+O80j4Pp8kLe1fWi0joB1CG4RWiuH\n",
+ "/2Ls1rSEMTr6dABtQ+zwxRHcFlEAoR9ZEDzfN1hIQEByWJd7TMnv738usyp52wMi\n",
+ "Gh/sruYHkpsO2tVoLCwm1OMSR1BLdGO1tJWSYN2+Tj2JVleQWcwwCgzDX4OlIdPF\n",
+ "/CM/6aQA6BkDeMW//tO4Ec4X+530zPiFKcZLAgMBAAGjgZgwgZUwDAYDVR0TAQH/\n",
+ "BAIwADAOBgNVHQ8BAf8EBAMCBaAwEwYDVR0lBAwwCgYIKwYBBQUHAwEwHQYDVR0O\n",
+ "BBYEFBEZYONIpHIGBtlfPSlUTcDccGMiMB8GA1UdIwQYMBaAFGSeLzT7Ur6wrLpF\n",
+ "YVDHmNi/TQp5MCAGA1UdEQQZMBeCFW9jc3AtbGVhZi5leGFtcGxlLmNvbTANBgkq\n",
+ "hkiG9w0BAQsFAAOCAQEAaFYoQjmWdBBiD/hfSgV34Jf+PVKkecuu60VKDasPLNyV\n",
+ "ZwZcW8dXF9NOYcebGIEx4rjQTC3xiHUknqfOzc2jmPdU/xBAyRiltQnUh3OFH1qn\n",
+ "bX0YNUgfluLW+YSwNkriFLuzESBVadGhlX94mQwoqYyQJ+6/Ht2j4P4ZiIDRWfgc\n",
+ "z9pN8YoCiXh/I/IxVIWunk7Dla+Gr6BDJ762iQMMzPQ5D3cRAe9BDRgGiN0lPeIa\n",
+ "LMtqDeZZ+dR0KZAr3yZcfyci8SNXwCBbjsoVjmuUj6dlN12pRKxxtBSa23KT6/Dy\n",
+ "ijXZ+PtyIJz8FVBxO0RoECLimwbUPHfNjAWjuJkugA==\n",
+ "-----END CERTIFICATE-----\n",
+ NULL
+};
+
+/* Load the fixed PKI. Any of the out params may be NULL to skip it. */
+static int load_pki(X509 **root, EVP_PKEY **root_key, X509 **leaf)
+{
+ if (root != NULL && !TEST_ptr(*root = X509_from_strings(kOcspTestRoot)))
+ return 0;
+ if (root_key != NULL
+ && !TEST_ptr(*root_key = PKEY_from_strings(kOcspTestRootKey)))
+ return 0;
+ if (leaf != NULL && !TEST_ptr(*leaf = X509_from_strings(kOcspTestLeaf)))
+ return 0;
+ return 1;
+}
+
+/*
+ * Build a signed OCSP response for |cert| (issued by |issuer|). |resp_status|
+ * is the outer response status, |cert_status| the single-response certificate
+ * status. thisUpdate and nextUpdate are the current time offset by
+ * |this_off_sec| and |next_off_sec| seconds, so callers can also produce
+ * expired or not-yet-valid responses. The response is signed by
+ * |signer|/|signer_key|. Returns a response the caller must free, or NULL.
+ */
+static OCSP_RESPONSE *make_ocsp_response(X509 *cert, X509 *issuer,
+ int resp_status, int cert_status, int this_off_sec, int next_off_sec,
+ X509 *signer, EVP_PKEY *signer_key)
+{
+ OCSP_RESPONSE *resp = NULL;
+ OCSP_BASICRESP *bs = NULL;
+ OCSP_CERTID *cid = NULL;
+ ASN1_TIME *thisupd = NULL, *nextupd = NULL, *revtime = NULL;
+
+ if (cert_status == V_OCSP_CERTSTATUS_REVOKED
+ && !TEST_ptr(revtime = X509_gmtime_adj(NULL, 0)))
+ goto end;
+
+ if (!TEST_ptr(bs = OCSP_BASICRESP_new())
+ || !TEST_ptr(thisupd = X509_time_adj_ex(NULL, 0, this_off_sec, NULL))
+ || !TEST_ptr(nextupd = X509_time_adj_ex(NULL, 0, next_off_sec, NULL))
+ || !TEST_ptr(cid = OCSP_cert_to_id(EVP_sha256(), cert, issuer))
+ || !TEST_ptr(OCSP_basic_add1_status(bs, cid, cert_status, 0, revtime,
+ thisupd, nextupd))
+ || !TEST_true(OCSP_basic_sign(bs, signer, signer_key, EVP_sha256(),
+ NULL, OCSP_NOCERTS)))
+ goto end;
+
+ resp = OCSP_response_create(resp_status, bs);
+
+end:
+ ASN1_TIME_free(revtime);
+ ASN1_TIME_free(thisupd);
+ ASN1_TIME_free(nextupd);
+ OCSP_CERTID_free(cid);
+ OCSP_BASICRESP_free(bs);
+ return resp;
+}
+
+/* Wrap |resp| into a stack, taking ownership on success. */
+static STACK_OF(OCSP_RESPONSE) *make_ocsp_resp_stack(OCSP_RESPONSE *resp)
+{
+ STACK_OF(OCSP_RESPONSE) *sk = sk_OCSP_RESPONSE_new_null();
+
+ if (!TEST_ptr(sk))
+ return NULL;
+ if (!TEST_true(sk_OCSP_RESPONSE_push(sk, resp))) {
+ sk_OCSP_RESPONSE_free(sk);
+ return NULL;
+ }
+ return sk;
+}
+
+/*
+ * Verify |leaf| against |root| with the stapled |resps| and |flags|, taking
+ * ownership of |resps|. Returns X509_V_OK or an X509_V_ERR_xxx code. The
+ * X509_verify_cert() call is wrapped for malloc-failure injection.
+ */
+static int verify_ocsp(X509 *leaf, X509 *root, STACK_OF(OCSP_RESPONSE) *resps,
+ unsigned long flags)
+{
+ X509_STORE *store = X509_STORE_new();
+ X509_STORE_CTX *ctx = X509_STORE_CTX_new();
+ X509_VERIFY_PARAM *param = X509_VERIFY_PARAM_new();
+ int status = X509_V_ERR_UNSPECIFIED;
+
+ if (!TEST_ptr(store) || !TEST_ptr(ctx) || !TEST_ptr(param))
+ goto end;
+
+ if (!TEST_true(X509_STORE_add_cert(store, root))
+ || !TEST_true(X509_STORE_CTX_init(ctx, store, leaf, NULL)))
+ goto end;
+
+ X509_STORE_CTX_set_ocsp_resp(ctx, resps);
+
+ X509_VERIFY_PARAM_set_depth(param, 16);
+ if (flags != 0)
+ X509_VERIFY_PARAM_set_flags(param, flags);
+ X509_STORE_CTX_set0_param(ctx, param);
+ param = NULL;
+
+ ERR_clear_error();
+ MFAIL_start();
+ status = X509_verify_cert(ctx) == 1 ? X509_V_OK
+ : X509_STORE_CTX_get_error(ctx);
+ MFAIL_end();
+
+end:
+ X509_VERIFY_PARAM_free(param);
+ X509_STORE_CTX_free(ctx);
+ X509_STORE_free(store);
+ sk_OCSP_RESPONSE_pop_free(resps, OCSP_RESPONSE_free);
+ return status;
+}
+
+/*
+ * Build a single response for the leaf and verify it, expecting |expected|.
+ * Every failure inside check_cert_ocsp_resp() surfaces as
+ * X509_V_ERR_OCSP_VERIFY_FAILED at the X509_verify_cert() level.
+ */
+static int run_ocsp_verify(int resp_status, int cert_status, int this_off_sec,
+ int next_off_sec, int expected)
+{
+ X509 *root = NULL, *leaf = NULL;
+ EVP_PKEY *root_key = NULL;
+ OCSP_RESPONSE *resp = NULL;
+ STACK_OF(OCSP_RESPONSE) *resps = NULL;
+ int testresult = 0;
+
+ if (!load_pki(&root, &root_key, &leaf))
+ goto end;
+
+ if (!TEST_ptr(resp = make_ocsp_response(leaf, root, resp_status, cert_status,
+ this_off_sec, next_off_sec, root, root_key))
+ || !TEST_ptr(resps = make_ocsp_resp_stack(resp)))
+ goto end;
+ resp = NULL; /* owned by resps */
+
+ testresult = TEST_int_eq(verify_ocsp(leaf, root, resps,
+ X509_V_FLAG_OCSP_RESP_CHECK),
+ expected);
+ resps = NULL; /* freed by verify_ocsp */
+
+end:
+ sk_OCSP_RESPONSE_pop_free(resps, OCSP_RESPONSE_free);
+ OCSP_RESPONSE_free(resp);
+ EVP_PKEY_free(root_key);
+ X509_free(leaf);
+ X509_free(root);
+ return testresult;
+}
+
+/*
+ * A good response that expired a moment ago is still accepted, because stapled
+ * responses are honoured for up to five minutes past nextUpdate.
+ */
+static int test_ocsp_resp_good(void)
+{
+ return run_ocsp_verify(OCSP_RESPONSE_STATUS_SUCCESSFUL,
+ V_OCSP_CERTSTATUS_GOOD, -10 * 60, -2 * 60, X509_V_OK);
+}
+
+/* An outer response status other than successful is rejected. */
+static int test_ocsp_resp_not_successful(void)
+{
+ return run_ocsp_verify(OCSP_RESPONSE_STATUS_TRYLATER,
+ V_OCSP_CERTSTATUS_GOOD, 0, 24 * 60 * 60, X509_V_ERR_OCSP_VERIFY_FAILED);
+}
+
+/* A response more than five minutes past nextUpdate is rejected as expired. */
+static int test_ocsp_resp_expired(void)
+{
+ return run_ocsp_verify(OCSP_RESPONSE_STATUS_SUCCESSFUL,
+ V_OCSP_CERTSTATUS_GOOD, -20 * 60, -10 * 60, X509_V_ERR_OCSP_VERIFY_FAILED);
+}
+
+/* No response for the leaf's depth: sk_OCSP_RESPONSE_num() <= error_depth. */
+static int test_ocsp_resp_none(void)
+{
+ X509 *root = NULL, *leaf = NULL;
+ STACK_OF(OCSP_RESPONSE) *resps = NULL;
+ int testresult = 0;
+
+ if (!load_pki(&root, NULL, &leaf)
+ || !TEST_ptr(resps = sk_OCSP_RESPONSE_new_null()))
+ goto end;
+
+ testresult = TEST_int_eq(verify_ocsp(leaf, root, resps,
+ X509_V_FLAG_OCSP_RESP_CHECK),
+ X509_V_ERR_OCSP_VERIFY_FAILED);
+ resps = NULL; /* freed by verify_ocsp */
+
+end:
+ sk_OCSP_RESPONSE_pop_free(resps, OCSP_RESPONSE_free);
+ X509_free(leaf);
+ X509_free(root);
+ return testresult;
+}
+
+/*
+ * A well-formed response carrying a single response for a different certificate:
+ * no CertID matches the leaf, so no status is found for it.
+ */
+static int test_ocsp_resp_wrong_cert(void)
+{
+ X509 *root = NULL, *leaf = NULL;
+ EVP_PKEY *root_key = NULL;
+ OCSP_RESPONSE *resp = NULL;
+ STACK_OF(OCSP_RESPONSE) *resps = NULL;
+ int testresult = 0;
+
+ if (!load_pki(&root, &root_key, &leaf))
+ goto end;
+
+ /* the response is about the root, not the leaf being verified */
+ if (!TEST_ptr(resp = make_ocsp_response(root, root,
+ OCSP_RESPONSE_STATUS_SUCCESSFUL, V_OCSP_CERTSTATUS_GOOD, 0,
+ 24 * 60 * 60, root, root_key))
+ || !TEST_ptr(resps = make_ocsp_resp_stack(resp)))
+ goto end;
+ resp = NULL; /* owned by resps */
+
+ testresult = TEST_int_eq(verify_ocsp(leaf, root, resps,
+ X509_V_FLAG_OCSP_RESP_CHECK),
+ X509_V_ERR_OCSP_VERIFY_FAILED);
+ resps = NULL; /* freed by verify_ocsp */
+
+end:
+ sk_OCSP_RESPONSE_pop_free(resps, OCSP_RESPONSE_free);
+ OCSP_RESPONSE_free(resp);
+ EVP_PKEY_free(root_key);
+ X509_free(leaf);
+ X509_free(root);
+ return testresult;
+}
+
+/* Exercise the success path under mfail. */
+static int test_ocsp_resp_mfail(void)
+{
+ X509 *root = NULL, *leaf = NULL;
+ EVP_PKEY *root_key = NULL;
+ OCSP_RESPONSE *resp = NULL;
+ STACK_OF(OCSP_RESPONSE) *resps = NULL;
+ int testresult = 0;
+
+ if (!load_pki(&root, &root_key, &leaf))
+ goto end;
+
+ if (!TEST_ptr(resp = make_ocsp_response(leaf, root,
+ OCSP_RESPONSE_STATUS_SUCCESSFUL, V_OCSP_CERTSTATUS_GOOD, 0,
+ 24 * 60 * 60, root, root_key))
+ || !TEST_ptr(resps = make_ocsp_resp_stack(resp)))
+ goto end;
+ resp = NULL; /* owned by resps */
+
+ testresult = verify_ocsp(leaf, root, resps, X509_V_FLAG_OCSP_RESP_CHECK)
+ == X509_V_OK;
+ resps = NULL; /* freed by verify_ocsp */
+
+end:
+ sk_OCSP_RESPONSE_pop_free(resps, OCSP_RESPONSE_free);
+ OCSP_RESPONSE_free(resp);
+ EVP_PKEY_free(root_key);
+ X509_free(leaf);
+ X509_free(root);
+ return testresult;
+}
+
+#endif /* OPENSSL_NO_OCSP */
+
+int setup_tests(void)
+{
+#ifndef OPENSSL_NO_OCSP
+ ADD_TEST(test_ocsp_resp_good);
+ ADD_TEST(test_ocsp_resp_not_successful);
+ ADD_TEST(test_ocsp_resp_expired);
+ ADD_TEST(test_ocsp_resp_none);
+ ADD_TEST(test_ocsp_resp_wrong_cert);
+ ADD_MFAIL_NO_CHECK_TEST(test_ocsp_resp_mfail);
+#endif
+ return 1;
+}
diff --git a/test/recipes/80-test_ocsp.t b/test/recipes/80-test_ocsp.t
index 3e12a0b23e..62ee9f13ca 100644
--- a/test/recipes/80-test_ocsp.t
+++ b/test/recipes/80-test_ocsp.t
@@ -54,7 +54,7 @@ sub test_ocsp {
$title); });
}
-plan tests => 13;
+plan tests => 14;
subtest "=== VALID OCSP RESPONSES ===" => sub {
plan tests => 7;
@@ -232,6 +232,12 @@ subtest "=== OCSP API TESTS===" => sub {
"running ocspapitest");
};
+subtest "=== OCSP VERIFICATION TESTS ===" => sub {
+ plan tests => 1;
+
+ ok(run(test(["ocsptest"])), "running ocsptest");
+};
+
subtest "=== UNTRUSTED ISSUER HINTS ===" => sub {
plan tests => 1;