diff --git a/.github/ci-deps/packages-ubuntu-24.04-full.txt b/.github/ci-deps/packages-ubuntu-24.04-full.txt index 9e4cbc106cf..6a3b4dbfaca 100644 --- a/.github/ci-deps/packages-ubuntu-24.04-full.txt +++ b/.github/ci-deps/packages-ubuntu-24.04-full.txt @@ -81,6 +81,7 @@ libuv1-dev linux-libc-dev make man2html +memcached meson mono-complete nghttp2 diff --git a/.github/workflows/openresty.yml b/.github/workflows/openresty.yml new file mode 100644 index 00000000000..b23d606a8d6 --- /dev/null +++ b/.github/workflows/openresty.yml @@ -0,0 +1,230 @@ +name: OpenResty Tests + +# START OF COMMON SECTION +on: + push: + branches: [ 'release/**' ] + pull_request: + types: [opened, synchronize, reopened, ready_for_review] + branches: [ '*' ] + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +permissions: + contents: read +# END OF COMMON SECTION + +jobs: + build_wolfssl: + name: Build wolfSSL + if: ${{ (github.repository_owner == 'wolfssl') && (github.event_name != 'pull_request' || github.event.pull_request.draft == false) }} + # Just to keep it the same as the testing target + runs-on: ubuntu-24.04 + # This should be a safe limit for the tests to run. + timeout-minutes: 5 + steps: + - name: Build wolfSSL + uses: wolfSSL/actions-build-autotools-project@v1 + with: + path: wolfssl + configure: >- + --enable-openresty --enable-curve25519 --enable-ed25519 + install: true + + - name: tar build-dir + run: tar -zcf build-dir.tgz build-dir + + - name: Upload built lib + uses: actions/upload-artifact@v6 + with: + name: wolf-install-openresty + path: build-dir.tgz + retention-days: 5 + + openresty_check: + strategy: + fail-fast: false + matrix: + include: + # The SSL test files of the bundled Lua modules. The port patch and + # the tests patch come from the OSP repo. + # + # Not run (cannot work with wolfSSL, the tests patch makes them + # skip): ssl_client_hello_by_lua* files (ngx_lua 166, 187; stream + # 162, 163; lua-resty-core ssl-client-hello.t) need + # SSL_CTX_set_client_hello_cb; proxy_ssl_*_by_lua* files (ngx_lua + # 169, 170-proxy-ssl-cert; stream 164, 165-proxy-ssl-cert-by; + # lua-resty-core proxy-ssl*.t) need SSL_set_retry_verify; FFI into + # libcrypto (ngx_lua 193, stream 171, lua-resty-core + # socket-tcp-settrustedstore.t) needs OpenSSL symbol names. + # Not run (need the network): ngx_lua and stream 129-ssl-socket.t + # and 170-ssl-session-reuse.t. + - ref: 1.31.1.1 + sha256: 65b78baadd3f0984055de89bf13f4a1932e5bfe9c31932037a134ea2b1a0ce42 + osp-repo: julek-dev/osp + osp-ref: openresty-1.31.1.1 + ngx-lua-tests: >- + 139-ssl-cert-by.t 140-ssl-c-api.t 142-ssl-session-store.t + 143-ssl-session-fetch.t 155-tls13.t 162-socket-tls-handshake.t + stream-lua-tests: >- + 139-ssl-cert-by.t 140-ssl-c-api.t 165-serversslhandshake.t + 166-serversslhandshake-starttls.t + 167-serversslhandshake-errors.t + resty-core-tests: >- + ssl.t stream/ssl.t ocsp.t ssl-session-store.t + ssl-session-fetch.t + name: ${{ matrix.ref }} + if: ${{ (github.repository_owner == 'wolfssl') && (github.event_name != 'pull_request' || github.event.pull_request.draft == false) }} + runs-on: ubuntu-24.04 + # Test::Nginx restarts nginx for every block, so the suites take a while. + timeout-minutes: 45 + needs: build_wolfssl + env: + OR_PREFIX: ${{ github.workspace }}/or-install + TEST_NGINX_BINARY: ${{ github.workspace }}/or-install/nginx/sbin/nginx + TEST_NGINX_USE_WOLFSSL: 1 + TEST_NGINX_NO_SHUFFLE: 1 + TEST_NGINX_SLEEP: 0.05 + TEST_NGINX_MEMCACHED_PORT: 11211 + steps: + - name: Checkout wolfSSL CI actions + uses: actions/checkout@v5 + with: + sparse-checkout: .github/actions + fetch-depth: 1 + + - name: Install test dependencies + uses: ./.github/actions/install-apt-deps + with: + packages: libpcre2-dev zlib1g-dev memcached + ghcr-debs-tag: ubuntu-24.04-full + + - name: Download lib + uses: actions/download-artifact@v7 + with: + name: wolf-install-openresty + + - name: untar build-dir + run: tar -xf build-dir.tgz + + - name: Setup Perl environment + uses: shogo82148/actions-setup-perl@v1 + with: + perl-version: '5.38.2' + + - name: Install Test::Nginx + run: cpanm --notest Test::Nginx@0.32 + + - name: Checkout OSP + uses: actions/checkout@v5 + with: + repository: ${{ matrix.osp-repo }} + ref: ${{ matrix.osp-ref }} + path: osp + sparse-checkout: openresty + + - name: Download OpenResty + run: | + curl -fsSL -o openresty.tar.gz \ + https://openresty.org/download/openresty-${{ matrix.ref }}.tar.gz + echo "${{ matrix.sha256 }} openresty.tar.gz" | sha256sum -c + tar -xf openresty.tar.gz + + - name: Apply OSP patches + working-directory: openresty-${{ matrix.ref }} + run: | + patch -p1 < ../osp/openresty/${{ matrix.ref }}.patch + patch -p1 < ../osp/openresty/${{ matrix.ref }}-tests.patch + + # --with-debug: the tests check the debug log. + - name: Build OpenResty + working-directory: openresty-${{ matrix.ref }} + run: | + ./configure --prefix=$OR_PREFIX \ + --with-wolfssl=$GITHUB_WORKSPACE/build-dir --with-http_ssl_module \ + --with-stream --with-stream_ssl_module \ + --with-stream_ssl_preread_module --with-http_v2_module \ + --with-mail --with-mail_ssl_module --with-debug -j$(nproc) + make -j$(nproc) + make install + + - name: Confirm OpenResty built with wolfSSL + run: | + ldd $TEST_NGINX_BINARY | grep wolfssl + $TEST_NGINX_BINARY -V 2>&1 | grep 'built with wolfSSL' + + # Short paths: the tests listen on unix sockets under t/servroot. + # lua-resty-core expects its sibling lua-resty-lrucache by that name. + - name: Prepare test directories + working-directory: openresty-${{ matrix.ref }}/bundle + run: | + mkdir -p /tmp/ort + mv ngx_lua-* /tmp/ort/ngx_lua + mv ngx_stream_lua-* /tmp/ort/ngx_stream_lua + mv lua-resty-core-* /tmp/ort/lua-resty-core + mv lua-resty-lrucache-* /tmp/ort/lua-resty-lrucache + mv encrypted-session-nginx-module-* /tmp/ort/encrypted-session + mv set-misc-nginx-module-* /tmp/ort/set-misc + + # Some blocks talk to memcached over UDP. + - name: Start memcached + run: | + pkill -x memcached || true + memcached -d -p 11211 -U 11211 -l 127.0.0.1 + + - if: ${{ runner.debug }} + name: Verbose prove + run: echo "prove_flags=-v" >> $GITHUB_ENV + + - name: Run ngx_lua tests + working-directory: /tmp/ort/ngx_lua + env: + LD_LIBRARY_PATH: ${{ github.workspace }}/build-dir/lib:${{ github.workspace }}/or-install/luajit/lib + run: | + prove ${{ env.prove_flags }} -I. \ + $(for t in ${{ matrix.ngx-lua-tests }}; do echo t/$t; done) + + - if: ${{ !cancelled() }} + name: Run ngx_stream_lua tests + working-directory: /tmp/ort/ngx_stream_lua + env: + LD_LIBRARY_PATH: ${{ github.workspace }}/build-dir/lib:${{ github.workspace }}/or-install/luajit/lib + run: | + prove ${{ env.prove_flags }} -I. \ + $(for t in ${{ matrix.stream-lua-tests }}; do echo t/$t; done) + + - if: ${{ !cancelled() }} + name: Run lua-resty-core tests + working-directory: /tmp/ort/lua-resty-core + env: + LD_LIBRARY_PATH: ${{ github.workspace }}/build-dir/lib:${{ github.workspace }}/or-install/luajit/lib + run: | + prove ${{ env.prove_flags }} -I. \ + $(for t in ${{ matrix.resty-core-tests }}; do echo t/$t; done) + + # These two modules use the OpenSSL compatibility layer directly. + - if: ${{ !cancelled() }} + name: Run encrypted-session tests + working-directory: /tmp/ort/encrypted-session + env: + LD_LIBRARY_PATH: ${{ github.workspace }}/build-dir/lib:${{ github.workspace }}/or-install/luajit/lib + run: prove ${{ env.prove_flags }} -I. t/sanity.t + + - if: ${{ !cancelled() }} + name: Run set-misc tests + working-directory: /tmp/ort/set-misc + env: + LD_LIBRARY_PATH: ${{ github.workspace }}/build-dir/lib:${{ github.workspace }}/or-install/luajit/lib + run: prove ${{ env.prove_flags }} -I. t/hmac.t + + - if: ${{ failure() }} + name: Upload nginx logs + uses: actions/upload-artifact@v6 + with: + name: openresty-${{ matrix.ref }}-logs + path: | + /tmp/ort/*/t/servroot*/logs/*.log + /tmp/ort/*/t/servroot*/conf/nginx.conf + retention-days: 5 diff --git a/CMakeLists.txt b/CMakeLists.txt index 254b8d3b932..6b3fb5cc60e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -3891,6 +3891,13 @@ if(NOT WOLFSSL_CONTEXT_EXTRA_USER_DATA STREQUAL "no") endif() endif() +# nginx 1.31 and the OpenResty Lua modules need six ex_data slots per object. +if((WOLFSSL_NGINX OR WOLFSSL_OPENRESTY) AND + (WOLFSSL_CONTEXT_EXTRA_USER_DATA STREQUAL "no" OR + WOLFSSL_CONTEXT_EXTRA_USER_DATA STREQUAL "yes")) + list(APPEND WOLFSSL_DEFINITIONS "-DMAX_EX_DATA=10") +endif() + # ECC minimum key size (bits) if(WOLFSSL_FIPS) set(WOLFSSL_ECC_MIN_KEY_SZ_DEFAULT "192") diff --git a/configure.ac b/configure.ac index 39601ee6e5e..ec55b9ca359 100644 --- a/configure.ac +++ b/configure.ac @@ -12308,6 +12308,13 @@ yes) AM_CFLAGS="$AM_CFLAGS -DHAVE_EX_DATA" ;; esac +# nginx 1.31 and the OpenResty Lua modules need six ex_data slots per object. +if test "$ENABLED_NGINX" = "yes" && \ + (test "$ENABLED_EX_DATA" = "no" || test "$ENABLED_EX_DATA" = "yes") +then + AM_CFLAGS="$AM_CFLAGS -DMAX_EX_DATA=10" +fi + # IoT-Safe support AC_ARG_ENABLE([iotsafe], [AS_HELP_STRING([--enable-iotsafe],[Enables support for IoT-Safe secure applet (default: disabled)])], diff --git a/src/internal.c b/src/internal.c index c29462ef3aa..96f2a2f4a6b 100644 --- a/src/internal.c +++ b/src/internal.c @@ -43738,9 +43738,18 @@ static int AddPSKtoPreMasterSecret(WOLFSSL* ssl) XMEMCPY(ssl->session->sessionID, bogusID, ID_LEN); ssl->session->sessionIDSz= bogusIDSz; } -#ifdef WOLFSSL_TICKET_HAVE_ID +#if defined(WOLFSSL_TICKET_HAVE_ID) && !defined(NO_SESSION_CACHE) else { - if (wolfSSL_GetSession(ssl, NULL, 1) != NULL) { + int found; + /* The external cache takes part in the 0-RTT anti-replay + * eviction, so it is only skipped when early data is off. */ +#ifdef WOLFSSL_EARLY_DATA + if (ssl->options.maxEarlyDataSz > 0) + found = wolfSSL_GetSessionFromCache(ssl, ssl->session); + else +#endif + found = wolfSSL_GetSessionFromInternalCache(ssl, ssl->session); + if (found == WOLFSSL_SUCCESS) { WOLFSSL_MSG("Found session matching the session id" " found in the ticket"); } diff --git a/src/ocsp.c b/src/ocsp.c index 87ab3800973..590672d308f 100644 --- a/src/ocsp.c +++ b/src/ocsp.c @@ -730,10 +730,15 @@ int wolfSSL_OCSP_resp_find_status(WOLFSSL_OCSP_BASICRESP *bs, if (status != NULL) *status = single->status->status; #ifdef WOLFSSL_OCSP_PARSE_STATUS - if (thisupd != NULL) - *thisupd = &single->status->thisDateParsed; - if (nextupd != NULL) - *nextupd = &single->status->nextDateParsed; + /* NULL when the response has no such field, as with OpenSSL. */ + if (thisupd != NULL) { + *thisupd = (single->status->thisDateParsed.length > 0) ? + &single->status->thisDateParsed : NULL; + } + if (nextupd != NULL) { + *nextupd = (single->status->nextDateParsed.length > 0) ? + &single->status->nextDateParsed : NULL; + } #else if (thisupd != NULL) *thisupd = NULL; @@ -1636,10 +1641,15 @@ int wolfSSL_OCSP_single_get0_status(WOLFSSL_OCSP_SINGLERESP *single, return -1; #ifdef WOLFSSL_OCSP_PARSE_STATUS - if (thisupd != NULL) - *thisupd = &single->status->thisDateParsed; - if (nextupd != NULL) - *nextupd = &single->status->nextDateParsed; + /* NULL when the response has no such field, as with OpenSSL. */ + if (thisupd != NULL) { + *thisupd = (single->status->thisDateParsed.length > 0) ? + &single->status->thisDateParsed : NULL; + } + if (nextupd != NULL) { + *nextupd = (single->status->nextDateParsed.length > 0) ? + &single->status->nextDateParsed : NULL; + } #else if (thisupd != NULL) *thisupd = NULL; diff --git a/src/ssl_sess.c b/src/ssl_sess.c index a8c91c7adb1..6cfc7bde1e1 100644 --- a/src/ssl_sess.c +++ b/src/ssl_sess.c @@ -1217,7 +1217,8 @@ int TlsSessionCacheGetAndWrLock(const byte *id, WOLFSSL_SESSION **sess, lockedRow, 0, side); } -int wolfSSL_GetSessionFromCache(WOLFSSL* ssl, WOLFSSL_SESSION* output) +static int GetSessionFromCache(WOLFSSL* ssl, WOLFSSL_SESSION* output, + byte internalOnly) { const WOLFSSL_SESSION* sess = NULL; const byte* id = NULL; @@ -1235,7 +1236,9 @@ int wolfSSL_GetSessionFromCache(WOLFSSL* ssl, WOLFSSL_SESSION* output) byte bogusID[ID_LEN]; byte bogusIDSz = 0; - WOLFSSL_ENTER("wolfSSL_GetSessionFromCache"); + WOLFSSL_ENTER("GetSessionFromCache"); + + (void)internalOnly; if (output == NULL) { WOLFSSL_MSG("NULL output"); @@ -1270,7 +1273,7 @@ int wolfSSL_GetSessionFromCache(WOLFSSL* ssl, WOLFSSL_SESSION* output) #ifdef HAVE_EXT_CACHE - if (ssl->ctx->get_sess_cb != NULL) { + if (!internalOnly && ssl->ctx->get_sess_cb != NULL) { int copy = 0; int found = 0; WOLFSSL_SESSION* extSess; @@ -1499,6 +1502,18 @@ int wolfSSL_GetSessionFromCache(WOLFSSL* ssl, WOLFSSL_SESSION* output) return error; } +int wolfSSL_GetSessionFromCache(WOLFSSL* ssl, WOLFSSL_SESSION* output) +{ + return GetSessionFromCache(ssl, output, 0); +} + +/* Lookup by the ID carried in a decrypted ticket without calling the + * external cache callback. */ +int wolfSSL_GetSessionFromInternalCache(WOLFSSL* ssl, WOLFSSL_SESSION* output) +{ + return GetSessionFromCache(ssl, output, 1); +} + WOLFSSL_SESSION* wolfSSL_GetSession(WOLFSSL* ssl, byte* masterSecret, byte restoreSessionCerts) { diff --git a/src/x509.c b/src/x509.c index 1365cc44074..f55a7740016 100644 --- a/src/x509.c +++ b/src/x509.c @@ -8937,6 +8937,11 @@ static WOLFSSL_X509* d2i_X509orX509REQ_bio(WOLFSSL_BIO* bio, WOLFSSL_X509* localX509 = NULL; byte* mem = NULL; int size; + int derSz; + int hdrSz = 2; + int len = 0; + word32 idx = 1; + byte hdr[6]; WOLFSSL_ENTER("wolfSSL_d2i_X509_bio"); @@ -8952,12 +8957,35 @@ static WOLFSSL_X509* d2i_X509orX509REQ_bio(WOLFSSL_BIO* bio, return NULL; } - if (!(mem = (byte*)XMALLOC(size, NULL, DYNAMIC_TYPE_OPENSSL))) { - WOLFSSL_MSG("malloc error"); + /* Consume only the first DER object. A BIO holding a chain is then + * decoded one certificate per call, as with OpenSSL. */ + if (wolfSSL_BIO_read(bio, hdr, hdrSz) != hdrSz) { + WOLFSSL_MSG("wolfSSL_BIO_read error"); + return NULL; + } + if (hdr[1] & ASN_LONG_LENGTH) { + int n = hdr[1] & 0x7F; + if (n < 1 || n > 4 || wolfSSL_BIO_read(bio, hdr + hdrSz, n) != n) { + WOLFSSL_MSG("Bad DER length"); + return NULL; + } + hdrSz += n; + } + if (hdr[0] != (ASN_SEQUENCE | ASN_CONSTRUCTED) || + GetLength_ex(hdr, &idx, &len, (word32)hdrSz, 0) < 0 || + len < 0 || (int)idx + len > size) { + WOLFSSL_MSG("Bad DER header"); return NULL; } + derSz = (int)idx + len; - if ((size = wolfSSL_BIO_read(bio, mem, size)) == 0) { + if (!(mem = (byte*)XMALLOC(derSz, NULL, DYNAMIC_TYPE_OPENSSL))) { + WOLFSSL_MSG("malloc error"); + return NULL; + } + XMEMCPY(mem, hdr, hdrSz); + if (derSz > hdrSz && wolfSSL_BIO_read(bio, mem + hdrSz, derSz - hdrSz) != + derSz - hdrSz) { WOLFSSL_MSG("wolfSSL_BIO_read error"); XFREE(mem, NULL, DYNAMIC_TYPE_OPENSSL); return NULL; @@ -8965,13 +8993,13 @@ static WOLFSSL_X509* d2i_X509orX509REQ_bio(WOLFSSL_BIO* bio, if (req) { #ifdef WOLFSSL_CERT_REQ - localX509 = wolfSSL_X509_REQ_d2i(NULL, mem, size); + localX509 = wolfSSL_X509_REQ_d2i(NULL, mem, derSz); #else WOLFSSL_MSG("CSR not compiled in"); #endif } else { - localX509 = wolfSSL_X509_d2i_ex(NULL, mem, size, bio->heap); + localX509 = wolfSSL_X509_d2i_ex(NULL, mem, derSz, bio->heap); } if (localX509 == NULL) { WOLFSSL_MSG("wolfSSL_X509_d2i error"); diff --git a/tests/api.c b/tests/api.c index af13b0fbc1b..1a925057d54 100644 --- a/tests/api.c +++ b/tests/api.c @@ -25111,6 +25111,8 @@ static int test_wolfSSL_OCSP_single_get0_status(void) certStatus.thisDateParsed.data[i] = i; certStatus.nextDateParsed.data[i] = i; } + certStatus.thisDateParsed.length = CTC_DATE_SIZE; + certStatus.nextDateParsed.length = CTC_DATE_SIZE; certStatus.status = CERT_GOOD; single.status = &certStatus; @@ -25120,6 +25122,16 @@ static int test_wolfSSL_OCSP_single_get0_status(void) ExpectPtrEq(thisDate, &certStatus.thisDateParsed); ExpectPtrEq(nextDate, &certStatus.nextDateParsed); + /* nextUpdate is optional. Absent gives NULL like OpenSSL. */ + certStatus.nextDateParsed.length = 0; + thisDate = NULL; + nextDate = &certStatus.nextDateParsed; + ret = wolfSSL_OCSP_single_get0_status(&single, NULL, NULL, &thisDate, + &nextDate); + ExpectIntEQ(ret, CERT_GOOD); + ExpectPtrEq(thisDate, &certStatus.thisDateParsed); + ExpectNull(nextDate); + ExpectIntEQ(wolfSSL_OCSP_single_get0_status(NULL, NULL, NULL, NULL, NULL), -1); ExpectIntEQ(wolfSSL_OCSP_single_get0_status(&single, NULL, NULL, NULL, @@ -42714,6 +42726,7 @@ TEST_CASE testCases[] = { TEST_DECL_GROUP("ocsp", test_ocsp_certid_enc_dec), TEST_DECL_GROUP("ocsp", test_ocsp_certid_dup), TEST_DECL_GROUP("ocsp", test_ocsp_resp_find_status_serial_prefix), + TEST_DECL_GROUP("ocsp", test_ocsp_resp_times), TEST_DECL(test_ocsp_tls_cert_cb), TEST_DECL_GROUP("ocsp", test_ocsp_status_request_v2_multi_revoked_single), TEST_DECL_GROUP("ocsp", test_ocsp_cert_unknown_crl_fallback), diff --git a/tests/api/test_ocsp.c b/tests/api/test_ocsp.c index 5eccaa7d88f..432c1e61d61 100644 --- a/tests/api/test_ocsp.c +++ b/tests/api/test_ocsp.c @@ -976,6 +976,8 @@ int test_ocsp_resp_find_status_serial_prefix(void) OcspEntry requestedId; CertStatus requestedStatus; int status; + WOLFSSL_ASN1_TIME* thisupd = NULL; + WOLFSSL_ASN1_TIME* nextupd = NULL; XMEMSET(&response, 0, sizeof(response)); XMEMSET(&single, 0, sizeof(single)); @@ -1008,6 +1010,21 @@ int test_ocsp_resp_find_status_serial_prefix(void) NULL, NULL, NULL, NULL), WOLFSSL_SUCCESS); ExpectIntEQ(status, CERT_GOOD); + /* No update times parsed: both must come back NULL. */ + ExpectIntEQ(wolfSSL_OCSP_resp_find_status(&response, &requestedId, &status, + NULL, NULL, &thisupd, &nextupd), WOLFSSL_SUCCESS); + ExpectNull(thisupd); + ExpectNull(nextupd); +#ifdef WOLFSSL_OCSP_PARSE_STATUS + /* thisUpdate present, nextUpdate absent. */ + responseStatus.thisDateParsed.length = 15; + nextupd = &responseStatus.nextDateParsed; + ExpectIntEQ(wolfSSL_OCSP_resp_find_status(&response, &requestedId, &status, + NULL, NULL, &thisupd, &nextupd), WOLFSSL_SUCCESS); + ExpectPtrEq(thisupd, &responseStatus.thisDateParsed); + ExpectNull(nextupd); +#endif + /* Request serial 01:02:03 (3 bytes) shares the 01:02 prefix of the * response serial. * The lookup must not bind the good response to this longer and @@ -1023,6 +1040,43 @@ int test_ocsp_resp_find_status_serial_prefix(void) return EXPECT_RESULT(); } +/* The test responses carry thisUpdate only. */ +int test_ocsp_resp_times(void) +{ + EXPECT_DECLS; +#if defined(HAVE_OCSP) && defined(OPENSSL_EXTRA) && !defined(NO_SHA) && \ + !defined(NO_RSA) + const unsigned char* ptr = (const unsigned char*)resp; + OcspResponse* response = NULL; + WOLFSSL_OCSP_BASICRESP* bs = NULL; + WOLFSSL_OCSP_SINGLERESP* single = NULL; + WOLFSSL_ASN1_TIME* thisupd = NULL; + WOLFSSL_ASN1_TIME* nextupd = NULL; + int reason = -1; + + ExpectNotNull(response = wolfSSL_d2i_OCSP_RESPONSE(NULL, &ptr, + sizeof(resp))); + ExpectNotNull(bs = wolfSSL_OCSP_response_get1_basic(response)); + ExpectNotNull(single = wolfSSL_OCSP_resp_get0(bs, 0)); + ExpectIntEQ(wolfSSL_OCSP_single_get0_status(single, &reason, NULL, + &thisupd, &nextupd), CERT_GOOD); +#ifdef WOLFSSL_OCSP_PARSE_STATUS + ExpectNotNull(thisupd); + if (thisupd != NULL) { + ExpectIntEQ(thisupd->type, ASN_GENERALIZED_TIME); + ExpectIntGT(thisupd->length, 0); + } +#else + ExpectNull(thisupd); +#endif + ExpectNull(nextupd); + + wolfSSL_OCSP_BASICRESP_free(bs); + wolfSSL_OCSP_RESPONSE_free(response); +#endif + return EXPECT_RESULT(); +} + #if defined(HAVE_OCSP) && defined(WOLFSSL_CERT_SETUP_CB) && \ defined(HAVE_SSL_MEMIO_TESTS_DEPENDENCIES) && !defined(NO_RSA) && \ (defined(HAVE_CERTIFICATE_STATUS_REQUEST) || \ diff --git a/tests/api/test_ocsp.h b/tests/api/test_ocsp.h index a9e85ba908f..d832f2c1811 100644 --- a/tests/api/test_ocsp.h +++ b/tests/api/test_ocsp.h @@ -25,6 +25,7 @@ int test_ocsp_certid_enc_dec(void); int test_ocsp_certid_dup(void); int test_ocsp_resp_find_status_serial_prefix(void); +int test_ocsp_resp_times(void); int test_ocsp_status_callback(void); int test_ocsp_basic_verify(void); int test_ocsp_responder_keyhash_binding(void); diff --git a/tests/api/test_ossl_x509.c b/tests/api/test_ossl_x509.c index dabbe81a29c..cffde5e84ff 100644 --- a/tests/api/test_ossl_x509.c +++ b/tests/api/test_ossl_x509.c @@ -640,6 +640,50 @@ int test_wolfSSL_X509(void) X509_free(x509); BIO_free(bio); bio = NULL; + + /* d2i_X509_bio reads one certificate per call and leaves the rest. */ + { + byte chain[4096]; + int svrSz = 0; + int caSz = 0; + X509* x509b = NULL; + + ExpectTrue((fp = XFOPEN("certs/server-cert.der", "rb")) != XBADFILE); + if (fp != XBADFILE) { + svrSz = (int)XFREAD(chain, 1, sizeof(chain), fp); + XFCLOSE(fp); + fp = XBADFILE; + } + ExpectIntGT(svrSz, 0); + ExpectTrue((fp = XFOPEN(der, "rb")) != XBADFILE); + if (fp != XBADFILE && svrSz > 0) { + caSz = (int)XFREAD(chain + svrSz, 1, sizeof(chain) - svrSz, fp); + } + if (fp != XBADFILE) { + XFCLOSE(fp); + fp = XBADFILE; + } + ExpectIntGT(caSz, 0); + + ExpectNotNull(bio = BIO_new_mem_buf(chain, svrSz + caSz)); + ExpectNotNull(x509 = d2i_X509_bio(bio, NULL)); + ExpectIntEQ(BIO_pending(bio), caSz); + ExpectNotNull(x509b = d2i_X509_bio(bio, NULL)); + ExpectIntEQ(BIO_pending(bio), 0); + ExpectNull(d2i_X509_bio(bio, NULL)); + ExpectIntNE(X509_cmp(x509, x509b), 0); + X509_free(x509); + x509 = NULL; + X509_free(x509b); + BIO_free(bio); + bio = NULL; + + /* Truncated DER is rejected. */ + ExpectNotNull(bio = BIO_new_mem_buf(chain, svrSz - 1)); + ExpectNull(d2i_X509_bio(bio, NULL)); + BIO_free(bio); + bio = NULL; + } #endif /* X509_up_ref test */ diff --git a/tests/api/test_session.c b/tests/api/test_session.c index 5ad90c8eb88..5abfc2ecadd 100644 --- a/tests/api/test_session.c +++ b/tests/api/test_session.c @@ -490,6 +490,16 @@ static int twcase_cache_intOn_extOn_noTicket(WOLFSSL_CTX* ctx) wolfSSL_CTX_set_verify(ctx, WOLFSSL_VERIFY_PEER, NULL); return TEST_SUCCESS; } +#ifdef WOLFSSL_EARLY_DATA +/* Early data on: ticket resumption consults the external cache. */ +static int twcase_cache_intOn_extOn_earlyData(WOLFSSL_CTX* ctx) +{ + EXPECT_DECLS; + ExpectIntEQ(twcase_cache_intOn_extOn(ctx), TEST_SUCCESS); + ExpectIntGE(wolfSSL_CTX_set_max_early_data(ctx, MAX_EARLY_DATA_SZ), 0); + return EXPECT_RESULT(); +} +#endif static int twcase_server_sess_ctx_pre_shutdown(WOLFSSL* ssl) { EXPECT_DECLS; @@ -575,6 +585,20 @@ static int twcase_client_sess_ctx_pre_shutdown(WOLFSSL* ssl) #endif return EXPECT_RESULT(); } +/* Client asks for a ticket so TLS 1.2 also resumes with one. */ +static int twcase_client_use_ticket(WOLFSSL_CTX* ctx) +{ + EXPECT_DECLS; + ExpectIntEQ(twcase_cache_intOff_extOff(ctx), TEST_SUCCESS); + ExpectIntEQ(wolfSSL_CTX_UseSessionTicket(ctx), WOLFSSL_SUCCESS); + return EXPECT_RESULT(); +} +static int twcase_resume_on_result(WOLFSSL* ssl) +{ + EXPECT_DECLS; + ExpectIntEQ(wolfSSL_session_reused(ssl), 1); + return EXPECT_RESULT(); +} static int twcase_client_set_sess_ssl_ready(WOLFSSL* ssl) { EXPECT_DECLS; @@ -602,6 +626,14 @@ static WC_MAYBE_UNUSED int test_wolfSSL_CTX_add_session_ext( EXPECT_DECLS; /* Test the default 33 sessions */ int j; + int tls13 = XSTRSTR(param->tls_version, "TLSv1_3") != NULL; + int dtls = XSTRSTR(param->tls_version, "DTLS") != NULL; +#ifdef WOLFSSL_EARLY_DATA + /* Case 6 enables early data, which is TLS 1.3 only. */ + const int cases = tls13 ? 7 : 6; +#else + const int cases = 6; +#endif /* Clear cache before starting */ wolfSSL_CTX_flush_sessions(NULL, -1); @@ -612,15 +644,13 @@ static WC_MAYBE_UNUSED int test_wolfSSL_CTX_add_session_ext( server_sessionCache.capacity = SESSION_CACHE_SIZE; fprintf(stderr, "\tBegin %s\n", param->tls_version); - for (j = 0; j < 5; j++) { - int tls13 = XSTRSTR(param->tls_version, "TLSv1_3") != NULL; - int dtls = XSTRSTR(param->tls_version, "DTLS") != NULL; + for (j = 0; j < cases; j++) { test_ssl_cbf client_cb; test_ssl_cbf server_cb; (void)dtls; - /* Test five cache configurations */ + /* Test the cache configurations */ twcase_client_first_session_ptr = NULL; twcase_server_first_session_ptr = NULL; twcase_server_current_ctx_ptr = NULL; @@ -657,8 +687,20 @@ static WC_MAYBE_UNUSED int test_wolfSSL_CTX_add_session_ext( case 4: server_cb.ctx_ready = twcase_cache_intOff_extOff; break; + case 5: + /* ticket resumption for all versions */ + server_cb.ctx_ready = twcase_cache_intOn_extOn; + break; +#ifdef WOLFSSL_EARLY_DATA + case 6: + server_cb.ctx_ready = twcase_cache_intOn_extOn_earlyData; + break; +#endif } - client_cb.ctx_ready = twcase_cache_intOff_extOff; + if (j == 5) + client_cb.ctx_ready = twcase_client_use_ticket; + else + client_cb.ctx_ready = twcase_cache_intOff_extOff; /* Add session to internal cache and save SSL session for testing */ server_cb.on_result = twcase_server_sess_ctx_pre_shutdown; @@ -684,6 +726,8 @@ static WC_MAYBE_UNUSED int test_wolfSSL_CTX_add_session_ext( case 0: case 1: case 2: + case 5: + case 6: /* cache cannot be searched with out a connection */ /* Add a new session */ ExpectIntEQ(twcase_new_session_called, 1); @@ -705,8 +749,8 @@ static WC_MAYBE_UNUSED int test_wolfSSL_CTX_add_session_ext( twcase_new_session_called = 0; twcase_remove_session_called = 0; twcase_get_session_called = 0; - server_cb.on_result = 0; - client_cb.on_result = 0; + server_cb.on_result = twcase_resume_on_result; + client_cb.on_result = twcase_resume_on_result; server_cb.ticNoInit = 1; /* Use default builtin */ server_cb.ctx = twcase_server_current_ctx_ptr; @@ -754,8 +798,9 @@ static WC_MAYBE_UNUSED int test_wolfSSL_CTX_add_session_ext( case 1: if (tls13) { /* (D)TLSv1.3 case */ - /* cache hit */ - ExpectIntEQ(twcase_get_session_called, 1); + /* Ticket resumption. The external cache is not + * consulted, as in OpenSSL. */ + ExpectIntEQ(twcase_get_session_called, 0); /* (D)TLSv1.3 creates a new ticket, * updates both internal and external cache */ ExpectIntEQ(twcase_new_session_called, 1); @@ -764,7 +809,7 @@ static WC_MAYBE_UNUSED int test_wolfSSL_CTX_add_session_ext( ExpectIntEQ(twcase_remove_session_called, 1); } else { - /* non (D)TLSv1.3 case */ + /* non (D)TLSv1.3 case, session ID resumption */ /* cache hit */ /* DTLS accesses cache once for stateless parsing and * once for stateful parsing */ @@ -782,8 +827,9 @@ static WC_MAYBE_UNUSED int test_wolfSSL_CTX_add_session_ext( case 2: if (tls13) { /* (D)TLSv1.3 case */ - /* cache hit */ - ExpectIntEQ(twcase_get_session_called, 1); + /* Ticket resumption. The external cache is not + * consulted, as in OpenSSL. */ + ExpectIntEQ(twcase_get_session_called, 0); /* (D)TLSv1.3 creates a new ticket, * updates both internal and external cache */ ExpectIntEQ(twcase_new_session_called, 1); @@ -792,7 +838,7 @@ static WC_MAYBE_UNUSED int test_wolfSSL_CTX_add_session_ext( ExpectIntEQ(twcase_remove_session_called, 1); } else { - /* non (D)TLSv1.3 case */ + /* non (D)TLSv1.3 case, session ID resumption */ /* cache hit */ /* DTLS accesses cache once for stateless parsing and * once for stateful parsing */ @@ -814,6 +860,29 @@ static WC_MAYBE_UNUSED int test_wolfSSL_CTX_add_session_ext( ExpectIntEQ(twcase_new_session_called, 0); ExpectIntEQ(twcase_remove_session_called, 0); break; + case 5: + /* Ticket resumption. The external cache is not consulted, + * as in OpenSSL. */ + ExpectIntEQ(twcase_get_session_called, 0); + if (tls13) { + ExpectIntEQ(twcase_new_session_called, 1); + ExpectIntEQ(twcase_remove_session_called, 1); + } + else { + /* The ticket is reused, no new session */ + ExpectIntEQ(twcase_new_session_called, 0); + ExpectIntEQ(twcase_remove_session_called, 1); + } + break; +#ifdef WOLFSSL_EARLY_DATA + case 6: + /* Ticket resumption with early data on. The external cache + * takes part in anti-replay, so it is consulted. */ + ExpectIntEQ(twcase_get_session_called, 1); + ExpectIntEQ(twcase_new_session_called, 1); + ExpectIntEQ(twcase_remove_session_called, 1); + break; +#endif } wolfSSL_SESSION_free(twcase_client_first_session_ptr); wolfSSL_SESSION_free(twcase_server_first_session_ptr); diff --git a/wolfssl/internal.h b/wolfssl/internal.h index 12f2f6f498b..5fe633b16d0 100644 --- a/wolfssl/internal.h +++ b/wolfssl/internal.h @@ -5193,6 +5193,8 @@ WOLFSSL_LOCAL int TlsSessionCacheGetAndWrLock(const byte *id, WOLFSSL_SESSION **sess, word32 *lockedRow, byte side); WOLFSSL_LOCAL void EvictSessionFromCache(WOLFSSL_SESSION* session); WOLFSSL_TEST_VIS int wolfSSL_GetSessionFromCache(WOLFSSL* ssl, WOLFSSL_SESSION* output); +WOLFSSL_LOCAL int wolfSSL_GetSessionFromInternalCache(WOLFSSL* ssl, + WOLFSSL_SESSION* output); WOLFSSL_LOCAL int wolfSSL_SetSession(WOLFSSL* ssl, WOLFSSL_SESSION* session); WOLFSSL_LOCAL void wolfSSL_FreeSession(WOLFSSL_CTX* ctx, WOLFSSL_SESSION* session);