From: Jakub Zelenka Date: Thu, 2 Jul 2026 11:45:16 +0000 (+0200) Subject: x509: add ocsptest for the OCSP stapled-response verification path X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=2d3e267b2b035859b1da38644721afef8d88d792;p=thirdparty%2Fopenssl.git 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 Reviewed-by: Tomas Mraz 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 94e6d088651..a5dcc7f89df 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 00000000000..96dc07423b8 --- /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 +#include +#include +#include +#include +#include +#include +#ifndef OPENSSL_NO_OCSP +#include +#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 3e12a0b23ec..62ee9f13cae 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;