diff --git a/.github/workflows/arduino.yml b/.github/workflows/arduino.yml index 4ed695b2fc..02291d8b51 100644 --- a/.github/workflows/arduino.yml +++ b/.github/workflows/arduino.yml @@ -94,11 +94,9 @@ jobs: - arduino:avr:nano - arduino:avr:uno - arduino:avr:yun - - arduino:samd:mkrwifi1010 - arduino:samd:mkr1000 - arduino:samd:mkrfox1200 - arduino:mbed_edge:edge_control - - arduino:mbed_nano:nanorp2040connect - arduino:mbed_portenta:envie_m7 - arduino:mbed_portenta:portenta_x8 - arduino:renesas_uno:unor4wifi diff --git a/.github/workflows/bind.yml b/.github/workflows/bind.yml index a427ead431..e4d3635b6e 100644 --- a/.github/workflows/bind.yml +++ b/.github/workflows/bind.yml @@ -44,7 +44,7 @@ jobs: fail-fast: false matrix: # List of releases to test - ref: [ 9.18.0, 9.18.28, 9.18.33 ] + ref: [ 9.18.0, 9.18.28, 9.18.33, 9.20.11 ] name: ${{ matrix.ref }} if: github.repository_owner == 'wolfssl' runs-on: ubuntu-24.04 @@ -66,7 +66,7 @@ jobs: export DEBIAN_FRONTEND=noninteractive sudo apt-get update # hostap dependencies - sudo apt-get install -y libuv1-dev libnghttp2-dev libcap-dev libcmocka-dev + sudo apt-get install -y libuv1-dev libnghttp2-dev libcap-dev libcmocka-dev liburcu-dev - name: Checkout OSP uses: actions/checkout@v4 diff --git a/.github/workflows/msmtp.yml b/.github/workflows/msmtp.yml new file mode 100644 index 0000000000..2b1fa7885c --- /dev/null +++ b/.github/workflows/msmtp.yml @@ -0,0 +1,108 @@ +name: msmtp Tests + +# START OF COMMON SECTION +on: + push: + branches: [ 'master', 'main', 'release/**' ] + pull_request: + branches: [ '*' ] + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true +# END OF COMMON SECTION + +jobs: + build_wolfssl: + name: Build wolfSSL + # Just to keep it the same as the testing target + if: github.repository_owner == 'wolfssl' + runs-on: ubuntu-24.04 + # This should be a safe limit for the tests to run. + timeout-minutes: 4 + steps: + - name: Build wolfSSL + uses: wolfSSL/actions-build-autotools-project@v1 + with: + path: wolfssl + configure: --enable-opensslextra --enable-opensslall + install: true + + - name: tar build-dir + run: tar -zcf build-dir.tgz build-dir + + - name: Upload built lib + uses: actions/upload-artifact@v4 + with: + name: wolf-install-msmtp + path: build-dir.tgz + retention-days: 5 + + msmtp_check: + strategy: + fail-fast: false + matrix: + ref: [ 1.8.28 ] + name: ${{ matrix.ref }} + if: github.repository_owner == 'wolfssl' + runs-on: ubuntu-24.04 + # This should be a safe limit for the tests to run. + timeout-minutes: 10 + needs: build_wolfssl + steps: + - name: Download lib + uses: actions/download-artifact@v4 + with: + name: wolf-install-msmtp + + - name: untar build-dir + run: tar -xf build-dir.tgz + + - name: Checkout OSP + uses: actions/checkout@v4 + with: + repository: wolfssl/osp + path: osp + + - name: Install dependencies + run: | + sudo apt-get update + sudo apt-get install -y \ + autoconf automake libtool pkg-config gettext \ + libidn2-dev libsecret-1-dev autopoint + + - name: Checkout msmtp + uses: actions/checkout@v4 + with: + repository: marlam/msmtp + ref: msmtp-${{ matrix.ref }} + path: msmtp-${{ matrix.ref }} + + - name: Apply wolfSSL patch + working-directory: msmtp-${{ matrix.ref }} + run: patch -p1 < $GITHUB_WORKSPACE/osp/msmtp/${{ matrix.ref }}/wolfssl-msmtp-${{ matrix.ref }}.patch + + - name: Regenerate build system + working-directory: msmtp-${{ matrix.ref }} + run: autoreconf -ivf + + - name: Configure msmtp with wolfSSL + working-directory: msmtp-${{ matrix.ref }} + run: | + PKG_CONFIG_PATH=$GITHUB_WORKSPACE/build-dir/lib/pkgconfig \ + ./configure --with-tls=wolfssl + + - name: Build msmtp + working-directory: msmtp-${{ matrix.ref }} + run: make -j$(nproc) + + - name: Run msmtp tests + working-directory: msmtp-${{ matrix.ref }} + run: LD_LIBRARY_PATH=$GITHUB_WORKSPACE/build-dir/lib make check + + - name: Confirm msmtp built with wolfSSL + run: ldd msmtp-${{ matrix.ref }}/src/msmtp | grep wolfssl + + - name: Print test logs on failure + if: ${{ failure() }} + run: tail -n +1 msmtp-${{ matrix.ref }}/tests/*.log diff --git a/.github/workflows/nginx.yml b/.github/workflows/nginx.yml index cc67610f2b..d457e111c2 100644 --- a/.github/workflows/nginx.yml +++ b/.github/workflows/nginx.yml @@ -12,6 +12,10 @@ concurrency: cancel-in-progress: true # END OF COMMON SECTION +# clang has better sanitizer support +env: + CC: clang + jobs: build_wolfssl: name: Build wolfSSL @@ -31,7 +35,8 @@ jobs: uses: wolfSSL/actions-build-autotools-project@v1 with: path: wolfssl - configure: --enable-nginx ${{ env.wolf_debug_flags }} + configure: >- + --enable-nginx --enable-curve25519 --enable-ed25519 ${{ env.wolf_debug_flags }} install: true - name: tar build-dir @@ -50,6 +55,41 @@ jobs: matrix: include: # in general we want to pass all tests that match *ssl* + - ref: 1.28.1 + test-ref: 0fccfcef1278263416043e0bbb3e0116b84026e4 + # Following tests pass with sanitizer on + sanitize-ok: >- + h2_ssl_proxy_cache.t h2_ssl.t h2_ssl_variables.t + h2_ssl_verify_client.t mail_imap_ssl.t mail_ssl_session_reuse.t + mail_ssl.t proxy_ssl_certificate_cache.t + proxy_ssl_certificate_empty.t proxy_ssl_certificate.t + proxy_ssl_certificate_vars.t proxy_ssl_name.t ssl_cache_reload.t + ssl_certificate_aux.t ssl_certificate_cache.t + ssl_certificate_chain.t ssl_certificates.t ssl_certificate.t + ssl_client_escaped_cert.t ssl_crl.t ssl_curve.t ssl_ocsp.t + ssl_password_file.t ssl_proxy_upgrade.t ssl_reject_handshake.t + ssl_session_reuse.t ssl_session_ticket_key.t ssl_sni_protocols.t + ssl_sni_reneg.t ssl_sni_sessions.t ssl_sni.t ssl_stapling.t ssl.t + ssl_verify_client.t ssl_verify_client_trusted.t ssl_verify_depth.t + stream_proxy_ssl_certificate_cache.t stream_proxy_ssl_certificate.t + stream_proxy_ssl_certificate_vars.t + stream_proxy_ssl_name_complex.t stream_proxy_ssl_name.t + stream_ssl_alpn.t stream_ssl_certificate_cache.t + stream_ssl_certificate.t stream_ssl_ocsp.t stream_ssl_preread_alpn.t + stream_ssl_preread_protocol.t stream_ssl_preread.t + stream_ssl_reject_handshake.t stream_ssl_session_reuse.t + stream_ssl_sni_protocols.t stream_ssl_stapling.t stream_ssl.t + stream_ssl_variables.t stream_ssl_verify_client.t + stream_upstream_zone_ssl.t upstream_zone_ssl.t + uwsgi_ssl_certificate.t uwsgi_ssl_certificate_vars.t + # Following tests do not pass with sanitizer on (with OpenSSL too) + sanitize-not-ok: >- + grpc_ssl.t h2_proxy_request_buffering_ssl.t h2_proxy_ssl.t + proxy_request_buffering_ssl.t proxy_ssl_conf_command.t + proxy_ssl_keepalive.t proxy_ssl.t proxy_ssl_verify.t ssl_cache.t + stream_proxy_protocol_ssl.t stream_proxy_ssl_conf_command.t + stream_proxy_ssl.t stream_proxy_ssl_verify.t + - ref: 1.25.0 test-ref: 5b2894ea1afd01a26c589ce11f310df118e42592 # Following tests pass with sanitizer on @@ -120,30 +160,19 @@ jobs: - name: untar build-dir run: tar -xf build-dir.tgz - - name: Install dependencies - run: | - sudo cpan -iT Proc::Find + - name: Openssl version + run: openssl version -a - # Locking in the version of SSLeay used with testing - - name: Download and install Net::SSLeay 1.94 manually - run: | - curl -LO https://www.cpan.org/modules/by-module/Net/CHRISN/Net-SSLeay-1.94.tar.gz - tar -xzf Net-SSLeay-1.94.tar.gz - cd Net-SSLeay-1.94 - perl Makefile.PL - make - sudo make install + - name: Setup Perl environment + uses: shogo82148/actions-setup-perl@v1 + with: + perl-version: '5.38.2' # SSL version 2.091 changes '' return to undef causing test case to fail. # Locking in the test version to use as 2.090 - - name: Download and install IO::Socket::SSL 2.090 manually + - name: Install dependencies run: | - curl -LO https://www.cpan.org/modules/by-module/IO/IO-Socket-SSL-2.090.tar.gz - tar -xzf IO-Socket-SSL-2.090.tar.gz - cd IO-Socket-SSL-2.090 - perl Makefile.PL - make - sudo make install + cpanm --notest Proc::Find Net::SSLeay@1.94 IO::Socket::SSL@2.090 - name: Checkout wolfssl-nginx uses: actions/checkout@v4 @@ -211,10 +240,6 @@ jobs: run: | echo "nginx_c_flags=-O0" >> $GITHUB_ENV - - name: workaround high-entropy ASLR - # not needed after either an update to llvm or runner is done - run: sudo sysctl vm.mmap_rnd_bits=28 - - name: Build nginx with sanitizer working-directory: nginx run: | @@ -229,19 +254,16 @@ jobs: working-directory: nginx run: ldd objs/nginx | grep wolfssl - - if: ${{ runner.debug }} - name: Run nginx-tests with sanitizer (debug) + - name: Create LSAN suppression file working-directory: nginx-tests run: | - LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$GITHUB_WORKSPACE/build-dir/lib \ - TMPDIR=$GITHUB_WORKSPACE TEST_NGINX_VERBOSE=y TEST_NGINX_CATLOG=y \ - TEST_NGINX_BINARY=../nginx/objs/nginx prove -v ${{ matrix.sanitize-ok }} + echo "leak:ngx_worker_process_init" > lsan.supp - if: ${{ !runner.debug }} name: Run nginx-tests with sanitizer working-directory: nginx-tests run: | LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$GITHUB_WORKSPACE/build-dir/lib \ + LSAN_OPTIONS=suppressions=$GITHUB_WORKSPACE/nginx-tests/lsan.supp \ TMPDIR=$GITHUB_WORKSPACE TEST_NGINX_BINARY=../nginx/objs/nginx \ prove ${{ matrix.sanitize-ok }} - diff --git a/.github/workflows/no-malloc.yml b/.github/workflows/no-malloc.yml index 1ed247122b..c04744e94b 100644 --- a/.github/workflows/no-malloc.yml +++ b/.github/workflows/no-malloc.yml @@ -19,6 +19,8 @@ jobs: config: [ # Add new configs here '--enable-rsa --enable-keygen --disable-dh CFLAGS="-DWOLFSSL_NO_MALLOC -DRSA_MIN_SIZE=1024 -pedantic -Wdeclaration-after-statement -DTEST_LIBWOLFSSL_SOURCES_INCLUSION_SEQUENCE"', + '--enable-ecc --enable-rsa --enable-keygen --enable-ed25519 --enable-curve25519 --enable-ed448 --enable-curve448 --enable-mlkem CFLAGS="-DWOLFSSL_NO_MALLOC -pedantic -Wdeclaration-after-statement -DTEST_LIBWOLFSSL_SOURCES_INCLUSION_SEQUENCE"', + '--enable-ecc --enable-rsa --enable-keygen --enable-ed25519 --enable-curve25519 --enable-ed448 --enable-curve448 --enable-mlkem --enable-staticmemory CFLAGS="-DWOLFSSL_NO_MALLOC -pedantic -Wdeclaration-after-statement -DTEST_LIBWOLFSSL_SOURCES_INCLUSION_SEQUENCE"', ] name: make check if: github.repository_owner == 'wolfssl' diff --git a/.github/workflows/os-check.yml b/.github/workflows/os-check.yml index 763716b027..724fa8191d 100644 --- a/.github/workflows/os-check.yml +++ b/.github/workflows/os-check.yml @@ -53,6 +53,8 @@ jobs: '--enable-opensslall --enable-opensslextra CPPFLAGS=-DWC_RNG_SEED_CB', '--enable-opensslall --enable-opensslextra CPPFLAGS=''-DWC_RNG_SEED_CB -DWOLFSSL_NO_GETPID'' ', + # PKCS#7 with RSA-PSS (CMS RSASSA-PSS signers) + '--enable-pkcs7 CPPFLAGS=-DWC_RSA_PSS', '--enable-opensslextra CPPFLAGS=''-DWOLFSSL_NO_CA_NAMES'' ', '--enable-opensslextra=x509small', 'CPPFLAGS=''-DWOLFSSL_EXTRA'' ', diff --git a/.github/workflows/rust-wrapper.yml b/.github/workflows/rust-wrapper.yml index 218f7d8320..d83aaa3121 100644 --- a/.github/workflows/rust-wrapper.yml +++ b/.github/workflows/rust-wrapper.yml @@ -38,6 +38,7 @@ jobs: # Add new configs here '', '--enable-all', + '--enable-all --enable-dilithium', '--enable-cryptonly --disable-examples', '--enable-cryptonly --disable-examples --disable-aes --disable-aesgcm', '--enable-cryptonly --disable-examples --disable-aescbc', diff --git a/.github/workflows/socat.yml b/.github/workflows/socat.yml index 1484027ece..89e4fcc788 100644 --- a/.github/workflows/socat.yml +++ b/.github/workflows/socat.yml @@ -23,7 +23,7 @@ jobs: uses: wolfSSL/actions-build-autotools-project@v1 with: path: wolfssl - configure: --enable-maxfragment --enable-opensslall --enable-opensslextra --enable-dtls --enable-oldtls --enable-tlsv10 --enable-ipv6 'CPPFLAGS=-DWOLFSSL_NO_DTLS_SIZE_CHECK -DOPENSSL_COMPATIBLE_DEFAULTS' + configure: --enable-all --enable-oldtls --enable-tlsv10 --enable-ipv6 'CPPFLAGS=-DWOLFSSL_NO_DTLS_SIZE_CHECK -DOPENSSL_COMPATIBLE_DEFAULTS' install: true - name: tar build-dir @@ -43,6 +43,14 @@ jobs: # This should be a safe limit for the tests to run. timeout-minutes: 30 needs: build_wolfssl + strategy: + fail-fast: false + matrix: + include: + - socat_version: "1.8.0.0" + expect_fail: "36,64,146,216,309,310,386,399,402,403,459,460,467,468,475,478,491,492,528" + - socat_version: "1.8.0.3" + expect_fail: "146,386,399,402,459,460,467,468,475,478,491,492,495,528" steps: - name: Install prereqs run: @@ -57,7 +65,7 @@ jobs: run: tar -xf build-dir.tgz - name: Download socat - run: curl -O http://www.dest-unreach.org/socat/download/socat-1.8.0.0.tar.gz && tar xvf socat-1.8.0.0.tar.gz + run: curl -O http://www.dest-unreach.org/socat/download/socat-${{ matrix.socat_version }}.tar.gz && tar xvf socat-${{ matrix.socat_version }}.tar.gz - name: Checkout OSP uses: actions/checkout@v4 @@ -66,16 +74,16 @@ jobs: path: osp - name: Build socat - working-directory: ./socat-1.8.0.0 + working-directory: ./socat-${{ matrix.socat_version }} run: | - patch -p1 < ../osp/socat/1.8.0.0/socat-1.8.0.0.patch + patch -p1 < ../osp/socat/${{ matrix.socat_version }}/socat-${{ matrix.socat_version }}.patch autoreconf -vfi ./configure --with-wolfssl=$GITHUB_WORKSPACE/build-dir --enable-default-ipv=4 make - name: Run socat tests - working-directory: ./socat-1.8.0.0 + working-directory: ./socat-${{ matrix.socat_version }} run: | export LD_LIBRARY_PATH=$GITHUB_WORKSPACE/build-dir/lib:$LD_LIBRARY_PATH export SHELL=/bin/bash - SOCAT=$GITHUB_WORKSPACE/socat-1.8.0.0/socat ./test.sh -t 0.5 --expect-fail 36,64,146,214,216,217,309,310,386,399,402,403,459,460,467,468,475,478,492,528,530 + SOCAT=$GITHUB_WORKSPACE/socat-${{ matrix.socat_version }}/socat ./test.sh -t 0.5 --expect-fail ${{ matrix.expect_fail }} diff --git a/.github/workflows/sssd.yml b/.github/workflows/sssd.yml index b160bf29d7..797a1f4fbf 100644 --- a/.github/workflows/sssd.yml +++ b/.github/workflows/sssd.yml @@ -44,7 +44,7 @@ jobs: fail-fast: false matrix: # List of releases to test - ref: [ 2.9.1 ] + ref: [ 2.9.1, 2.10.2 ] name: ${{ matrix.ref }} if: github.repository_owner == 'wolfssl' runs-on: ubuntu-24.04 @@ -61,7 +61,8 @@ jobs: # Don't prompt for anything export DEBIAN_FRONTEND=noninteractive sudo apt-get update - sudo apt-get install -y build-essential autoconf libldb-dev libldb2 python3-ldb bc + sudo apt-get install -y build-essential autoconf libldb-dev \ + libldb2 python3-ldb bc libcap-dev - name: Setup env run: | diff --git a/.wolfssl_known_macro_extras b/.wolfssl_known_macro_extras index 97decd7acb..50527e10f6 100644 --- a/.wolfssl_known_macro_extras +++ b/.wolfssl_known_macro_extras @@ -220,6 +220,7 @@ ENABLED_BSDKM_REGISTER ENABLE_SECURE_SOCKETS_LOGS ESP32 ESP8266 +ESPIPE ESP_ENABLE_WOLFSSH ESP_IDF_VERSION ESP_IDF_VERSION_MAJOR @@ -367,6 +368,7 @@ NO_ASM NO_ASN_OLD_TYPE_NAMES NO_CAMELLIA_CBC NO_CERT +NO_CERT_IN_TICKET NO_CIPHER_SUITE_ALIASES NO_CLIENT_CACHE NO_CLOCK_SPEEDUP @@ -1094,6 +1096,7 @@ __clang_major__ __cplusplus __ghc__ __ghs__ +__has_attribute __hpux__ __i386 __i386__ diff --git a/configure.ac b/configure.ac index fe4c830c34..c3cbbb5be6 100644 --- a/configure.ac +++ b/configure.ac @@ -2743,7 +2743,8 @@ if test "$ENABLED_LIBWEBSOCKETS" = "yes" || test "$ENABLED_OPENVPN" = "yes" || \ test "$ENABLED_OPENRESTY" = "yes" || test "$ENABLED_RSYSLOG" = "yes" || \ test "$ENABLED_KRB" = "yes" || test "$ENABLED_CHRONY" = "yes" || \ test "$ENABLED_FFMPEG" = "yes" || test "$ENABLED_STRONGSWAN" = "yes" || \ - test "$ENABLED_OPENLDAP" = "yes" || test "x$ENABLED_MOSQUITTO" = "xyes" || test "$ENABLED_HITCH" = "yes" + test "$ENABLED_OPENLDAP" = "yes" || test "x$ENABLED_MOSQUITTO" = "xyes" || \ + test "$ENABLED_HITCH" = "yes" || test "$ENABLED_NGINX" = "yes" then ENABLED_OPENSSLALL="yes" fi diff --git a/doc/dox_comments/header_files/cryptocb.h b/doc/dox_comments/header_files/cryptocb.h index b6a21922f3..e236e1271f 100644 --- a/doc/dox_comments/header_files/cryptocb.h +++ b/doc/dox_comments/header_files/cryptocb.h @@ -52,6 +52,17 @@ } } #endif + #if defined(WC_RSA_PSS) && !defined(NO_RSA) + if (info->pk.type == WC_PK_TYPE_RSA_PSS) { + // RSA-PSS sign/verify + ret = wc_RsaPSS_Sign_ex( + info->pk.rsa.in, info->pk.rsa.inLen, + info->pk.rsa.out, *info->pk.rsa.outLen, + WC_HASH_TYPE_SHA256, WC_MGF1SHA256, + RSA_PSS_SALT_LEN_DEFAULT, + info->pk.rsa.key, info->pk.rsa.rng); + } + #endif #ifdef HAVE_ECC if (info->pk.type == WC_PK_TYPE_ECDSA_SIGN) { // ECDSA diff --git a/doc/dox_comments/header_files/doxygen_pages.h b/doc/dox_comments/header_files/doxygen_pages.h index 58ae75852e..beee6c6241 100644 --- a/doc/dox_comments/header_files/doxygen_pages.h +++ b/doc/dox_comments/header_files/doxygen_pages.h @@ -51,6 +51,7 @@
  • \ref MD5
  • \ref Password
  • \ref PKCS7
  • +
  • \ref PKCS7_RSA_PSS
  • \ref PKCS11
  • \ref Poly1305
  • \ref RIPEMD
  • @@ -97,4 +98,13 @@ \sa wc_CryptoCb_AesSetKey \sa \ref Crypto Callbacks */ +/*! + \page PKCS7_RSA_PSS PKCS#7 RSA-PSS (CMS) + PKCS#7 SignedData supports RSA-PSS signers (CMS RSASSA-PSS). When WC_RSA_PSS + is defined, use wc_PKCS7_InitWithCert with a signer certificate that has + RSA-PSS (id-RSASSA-PSS) and set hashOID and optional rng; encode produces + full RSASSA-PSS-params (hashAlgorithm, mgfAlgorithm, saltLength, + trailerField). Verify accepts NULL, empty, or absent parameters with + RFC defaults. See \ref PKCS7 for the main API. +*/ diff --git a/doc/dox_comments/header_files/pkcs7.h b/doc/dox_comments/header_files/pkcs7.h index 69d927c692..d7f679fafd 100644 --- a/doc/dox_comments/header_files/pkcs7.h +++ b/doc/dox_comments/header_files/pkcs7.h @@ -147,7 +147,7 @@ int wc_PKCS7_EncodeData(wc_PKCS7* pkcs7, byte* output, \brief This function builds the PKCS7 signed data content type, encoding the PKCS7 structure into a buffer containing a parsable PKCS7 - signed data packet. + signed data packet. For RSA-PSS signers (WC_RSA_PSS), see \ref PKCS7_RSA_PSS. \return Success On successfully encoding the PKCS7 data into the buffer, returns the index parsed up to in the PKCS7 structure. This index also diff --git a/examples/configs/README.md b/examples/configs/README.md index bb87653953..0a2f3193fa 100644 --- a/examples/configs/README.md +++ b/examples/configs/README.md @@ -23,7 +23,7 @@ Example wolfSSL configuration file templates for use when autoconf is not availa * `user_settings_openssl_compat.h`: OpenSSL compatibility layer for drop-in replacement. Enables OPENSSL_ALL and related APIs. * `user_settings_baremetal.h`: Bare metal configuration. No filesystem, static memory only, minimal footprint. * `user_settings_rsa_only.h`: RSA-only configuration (no ECC). For legacy systems requiring RSA cipher suites. -* `user_settings_pkcs7.h`: PKCS#7/CMS configuration for signing and encryption. S/MIME, firmware signing. +* `user_settings_pkcs7.h`: PKCS#7/CMS configuration for signing and encryption. S/MIME, firmware signing. For RSA-PSS SignedData (CMS RSASSA-PSS), define `WC_RSA_PSS`; see doxygen \ref PKCS7_RSA_PSS. * `user_settings_ca.h`: Certificate Authority / PKI operations. Certificate generation, signing, CRL, OCSP. * `user_settings_wolfboot_keytools.h`: wolfBoot key generation and signing tool. Supports ECC, RSA, ED25519, ED448, and post-quantum (ML-DSA/Dilithium, LMS, XMSS). * `user_settings_wolfssh.h`: Minimum options for building wolfSSH. See comment at top for ./configure used to generate. diff --git a/examples/configs/user_settings_pkcs7.h b/examples/configs/user_settings_pkcs7.h index 5375e1fb2b..04aa77e074 100644 --- a/examples/configs/user_settings_pkcs7.h +++ b/examples/configs/user_settings_pkcs7.h @@ -115,6 +115,7 @@ extern "C" { #undef NO_RSA #define WOLFSSL_KEY_GEN #define WC_RSA_NO_PADDING + #define WC_RSA_PSS /* RSA-PSS SignedData (id-RSASSA-PSS); see PKCS7_RSA_PSS */ #else #define NO_RSA #endif diff --git a/src/bio.c b/src/bio.c index e8f78e8187..02431dd38a 100644 --- a/src/bio.c +++ b/src/bio.c @@ -1938,6 +1938,8 @@ int wolfSSL_BIO_get_len(WOLFSSL_BIO *bio) len = BAD_FUNC_ARG; if (len == 0) { len = wolfssl_file_len(file, &memSz); + if (len == WC_NO_ERR_TRACE(WOLFSSL_BAD_FILETYPE)) + len = 0; } if (len == 0) { len = (int)memSz; diff --git a/src/crl.c b/src/crl.c index 524adee47b..c04eccb6fc 100644 --- a/src/crl.c +++ b/src/crl.c @@ -98,6 +98,35 @@ int InitCRL(WOLFSSL_CRL* crl, WOLFSSL_CERT_MANAGER* cm) } +#ifdef CRL_STATIC_REVOKED_LIST +/* Compare two RevokedCert entries by (serialSz, serialNumber) for sorting. + * Returns < 0, 0, or > 0 like memcmp. */ +static int CompareRevokedCert(const RevokedCert* a, const RevokedCert* b) +{ + if (a->serialSz != b->serialSz) + return a->serialSz - b->serialSz; + return XMEMCMP(a->serialNumber, b->serialNumber, (size_t)a->serialSz); +} + +/* Sort revoked cert array in-place using insertion sort. The array is bounded + * by CRL_MAX_REVOKED_CERTS so O(n^2) is fine. */ +static void SortCRL_CertList(RevokedCert* certs, int totalCerts) +{ + int i, j; + RevokedCert tmp; + + for (i = 1; i < totalCerts; i++) { + XMEMCPY(&tmp, &certs[i], sizeof(RevokedCert)); + j = i - 1; + while (j >= 0 && CompareRevokedCert(&certs[j], &tmp) > 0) { + XMEMCPY(&certs[j + 1], &certs[j], sizeof(RevokedCert)); + j--; + } + XMEMCPY(&certs[j + 1], &tmp, sizeof(RevokedCert)); + } +} +#endif /* CRL_STATIC_REVOKED_LIST */ + /* Initialize CRL Entry */ static int InitCRL_Entry(CRL_Entry* crle, DecodedCRL* dcrl, const byte* buff, int verified, void* heap) @@ -132,12 +161,15 @@ static int InitCRL_Entry(CRL_Entry* crle, DecodedCRL* dcrl, const byte* buff, #endif #ifdef CRL_STATIC_REVOKED_LIST /* ParseCRL_CertList() has already cached the Revoked certs into - the crle->certs array */ + the crle->certs array. Sort it so binary search in + FindRevokedSerial works correctly. */ + crle->totalCerts = dcrl->totalCerts; + SortCRL_CertList(crle->certs, crle->totalCerts); #else crle->certs = dcrl->certs; /* take ownership */ + crle->totalCerts = dcrl->totalCerts; #endif dcrl->certs = NULL; - crle->totalCerts = dcrl->totalCerts; crle->crlNumberSet = dcrl->crlNumberSet; if (crle->crlNumberSet) { XMEMCPY(crle->crlNumber, dcrl->crlNumber, sizeof(crle->crlNumber)); @@ -313,25 +345,52 @@ static int FindRevokedSerial(RevokedCert* rc, byte* serial, int serialSz, int ret = 0; byte hash[SIGNER_DIGEST_SIZE]; #ifdef CRL_STATIC_REVOKED_LIST - /* do binary search */ - int low, high, mid; + if (serialHash == NULL) { + /* Binary search by (serialSz, serialNumber). The array was sorted in + * InitCRL_Entry by the same comparison key. */ + int low = 0; + int high = totalCerts - 1; - low = 0; - high = totalCerts - 1; + while (low <= high) { + int mid = (low + high) / 2; + int cmp; - while (low <= high) { - mid = (low + high) / 2; + /* Compare by serial size first, then by serial content. Shorter + * serials sort before longer ones. */ + if (rc[mid].serialSz != serialSz) { + cmp = rc[mid].serialSz - serialSz; + } + else { + cmp = XMEMCMP(rc[mid].serialNumber, serial, + (size_t)rc[mid].serialSz); + } - if (XMEMCMP(rc[mid].serialNumber, serial, rc->serialSz) < 0) { - low = mid + 1; + if (cmp < 0) { + low = mid + 1; + } + else if (cmp > 0) { + high = mid - 1; + } + else { + WOLFSSL_MSG("Cert revoked"); + ret = CRL_CERT_REVOKED; + break; + } } - else if (XMEMCMP(rc[mid].serialNumber, serial, rc->serialSz) > 0) { - high = mid - 1; - } - else { - WOLFSSL_MSG("Cert revoked"); - ret = CRL_CERT_REVOKED; - break; + } + else { + /* Hash-based lookup -- linear scan required since the array is sorted + * by serial number, not by hash. */ + int i; + for (i = 0; i < totalCerts; i++) { + ret = CalcHashId(rc[i].serialNumber, (word32)rc[i].serialSz, hash); + if (ret != 0) + break; + if (XMEMCMP(hash, serialHash, SIGNER_DIGEST_SIZE) == 0) { + WOLFSSL_MSG("Cert revoked"); + ret = CRL_CERT_REVOKED; + break; + } } } #else @@ -1369,6 +1428,28 @@ WOLFSSL_X509_CRL* wolfSSL_X509_CRL_dup(const WOLFSSL_X509_CRL* crl) return ret; } +#ifdef OPENSSL_ALL +int wolfSSL_X509_CRL_up_ref(WOLFSSL_X509_CRL* crl) +{ + int ret; + + if (crl == NULL) + return WOLFSSL_FAILURE; + + wolfSSL_RefInc(&crl->ref, &ret); +#ifdef WOLFSSL_REFCNT_ERROR_RETURN + if (ret != 0) { + WOLFSSL_MSG("Failed to lock x509 mutex"); + return WOLFSSL_FAILURE; + } +#else + (void)ret; +#endif + + return WOLFSSL_SUCCESS; +} +#endif + /* returns WOLFSSL_SUCCESS on success. Does not take ownership of newcrl */ int wolfSSL_X509_STORE_add_crl(WOLFSSL_X509_STORE *store, WOLFSSL_X509_CRL *newcrl) { diff --git a/src/dtls.c b/src/dtls.c index 59bcc2046d..17e8b1b4c2 100644 --- a/src/dtls.c +++ b/src/dtls.c @@ -403,8 +403,9 @@ static int TlsTicketIsValid(const WOLFSSL* ssl, WolfSSL_ConstVector exts, if (!IsAtLeastTLSv1_3(it->pv)) *resume = TRUE; } - if (it != NULL) - ForceZero(it, sizeof(InternalTicket)); + /* `it` points into tempTicket on successful decryption so clearing it will + * also satisfy the WOLFSSL_CHECK_MEM_ZERO check. */ + ForceZero(tempTicket, SESSION_TICKET_LEN); return 0; } #endif /* HAVE_SESSION_TICKET */ diff --git a/src/internal.c b/src/internal.c index 9352af5bd9..425e57a503 100644 --- a/src/internal.c +++ b/src/internal.c @@ -1123,8 +1123,8 @@ static int ImportKeyState(WOLFSSL* ssl, const byte* exp, word32 len, byte ver, idx += OPAQUE16_LEN; if (wordCount > WOLFSSL_DTLS_WINDOW_WORDS) { + wordAdj = (wordCount - WOLFSSL_DTLS_WINDOW_WORDS) * sizeof(word32); wordCount = WOLFSSL_DTLS_WINDOW_WORDS; - wordAdj = (WOLFSSL_DTLS_WINDOW_WORDS - wordCount) * sizeof(word32); } XMEMSET(keys->peerSeq[0].window, 0xFF, DTLS_SEQ_SZ); @@ -1139,8 +1139,8 @@ static int ImportKeyState(WOLFSSL* ssl, const byte* exp, word32 len, byte ver, idx += OPAQUE16_LEN; if (wordCount > WOLFSSL_DTLS_WINDOW_WORDS) { + wordAdj = (wordCount - WOLFSSL_DTLS_WINDOW_WORDS) * sizeof(word32); wordCount = WOLFSSL_DTLS_WINDOW_WORDS; - wordAdj = (WOLFSSL_DTLS_WINDOW_WORDS - wordCount) * sizeof(word32); } XMEMSET(keys->peerSeq[0].prevWindow, 0xFF, DTLS_SEQ_SZ); @@ -10756,7 +10756,9 @@ static void AddRecordHeader(byte* output, word32 length, byte type, #if !defined(WOLFSSL_NO_TLS12) || (defined(HAVE_SESSION_TICKET) && \ - !defined(NO_WOLFSSL_SERVER)) + !defined(NO_WOLFSSL_SERVER)) || \ + defined(HAVE_CERTIFICATE_STATUS_REQUEST) || \ + defined(HAVE_CERTIFICATE_STATUS_REQUEST_V2) || !defined(NO_CERTS) /* add handshake header for message */ static void AddHandShakeHeader(byte* output, word32 length, word32 fragOffset, word32 fragLength, @@ -10791,7 +10793,7 @@ static void AddHandShakeHeader(byte* output, word32 length, } /* add both headers for handshake message */ -static void AddHeaders(byte* output, word32 length, byte type, WOLFSSL* ssl) +WC_MAYBE_UNUSED static void AddHeaders(byte* output, word32 length, byte type, WOLFSSL* ssl) { word32 lengthAdj = HANDSHAKE_HEADER_SZ; word32 outputAdj = RECORD_HEADER_SZ; @@ -10806,17 +10808,14 @@ static void AddHeaders(byte* output, word32 length, byte type, WOLFSSL* ssl) AddRecordHeader(output, length + lengthAdj, handshake, ssl, CUR_ORDER); AddHandShakeHeader(output + outputAdj, length, 0, length, type, ssl); } -#endif /* !WOLFSSL_NO_TLS12 || (HAVE_SESSION_TICKET && !NO_WOLFSSL_SERVER) */ +#endif /* !WOLFSSL_NO_TLS12 || (HAVE_SESSION_TICKET && !NO_WOLFSSL_SERVER) || + * HAVE_CERTIFICATE_STATUS_REQUEST || + * HAVE_CERTIFICATE_STATUS_REQUEST_V2 || !NO_CERTS */ -#ifndef WOLFSSL_NO_TLS12 -#if (!defined(NO_CERTS) && (!defined(NO_WOLFSSL_SERVER) || \ - !defined(WOLFSSL_NO_CLIENT_AUTH))) || \ - ((!defined(NO_WOLFSSL_SERVER) || \ - (!defined(NO_WOLFSSL_CLIENT) && !defined(NO_CERTS) && \ - !defined(WOLFSSL_NO_CLIENT_AUTH))) && defined(WOLFSSL_DTLS)) -static void AddFragHeaders(byte* output, word32 fragSz, word32 fragOffset, - word32 length, byte type, WOLFSSL* ssl) +#if !defined(WOLFSSL_NO_TLS12) || (defined(WOLFSSL_DTLS) && defined(WOLFSSL_TLS13)) +WC_MAYBE_UNUSED static void AddFragHeaders(byte* output, word32 fragSz, + word32 fragOffset, word32 length, byte type, WOLFSSL* ssl) { word32 lengthAdj = HANDSHAKE_HEADER_SZ; word32 outputAdj = RECORD_HEADER_SZ; @@ -10832,11 +10831,8 @@ static void AddFragHeaders(byte* output, word32 fragSz, word32 fragOffset, AddRecordHeader(output, fragSz + lengthAdj, handshake, ssl, CUR_ORDER); AddHandShakeHeader(output + outputAdj, length, fragOffset, fragSz, type, ssl); } -#endif +#endif /* !WOLFSSL_NO_TLS12 || (WOLFSSL_DTLS && WOLFSSL_TLS13) */ -#if !defined(NO_WOLFSSL_SERVER) || \ - (!defined(NO_WOLFSSL_CLIENT) && !defined(NO_CERTS) && \ - !defined(WOLFSSL_NO_CLIENT_AUTH)) /** * Send the handshake message. This function handles fragmenting the message * so that it will fit into the desired MTU or the max fragment size. @@ -10850,8 +10846,8 @@ static void AddFragHeaders(byte* output, word32 fragSz, word32 fragOffset, * @param type Type of message being sent * @return 0 on success and negative otherwise */ -static int SendHandshakeMsg(WOLFSSL* ssl, byte* input, word32 inputSz, - enum HandShakeType type, const char* packetName) +WC_MAYBE_UNUSED static int SendHandshakeMsg(WOLFSSL* ssl, byte* input, + word32 inputSz, enum HandShakeType type, const char* packetName) { int maxFrag; int ret = 0; @@ -11012,10 +11008,6 @@ static int SendHandshakeMsg(WOLFSSL* ssl, byte* input, word32 inputSz, ssl->options.buildingMsg = 0; return ret; } -#endif /* !NO_WOLFSSL_SERVER || (!NO_WOLFSSL_CLIENT && !NO_CERTS && - * !WOLFSSL_NO_CLIENT_AUTH) */ - -#endif /* !WOLFSSL_NO_TLS12 */ /* return bytes received, WOLFSSL_FATAL_ERROR on error, @@ -12232,12 +12224,16 @@ static int GetRecordHeader(WOLFSSL* ssl, word32* inOutIdx, /* record layer length check */ #ifdef HAVE_MAX_FRAGMENT - if (*size > (ssl->max_fragment + MAX_COMP_EXTRA + MAX_MSG_EXTRA)) { + if (*size > (ssl->max_fragment + MAX_MSG_EXTRA + + (ssl->options.usingCompression ? MAX_COMP_EXTRA : 0))) { + WOLFSSL_MSG_EX("Record length %d exceeds max fragment size", *size); WOLFSSL_ERROR_VERBOSE(LENGTH_ERROR); return LENGTH_ERROR; } #else - if (*size > (MAX_RECORD_SIZE + MAX_COMP_EXTRA + MAX_MSG_EXTRA)) { + if (*size > (MAX_RECORD_SIZE + MAX_MSG_EXTRA + + (ssl->options.usingCompression ? MAX_COMP_EXTRA : 0))) { + WOLFSSL_MSG_EX("Record length %d exceeds max record size", *size); WOLFSSL_ERROR_VERBOSE(LENGTH_ERROR); return LENGTH_ERROR; } @@ -13393,9 +13389,7 @@ int CheckIPAddr(DecodedCert* dCert, const char* ipasc) } -#if defined(SESSION_CERTS) && (!defined(NO_WOLFSSL_CLIENT) || \ - !defined(WOLFSSL_NO_CLIENT_AUTH)) -static void AddSessionCertToChain(WOLFSSL_X509_CHAIN* chain, +WC_MAYBE_UNUSED static void AddSessionCertToChain(WOLFSSL_X509_CHAIN* chain, byte* certBuf, word32 certSz) { if (chain->count < MAX_CHAIN_DEPTH && @@ -13408,7 +13402,6 @@ static void AddSessionCertToChain(WOLFSSL_X509_CHAIN* chain, WOLFSSL_MSG("Couldn't store chain cert for session"); } } -#endif #if defined(KEEP_PEER_CERT) || defined(SESSION_CERTS) || \ defined(OPENSSL_EXTRA) || defined(OPENSSL_EXTRA_X509_SMALL) || \ @@ -38900,9 +38893,15 @@ static int AddPSKtoPreMasterSecret(WOLFSSL* ssl) int error; word32 itHash = 0; byte zeros[WOLFSSL_TICKET_MAC_SZ]; /* biggest cmp size */ + int internalTicketSz; + byte* mac; +#if defined(OPENSSL_ALL) && defined(KEEP_PEER_CERT) && \ + !defined(NO_CERT_IN_TICKET) + word16 peerCertSz = 0; +#endif + WOLFSSL_ASSERT_GE(sizeof(ssl->session->staticTicket), WOLFSSL_TICKET_ENC_SZ); WOLFSSL_ASSERT_SIZEOF_GE(ssl->session->staticTicket, *et); - WOLFSSL_ASSERT_SIZEOF_GE(et->enc_ticket, *it); if (ssl->session->ticket != ssl->session->staticTicket) { /* Always use the static ticket buffer */ @@ -38918,7 +38917,7 @@ static int AddPSKtoPreMasterSecret(WOLFSSL* ssl) if (ssl->error != WC_NO_ERR_TRACE(WC_PENDING_E)) #endif { - XMEMSET(et, 0, sizeof(*et)); + XMEMSET(ssl->session->ticket, 0, SESSION_TICKET_LEN); } /* build internal */ @@ -38982,6 +38981,55 @@ static int AddPSKtoPreMasterSecret(WOLFSSL* ssl) XMEMCPY(it->sessionCtx, ssl->sessionCtx, ID_LEN); #endif +#if defined(OPENSSL_ALL) && defined(KEEP_PEER_CERT) && \ + !defined(NO_CERT_IN_TICKET) + /* Store peer certificate in ticket for session resumption. + * Try ssl->peerCert first, then ssl->session->chain as fallback. + * Skip for DTLS to keep ticket size small for MTU constraints. */ + if (ssl->options.dtls) { + c16toa(0, it->peerCertLen); + peerCertSz = 0; + } + else { + const byte* certDer = NULL; + word32 certDerSz = 0; + + if (ssl->peerCert.derCert != NULL && + ssl->peerCert.derCert->length > 0) { + /* Use current peer certificate */ + certDer = ssl->peerCert.derCert->buffer; + certDerSz = ssl->peerCert.derCert->length; + } +#ifdef SESSION_CERTS + else if (ssl->session->chain.count > 0) { + /* Use peer certificate from session chain */ + certDer = ssl->session->chain.certs[0].buffer; + certDerSz = ssl->session->chain.certs[0].length; + } +#endif + + if (certDer != NULL && certDerSz > 0 && + certDerSz <= MAX_TICKET_PEER_CERT_SZ +#ifdef HAVE_MAX_FRAGMENT + /* We don't support fragmentation in + * SendTls13NewSessionTicket yet. */ + && (!IsAtLeastTLSv1_3(ssl->version) || + ssl->max_fragment == MAX_RECORD_SIZE) +#endif + ) { + c16toa((word16)certDerSz, it->peerCertLen); + XMEMCPY(it->peerCert, certDer, certDerSz); + peerCertSz = (word16)certDerSz; + } + else { + if (certDerSz > MAX_TICKET_PEER_CERT_SZ) + WOLFSSL_MSG("Peer cert too large for ticket, skipping"); + c16toa(0, it->peerCertLen); + peerCertSz = 0; + } + } +#endif + #ifdef WOLFSSL_TICKET_HAVE_ID { const byte* id = NULL; @@ -38994,6 +39042,14 @@ static int AddPSKtoPreMasterSecret(WOLFSSL* ssl) } #endif + internalTicketSz = (int)WOLFSSL_INTERNAL_TICKET_BASE_SZ; +#if defined(OPENSSL_ALL) && defined(KEEP_PEER_CERT) && \ + !defined(NO_CERT_IN_TICKET) + internalTicketSz += peerCertSz; +#endif + /* MAC is placed after the encrypted data */ + mac = et->enc_ticket + WOLFSSL_TICKET_ENC_SZ; + /* encrypt */ encLen = WOLFSSL_TICKET_ENC_SZ; /* max size user can use */ if (ssl->ctx->ticketEncCb == NULL @@ -39010,10 +39066,10 @@ static int AddPSKtoPreMasterSecret(WOLFSSL* ssl) ret = BAD_TICKET_ENCRYPT; } else { - itHash = HashObject((byte*)it, sizeof(*it), &error); + itHash = HashObject((byte*)it, (word32)internalTicketSz, &error); if (error == 0) { - ret = ssl->ctx->ticketEncCb(ssl, et->key_name, et->iv, et->mac, - 1, et->enc_ticket, WOLFSSL_INTERNAL_TICKET_LEN, &encLen, + ret = ssl->ctx->ticketEncCb(ssl, et->key_name, et->iv, mac, + 1, et->enc_ticket, internalTicketSz, &encLen, SSL_TICKET_CTX(ssl)); } else { @@ -39028,7 +39084,7 @@ static int AddPSKtoPreMasterSecret(WOLFSSL* ssl) #endif goto error; } - if (encLen < (int)WOLFSSL_INTERNAL_TICKET_LEN || + if (encLen < internalTicketSz || encLen > (int)WOLFSSL_TICKET_ENC_SZ) { WOLFSSL_MSG("Bad user ticket encrypt size"); ret = BAD_TICKET_KEY_CB_SZ; @@ -39037,7 +39093,8 @@ static int AddPSKtoPreMasterSecret(WOLFSSL* ssl) /* sanity checks on encrypt callback */ /* internal ticket can't be the same if encrypted */ - if (itHash == HashObject((byte*)it, sizeof(*it), &error) || error != 0) + if (itHash == HashObject((byte*)it, (word32)internalTicketSz, &error) || + error != 0) { WOLFSSL_MSG("User ticket encrypt didn't encrypt or hash failed"); ret = BAD_TICKET_ENCRYPT; @@ -39061,7 +39118,7 @@ static int AddPSKtoPreMasterSecret(WOLFSSL* ssl) } /* mac */ - if (XMEMCMP(et->mac, zeros, WOLFSSL_TICKET_MAC_SZ) == 0) { + if (XMEMCMP(mac, zeros, WOLFSSL_TICKET_MAC_SZ) == 0) { WOLFSSL_MSG("User ticket encrypt didn't set mac"); ret = BAD_TICKET_ENCRYPT; goto error; @@ -39071,7 +39128,7 @@ static int AddPSKtoPreMasterSecret(WOLFSSL* ssl) c16toa((word16)encLen, et->enc_len); if (encLen < (int)WOLFSSL_TICKET_ENC_SZ) { /* move mac up since whole enc buffer not used */ - XMEMMOVE(et->enc_ticket + encLen, et->mac, + XMEMMOVE(et->enc_ticket + encLen, mac, WOLFSSL_TICKET_MAC_SZ); } ssl->session->ticketLen = @@ -39081,11 +39138,12 @@ static int AddPSKtoPreMasterSecret(WOLFSSL* ssl) error: #ifdef WOLFSSL_CHECK_MEM_ZERO /* Ticket has sensitive data in it now. */ - wc_MemZero_Add("Create Ticket internal", it, sizeof(InternalTicket)); + wc_MemZero_Add("Create Ticket internal", it, + WOLFSSL_INTERNAL_TICKET_MAX_SZ); #endif - ForceZero(it, sizeof(*it)); + ForceZero(it, WOLFSSL_INTERNAL_TICKET_MAX_SZ); #ifdef WOLFSSL_CHECK_MEM_ZERO - wc_MemZero_Check(it, sizeof(InternalTicket)); + wc_MemZero_Check(it, WOLFSSL_INTERNAL_TICKET_MAX_SZ); #endif WOLFSSL_ERROR_VERBOSE(ret); return ret; @@ -39136,7 +39194,7 @@ static int AddPSKtoPreMasterSecret(WOLFSSL* ssl) } else { /* Callback uses ssl without const but for DTLS, it really shouldn't - * modify its state. */ + * modify its state. MAC is located after encrypted data. */ ret = ssl->ctx->ticketEncCb((WOLFSSL*)ssl, et->key_name, et->iv, et->enc_ticket + inLen, 0, et->enc_ticket, inLen, &outLen, @@ -39274,6 +39332,48 @@ static int AddPSKtoPreMasterSecret(WOLFSSL* ssl) } #endif /* WOLFSSL_SLT13 */ +#if defined(OPENSSL_ALL) && defined(KEEP_PEER_CERT) && \ + !defined(NO_CERT_IN_TICKET) + static void RestorePeerCertFromTicket(WOLFSSL* ssl, InternalTicket* it) + { + word16 peerCertLen = 0; + ato16(it->peerCertLen, &peerCertLen); + + if (peerCertLen > 0 && peerCertLen <= MAX_TICKET_PEER_CERT_SZ) { +#ifdef SESSION_CERTS + /* Clear existing chain and add the peer certificate */ + ssl->session->chain.count = 0; + AddSessionCertToChain(&ssl->session->chain, + it->peerCert, peerCertLen); +#endif + /* Also decode into ssl->peerCert for direct access */ + { + int ret; + DecodedCert* dCert; + + dCert = (DecodedCert*)XMALLOC(sizeof(DecodedCert), ssl->heap, + DYNAMIC_TYPE_DCERT); + if (dCert != NULL) { + InitDecodedCert(dCert, it->peerCert, peerCertLen, ssl->heap); + ret = ParseCertRelative(dCert, CERT_TYPE, 0, NULL, NULL); + if (ret == 0) { + FreeX509(&ssl->peerCert); + InitX509(&ssl->peerCert, 0, ssl->heap); + ret = CopyDecodedToX509(&ssl->peerCert, dCert); + if (ret != 0) { + /* Failed to copy - clear peerCert */ + FreeX509(&ssl->peerCert); + InitX509(&ssl->peerCert, 0, ssl->heap); + } + } + FreeDecodedCert(dCert); + XFREE(dCert, ssl->heap, DYNAMIC_TYPE_DCERT); + } + } + } + } +#endif /* OPENSSL_ALL && KEEP_PEER_CERT && !NO_CERT_IN_TICKET */ + void DoClientTicketFinalize(WOLFSSL* ssl, InternalTicket* it, const WOLFSSL_SESSION* sess) { @@ -39361,6 +39461,12 @@ static int AddPSKtoPreMasterSecret(WOLFSSL* ssl) ato16(it->namedGroup, &ssl->session->namedGroup); #endif } + +#if defined(OPENSSL_ALL) && defined(KEEP_PEER_CERT) && \ + !defined(NO_CERT_IN_TICKET) + RestorePeerCertFromTicket(ssl, it); +#endif + ssl->version.minor = it->pv.minor; } @@ -39405,6 +39511,23 @@ static int AddPSKtoPreMasterSecret(WOLFSSL* ssl) #ifdef OPENSSL_EXTRA it->sessionCtxSz = sess->sessionCtxSz; XMEMCPY(it->sessionCtx, sess->sessionCtx, sess->sessionCtxSz); +#endif +#if defined(OPENSSL_ALL) && defined(KEEP_PEER_CERT) && \ + defined(SESSION_CERTS) && !defined(NO_CERT_IN_TICKET) + /* Store peer certificate from session chain */ + if (sess->chain.count > 0) { + word32 certLen = sess->chain.certs[0].length; + if (certLen <= MAX_TICKET_PEER_CERT_SZ) { + c16toa((word16)certLen, it->peerCertLen); + XMEMCPY(it->peerCert, sess->chain.certs[0].buffer, certLen); + } + else { + c16toa(0, it->peerCertLen); + } + } + else { + c16toa(0, it->peerCertLen); + } #endif } @@ -39475,9 +39598,10 @@ static int AddPSKtoPreMasterSecret(WOLFSSL* ssl) byte* tmp; WOLFSSL_MSG("Found session matching the session id" " found in the ticket"); - /* Allocate and populate an InternalTicket */ + /* Allocate and populate an InternalTicket. Need max size + * because PopulateInternalTicketFromSession may write peer cert */ #ifdef WOLFSSL_NO_REALLOC - tmp = (byte*)XMALLOC(sizeof(InternalTicket), ssl->heap, + tmp = (byte*)XMALLOC(WOLFSSL_INTERNAL_TICKET_MAX_SZ, ssl->heap, DYNAMIC_TYPE_TLSX); if (tmp != NULL && psk->identity != NULL) { @@ -39486,13 +39610,13 @@ static int AddPSKtoPreMasterSecret(WOLFSSL* ssl) psk->identity = NULL; } #else - tmp = (byte*)XREALLOC(psk->identity, sizeof(InternalTicket), + tmp = (byte*)XREALLOC(psk->identity, WOLFSSL_INTERNAL_TICKET_MAX_SZ, ssl->heap, DYNAMIC_TYPE_TLSX); #endif if (tmp != NULL) { - XMEMSET(tmp, 0, sizeof(InternalTicket)); + XMEMSET(tmp, 0, WOLFSSL_INTERNAL_TICKET_MAX_SZ); psk->identity = tmp; - psk->identityLen = sizeof(InternalTicket); + psk->identityLen = WOLFSSL_INTERNAL_TICKET_MAX_SZ; psk->it = (InternalTicket*)tmp; PopulateInternalTicketFromSession(sess, psk->it); decryptRet = WOLFSSL_TICKET_RET_OK; @@ -39527,8 +39651,8 @@ static int AddPSKtoPreMasterSecret(WOLFSSL* ssl) } #ifdef WOLFSSL_CHECK_MEM_ZERO /* Internal ticket successfully decrypted. */ - wc_MemZero_Add("Do Client Ticket internal", psk->it, - sizeof(InternalTicket)); + wc_MemZero_Add("Do Client Ticket internal", psk->identity, + psk->identityLen); #endif ret = DoClientTicketCheckVersion(ssl, psk->it); @@ -39536,7 +39660,7 @@ static int AddPSKtoPreMasterSecret(WOLFSSL* ssl) psk->decryptRet = PSK_DECRYPT_FAIL; ForceZero(psk->identity, psk->identityLen); #ifdef WOLFSSL_CHECK_MEM_ZERO - wc_MemZero_Check(psk->it, sizeof(InternalTicket)); + wc_MemZero_Check(psk->it, psk->identityLen); #endif WOLFSSL_LEAVE("DoClientTicket_ex", ret); return ret; @@ -39553,7 +39677,13 @@ static int AddPSKtoPreMasterSecret(WOLFSSL* ssl) int ret; InternalTicket* it = NULL; #ifdef WOLFSSL_TLS13 - InternalTicket staticIt; + /* Static buffer for stateful tickets - need max size for peer cert */ + #ifdef WOLFSSL_SMALL_STACK + byte* staticItBuf = NULL; + #else + byte staticItBuf[WOLFSSL_INTERNAL_TICKET_MAX_SZ]; + #endif + InternalTicket* staticIt = NULL; const WOLFSSL_SESSION* sess = NULL; psk_sess_free_cb_ctx freeCtx; @@ -39585,18 +39715,27 @@ static int AddPSKtoPreMasterSecret(WOLFSSL* ssl) * stateless tickets are much longer. */ sess = GetSesionFromCacheOrExt(ssl, input, &freeCtx); if (sess != NULL) { - it = &staticIt; - XMEMSET(it, 0, sizeof(InternalTicket)); + #ifdef WOLFSSL_SMALL_STACK + staticItBuf = (byte*)XMALLOC(WOLFSSL_INTERNAL_TICKET_MAX_SZ, + ssl->heap, DYNAMIC_TYPE_TMP_BUFFER); + if (staticItBuf == NULL) { + decryptRet = WOLFSSL_TICKET_RET_FATAL; + goto cleanup; + } + #endif + staticIt = (InternalTicket*)staticItBuf; + it = staticIt; + XMEMSET(it, 0, WOLFSSL_INTERNAL_TICKET_MAX_SZ); PopulateInternalTicketFromSession(sess, it); decryptRet = WOLFSSL_TICKET_RET_OK; } } else #endif - if (len >= sizeof(*it)) + if (len >= WOLFSSL_INTERNAL_TICKET_LEN + WOLFSSL_TICKET_FIXED_SZ) decryptRet = DoDecryptTicket(ssl, input, len, &it); else - WOLFSSL_MSG("Ticket is smaller than InternalTicket. Rejecting."); + WOLFSSL_MSG("Ticket is smaller than minimum size. Rejecting."); if (decryptRet != WOLFSSL_TICKET_RET_OK && @@ -39605,8 +39744,10 @@ static int AddPSKtoPreMasterSecret(WOLFSSL* ssl) goto cleanup; } #ifdef WOLFSSL_CHECK_MEM_ZERO - /* Internal ticket successfully decrypted. */ - wc_MemZero_Add("Do Client Ticket internal", it, sizeof(InternalTicket)); + /* Internal ticket successfully decrypted. Zero at least the minimum + * internal ticket size (contains master secret). */ + wc_MemZero_Add("Do Client Ticket internal", it, + WOLFSSL_INTERNAL_TICKET_LEN); #endif ret = DoClientTicketCheckVersion(ssl, it); @@ -39619,12 +39760,19 @@ static int AddPSKtoPreMasterSecret(WOLFSSL* ssl) cleanup: if (it != NULL) { - ForceZero(it, sizeof(*it)); + /* Zero the minimum internal ticket size. The it pointer points into + * the input buffer which may be smaller than MAX_SZ. We use the + * minimum length (WOLFSSL_INTERNAL_TICKET_LEN) which is guaranteed + * to fit and contains the sensitive master secret. */ + ForceZero(it, WOLFSSL_INTERNAL_TICKET_LEN); #ifdef WOLFSSL_CHECK_MEM_ZERO - wc_MemZero_Check(it, sizeof(InternalTicket)); + wc_MemZero_Check(it, WOLFSSL_INTERNAL_TICKET_LEN); #endif } #ifdef WOLFSSL_TLS13 + #ifdef WOLFSSL_SMALL_STACK + XFREE(staticItBuf, ssl->heap, DYNAMIC_TYPE_TMP_BUFFER); + #endif if (sess != NULL) FreeSessionFromCacheOrExt(ssl, sess, &freeCtx); #endif @@ -39640,10 +39788,7 @@ cleanup: psk->decryptRet = PSK_DECRYPT_NONE; ForceZero(psk->identity, psk->identityLen); #ifdef WOLFSSL_CHECK_MEM_ZERO - /* We want to check the InternalTicket area since that is what - * we registered in DoClientTicket_ex */ - wc_MemZero_Check((((ExternalTicket*)psk->identity)->enc_ticket), - sizeof(InternalTicket)); + wc_MemZero_Check(psk->identity, psk->identityLen); #endif } } @@ -39659,11 +39804,15 @@ cleanup: int sendSz; word32 length = SESSION_HINT_SZ + LENGTH_SZ; word32 idx = RECORD_HEADER_SZ + HANDSHAKE_HEADER_SZ; + word32 headerSz = 0; WOLFSSL_START(WC_FUNC_TICKET_SEND); WOLFSSL_ENTER("SendTicket"); - if (ssl->options.createTicket) { + if (ssl->options.createTicket && + /* This will be set when SendHandshakeMsg returns WANT_WRITE. Create + * a new ticket only once. */ + !ssl->options.buildingMsg) { ret = SetupTicket(ssl); if (ret != 0) return ret; @@ -39685,20 +39834,13 @@ cleanup: idx += DTLS_RECORD_EXTRA + DTLS_HANDSHAKE_EXTRA; #endif } + headerSz = idx; - if (IsEncryptionOn(ssl, 1) && ssl->options.handShakeDone) - sendSz += cipherExtraData(ssl); - - /* Set this in case CheckAvailableSize returns a WANT_WRITE so that state - * is not advanced yet */ - ssl->options.buildingMsg = 1; - - /* check for available size */ - if ((ret = CheckAvailableSize(ssl, sendSz)) != 0) - return ret; + output = (byte*)XMALLOC(sendSz, ssl->heap, DYNAMIC_TYPE_TMP_BUFFER); + if (output == NULL) + return MEMORY_E; /* get output buffer */ - output = GetOutputBuffer(ssl); AddHeaders(output, length, session_ticket, ssl); /* hint */ @@ -39713,45 +39855,9 @@ cleanup: XMEMCPY(output + idx, ssl->session->ticket, ssl->session->ticketLen); idx += ssl->session->ticketLen; - if (IsEncryptionOn(ssl, 1) && ssl->options.handShakeDone) { - byte* input; - int inputSz = (int)idx; /* build msg adds rec hdr */ - int recordHeaderSz = RECORD_HEADER_SZ; - - if (ssl->options.dtls) - recordHeaderSz += DTLS_RECORD_EXTRA; - inputSz -= recordHeaderSz; - input = (byte*)XMALLOC(inputSz, ssl->heap, DYNAMIC_TYPE_IN_BUFFER); - if (input == NULL) - return MEMORY_E; - - XMEMCPY(input, output + recordHeaderSz, inputSz); - sendSz = BuildMessage(ssl, output, sendSz, input, inputSz, - handshake, 1, 0, 0, CUR_ORDER); - XFREE(input, ssl->heap, DYNAMIC_TYPE_IN_BUFFER); - - if (sendSz < 0) - return sendSz; - } - else { - #ifdef WOLFSSL_DTLS - if (ssl->options.dtls) { - if ((ret = DtlsMsgPoolSave(ssl, output, (word32)sendSz, session_ticket)) != 0) - return ret; - - DtlsSEQIncrement(ssl, CUR_ORDER); - } - #endif - ret = HashOutput(ssl, output, sendSz, 0); - if (ret != 0) - return ret; - } - - ssl->buffers.outputBuffer.length += sendSz; - ssl->options.buildingMsg = 0; - - if (!ssl->options.groupMessages) - ret = SendBuffered(ssl); + ret = SendHandshakeMsg(ssl, output, idx - headerSz, session_ticket, + "Session Ticket"); + XFREE(output, ssl->heap, DYNAMIC_TYPE_TMP_BUFFER); WOLFSSL_LEAVE("SendTicket", ret); WOLFSSL_END(WC_FUNC_TICKET_SEND); @@ -40249,7 +40355,8 @@ static int DefTicketEncCb(WOLFSSL* ssl, byte key_name[WOLFSSL_TICKET_NAME_SZ], WOLFSSL_ENTER("DefTicketEncCb"); - if ((!enc) && (inLen != WOLFSSL_INTERNAL_TICKET_LEN)) { + /* For decryption, check minimum internal ticket size */ + if ((!enc) && (inLen < (int)WOLFSSL_INTERNAL_TICKET_LEN)) { return BUFFER_E; } diff --git a/src/pk_ec.c b/src/pk_ec.c index e0300cd0a8..0c66482db6 100644 --- a/src/pk_ec.c +++ b/src/pk_ec.c @@ -2824,6 +2824,37 @@ int wolfSSL_EC_POINT_copy(WOLFSSL_EC_POINT *dest, const WOLFSSL_EC_POINT *src) return ret; } +/* Duplicates an EC point. + * + * @param [in] src EC point to duplicate. + * @param [in] group EC group for the new point. + * @return New EC point on success. + * @return NULL on failure. + */ +WOLFSSL_EC_POINT *wolfSSL_EC_POINT_dup(const WOLFSSL_EC_POINT *src, + const WOLFSSL_EC_GROUP *group) +{ + WOLFSSL_EC_POINT *dest; + + WOLFSSL_ENTER("wolfSSL_EC_POINT_dup"); + + if ((src == NULL) || (group == NULL)) { + return NULL; + } + + dest = wolfSSL_EC_POINT_new(group); + if (dest == NULL) { + return NULL; + } + + if (wolfSSL_EC_POINT_copy(dest, src) != 1) { + wolfSSL_EC_POINT_free(dest); + return NULL; + } + + return dest; +} + /* Checks whether point is at infinity. * * Return code compliant with OpenSSL. diff --git a/src/ssl.c b/src/ssl.c index 10fd73ca60..8f693bba1b 100644 --- a/src/ssl.c +++ b/src/ssl.c @@ -5726,6 +5726,13 @@ int wolfSSL_export_keying_material(WOLFSSL *ssl, return WOLFSSL_FAILURE; } + /* Sanity check contextLen to prevent integer overflow when cast to word32 + * and to ensure it fits in the 2-byte length encoding (max 65535). */ + if (use_context && contextLen > WOLFSSL_MAX_16BIT) { + WOLFSSL_MSG("contextLen too large"); + return WOLFSSL_FAILURE; + } + /* clientRandom + serverRandom * OR * clientRandom + serverRandom + ctx len encoding + ctx */ @@ -15508,11 +15515,10 @@ WOLFSSL_CTX* wolfSSL_set_SSL_CTX(WOLFSSL* ssl, WOLFSSL_CTX* ctx) #endif #ifndef WOLFSSL_BLIND_PRIVATE_KEY #ifdef WOLFSSL_COPY_KEY + if (ssl->buffers.key != NULL && ssl->buffers.weOwnKey) { + FreeDer(&ssl->buffers.key); + } if (ctx->privateKey != NULL) { - if (ssl->buffers.key != NULL) { - FreeDer(&ssl->buffers.key); - ssl->buffers.key = NULL; - } ret = AllocCopyDer(&ssl->buffers.key, ctx->privateKey->buffer, ctx->privateKey->length, ctx->privateKey->type, ctx->privateKey->heap); @@ -16668,8 +16674,12 @@ int wolfSSL_select_next_proto(unsigned char **out, unsigned char *outLen, for (i = 0; i < inLen; i += lenIn) { lenIn = in[i++]; + if (lenIn == 0 || i + lenIn > inLen) + break; for (j = 0; j < clientLen; j += lenClient) { lenClient = clientNames[j++]; + if (lenClient == 0 || j + lenClient > clientLen) + break; if (lenIn != lenClient) continue; @@ -16682,8 +16692,14 @@ int wolfSSL_select_next_proto(unsigned char **out, unsigned char *outLen, } } - *out = (unsigned char *)clientNames + 1; - *outLen = clientNames[0]; + if (clientLen > 0 && (unsigned int)clientNames[0] + 1 <= clientLen) { + *out = (unsigned char *)clientNames + 1; + *outLen = clientNames[0]; + } + else { + *out = (unsigned char *)clientNames; + *outLen = 0; + } return WOLFSSL_NPN_NO_OVERLAP; } diff --git a/src/ssl_api_cert.c b/src/ssl_api_cert.c index 82a2cbff39..f54edcd8e4 100644 --- a/src/ssl_api_cert.c +++ b/src/ssl_api_cert.c @@ -869,7 +869,8 @@ int wolfSSL_UnloadCertsKeys(WOLFSSL* ssl) if (ssl->buffers.weOwnKey) { WOLFSSL_MSG("Unloading key"); - ForceZero(ssl->buffers.key->buffer, ssl->buffers.key->length); + if (ssl->buffers.key != NULL && ssl->buffers.key->buffer != NULL) + ForceZero(ssl->buffers.key->buffer, ssl->buffers.key->length); FreeDer(&ssl->buffers.key); #ifdef WOLFSSL_BLIND_PRIVATE_KEY FreeDer(&ssl->buffers.keyMask); @@ -880,7 +881,11 @@ int wolfSSL_UnloadCertsKeys(WOLFSSL* ssl) #ifdef WOLFSSL_DUAL_ALG_CERTS if (ssl->buffers.weOwnAltKey) { WOLFSSL_MSG("Unloading alt key"); - ForceZero(ssl->buffers.altKey->buffer, ssl->buffers.altKey->length); + if (ssl->buffers.altKey != NULL && + ssl->buffers.altKey->buffer != NULL) { + ForceZero(ssl->buffers.altKey->buffer, + ssl->buffers.altKey->length); + } FreeDer(&ssl->buffers.altKey); #ifdef WOLFSSL_BLIND_PRIVATE_KEY FreeDer(&ssl->buffers.altKeyMask); @@ -1710,6 +1715,22 @@ WOLFSSL_X509* wolfSSL_get_certificate(WOLFSSL* ssl) else if (ssl->buffers.weOwnCert) { /* Check if we already have a certificate allocated. */ if (ssl->ourCert == NULL) { + /* Check if ctx has ourCert set - if so, use it instead of creating + * a new X509. This maintains pointer compatibility with + * applications (like nginx OCSP stapling) that use the X509 pointer + * from SSL_CTX_use_certificate as a lookup key. */ + if (ssl->ctx != NULL && ssl->ctx->ourCert != NULL) { + /* Compare cert buffers to make sure they are the same */ + if (ssl->buffers.certificate == NULL || + ssl->buffers.certificate->buffer == NULL || + (ssl->buffers.certificate->length == + ssl->ctx->certificate->length && + XMEMCMP(ssl->buffers.certificate->buffer, + ssl->ctx->certificate->buffer, + ssl->buffers.certificate->length) == 0)) { + return ssl->ctx->ourCert; + } + } /* We own certificate so this should never happen. */ if (ssl->buffers.certificate == NULL) { WOLFSSL_MSG("Certificate buffer not set!"); diff --git a/src/ssl_load.c b/src/ssl_load.c index 3fc7b033e2..0a0fb9e467 100644 --- a/src/ssl_load.c +++ b/src/ssl_load.c @@ -4831,12 +4831,21 @@ static int wolfssl_add_to_chain(DerBuffer** chain, int weOwn, const byte* cert, /* Get length of previous chain. */ len = oldChain->length; } - /* Allocate DER buffer bug enough to hold old and new certificates. */ - ret = AllocDer(&newChain, len + CERT_HEADER_SZ + certSz, CERT_TYPE, heap); - if (ret != 0) { - WOLFSSL_MSG("AllocDer error"); + /* Check for integer overflow in size calculation. */ + if ((len > WOLFSSL_MAX_32BIT - CERT_HEADER_SZ) || + (certSz > WOLFSSL_MAX_32BIT - CERT_HEADER_SZ - len)) { + WOLFSSL_MSG("wolfssl_add_to_chain overflow"); res = 0; } + if (res == 1) { + /* Allocate DER buffer big enough to hold old and new certificates. */ + ret = AllocDer(&newChain, len + CERT_HEADER_SZ + certSz, CERT_TYPE, + heap); + if (ret != 0) { + WOLFSSL_MSG("AllocDer error"); + res = 0; + } + } if (res == 1) { if (oldChain != NULL) { diff --git a/src/ssl_misc.c b/src/ssl_misc.c index bf10255fd4..072f2cc133 100644 --- a/src/ssl_misc.c +++ b/src/ssl_misc.c @@ -230,7 +230,15 @@ static int wolfssl_file_len(XFILE fp, long* fileSz) /* Get file offset at end of file. */ curr = (long)XFTELL(fp); if (curr < 0) { - ret = WOLFSSL_BAD_FILE; +#ifdef ESPIPE + if (errno == ESPIPE) { + WOLFSSL_ERROR_MSG("wolfssl_file_len: file is a pipe"); + *fileSz = 0; + ret = WOLFSSL_BAD_FILETYPE; + } + else +#endif + ret = WOLFSSL_BAD_FILE; } } /* Move to end of file. */ diff --git a/src/ssl_sk.c b/src/ssl_sk.c index c78a56aab5..a8cf68d52e 100644 --- a/src/ssl_sk.c +++ b/src/ssl_sk.c @@ -448,8 +448,24 @@ static int wolfssl_sk_dup_data(WOLFSSL_STACK* dst, WOLFSSL_STACK* src) break; } break; + case STACK_TYPE_X509_CRL: +#if defined(OPENSSL_EXTRA) && defined(HAVE_CRL) + if (src->data.crl == NULL) { + break; + } + dst->data.crl = wolfSSL_X509_CRL_dup(src->data.crl); + if (dst->data.crl == NULL) { + WOLFSSL_MSG("wolfSSL_X509_CRL_dup error"); + err = 1; + break; + } +#else + WOLFSSL_MSG("CRL support not enabled"); + err = 1; +#endif + break; case STACK_TYPE_X509_OBJ: - #if defined(OPENSSL_ALL) +#if defined(OPENSSL_ALL) if (src->data.x509_obj == NULL) { break; } @@ -460,8 +476,11 @@ static int wolfssl_sk_dup_data(WOLFSSL_STACK* dst, WOLFSSL_STACK* src) err = 1; break; } +#else + WOLFSSL_MSG("OPENSSL_ALL support not enabled"); + err = 1; +#endif break; - #endif case STACK_TYPE_BIO: case STACK_TYPE_STRING: case STACK_TYPE_ACCESS_DESCRIPTION: @@ -475,7 +494,6 @@ static int wolfssl_sk_dup_data(WOLFSSL_STACK* dst, WOLFSSL_STACK* src) case STACK_TYPE_BY_DIR_entry: case STACK_TYPE_BY_DIR_hash: case STACK_TYPE_DIST_POINT: - case STACK_TYPE_X509_CRL: case STACK_TYPE_GENERAL_SUBTREE: default: WOLFSSL_MSG("Unsupported stack type"); @@ -488,7 +506,8 @@ static int wolfssl_sk_dup_data(WOLFSSL_STACK* dst, WOLFSSL_STACK* src) /* Duplicate the stack of nodes. * - * TODO: OpenSSL does a shallow copy but we have wolfSSL_shallow_sk_dup(). + * OpenSSL does a shallow copy but we map to wolfSSL_shallow_sk_dup() + * when we want a shallow copy. * * Data is copied/duplicated - deep copy. * @@ -683,7 +702,7 @@ void* wolfSSL_sk_value(const WOLFSSL_STACK* sk, int i) #if (!defined(NO_CERTS) && (defined(OPENSSL_EXTRA) || \ defined(WOLFSSL_WPAS_SMALL))) || defined(WOLFSSL_QT) || \ defined(OPENSSL_ALL) -/* Put the data into a node at the top of the stack. +/* Put the data into a node at the end of the list. * * @param [in, out] stack Stack of objects. * @param [in] data Data to store in stack. @@ -698,7 +717,7 @@ int wolfSSL_sk_push(WOLFSSL_STACK* stack, const void *data) return wolfSSL_sk_insert(stack, data, -1); } -/* Put the data into a node at an index in the stack. +/* Put the data into a node at an index in the list. * * @param [in, out] stack Stack of objects. * @param [in] data Data to store in stack. diff --git a/src/tls.c b/src/tls.c index 207c873b46..1cad29ab2c 100644 --- a/src/tls.c +++ b/src/tls.c @@ -2135,10 +2135,10 @@ static void TLSX_SNI_FreeAll(SNI* list, void* heap) } /** Tells the buffered size of the SNI objects in a list. */ -static word16 TLSX_SNI_GetSize(SNI* list) +WOLFSSL_TEST_VIS word16 TLSX_SNI_GetSize(SNI* list) { SNI* sni; - word16 length = OPAQUE16_LEN; /* list length */ + word32 length = OPAQUE16_LEN; /* list length */ while ((sni = list)) { list = sni->next; @@ -2147,12 +2147,16 @@ static word16 TLSX_SNI_GetSize(SNI* list) switch (sni->type) { case WOLFSSL_SNI_HOST_NAME: - length += (word16)XSTRLEN((char*)sni->data.host_name); + length += (word32)XSTRLEN((char*)sni->data.host_name); break; } + + if (length > WOLFSSL_MAX_16BIT) { + return 0; + } } - return length; + return (word16)length; } /** Writes the SNI objects of a list in a buffer. */ @@ -3216,7 +3220,7 @@ static void TLSX_CSR_Free(CertificateStatusRequest* csr, void* heap) word16 TLSX_CSR_GetSize_ex(CertificateStatusRequest* csr, byte isRequest, int idx) { - word16 size = 0; + word32 size = 0; /* shut up compiler warnings */ (void) csr; (void) isRequest; @@ -3237,15 +3241,25 @@ word16 TLSX_CSR_GetSize_ex(CertificateStatusRequest* csr, byte isRequest, if (csr->ssl != NULL && SSL_CM(csr->ssl) != NULL && SSL_CM(csr->ssl)->ocsp_stapling != NULL && SSL_CM(csr->ssl)->ocsp_stapling->statusCb != NULL) { - return OPAQUE8_LEN + OPAQUE24_LEN + csr->ssl->ocspCsrResp[idx].length; + if (WOLFSSL_MAX_16BIT - OPAQUE8_LEN - OPAQUE24_LEN < + csr->ssl->ocspCsrResp[idx].length) { + return 0; + } + size = OPAQUE8_LEN + OPAQUE24_LEN + + csr->ssl->ocspCsrResp[idx].length; + return (word16)size; } - return (word16)(OPAQUE8_LEN + OPAQUE24_LEN + - csr->responses[idx].length); + if (WOLFSSL_MAX_16BIT - OPAQUE8_LEN - OPAQUE24_LEN < + csr->responses[idx].length) { + return 0; + } + size = OPAQUE8_LEN + OPAQUE24_LEN + csr->responses[idx].length; + return (word16)size; } #else (void)idx; #endif - return size; + return (word16)size; } #if (defined(WOLFSSL_TLS13) && !defined(NO_WOLFSSL_SERVER)) @@ -3855,7 +3869,7 @@ static void TLSX_CSR2_FreeAll(CertificateStatusRequestItemV2* csr2, void* heap) static word16 TLSX_CSR2_GetSize(CertificateStatusRequestItemV2* csr2, byte isRequest) { - word16 size = 0; + word32 size = 0; /* shut up compiler warnings */ (void) csr2; (void) isRequest; @@ -3876,11 +3890,15 @@ static word16 TLSX_CSR2_GetSize(CertificateStatusRequestItemV2* csr2, size += OCSP_NONCE_EXT_SZ; break; } + + if (size > WOLFSSL_MAX_16BIT) { + return 0; + } } } #endif - return size; + return (word16)size; } static int TLSX_CSR2_Write(CertificateStatusRequestItemV2* csr2, @@ -13605,6 +13623,9 @@ static int TLSX_ECH_Parse(WOLFSSL* ssl, const byte* readBuf, word16 size, } /* read hello inner len */ ato16(readBuf_p, &ech->innerClientHelloLen); + if (ech->innerClientHelloLen < WC_AES_BLOCK_SIZE) { + return BUFFER_ERROR; + } ech->innerClientHelloLen -= WC_AES_BLOCK_SIZE; readBuf_p += 2; ech->outerClientPayload = readBuf_p; diff --git a/src/tls13.c b/src/tls13.c index 101b31541a..d057bd5158 100644 --- a/src/tls13.c +++ b/src/tls13.c @@ -1023,6 +1023,11 @@ int Tls13_Exporter(WOLFSSL* ssl, unsigned char *out, size_t outLen, if (ret != 0) return ret; + /* Sanity check contextLen to prevent truncation when cast to word32. */ + if (contextLen > WOLFSSL_MAX_32BIT) { + return BAD_FUNC_ARG; + } + /* Hash(context_value) */ ret = wc_Hash(hashType, context, (word32)contextLen, hashOut, WC_MAX_DIGEST_SIZE); if (ret != 0) @@ -7791,7 +7796,6 @@ static int SendTls13CertificateRequest(WOLFSSL* ssl, byte* reqCtx, int sendSz; word32 i; word32 reqSz; - word16 hashSigAlgoSz = 0; SignatureAlgorithms* sa; WOLFSSL_START(WC_FUNC_CERTIFICATE_REQUEST_SEND); @@ -7802,14 +7806,11 @@ static int SendTls13CertificateRequest(WOLFSSL* ssl, byte* reqCtx, if (ssl->options.side != WOLFSSL_SERVER_END) return SIDE_ERROR; - /* Get the length of the hashSigAlgo buffer */ - InitSuitesHashSigAlgo(NULL, SIG_ALL, 1, 1, ssl->buffers.keySz, - &hashSigAlgoSz); - sa = TLSX_SignatureAlgorithms_New(ssl, hashSigAlgoSz, ssl->heap); + /* Use ssl->suites->hashSigAlgo so wolfSSL_set1_sigalgs_list() is honored. + * hashSigAlgoSz=0 makes GetSize/Write fall back to WOLFSSL_SUITES(ssl). */ + sa = TLSX_SignatureAlgorithms_New(ssl, 0, ssl->heap); if (sa == NULL) return MEMORY_ERROR; - InitSuitesHashSigAlgo(sa->hashSigAlgo, SIG_ALL, 1, 1, ssl->buffers.keySz, - &hashSigAlgoSz); ret = TLSX_Push(&ssl->extensions, TLSX_SIGNATURE_ALGORITHMS, sa, ssl->heap); if (ret != 0) { TLSX_SignatureAlgorithms_FreeAll(sa, ssl->heap); diff --git a/src/x509.c b/src/x509.c index 6b42fa2d21..fd46e89c40 100644 --- a/src/x509.c +++ b/src/x509.c @@ -38,6 +38,10 @@ #include #endif +/* 16 times MAX_X509_SIZE should be more than enough to read any X509 + * certificate file */ +#define MAX_BIO_READ_BUFFER (MAX_X509_SIZE * 16) + #if defined(OPENSSL_ALL) || defined(OPENSSL_EXTRA) unsigned int wolfSSL_X509_get_extension_flags(WOLFSSL_X509* x509) { @@ -4672,6 +4676,14 @@ WOLFSSL_STACK* wolfSSL_sk_X509_CRL_new(void) return s; } +WOLFSSL_STACK* wolfSSL_sk_X509_CRL_new_null(void) +{ + WOLFSSL_STACK* s = wolfSSL_sk_new_null(); + if (s != NULL) + s->type = STACK_TYPE_X509_CRL; + return s; +} + void wolfSSL_sk_X509_CRL_pop_free(WOLF_STACK_OF(WOLFSSL_X509_CRL)* sk, void (*f) (WOLFSSL_X509_CRL*)) { @@ -6056,14 +6068,25 @@ static WOLFSSL_X509* loadX509orX509REQFromBuffer( /* ready to be decoded. */ if (der != NULL && der->buffer != NULL) { WC_DECLARE_VAR(cert, DecodedCert, 1, 0); + /* For TRUSTED_CERT_TYPE, the DER buffer contains the certificate + * followed by auxiliary trust info. ParseCertRelative expects CERT_TYPE + * and will parse only the certificate portion, ignoring the rest. */ + int parseType = (type == TRUSTED_CERT_TYPE) ? CERT_TYPE : type; WC_ALLOC_VAR_EX(cert, DecodedCert, 1, NULL, DYNAMIC_TYPE_DCERT, ret=MEMORY_ERROR); if (WC_VAR_OK(cert)) { InitDecodedCert(cert, der->buffer, der->length, NULL); - ret = ParseCertRelative(cert, type, 0, NULL, NULL); + ret = ParseCertRelative(cert, parseType, 0, NULL, NULL); if (ret == 0) { + /* For TRUSTED_CERT_TYPE, truncate the DER buffer to exclude + * auxiliary trust data. ParseCertRelative sets srcIdx to the + * end of the certificate, so we adjust cert->maxIdx accordingly. */ + if (type == TRUSTED_CERT_TYPE && cert->srcIdx < cert->maxIdx) { + cert->maxIdx = cert->srcIdx; + } + x509 = (WOLFSSL_X509*)XMALLOC(sizeof(WOLFSSL_X509), NULL, DYNAMIC_TYPE_X509); if (x509 != NULL) { @@ -12922,22 +12945,23 @@ static WOLFSSL_X509 *loadX509orX509REQFromPemBio(WOLFSSL_BIO *bp, int pemSz; long i = 0, l, footerSz; const char* footer = NULL; + int streaming = 0; /* Flag to indicate if source is streaming (FIFO) */ + const char* altFooter = NULL; + long altFooterSz = 0; WOLFSSL_ENTER("loadX509orX509REQFromPemBio"); - if (bp == NULL || (type != CERT_TYPE && type != CERTREQ_TYPE)) { + if (bp == NULL || (type != CERT_TYPE && type != CERTREQ_TYPE && + type != TRUSTED_CERT_TYPE)) { WOLFSSL_LEAVE("wolfSSL_PEM_read_bio_X509", BAD_FUNC_ARG); return NULL; } if ((l = wolfSSL_BIO_get_len(bp)) <= 0) { - /* No certificate in buffer */ -#if defined (WOLFSSL_HAPROXY) - WOLFSSL_ERROR(PEM_R_NO_START_LINE); -#else - WOLFSSL_ERROR(ASN_NO_PEM_HEADER); -#endif - return NULL; + /* No certificate size available - could be FIFO or other streaming + * source. Use MAX_X509_SIZE as initial buffer, will resize if needed. */ + l = MAX_X509_SIZE; + streaming = 1; } pemSz = (int)l; @@ -12953,27 +12977,79 @@ static WOLFSSL_X509 *loadX509orX509REQFromPemBio(WOLFSSL_BIO *bp, } footerSz = (long)XSTRLEN(footer); + /* For TRUSTED_CERT_TYPE, also prepare to check for regular CERT footer + * as the file might contain regular certificates instead of TRUSTED + * format */ + if (type == TRUSTED_CERT_TYPE) { + wc_PemGetHeaderFooter(CERT_TYPE, NULL, &altFooter); + if (altFooter != NULL) { + altFooterSz = (long)XSTRLEN(altFooter); + } + } + /* TODO: Inefficient * reading in one byte at a time until see the footer */ while ((l = wolfSSL_BIO_read(bp, (char *)&pem[i], 1)) == 1) { + int foundFooter = 0; i++; - if (i > footerSz && XMEMCMP((char *)&pem[i-footerSz], footer, - footerSz) == 0) { - if (wolfSSL_BIO_read(bp, (char *)&pem[i], 1) == 1) { + /* Check if buffer is full and we're reading from streaming source */ + if (i >= pemSz && streaming) { + /* Double the buffer size for streaming sources */ + int newSz = pemSz * 2; + unsigned char* newPem; + + /* Sanity check: don't grow beyond reasonable limit */ + if (newSz > MAX_BIO_READ_BUFFER) { + WOLFSSL_MSG("PEM data too large for streaming source"); + XFREE(pem, 0, DYNAMIC_TYPE_PEM); + return NULL; + } + + newPem = (unsigned char*)XREALLOC(pem, newSz, 0, DYNAMIC_TYPE_PEM); + if (newPem == NULL) { + WOLFSSL_MSG("Failed to resize PEM buffer"); + XFREE(pem, 0, DYNAMIC_TYPE_PEM); + return NULL; + } + + pem = newPem; + pemSz = newSz; + } + else if (i > pemSz) { + /* Buffer full for non-streaming source - this shouldn't happen */ + break; + } + + /* Check for the expected footer OR alternate footer (for + * TRUSTED_CERT_TYPE) */ + if (i > footerSz && + XMEMCMP((char *)&pem[i-footerSz], footer, footerSz) == 0) { + foundFooter = 1; + } + else if (i > altFooterSz && altFooter != NULL && + XMEMCMP((char *)&pem[i-altFooterSz], altFooter, altFooterSz) == 0) { + foundFooter = 1; + } + + if (foundFooter) { + if (i < pemSz && wolfSSL_BIO_read(bp, (char *)&pem[i], 1) == 1) { /* attempt to read newline following footer */ i++; - if (pem[i-1] == '\r') { + if (i < pemSz && pem[i-1] == '\r') { /* found \r , Windows line ending is \r\n so try to read one * more byte for \n, ignoring return value */ - (void)wolfSSL_BIO_read(bp, (char *)&pem[i++], 1); + if (i < pemSz) { + (void)wolfSSL_BIO_read(bp, (char *)&pem[i++], 1); + } } } break; } } - if (l == 0) + if (l == 0 && i == 0) { WOLFSSL_ERROR(ASN_NO_PEM_HEADER); + } if (i > pemSz) { WOLFSSL_MSG("Error parsing PEM"); } @@ -12985,6 +13061,12 @@ static WOLFSSL_X509 *loadX509orX509REQFromPemBio(WOLFSSL_BIO *bp, CERTREQ_TYPE, cb, u); else #endif + /* Use TRUSTED_CERT_TYPE if input was TRUSTED CERTIFICATE format, + * otherwise use CERT_TYPE for regular certificates */ + if (type == TRUSTED_CERT_TYPE) + x509 = loadX509orX509REQFromBuffer(pem, pemSz, WOLFSSL_FILETYPE_PEM, + TRUSTED_CERT_TYPE, cb, u); + else x509 = loadX509orX509REQFromBuffer(pem, pemSz, WOLFSSL_FILETYPE_PEM, CERT_TYPE, cb, u); } @@ -13005,6 +13087,86 @@ static WOLFSSL_X509 *loadX509orX509REQFromPemBio(WOLFSSL_BIO *bp, } +WC_MAYBE_UNUSED +static unsigned char* ReadPemFromBioToBuffer(WOLFSSL_BIO *bp, int *pemSz) +{ + unsigned char* pem = NULL; + + WOLFSSL_ENTER("ReadPemFromBioToBuffer"); + + if (bp == NULL || pemSz == NULL) { + WOLFSSL_LEAVE("ReadPemFromBioToBuffer", BAD_FUNC_ARG); + return NULL; + } + + if ((*pemSz = wolfSSL_BIO_get_len(bp)) <= 0) { + /* No certificate size available - could be FIFO or other streaming + * source. Use MAX_X509_SIZE as initial buffer, read in loop. */ + int totalRead = 0; + int readSz; + + *pemSz = MAX_X509_SIZE; + pem = (unsigned char*)XMALLOC(*pemSz, 0, DYNAMIC_TYPE_PEM); + if (pem == NULL) { + return NULL; + } + + /* Read from streaming source until EOF or buffer full */ + while ((readSz = wolfSSL_BIO_read(bp, pem + totalRead, + *pemSz - totalRead)) > 0) { + totalRead += readSz; + + /* If buffer is full, try to grow it */ + if (totalRead >= *pemSz) { + int newSz = *pemSz * 2; + unsigned char* newPem; + + /* Sanity check */ + if (newSz > MAX_BIO_READ_BUFFER) { + WOLFSSL_MSG("PEM data too large for streaming source"); + XFREE(pem, NULL, DYNAMIC_TYPE_PEM); + return NULL; + } + + newPem = (unsigned char*)XREALLOC(pem, newSz, 0, + DYNAMIC_TYPE_PEM); + if (newPem == NULL) { + XFREE(pem, NULL, DYNAMIC_TYPE_PEM); + return NULL; + } + pem = newPem; + *pemSz = newSz; + } + } + + *pemSz = totalRead; + if (*pemSz <= 0) { + XFREE(pem, NULL, DYNAMIC_TYPE_PEM); + return NULL; + } + } + else { + /* Known size - allocate and read */ + pem = (unsigned char*)XMALLOC(*pemSz, 0, DYNAMIC_TYPE_PEM); + if (pem == NULL) { + return NULL; + } + + XMEMSET(pem, 0, *pemSz); + + *pemSz = wolfSSL_BIO_read(bp, pem, *pemSz); + if (*pemSz <= 0) { + XFREE(pem, NULL, DYNAMIC_TYPE_PEM); + return NULL; + } + } + + WOLFSSL_LEAVE("ReadPemFromBioToBuffer", 0); + return pem; +} + + + #if defined(WOLFSSL_ACERT) WOLFSSL_X509_ACERT *wolfSSL_PEM_read_bio_X509_ACERT(WOLFSSL_BIO *bp, WOLFSSL_X509_ACERT **x, @@ -13019,29 +13181,14 @@ static WOLFSSL_X509 *loadX509orX509REQFromPemBio(WOLFSSL_BIO *bp, WOLFSSL_ENTER("wolfSSL_PEM_read_bio_X509_ACERT"); if (bp == NULL) { - WOLFSSL_LEAVE("wolfSSL_PEM_read_bio_X509_ACERT", BAD_FUNC_ARG); return NULL; } - if ((pemSz = wolfSSL_BIO_get_len(bp)) <= 0) { - /* No certificate in buffer */ - WOLFSSL_ERROR(ASN_NO_PEM_HEADER); - return NULL; - } - - pem = (unsigned char*)XMALLOC(pemSz, 0, DYNAMIC_TYPE_PEM); - + pem = ReadPemFromBioToBuffer(bp, &pemSz); if (pem == NULL) { return NULL; } - XMEMSET(pem, 0, pemSz); - - if (wolfSSL_BIO_read(bp, pem, pemSz) != pemSz) { - XFREE(pem, NULL, DYNAMIC_TYPE_PEM); - return NULL; - } - x509 = wolfSSL_X509_ACERT_load_certificate_buffer(pem, pemSz, WOLFSSL_FILETYPE_PEM); @@ -13079,14 +13226,15 @@ static WOLFSSL_X509 *loadX509orX509REQFromPemBio(WOLFSSL_BIO *bp, WOLFSSL_X509 **x, wc_pem_password_cb *cb, void *u) { - WOLFSSL_ENTER("wolfSSL_PEM_read_bio_X509"); + WOLFSSL_ENTER("wolfSSL_PEM_read_bio_X509_AUX"); /* AUX info is; trusted/rejected uses, friendly name, private key id, * and potentially a stack of "other" info. wolfSSL does not store * friendly name or private key id yet in WOLFSSL_X509 for human * readability and does not support extra trusted/rejected uses for - * root CA. */ - return wolfSSL_PEM_read_bio_X509(bp, x, cb, u); + * root CA. Use TRUSTED_CERT_TYPE to properly parse TRUSTED CERTIFICATE + * format and strip auxiliary data. */ + return loadX509orX509REQFromPemBio(bp, x, cb, u, TRUSTED_CERT_TYPE); } #ifdef WOLFSSL_CERT_REQ @@ -13139,21 +13287,14 @@ WOLFSSL_X509 *wolfSSL_PEM_read_bio_X509_REQ(WOLFSSL_BIO *bp, WOLFSSL_X509 **x, { #if defined(WOLFSSL_PEM_TO_DER) && defined(HAVE_CRL) unsigned char* pem = NULL; - int pemSz; - int derSz; + int pemSz = 0; + int derSz = 0; DerBuffer* der = NULL; WOLFSSL_X509_CRL* crl = NULL; - if ((pemSz = wolfSSL_BIO_get_len(bp)) <= 0) { - goto err; - } + WOLFSSL_ENTER("wolfSSL_PEM_read_bio_X509_CRL"); - pem = (unsigned char*)XMALLOC(pemSz, 0, DYNAMIC_TYPE_PEM); - if (pem == NULL) { - goto err; - } - - if (wolfSSL_BIO_read(bp, pem, pemSz) != pemSz) { + if ((pem = ReadPemFromBioToBuffer(bp, &pemSz)) == NULL) { goto err; } @@ -13166,6 +13307,9 @@ WOLFSSL_X509 *wolfSSL_PEM_read_bio_X509_REQ(WOLFSSL_BIO *bp, WOLFSSL_X509 **x, } err: + if (pemSz == 0) { + WOLFSSL_ERROR(ASN_NO_PEM_HEADER); + } XFREE(pem, 0, DYNAMIC_TYPE_PEM); if (der != NULL) { FreeDer(&der); @@ -15393,48 +15537,39 @@ void wolfSSL_X509_email_free(WOLF_STACK_OF(WOLFSSL_STRING) *sk) wolfSSL_sk_pop_free(sk, NULL); } -static int x509_aia_append_string(WOLFSSL_STACK** head, - const byte* uri, word32 uriSz) +static int x509_aia_append_string(WOLFSSL_STACK* list, const byte* uri, + word32 uriSz) { - WOLFSSL_STACK* node; - char* url; - - url = (char*)XMALLOC(uriSz + 1, NULL, DYNAMIC_TYPE_OPENSSL); + WOLFSSL_STRING url = (WOLFSSL_STRING)XMALLOC(uriSz + 1, NULL, + DYNAMIC_TYPE_OPENSSL); if (url == NULL) - return WOLFSSL_FAILURE; - + return -1; XMEMCPY(url, uri, uriSz); url[uriSz] = '\0'; - node = wolfSSL_sk_new_node(*head != NULL ? (*head)->heap : NULL); - if (node == NULL) { + if (wolfSSL_sk_push(list, url) <= 0) { XFREE(url, NULL, DYNAMIC_TYPE_OPENSSL); - return WOLFSSL_FAILURE; + return -1; } - node->type = STACK_TYPE_STRING; - node->data.string = url; - - if (wolfSSL_sk_push_back_node(head, node) != WOLFSSL_SUCCESS) { - XFREE(url, NULL, DYNAMIC_TYPE_OPENSSL); - wolfSSL_sk_free_node(node); - return WOLFSSL_FAILURE; - } - - return WOLFSSL_SUCCESS; + return 0; } static WOLFSSL_STACK* x509_get1_aia_by_method(WOLFSSL_X509* x, word32 method, const byte* fallback, int fallbackSz) { - WOLFSSL_STACK* head = NULL; + WOLFSSL_STACK* ret = NULL; int i; if (x == NULL) return NULL; - /* Collect matching URIs from the multi-entry list into a new stack; - * fall back to the legacy single-entry field for compatibility. */ + ret = wolfSSL_sk_WOLFSSL_STRING_new(); + if (ret == NULL) + return NULL; + + /* Build from multi-entry list when available; otherwise fall back to the + * legacy single-entry fields to preserve previous behavior. */ if (x->authInfoListSz > 0) { for (i = 0; i < x->authInfoListSz; i++) { if (x->authInfoList[i].method != method || @@ -15443,22 +15578,28 @@ static WOLFSSL_STACK* x509_get1_aia_by_method(WOLFSSL_X509* x, word32 method, continue; } - if (x509_aia_append_string(&head, x->authInfoList[i].uri, - x->authInfoList[i].uriSz) != WOLFSSL_SUCCESS) { - wolfSSL_sk_pop_free(head, NULL); + if (x509_aia_append_string(ret, x->authInfoList[i].uri, + x->authInfoList[i].uriSz) != 0) { + wolfSSL_X509_email_free(ret); return NULL; } } } - if (head == NULL && fallback != NULL && fallbackSz > 0) { - if (x509_aia_append_string(&head, fallback, (word32)fallbackSz) - != WOLFSSL_SUCCESS) { - wolfSSL_sk_pop_free(head, NULL); + /* Only use fallback when nothing was found in the list */ + if (wolfSSL_sk_num(ret) == 0 && fallback != NULL && fallbackSz > 0) { + if (x509_aia_append_string(ret, fallback, (word32)fallbackSz) != 0) { + wolfSSL_X509_email_free(ret); return NULL; } } - return head; + /* Return NULL when empty */ + if (wolfSSL_sk_num(ret) == 0) { + wolfSSL_X509_email_free(ret); + ret = NULL; + } + + return ret; } WOLF_STACK_OF(WOLFSSL_STRING) *wolfSSL_X509_get1_ocsp(WOLFSSL_X509 *x) diff --git a/tests/api.c b/tests/api.c index 2cdd81c79b..0cda4e114a 100644 --- a/tests/api.c +++ b/tests/api.c @@ -3515,6 +3515,60 @@ static int test_wolfSSL_CTX_add1_chain_cert(void) return EXPECT_RESULT(); } +/* Test that wolfssl_add_to_chain rejects sizes that would overflow word32. + * ZD #21241 */ +static int test_wolfSSL_add_to_chain_overflow(void) +{ + EXPECT_DECLS; +#if !defined(NO_CERTS) && defined(OPENSSL_EXTRA) && \ + defined(KEEP_OUR_CERT) && !defined(NO_RSA) && !defined(NO_TLS) && \ + !defined(NO_WOLFSSL_CLIENT) && !defined(NO_FILESYSTEM) + WOLFSSL_CTX* ctx = NULL; + WOLFSSL_X509* x509 = NULL; + DerBuffer* fakeChain = NULL; + + ExpectNotNull(ctx = wolfSSL_CTX_new(wolfSSLv23_client_method())); + + /* Load a real cert so ctx->certificate is set (first add goes there). */ + ExpectNotNull(x509 = wolfSSL_X509_load_certificate_file( + "./certs/intermediate/client-int-cert.pem", WOLFSSL_FILETYPE_PEM)); + ExpectIntEQ(SSL_CTX_add1_chain_cert(ctx, x509), 1); + wolfSSL_X509_free(x509); + x509 = NULL; + + /* Now ctx->certificate is set, next add goes to certChain via + * wolfssl_add_to_chain. Fake a chain whose length is near UINT32_MAX + * so the size calculation (len + CERT_HEADER_SZ + certSz) overflows. */ + fakeChain = (DerBuffer*)XMALLOC(sizeof(DerBuffer) + 16, ctx->heap, + DYNAMIC_TYPE_CERT); + ExpectNotNull(fakeChain); + if (EXPECT_SUCCESS()) { + XMEMSET(fakeChain, 0, sizeof(DerBuffer) + 16); + fakeChain->buffer = (byte*)(fakeChain + 1); + fakeChain->length = WOLFSSL_MAX_32BIT - 2; /* will overflow with any cert */ + fakeChain->type = CERT_TYPE; + fakeChain->dynType = DYNAMIC_TYPE_CERT; + /* Replace the real chain with our fake one. */ + if (ctx->certChain != NULL) { + XFREE(ctx->certChain, ctx->heap, DYNAMIC_TYPE_CERT); + } + ctx->certChain = fakeChain; + } + else { + XFREE(fakeChain, ctx ? ctx->heap : NULL, DYNAMIC_TYPE_CERT); + } + + /* Try to add another cert - this MUST fail due to overflow guard. */ + ExpectNotNull(x509 = wolfSSL_X509_load_certificate_file( + "./certs/intermediate/ca-int2-cert.pem", WOLFSSL_FILETYPE_PEM)); + ExpectIntEQ(SSL_CTX_add1_chain_cert(ctx, x509), 0); + wolfSSL_X509_free(x509); + + wolfSSL_CTX_free(ctx); +#endif + return EXPECT_RESULT(); +} + static int test_wolfSSL_CTX_use_certificate_chain_buffer_format(void) { EXPECT_DECLS; @@ -9172,6 +9226,117 @@ static int test_wolfSSL_dtls_export_peers(void) return EXPECT_RESULT(); } +/* Test that ImportKeyState correctly skips extra window words when importing + * state from a peer compiled with a larger WOLFSSL_DTLS_WINDOW_WORDS. */ +static int test_wolfSSL_dtls_import_state_extra_window_words(void) +{ + EXPECT_DECLS; +#if defined(WOLFSSL_DTLS) && defined(WOLFSSL_SESSION_EXPORT) + WOLFSSL_CTX* ctx = NULL; + WOLFSSL* ssl = NULL; + unsigned int stateSz = 0; + byte* state = NULL; + byte* modified = NULL; + unsigned int modifiedSz; + word16 origKeyLen; + word16 origTotalLen; + /* Offset from start of key state data to the first wordCount field. + * Layout: 4 sequence numbers (16 bytes) + DTLS-specific fields (42 bytes) + + * encryptSz(4) + padSz(4) + encryptionOn(1) + decryptedCur(1) = 68 */ + const int keyStateWindowOffset = 68; + /* Buffer header: 2 proto + 2 total_len + 2 key_len = 6 */ + const int headerSz = 6; + int idx, modIdx; + int extraPerWindow = 2 * (int)sizeof(word32); /* 8 bytes extra per window */ + int totalExtra = extraPerWindow * 2; /* 16 bytes extra total */ + + /* Create DTLS context and SSL object */ + ExpectNotNull(ctx = wolfSSL_CTX_new(wolfDTLSv1_2_server_method())); + ExpectNotNull(ssl = wolfSSL_new(ctx)); + + /* Get required buffer size and export state-only */ + ExpectIntEQ(wolfSSL_dtls_export_state_only(ssl, NULL, &stateSz), 0); + ExpectIntGT((int)stateSz, 0); + state = (byte*)XMALLOC(stateSz, HEAP_HINT, DYNAMIC_TYPE_TMP_BUFFER); + ExpectNotNull(state); + ExpectIntGT(wolfSSL_dtls_export_state_only(ssl, state, &stateSz), 0); + + /* Build a modified buffer that simulates a peer with + * WOLFSSL_DTLS_WINDOW_WORDS = WOLFSSL_DTLS_WINDOW_WORDS + 2. + * Each window section gets 2 extra word32 values (8 bytes). + * Two windows => 16 extra bytes total. */ + modifiedSz = stateSz + totalExtra; + modified = (byte*)XMALLOC(modifiedSz, HEAP_HINT, DYNAMIC_TYPE_TMP_BUFFER); + ExpectNotNull(modified); + + if (EXPECT_SUCCESS()) { + int windowWords = WOLFSSL_DTLS_WINDOW_WORDS; + int windowDataSz = windowWords * (int)sizeof(word32); + + XMEMSET(modified, 0, modifiedSz); + + /* Copy protocol/version bytes (first 2 bytes) */ + XMEMCPY(modified, state, 2); + + /* Read original total length and key state length */ + ato16(state + 2, &origTotalLen); + ato16(state + 4, &origKeyLen); + + /* Write updated total length and key state length */ + c16toa((word16)(origTotalLen + totalExtra), modified + 2); + c16toa((word16)(origKeyLen + totalExtra), modified + 4); + + /* Copy key state data up to first window section */ + idx = headerSz; + modIdx = headerSz; + XMEMCPY(modified + modIdx, state + idx, keyStateWindowOffset); + idx += keyStateWindowOffset; + modIdx += keyStateWindowOffset; + + /* First window: write increased wordCount */ + c16toa((word16)(windowWords + 2), modified + modIdx); + idx += OPAQUE16_LEN; + modIdx += OPAQUE16_LEN; + + /* Copy original window data */ + XMEMCPY(modified + modIdx, state + idx, windowDataSz); + idx += windowDataSz; + modIdx += windowDataSz; + + /* Insert 2 extra word32 padding values */ + XMEMSET(modified + modIdx, 0, extraPerWindow); + modIdx += extraPerWindow; + + /* Second window (prevWindow): same transformation */ + c16toa((word16)(windowWords + 2), modified + modIdx); + idx += OPAQUE16_LEN; + modIdx += OPAQUE16_LEN; + + XMEMCPY(modified + modIdx, state + idx, windowDataSz); + idx += windowDataSz; + modIdx += windowDataSz; + + XMEMSET(modified + modIdx, 0, extraPerWindow); + modIdx += extraPerWindow; + + /* Copy remainder of key state (after both windows) */ + XMEMCPY(modified + modIdx, state + idx, stateSz - idx); + } + + /* Import the modified state - should succeed with the fix */ + wolfSSL_free(ssl); + ssl = NULL; + ExpectNotNull(ssl = wolfSSL_new(ctx)); + ExpectIntGT(wolfSSL_dtls_import(ssl, modified, modifiedSz), 0); + + XFREE(state, HEAP_HINT, DYNAMIC_TYPE_TMP_BUFFER); + XFREE(modified, HEAP_HINT, DYNAMIC_TYPE_TMP_BUFFER); + wolfSSL_free(ssl); + wolfSSL_CTX_free(ctx); +#endif + return EXPECT_RESULT(); +} + static int test_wolfSSL_UseTrustedCA(void) { EXPECT_DECLS; @@ -9865,6 +10030,124 @@ static int test_wolfSSL_set_alpn_protos(void) return res; } +static int test_wolfSSL_select_next_proto(void) +{ + EXPECT_DECLS; + unsigned char *out = NULL; + unsigned char outLen = 0; + int ret; + + /* Wire format: length-prefixed protocol names */ + unsigned char serverList[] = { + 8, 'h','t','t','p','/','1','.','1', + 6, 's','p','d','y','/','2' + }; + unsigned int serverLen = sizeof(serverList); + + unsigned char clientList[] = { + 6, 's','p','d','y','/','2' + }; + unsigned int clientLen = sizeof(clientList); + + unsigned char clientListHttp[] = { + 8, 'h','t','t','p','/','1','.','1' + }; + unsigned int clientListHttpLen = sizeof(clientListHttp); + + unsigned char clientListNoMatch[] = { + 6, 's','p','d','y','/','3' + }; + unsigned int clientListNoMatchLen = sizeof(clientListNoMatch); + + /* Test 1: NULL parameters return UNSUPPORTED */ + ExpectIntEQ(wolfSSL_select_next_proto(NULL, &outLen, serverList, + serverLen, clientList, clientLen), WOLFSSL_NPN_UNSUPPORTED); + ExpectIntEQ(wolfSSL_select_next_proto(&out, NULL, serverList, + serverLen, clientList, clientLen), WOLFSSL_NPN_UNSUPPORTED); + ExpectIntEQ(wolfSSL_select_next_proto(&out, &outLen, NULL, + serverLen, clientList, clientLen), WOLFSSL_NPN_UNSUPPORTED); + ExpectIntEQ(wolfSSL_select_next_proto(&out, &outLen, serverList, + serverLen, NULL, clientLen), WOLFSSL_NPN_UNSUPPORTED); + + /* Test 2: Normal match - client wants "spdy/2", server offers it */ + out = NULL; + outLen = 0; + ret = wolfSSL_select_next_proto(&out, &outLen, serverList, serverLen, + clientList, clientLen); + ExpectIntEQ(ret, WOLFSSL_NPN_NEGOTIATED); + ExpectIntEQ(outLen, 6); + ExpectNotNull(out); + ExpectIntEQ(XMEMCMP(out, "spdy/2", 6), 0); + + /* Test 3: No overlap - server offers "http/1.1,spdy/2", client wants + * "spdy/3". Falls back to first client protocol. */ + out = NULL; + outLen = 0; + ret = wolfSSL_select_next_proto(&out, &outLen, serverList, serverLen, + clientListNoMatch, clientListNoMatchLen); + ExpectIntEQ(ret, WOLFSSL_NPN_NO_OVERLAP); + ExpectIntEQ(outLen, 6); + ExpectNotNull(out); + ExpectIntEQ(XMEMCMP(out, "spdy/3", 6), 0); + + /* Test 4: Malformed server list - length byte overruns buffer. + * Must NOT crash (heap over-read). */ + { + unsigned char malformedServer[] = { 200, 'h','t','t','p' }; + out = NULL; + outLen = 0; + ret = wolfSSL_select_next_proto(&out, &outLen, malformedServer, + sizeof(malformedServer), clientList, clientLen); + ExpectIntEQ(ret, WOLFSSL_NPN_NO_OVERLAP); + } + + /* Test 5: Malformed client list - length byte overruns buffer. + * Must NOT crash. */ + { + unsigned char malformedClient[] = { 200, 's','p','d','y' }; + out = NULL; + outLen = 0; + ret = wolfSSL_select_next_proto(&out, &outLen, serverList, serverLen, + malformedClient, sizeof(malformedClient)); + ExpectIntEQ(ret, WOLFSSL_NPN_NO_OVERLAP); + } + + /* Test 6: Zero-length entry in server list - must NOT infinite loop */ + { + unsigned char zeroLenServer[] = { 0, 6, 's','p','d','y','/','2' }; + out = NULL; + outLen = 0; + ret = wolfSSL_select_next_proto(&out, &outLen, zeroLenServer, + sizeof(zeroLenServer), clientList, clientLen); + /* Zero-length entry causes break, so no match found */ + ExpectIntEQ(ret, WOLFSSL_NPN_NO_OVERLAP); + } + + /* Test 7: Empty client list (clientLen == 0) - must NOT dereference + * clientNames[0]. */ + { + unsigned char emptyClient[] = { 0 }; + out = NULL; + outLen = 0; + ret = wolfSSL_select_next_proto(&out, &outLen, serverList, serverLen, + emptyClient, 0); + ExpectIntEQ(ret, WOLFSSL_NPN_NO_OVERLAP); + ExpectIntEQ(outLen, 0); + } + + /* Test 8: First protocol match - both start with "http/1.1" */ + out = NULL; + outLen = 0; + ret = wolfSSL_select_next_proto(&out, &outLen, serverList, serverLen, + clientListHttp, clientListHttpLen); + ExpectIntEQ(ret, WOLFSSL_NPN_NEGOTIATED); + ExpectIntEQ(outLen, 8); + ExpectNotNull(out); + ExpectIntEQ(XMEMCMP(out, "http/1.1", 8), 0); + + return EXPECT_RESULT(); +} + #endif /* HAVE_ALPN_PROTOS_SUPPORT */ static int test_wolfSSL_wolfSSL_UseSecureRenegotiation(void) @@ -24070,6 +24353,11 @@ static int test_export_keying_material_cb(WOLFSSL_CTX *ctx, WOLFSSL *ssl) NULL, 0, 0), 0); ExpectIntEQ(wolfSSL_export_keying_material(ssl, ekm, sizeof(ekm), "key expansion", XSTR_SIZEOF("key expansion"), NULL, 0, 0), 0); + /* contextLen overflow: values exceeding UINT16_MAX must be rejected to + * prevent integer overflow in seedLen calculation (ZD #21242). */ + ExpectIntEQ(wolfSSL_export_keying_material(ssl, ekm, sizeof(ekm), + "Test label", XSTR_SIZEOF("Test label"), ekm, + (size_t)0xFFFF + 1, 1), 0); return EXPECT_RESULT(); } @@ -32759,6 +33047,7 @@ TEST_CASE testCases[] = { TEST_DECL(test_wolfSSL_CTX_load_verify_buffer_ex), TEST_DECL(test_wolfSSL_CTX_load_verify_chain_buffer_format), TEST_DECL(test_wolfSSL_CTX_add1_chain_cert), + TEST_DECL(test_wolfSSL_add_to_chain_overflow), TEST_DECL(test_wolfSSL_CTX_use_certificate_chain_buffer_format), TEST_DECL(test_wolfSSL_CTX_use_certificate_chain_file_format), TEST_DECL(test_wolfSSL_use_certificate_chain_file), @@ -32801,6 +33090,7 @@ TEST_CASE testCases[] = { TEST_DECL(test_wolfSSL_tls_export), #endif TEST_DECL(test_wolfSSL_dtls_export_peers), + TEST_DECL(test_wolfSSL_dtls_import_state_extra_window_words), TEST_DECL(test_wolfSSL_SetMinVersion), TEST_DECL(test_wolfSSL_CTX_SetMinVersion), @@ -32848,12 +33138,14 @@ TEST_CASE testCases[] = { #ifdef HAVE_ALPN_PROTOS_SUPPORT /* Uses Assert in handshake callback. */ TEST_DECL(test_wolfSSL_set_alpn_protos), + TEST_DECL(test_wolfSSL_select_next_proto), #endif TEST_DECL(test_tls_ems_downgrade), TEST_DECL(test_wolfSSL_DisableExtendedMasterSecret), TEST_DECL(test_certificate_authorities_certificate_request), TEST_DECL(test_certificate_authorities_client_hello), TEST_DECL(test_TLSX_TCA_Find), + TEST_DECL(test_TLSX_SNI_GetSize_overflow), TEST_DECL(test_wolfSSL_wolfSSL_UseSecureRenegotiation), TEST_DECL(test_wolfSSL_SCR_Reconnect), TEST_DECL(test_wolfSSL_SCR_check_enabled), diff --git a/tests/api/test_asn.c b/tests/api/test_asn.c index d7e0fd3322..ecfe4ede50 100644 --- a/tests/api/test_asn.c +++ b/tests/api/test_asn.c @@ -24,6 +24,7 @@ #include #include +#include #if defined(WC_ENABLE_ASYM_KEY_EXPORT) && defined(HAVE_ED25519) static int test_SetAsymKeyDer_once(byte* privKey, word32 privKeySz, byte* pubKey, @@ -787,3 +788,135 @@ int test_wolfssl_local_MatchBaseName(void) return EXPECT_RESULT(); } + +/* + * Testing wc_DecodeRsaPssParams with known DER byte arrays. + * Exercises both WOLFSSL_ASN_TEMPLATE and non-template paths. + */ +int test_wc_DecodeRsaPssParams(void) +{ + EXPECT_DECLS; +#if defined(WC_RSA_PSS) && !defined(NO_RSA) && !defined(NO_ASN) + enum wc_HashType hash; + int mgf; + int saltLen; + + /* SHA-256 / MGF1-SHA-256 / saltLen=32 */ + static const byte pssParamsSha256[] = { + 0x30, 0x34, + 0xA0, 0x0F, + 0x30, 0x0D, + 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, + 0x04, 0x02, 0x01, + 0x05, 0x00, + 0xA1, 0x1C, + 0x30, 0x1A, + 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, + 0x01, 0x01, 0x08, + 0x30, 0x0D, + 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, + 0x04, 0x02, 0x01, + 0x05, 0x00, + 0xA2, 0x03, + 0x02, 0x01, 0x20, + }; + + /* Hash-only: SHA-256 hash, defaults for MGF and salt */ + static const byte pssParamsHashOnly[] = { + 0x30, 0x11, + 0xA0, 0x0F, + 0x30, 0x0D, + 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, + 0x04, 0x02, 0x01, + 0x05, 0x00, + }; + + /* Salt-only: default hash/mgf, saltLen=48 */ + static const byte pssParamsSaltOnly[] = { + 0x30, 0x05, + 0xA2, 0x03, + 0x02, 0x01, 0x30, + }; + + /* NULL tag (05 00) means all defaults */ + static const byte pssParamsNull[] = { 0x05, 0x00 }; + + /* Empty SEQUENCE means all non-default fields omitted => defaults */ + static const byte pssParamsEmptySeq[] = { 0x30, 0x00 }; + + /* --- Test 1: sz=0 => all defaults --- */ + hash = WC_HASH_TYPE_NONE; + mgf = 0; + saltLen = 0; + ExpectIntEQ(wc_DecodeRsaPssParams((const byte*)"", 0, + &hash, &mgf, &saltLen), 0); + ExpectIntEQ((int)hash, (int)WC_HASH_TYPE_SHA); + ExpectIntEQ(mgf, WC_MGF1SHA1); + ExpectIntEQ(saltLen, 20); + + /* --- Test 2: NULL tag => all defaults --- */ + hash = WC_HASH_TYPE_NONE; + mgf = 0; + saltLen = 0; + ExpectIntEQ(wc_DecodeRsaPssParams(pssParamsNull, + (word32)sizeof(pssParamsNull), &hash, &mgf, &saltLen), 0); + ExpectIntEQ((int)hash, (int)WC_HASH_TYPE_SHA); + ExpectIntEQ(mgf, WC_MGF1SHA1); + ExpectIntEQ(saltLen, 20); + + /* --- Test 3: Empty SEQUENCE => all defaults --- */ + hash = WC_HASH_TYPE_NONE; + mgf = 0; + saltLen = 0; + ExpectIntEQ(wc_DecodeRsaPssParams(pssParamsEmptySeq, + (word32)sizeof(pssParamsEmptySeq), &hash, &mgf, &saltLen), 0); + ExpectIntEQ((int)hash, (int)WC_HASH_TYPE_SHA); + ExpectIntEQ(mgf, WC_MGF1SHA1); + ExpectIntEQ(saltLen, 20); + +#ifndef NO_SHA256 + /* --- Test 4: SHA-256 / MGF1-SHA-256 / salt=32 --- */ + hash = WC_HASH_TYPE_NONE; + mgf = 0; + saltLen = 0; + ExpectIntEQ(wc_DecodeRsaPssParams(pssParamsSha256, + (word32)sizeof(pssParamsSha256), &hash, &mgf, &saltLen), 0); + ExpectIntEQ((int)hash, (int)WC_HASH_TYPE_SHA256); + ExpectIntEQ(mgf, WC_MGF1SHA256); + ExpectIntEQ(saltLen, 32); + + /* --- Test 5: Hash only => SHA-256, default MGF/salt --- */ + hash = WC_HASH_TYPE_NONE; + mgf = 0; + saltLen = 0; + ExpectIntEQ(wc_DecodeRsaPssParams(pssParamsHashOnly, + (word32)sizeof(pssParamsHashOnly), &hash, &mgf, &saltLen), 0); + ExpectIntEQ((int)hash, (int)WC_HASH_TYPE_SHA256); + ExpectIntEQ(mgf, WC_MGF1SHA1); + ExpectIntEQ(saltLen, 20); +#endif + + /* --- Test 6: Salt only => default hash/MGF, salt=48 --- */ + hash = WC_HASH_TYPE_NONE; + mgf = 0; + saltLen = 0; + ExpectIntEQ(wc_DecodeRsaPssParams(pssParamsSaltOnly, + (word32)sizeof(pssParamsSaltOnly), &hash, &mgf, &saltLen), 0); + ExpectIntEQ((int)hash, (int)WC_HASH_TYPE_SHA); + ExpectIntEQ(mgf, WC_MGF1SHA1); + ExpectIntEQ(saltLen, 48); + + /* --- Test 7: NULL pointer -> BAD_FUNC_ARG --- */ + ExpectIntEQ(wc_DecodeRsaPssParams(NULL, 10, &hash, &mgf, &saltLen), + WC_NO_ERR_TRACE(BAD_FUNC_ARG)); + + /* --- Test 8: Bad leading tag => ASN_PARSE_E --- */ + { + static const byte badTag[] = { 0x01, 0x00 }; + ExpectIntEQ(wc_DecodeRsaPssParams(badTag, (word32)sizeof(badTag), + &hash, &mgf, &saltLen), WC_NO_ERR_TRACE(ASN_PARSE_E)); + } + +#endif /* WC_RSA_PSS && !NO_RSA && !NO_ASN */ + return EXPECT_RESULT(); +} diff --git a/tests/api/test_asn.h b/tests/api/test_asn.h index 7f2879dbce..2403bc81d1 100644 --- a/tests/api/test_asn.h +++ b/tests/api/test_asn.h @@ -28,11 +28,13 @@ int test_SetAsymKeyDer(void); int test_GetSetShortInt(void); int test_wc_IndexSequenceOf(void); int test_wolfssl_local_MatchBaseName(void); +int test_wc_DecodeRsaPssParams(void); #define TEST_ASN_DECLS \ TEST_DECL_GROUP("asn", test_SetAsymKeyDer), \ TEST_DECL_GROUP("asn", test_GetSetShortInt), \ TEST_DECL_GROUP("asn", test_wc_IndexSequenceOf), \ - TEST_DECL_GROUP("asn", test_wolfssl_local_MatchBaseName) + TEST_DECL_GROUP("asn", test_wolfssl_local_MatchBaseName), \ + TEST_DECL_GROUP("asn", test_wc_DecodeRsaPssParams) #endif /* WOLFCRYPT_TEST_ASN_H */ diff --git a/tests/api/test_certman.c b/tests/api/test_certman.c index 1e72ee4436..549b983945 100644 --- a/tests/api/test_certman.c +++ b/tests/api/test_certman.c @@ -1781,6 +1781,118 @@ int test_wolfSSL_CertManagerCRL(void) return EXPECT_RESULT(); } +int test_wolfSSL_CRL_static_revoked_list(void) +{ + EXPECT_DECLS; +#if defined(CRL_STATIC_REVOKED_LIST) && defined(HAVE_CRL) && \ + !defined(NO_RSA) && !defined(NO_CERTS) + /* CRL signed by certs/ca-cert.pem that revokes serials 05, 02, 01 in that + * (unsorted) wire order. The unsorted order exposes a binary search bug in + * FindRevokedSerial when CRL_STATIC_REVOKED_LIST is enabled: the revoked + * cert array is never sorted after parsing, so binary search misses entries + * that are out of order. + * + * Generated with Python cryptography library: + * builder.add_revoked_certificate(serial=5) + * builder.add_revoked_certificate(serial=2) + * builder.add_revoked_certificate(serial=1) + * crl = builder.sign(ca_key, hashes.SHA256()) + */ + static const unsigned char crl_multi_revoked[] = { + 0x30, 0x82, 0x02, 0x1D, 0x30, 0x82, 0x01, 0x05, + 0x02, 0x01, 0x01, 0x30, 0x0D, 0x06, 0x09, 0x2A, + 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x0B, + 0x05, 0x00, 0x30, 0x81, 0x94, 0x31, 0x0B, 0x30, + 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, + 0x55, 0x53, 0x31, 0x10, 0x30, 0x0E, 0x06, 0x03, + 0x55, 0x04, 0x08, 0x0C, 0x07, 0x4D, 0x6F, 0x6E, + 0x74, 0x61, 0x6E, 0x61, 0x31, 0x10, 0x30, 0x0E, + 0x06, 0x03, 0x55, 0x04, 0x07, 0x0C, 0x07, 0x42, + 0x6F, 0x7A, 0x65, 0x6D, 0x61, 0x6E, 0x31, 0x11, + 0x30, 0x0F, 0x06, 0x03, 0x55, 0x04, 0x0A, 0x0C, + 0x08, 0x53, 0x61, 0x77, 0x74, 0x6F, 0x6F, 0x74, + 0x68, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, + 0x04, 0x0B, 0x0C, 0x0A, 0x43, 0x6F, 0x6E, 0x73, + 0x75, 0x6C, 0x74, 0x69, 0x6E, 0x67, 0x31, 0x18, + 0x30, 0x16, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0C, + 0x0F, 0x77, 0x77, 0x77, 0x2E, 0x77, 0x6F, 0x6C, + 0x66, 0x73, 0x73, 0x6C, 0x2E, 0x63, 0x6F, 0x6D, + 0x31, 0x1F, 0x30, 0x1D, 0x06, 0x09, 0x2A, 0x86, + 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x09, 0x01, 0x16, + 0x10, 0x69, 0x6E, 0x66, 0x6F, 0x40, 0x77, 0x6F, + 0x6C, 0x66, 0x73, 0x73, 0x6C, 0x2E, 0x63, 0x6F, + 0x6D, 0x17, 0x0D, 0x32, 0x36, 0x30, 0x31, 0x30, + 0x31, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x5A, + 0x17, 0x0D, 0x33, 0x36, 0x30, 0x31, 0x30, 0x31, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x5A, 0x30, + 0x3C, 0x30, 0x12, 0x02, 0x01, 0x05, 0x17, 0x0D, + 0x32, 0x33, 0x30, 0x31, 0x30, 0x31, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x5A, 0x30, 0x12, 0x02, + 0x01, 0x02, 0x17, 0x0D, 0x32, 0x33, 0x30, 0x32, + 0x30, 0x31, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x5A, 0x30, 0x12, 0x02, 0x01, 0x01, 0x17, 0x0D, + 0x32, 0x33, 0x30, 0x33, 0x30, 0x31, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x5A, 0x30, 0x0D, 0x06, + 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, + 0x01, 0x0B, 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, + 0x00, 0x15, 0x9F, 0xC1, 0x9E, 0x17, 0xB3, 0x5A, + 0xF1, 0x48, 0xA5, 0x87, 0x2A, 0x84, 0xD1, 0x93, + 0x8D, 0x19, 0x24, 0xCB, 0xC5, 0x32, 0x56, 0x10, + 0x6C, 0x4D, 0xF5, 0xD1, 0x9A, 0xC0, 0x1A, 0x8B, + 0x1C, 0x84, 0x6B, 0x4B, 0x20, 0xA7, 0xA4, 0x2C, + 0x11, 0x5C, 0x23, 0xBD, 0x0C, 0xB1, 0x33, 0xBE, + 0x38, 0x1B, 0xCB, 0xDB, 0x8E, 0xD4, 0x0F, 0x62, + 0x0D, 0xB5, 0x18, 0x21, 0x28, 0x0B, 0x77, 0xB9, + 0xB4, 0xA8, 0xE9, 0xA0, 0x25, 0x00, 0x83, 0xED, + 0x64, 0x49, 0x8E, 0x52, 0xD9, 0x8D, 0xAF, 0xC2, + 0x16, 0x3E, 0xD3, 0x93, 0x09, 0xB9, 0x18, 0xBB, + 0x6C, 0x41, 0xDF, 0x59, 0x59, 0x53, 0x8C, 0x64, + 0x8B, 0xD1, 0x9D, 0xBB, 0x92, 0x8F, 0xB2, 0x26, + 0x27, 0x78, 0x41, 0xFB, 0xF8, 0xB1, 0x2F, 0x8F, + 0xA1, 0x85, 0xB6, 0xC7, 0x8E, 0x42, 0x72, 0xEF, + 0xF4, 0x3F, 0xC4, 0xAF, 0x40, 0x95, 0xCA, 0x94, + 0xE5, 0x88, 0x89, 0x18, 0x32, 0x54, 0xC3, 0xC4, + 0xBE, 0x7E, 0x48, 0x1B, 0x3D, 0xB3, 0x6C, 0x11, + 0x54, 0x6F, 0x9E, 0xFE, 0x09, 0x5B, 0x72, 0x3F, + 0xD7, 0xA0, 0x02, 0xFF, 0x43, 0x01, 0xFE, 0x23, + 0xF8, 0x72, 0xCD, 0xA9, 0x76, 0x36, 0x31, 0x78, + 0x21, 0xCB, 0x0E, 0xC2, 0x25, 0x8D, 0x0B, 0x4C, + 0x2C, 0xAA, 0x6A, 0x80, 0x6E, 0xE2, 0x1E, 0xAC, + 0x70, 0x5D, 0x4A, 0xAA, 0x56, 0x17, 0xF0, 0x2D, + 0xA2, 0x2A, 0x4E, 0x2B, 0xC8, 0xC9, 0x87, 0x8E, + 0x07, 0xEB, 0xD8, 0x36, 0x42, 0x39, 0xA0, 0xA4, + 0xF6, 0x34, 0xC2, 0x5F, 0xE1, 0x21, 0x07, 0x50, + 0x4B, 0x37, 0x15, 0x7D, 0xF9, 0x18, 0x54, 0x13, + 0xC0, 0x1D, 0x0A, 0x27, 0x3A, 0x63, 0xD2, 0xC3, + 0xD5, 0x57, 0x5E, 0x67, 0x56, 0x65, 0x9E, 0x2E, + 0x4D, 0xB4, 0x96, 0x54, 0x7A, 0x3D, 0xFD, 0xF9, + 0xCF, 0xCD, 0x10, 0x65, 0x05, 0x97, 0x53, 0x72, + 0x12 + }; + WOLFSSL_CERT_MANAGER* cm = NULL; + + /* Set up CertManager with the CA and CRL checking enabled */ + ExpectNotNull(cm = wolfSSL_CertManagerNew()); + ExpectIntEQ(wolfSSL_CertManagerLoadCA(cm, "./certs/ca-cert.pem", NULL), + WOLFSSL_SUCCESS); + ExpectIntEQ(wolfSSL_CertManagerEnableCRL(cm, WOLFSSL_CRL_CHECKALL), + WOLFSSL_SUCCESS); + + /* Load the CRL that revokes serials {05, 02, 01} in unsorted wire order */ + ExpectIntEQ(wolfSSL_CertManagerLoadCRLBuffer(cm, crl_multi_revoked, + sizeof(crl_multi_revoked), WOLFSSL_FILETYPE_ASN1), WOLFSSL_SUCCESS); + + /* server-cert.pem has serial 01, which is in the CRL but at the last + * position in the unsorted array. Binary search on unsorted data misses + * it, so this assertion fails before the bug fix. */ + ExpectIntEQ(wolfSSL_CertManagerCheckCRL(cm, server_cert_der_2048, + sizeof_server_cert_der_2048), WC_NO_ERR_TRACE(CRL_CERT_REVOKED)); + + wolfSSL_CertManagerFree(cm); +#endif + return EXPECT_RESULT(); +} + int test_wolfSSL_CRL_duplicate_extensions(void) { EXPECT_DECLS; diff --git a/tests/api/test_certman.h b/tests/api/test_certman.h index 6236cbb88d..b896475ce6 100644 --- a/tests/api/test_certman.h +++ b/tests/api/test_certman.h @@ -36,6 +36,7 @@ int test_wolfSSL_CertManagerNameConstraint3(void); int test_wolfSSL_CertManagerNameConstraint4(void); int test_wolfSSL_CertManagerNameConstraint5(void); int test_wolfSSL_CertManagerCRL(void); +int test_wolfSSL_CRL_static_revoked_list(void); int test_wolfSSL_CRL_duplicate_extensions(void); int test_wolfSSL_CertManagerCheckOCSPResponse(void); int test_various_pathlen_chains(void); @@ -53,6 +54,7 @@ int test_various_pathlen_chains(void); TEST_DECL_GROUP("certman", test_wolfSSL_CertManagerNameConstraint4), \ TEST_DECL_GROUP("certman", test_wolfSSL_CertManagerNameConstraint5), \ TEST_DECL_GROUP("certman", test_wolfSSL_CertManagerCRL), \ + TEST_DECL_GROUP("certman", test_wolfSSL_CRL_static_revoked_list), \ TEST_DECL_GROUP("certman", test_wolfSSL_CRL_duplicate_extensions), \ TEST_DECL_GROUP("certman", test_wolfSSL_CertManagerCheckOCSPResponse), \ TEST_DECL_GROUP("certman", test_various_pathlen_chains) diff --git a/tests/api/test_ossl_ec.c b/tests/api/test_ossl_ec.c index c715f343b7..7f788984cd 100644 --- a/tests/api/test_ossl_ec.c +++ b/tests/api/test_ossl_ec.c @@ -314,6 +314,7 @@ int test_wolfSSL_EC_POINT(void) EC_POINT* set_point = NULL; EC_POINT* get_point = NULL; EC_POINT* infinity = NULL; + EC_POINT* dup_point = NULL; BIGNUM* k = NULL; BIGNUM* Gx = NULL; BIGNUM* Gy = NULL; @@ -507,6 +508,12 @@ int test_wolfSSL_EC_POINT(void) ExpectIntEQ(EC_POINT_copy(new_point, NULL), 0); ExpectIntEQ(EC_POINT_copy(new_point, set_point), 1); + /* Test duplicating */ + ExpectNull(EC_POINT_dup(NULL, group)); + ExpectNull(EC_POINT_dup(set_point, NULL)); + ExpectNotNull(dup_point = EC_POINT_dup(set_point, group)); + ExpectIntEQ(EC_POINT_cmp(group, dup_point, set_point, ctx), 0); + /* Test inverting */ ExpectIntEQ(EC_POINT_invert(NULL, NULL, ctx), 0); ExpectIntEQ(EC_POINT_invert(NULL, new_point, ctx), 0); @@ -526,6 +533,12 @@ int test_wolfSSL_EC_POINT(void) ExpectIntEQ(EC_POINT_add(group, orig_point, orig_point, new_point, NULL), 1); ExpectIntEQ(EC_POINT_cmp(group, orig_point, set_point, NULL), 0); + /* dup_point equals set_point so let's test with that too */ + ExpectIntEQ(EC_POINT_add(group, orig_point, dup_point, dup_point, NULL), + 1); + ExpectIntEQ(EC_POINT_add(group, orig_point, orig_point, new_point, + NULL), 1); + ExpectIntEQ(EC_POINT_cmp(group, orig_point, set_point, NULL), 0); EC_POINT_free(orig_point); } #endif @@ -769,6 +782,7 @@ int test_wolfSSL_EC_POINT(void) BN_free(k); BN_free(set_point_bn); EC_POINT_free(infinity); + EC_POINT_free(dup_point); EC_POINT_free(new_point); EC_POINT_free(set_point); EC_POINT_clear_free(Gxy); diff --git a/tests/api/test_pkcs7.c b/tests/api/test_pkcs7.c index edbbb2b4ef..5f3644869f 100644 --- a/tests/api/test_pkcs7.c +++ b/tests/api/test_pkcs7.c @@ -947,6 +947,98 @@ int test_wc_PKCS7_EncodeSignedData(void) } /* END test_wc_PKCS7_EncodeSignedData */ +/* + * Testing wc_PKCS7_EncodeSignedData() with RSA-PSS signer certificate. + * Uses certs/rsapss/client-rsapss.der and client-rsapss-priv.der. + * Requires both encode and round-trip verify to succeed. + */ +#if defined(HAVE_PKCS7) && defined(WC_RSA_PSS) && !defined(NO_RSA) && \ + !defined(NO_FILESYSTEM) && !defined(NO_SHA256) +int test_wc_PKCS7_EncodeSignedData_RSA_PSS(void) +{ + EXPECT_DECLS; + PKCS7* pkcs7 = NULL; + WC_RNG rng; + byte output[FOURK_BUF]; + byte cert[FOURK_BUF]; + byte key[FOURK_BUF]; + word32 outputSz = (word32)sizeof(output); + word32 certSz = 0; + word32 keySz = 0; + XFILE fp = XBADFILE; + byte data[] = "Test data for RSA-PSS SignedData."; + + XMEMSET(&rng, 0, sizeof(WC_RNG)); + XMEMSET(output, 0, outputSz); + XMEMSET(cert, 0, sizeof(cert)); + XMEMSET(key, 0, sizeof(key)); + + ExpectIntEQ(wc_InitRng(&rng), 0); + ExpectNotNull(pkcs7 = wc_PKCS7_New(HEAP_HINT, testDevId)); + + ExpectTrue((fp = XFOPEN("./certs/rsapss/client-rsapss.der", "rb")) != XBADFILE); + if (fp != XBADFILE) { + ExpectIntGT(certSz = (word32)XFREAD(cert, 1, sizeof(cert), fp), 0); + XFCLOSE(fp); + fp = XBADFILE; + } + + ExpectTrue((fp = XFOPEN("./certs/rsapss/client-rsapss-priv.der", "rb")) != XBADFILE); + if (fp != XBADFILE) { + ExpectIntGT(keySz = (word32)XFREAD(key, 1, sizeof(key), fp), 0); + XFCLOSE(fp); + fp = XBADFILE; + } + + ExpectIntEQ(wc_PKCS7_InitWithCert(pkcs7, cert, certSz), 0); + + if (pkcs7 != NULL) { + /* Force RSA-PSS so SignerInfo uses id-RSASSA-PSS (cert may use RSA + * in subjectPublicKeyInfo). WC_RSA_PSS is guaranteed by outer guard. */ + pkcs7->publicKeyOID = RSAPSSk; + + pkcs7->content = data; + pkcs7->contentSz = (word32)sizeof(data); + pkcs7->contentOID = DATA; + pkcs7->hashOID = SHA256h; + pkcs7->encryptOID = RSAk; + pkcs7->privateKey = key; + pkcs7->privateKeySz = keySz; + pkcs7->rng = &rng; + pkcs7->signedAttribs = NULL; + pkcs7->signedAttribsSz = 0; + } + + /* EncodeSignedData with RSA-PSS cert: require encode and verify success */ + { + int outLen = wc_PKCS7_EncodeSignedData(pkcs7, output, outputSz); + ExpectIntGT(outLen, 0); + if (outLen > 0) { + int verifyRet = wc_PKCS7_VerifySignedData(pkcs7, output, + (word32)outLen); + ExpectIntEQ(verifyRet, 0); + + if (pkcs7 != NULL) { + /* Verify decoded RSASSA-PSS parameters match what we + * encoded: + * hashAlgorithm = SHA-256 + * maskGenAlgorithm = MGF1-SHA-256 + * saltLength = 32 (== SHA-256 digest length) */ + ExpectIntEQ(pkcs7->pssHashType, (int)WC_HASH_TYPE_SHA256); + ExpectIntEQ(pkcs7->pssMgf, WC_MGF1SHA256); + ExpectIntEQ(pkcs7->pssSaltLen, 32); + } + } + } + + wc_PKCS7_Free(pkcs7); + DoExpectIntEQ(wc_FreeRng(&rng), 0); + + return EXPECT_RESULT(); +} /* END test_wc_PKCS7_EncodeSignedData_RSA_PSS */ +#endif + + /* * Testing wc_PKCS7_EncodeSignedData_ex() and wc_PKCS7_VerifySignedData_ex() */ diff --git a/tests/api/test_pkcs7.h b/tests/api/test_pkcs7.h index 7bc6207880..c55307cacc 100644 --- a/tests/api/test_pkcs7.h +++ b/tests/api/test_pkcs7.h @@ -29,6 +29,10 @@ int test_wc_PKCS7_Init(void); int test_wc_PKCS7_InitWithCert(void); int test_wc_PKCS7_EncodeData(void); int test_wc_PKCS7_EncodeSignedData(void); +#if defined(HAVE_PKCS7) && defined(WC_RSA_PSS) && !defined(NO_RSA) && \ + !defined(NO_FILESYSTEM) && !defined(NO_SHA256) +int test_wc_PKCS7_EncodeSignedData_RSA_PSS(void); +#endif int test_wc_PKCS7_EncodeSignedData_ex(void); int test_wc_PKCS7_VerifySignedData_RSA(void); int test_wc_PKCS7_VerifySignedData_ECC(void); @@ -55,10 +59,19 @@ int test_wc_PKCS7_VerifySignedData_PKCS7ContentSeq(void); TEST_DECL_GROUP("pkcs7", test_wc_PKCS7_New), \ TEST_DECL_GROUP("pkcs7", test_wc_PKCS7_Init) +#if defined(HAVE_PKCS7) && defined(WC_RSA_PSS) && !defined(NO_RSA) && \ + !defined(NO_FILESYSTEM) && !defined(NO_SHA256) +#define TEST_PKCS7_RSA_PSS_SD_DECL \ + TEST_DECL_GROUP("pkcs7_sd", test_wc_PKCS7_EncodeSignedData_RSA_PSS), +#else +#define TEST_PKCS7_RSA_PSS_SD_DECL +#endif + #define TEST_PKCS7_SIGNED_DATA_DECLS \ TEST_DECL_GROUP("pkcs7_sd", test_wc_PKCS7_InitWithCert), \ TEST_DECL_GROUP("pkcs7_sd", test_wc_PKCS7_EncodeData), \ TEST_DECL_GROUP("pkcs7_sd", test_wc_PKCS7_EncodeSignedData), \ + TEST_PKCS7_RSA_PSS_SD_DECL \ TEST_DECL_GROUP("pkcs7_sd", test_wc_PKCS7_EncodeSignedData_ex), \ TEST_DECL_GROUP("pkcs7_sd", test_wc_PKCS7_VerifySignedData_RSA), \ TEST_DECL_GROUP("pkcs7_sd", test_wc_PKCS7_VerifySignedData_ECC), \ diff --git a/tests/api/test_tls13.c b/tests/api/test_tls13.c index 9c77126f46..a6f411544b 100644 --- a/tests/api/test_tls13.c +++ b/tests/api/test_tls13.c @@ -3100,3 +3100,89 @@ int test_tls13_plaintext_alert(void) return EXPECT_RESULT(); } +/* Test that wolfSSL_set1_sigalgs_list() is honored in TLS 1.3 + */ +int test_tls13_cert_req_sigalgs(void) +{ + EXPECT_DECLS; +#if defined(WOLFSSL_TLS13) && defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \ + !defined(NO_CERTS) && !defined(NO_RSA) && defined(WC_RSA_PSS) && \ + defined(HAVE_ECC) && !defined(NO_WOLFSSL_CLIENT) && \ + !defined(NO_WOLFSSL_SERVER) && defined(OPENSSL_EXTRA) && \ + !defined(NO_FILESYSTEM) + WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL; + WOLFSSL *ssl_c = NULL, *ssl_s = NULL; + struct test_memio_ctx test_ctx; + + XMEMSET(&test_ctx, 0, sizeof(test_ctx)); + ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s, + wolfTLSv1_3_client_method, wolfTLSv1_3_server_method), 0); + + /* Server: require client cert and load ECC client cert for verification */ + if (EXPECT_SUCCESS()) { + wolfSSL_set_verify(ssl_s, + WOLFSSL_VERIFY_PEER | WOLFSSL_VERIFY_FAIL_IF_NO_PEER_CERT, NULL); + ExpectIntEQ(wolfSSL_CTX_load_verify_locations(ctx_s, + cliEccCertFile, 0), WOLFSSL_SUCCESS); + } + + /* Server: restrict CertificateRequest to RSA-PSS+SHA256 only */ + if (EXPECT_SUCCESS()) { + ExpectIntEQ(wolfSSL_set1_sigalgs_list(ssl_s, "RSA-PSS+SHA256"), + WOLFSSL_SUCCESS); + } + + /* Client: load ECC cert/key */ + if (EXPECT_SUCCESS()) { + ExpectIntEQ(wolfSSL_use_certificate_file(ssl_c, cliEccCertFile, + CERT_FILETYPE), WOLFSSL_SUCCESS); + ExpectIntEQ(wolfSSL_use_PrivateKey_file(ssl_c, cliEccKeyFile, + CERT_FILETYPE), WOLFSSL_SUCCESS); + } + + /* Handshake must fail: ECC client cannot match RSA-PSS+SHA256 */ + ExpectIntNE(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0); + + wolfSSL_free(ssl_c); ssl_c = NULL; + wolfSSL_free(ssl_s); ssl_s = NULL; + wolfSSL_CTX_free(ctx_c); ctx_c = NULL; + wolfSSL_CTX_free(ctx_s); ctx_s = NULL; + + XMEMSET(&test_ctx, 0, sizeof(test_ctx)); + ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s, + wolfTLSv1_3_client_method, wolfTLSv1_3_server_method), 0); + + /* Server: require client cert and load RSA client cert for verification */ + if (EXPECT_SUCCESS()) { + wolfSSL_set_verify(ssl_s, + WOLFSSL_VERIFY_PEER | WOLFSSL_VERIFY_FAIL_IF_NO_PEER_CERT, NULL); + ExpectIntEQ(wolfSSL_CTX_load_verify_locations(ctx_s, + cliCertFile, 0), WOLFSSL_SUCCESS); + } + + /* Server: restrict CertificateRequest to RSA-PSS+SHA256 only */ + if (EXPECT_SUCCESS()) { + ExpectIntEQ(wolfSSL_set1_sigalgs_list(ssl_s, "RSA-PSS+SHA256"), + WOLFSSL_SUCCESS); + } + + /* Client: load RSA cert/key */ + if (EXPECT_SUCCESS()) { + ExpectIntEQ(wolfSSL_use_certificate_file(ssl_c, cliCertFile, + CERT_FILETYPE), WOLFSSL_SUCCESS); + ExpectIntEQ(wolfSSL_use_PrivateKey_file(ssl_c, cliKeyFile, + CERT_FILETYPE), WOLFSSL_SUCCESS); + } + + /* Handshake must succeed: RSA client satisfies RSA-PSS+SHA256 */ + ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0); + + wolfSSL_free(ssl_c); ssl_c = NULL; + wolfSSL_free(ssl_s); ssl_s = NULL; + wolfSSL_CTX_free(ctx_c); ctx_c = NULL; + wolfSSL_CTX_free(ctx_s); ctx_s = NULL; +#endif + + return EXPECT_RESULT(); +} + diff --git a/tests/api/test_tls13.h b/tests/api/test_tls13.h index 8b5f48ef4d..67b23d57fe 100644 --- a/tests/api/test_tls13.h +++ b/tests/api/test_tls13.h @@ -38,6 +38,7 @@ int test_tls13_duplicate_extension(void); int test_key_share_mismatch(void); int test_tls13_middlebox_compat_empty_session_id(void); int test_tls13_plaintext_alert(void); +int test_tls13_cert_req_sigalgs(void); #define TEST_TLS13_DECLS \ TEST_DECL_GROUP("tls13", test_tls13_apis), \ @@ -53,6 +54,7 @@ int test_tls13_plaintext_alert(void); TEST_DECL_GROUP("tls13", test_tls13_duplicate_extension), \ TEST_DECL_GROUP("tls13", test_key_share_mismatch), \ TEST_DECL_GROUP("tls13", test_tls13_middlebox_compat_empty_session_id), \ - TEST_DECL_GROUP("tls13", test_tls13_plaintext_alert) + TEST_DECL_GROUP("tls13", test_tls13_plaintext_alert), \ + TEST_DECL_GROUP("tls13", test_tls13_cert_req_sigalgs) #endif /* WOLFCRYPT_TEST_TLS13_H */ diff --git a/tests/api/test_tls_ext.c b/tests/api/test_tls_ext.c index 4c89d7edf6..ad6053f727 100644 --- a/tests/api/test_tls_ext.c +++ b/tests/api/test_tls_ext.c @@ -545,3 +545,67 @@ int test_certificate_authorities_client_hello(void) { #endif return EXPECT_RESULT(); } + +/* Test that the SNI size calculation returns 0 on overflow instead of + * wrapping around to a small value (integer overflow vulnerability). */ +int test_TLSX_SNI_GetSize_overflow(void) +{ + EXPECT_DECLS; +#if defined(HAVE_SNI) && !defined(NO_WOLFSSL_CLIENT) && !defined(NO_TLS) + WOLFSSL_CTX* ctx = NULL; + WOLFSSL* ssl = NULL; + TLSX* sni_ext = NULL; + SNI* head = NULL; + SNI* sni = NULL; + int i; + /* Each SNI adds ENUM_LEN(1) + OPAQUE16_LEN(2) + hostname_len to the size. + * With a 1-byte hostname, each entry adds 4 bytes. Starting from + * OPAQUE16_LEN(2) base, we need enough entries to exceed UINT16_MAX. */ + const int num_sni = (0xFFFF / 4) + 2; + + ExpectNotNull(ctx = wolfSSL_CTX_new(wolfSSLv23_client_method())); + ExpectNotNull(ssl = wolfSSL_new(ctx)); + + /* Add initial SNI via public API */ + ExpectIntEQ(WOLFSSL_SUCCESS, + wolfSSL_UseSNI(ssl, WOLFSSL_SNI_HOST_NAME, "a", 1)); + + /* Find the SNI extension and manually build a long chain */ + if (EXPECT_SUCCESS()) { + sni_ext = TLSX_Find(ssl->extensions, TLSX_SERVER_NAME); + ExpectNotNull(sni_ext); + } + + if (EXPECT_SUCCESS()) { + head = (SNI*)sni_ext->data; + ExpectNotNull(head); + } + + /* Append many SNI nodes to force overflow in the size calculation */ + for (i = 1; EXPECT_SUCCESS() && i < num_sni; i++) { + sni = (SNI*)XMALLOC(sizeof(SNI), NULL, DYNAMIC_TYPE_TLSX); + ExpectNotNull(sni); + if (sni != NULL) { + XMEMSET(sni, 0, sizeof(SNI)); + sni->type = WOLFSSL_SNI_HOST_NAME; + sni->data.host_name = (char*)XMALLOC(2, NULL, DYNAMIC_TYPE_TLSX); + ExpectNotNull(sni->data.host_name); + if (sni->data.host_name != NULL) { + sni->data.host_name[0] = 'a'; + sni->data.host_name[1] = '\0'; + } + sni->next = head->next; + head->next = sni; + } + } + + if (EXPECT_SUCCESS()) { + /* The fixed calculation should return 0 (overflow detected) */ + ExpectIntEQ(TLSX_SNI_GetSize((SNI*)sni_ext->data), 0); + } + + wolfSSL_free(ssl); + wolfSSL_CTX_free(ctx); +#endif + return EXPECT_RESULT(); +} diff --git a/tests/api/test_tls_ext.h b/tests/api/test_tls_ext.h index c02067522d..ec7a5223be 100644 --- a/tests/api/test_tls_ext.h +++ b/tests/api/test_tls_ext.h @@ -27,5 +27,6 @@ int test_wolfSSL_DisableExtendedMasterSecret(void); int test_certificate_authorities_certificate_request(void); int test_certificate_authorities_client_hello(void); int test_TLSX_TCA_Find(void); +int test_TLSX_SNI_GetSize_overflow(void); #endif /* TESTS_API_TEST_TLS_EMS_H */ diff --git a/tests/suites.c b/tests/suites.c index 34fe772d27..b0cbca6ff7 100644 --- a/tests/suites.c +++ b/tests/suites.c @@ -909,6 +909,10 @@ int SuiteTest(int argc, char** argv) char argv0[3][80]; char* myArgv[3]; +#ifdef WOLFSSL_STATIC_MEMORY + byte memory[200000]; +#endif + printf(" Begin Cipher Suite Tests\n"); /* setup */ @@ -918,10 +922,6 @@ int SuiteTest(int argc, char** argv) args.argv = myArgv; XSTRLCPY(argv0[0], "SuiteTest", sizeof(argv0[0])); -#ifdef WOLFSSL_STATIC_MEMORY - byte memory[200000]; -#endif - cipherSuiteCtx = wolfSSL_CTX_new(wolfSSLv23_client_method()); if (cipherSuiteCtx == NULL) { printf("can't get cipher suite ctx\n"); diff --git a/tests/test-maxfrag-dtls.conf b/tests/test-maxfrag-dtls.conf index 67aef1776e..f41419ed0c 100644 --- a/tests/test-maxfrag-dtls.conf +++ b/tests/test-maxfrag-dtls.conf @@ -202,14 +202,3 @@ -v 3 -l ECDHE-RSA-AES256-GCM-SHA384 -F 6 - -# server DTLSv1.2 DHE-RSA-AES256-GCM-SHA384 --u --v 3 --l DHE-RSA-AES256-GCM-SHA384 - -# client DTLSv1.2 DHE-RSA-AES256-GCM-SHA384 --u --v 3 --l DHE-RSA-AES256-GCM-SHA384 --F 6 diff --git a/tests/test-maxfrag.conf b/tests/test-maxfrag.conf index 32cdfe2047..2253ab3c58 100644 --- a/tests/test-maxfrag.conf +++ b/tests/test-maxfrag.conf @@ -169,15 +169,6 @@ -l ECDHE-RSA-AES256-GCM-SHA384 -F 6 -# server TLSv1.2 DHE-RSA-AES256-GCM-SHA384 --v 3 --l DHE-RSA-AES256-GCM-SHA384 - -# client TLSv1.2 DHE-RSA-AES256-GCM-SHA384 --v 3 --l DHE-RSA-AES256-GCM-SHA384 --F 6 - # server TLSv1.2 DHE-RSA-AES256-GCM-SHA384 -v 3 -l DHE-RSA-AES256-GCM-SHA384 diff --git a/wolfcrypt/src/aes.c b/wolfcrypt/src/aes.c index 29911ccfa2..a2e5b6c8d3 100644 --- a/wolfcrypt/src/aes.c +++ b/wolfcrypt/src/aes.c @@ -70,9 +70,9 @@ block cipher mechanism that uses n-bit binary string parameter key with 128-bits #include #endif -#if defined(WOLFSSL_AES_SIV) +#ifdef WOLFSSL_CMAC #include -#endif /* WOLFSSL_AES_SIV */ +#endif #if defined(WOLFSSL_HAVE_PSA) && !defined(WOLFSSL_PSA_NO_AES) #include @@ -91,6 +91,11 @@ block cipher mechanism that uses n-bit binary string parameter key with 128-bits #if defined(WOLFSSL_TI_CRYPT) #include + + #define AesEncrypt_preFetchOpt(aes, inBlock, outBlock, do_preFetch) \ + wc_AesEncryptDirect(aes, outBlock, inBlock) + #define AesDecrypt_preFetchOpt(aes, inBlock, outBlock, do_preFetch) \ + wc_AesDecryptDirect(aes, outBlock, inBlock) #else @@ -2142,8 +2147,12 @@ static word32 GetTable8_4(const byte* t, byte o0, byte o1, byte o2, byte o3) * @param [out] outBlock Encrypted block. * @param [in] r Rounds divided by 2. */ +#define WC_AES_HAVE_PREFETCH_ARG +static int always_prefetch = 0; +WC_MAYBE_UNUSED static int never_prefetch = 1; +WC_ARGS_NOT_NULL((1, 2, 3, 5)) static void AesEncrypt_C(Aes* aes, const byte* inBlock, byte* outBlock, - word32 r) + word32 r, int *prefetch_ptr) { word32 s0 = 0, s1 = 0, s2 = 0, s3 = 0; word32 t0 = 0, t1 = 0, t2 = 0, t3 = 0; @@ -2178,8 +2187,15 @@ static void AesEncrypt_C(Aes* aes, const byte* inBlock, byte* outBlock, s3 ^= rk[3]; #ifndef WOLFSSL_AES_SMALL_TABLES + #ifndef WC_NO_CACHE_RESISTANT - s0 |= PreFetchTe(); + if (*prefetch_ptr == 0) { + s0 |= PreFetchTe(); + if (prefetch_ptr != &always_prefetch) + *prefetch_ptr = 1; + } +#else + (void)prefetch_ptr; #endif #ifndef WOLFSSL_AES_TOUCH_LINES @@ -2320,9 +2336,17 @@ static void AesEncrypt_C(Aes* aes, const byte* inBlock, byte* outBlock, s2 ^= u2 & 0x000000ff; s3 ^= u3 & 0x000000ff; } #endif -#else + +#else /* WOLFSSL_AES_SMALL_TABLES */ + #ifndef WC_NO_CACHE_RESISTANT - s0 |= PreFetchSBox(); + if (*prefetch_ptr == 0) { + s0 |= PreFetchSBox(); + if (prefetch_ptr != &always_prefetch) + *prefetch_ptr = 1; + } +#else + (void)prefetch_ptr; #endif r *= 2; @@ -2399,7 +2423,8 @@ static void AesEncrypt_C(Aes* aes, const byte* inBlock, byte* outBlock, s1 = t1 ^ rk[1]; s2 = t2 ^ rk[2]; s3 = t3 ^ rk[3]; -#endif + +#endif /* WOLFSSL_AES_SMALL_TABLES */ /* write out */ #ifdef LITTLE_ENDIAN_ORDER @@ -2429,9 +2454,10 @@ static void AesEncrypt_C(Aes* aes, const byte* inBlock, byte* outBlock, static void AesEncryptBlocks_C(Aes* aes, const byte* in, byte* out, word32 sz) { word32 i; + int did_prefetches = 0; for (i = 0; i < sz; i += WC_AES_BLOCK_SIZE) { - AesEncrypt_C(aes, in, out, aes->rounds >> 1); + AesEncrypt_C(aes, in, out, aes->rounds >> 1, &did_prefetches); in += WC_AES_BLOCK_SIZE; out += WC_AES_BLOCK_SIZE; } @@ -2998,10 +3024,18 @@ extern void AesEncryptBlocks_C(Aes* aes, const byte* in, byte* out, word32 sz); #endif /* !WC_AES_BITSLICED */ -/* this section disabled with NO_AES_192 */ -/* calling this one when missing NO_AES_192 */ +#ifdef WC_AES_HAVE_PREFETCH_ARG +#define wc_AesEncrypt(aes, inBlock, outBlock) \ + AesEncrypt_preFetchOpt(aes, inBlock, outBlock, &always_prefetch) +WC_ALL_ARGS_NOT_NULL static WARN_UNUSED_RESULT int AesEncrypt_preFetchOpt( + Aes* aes, const byte* inBlock, byte* outBlock, int *prefetch_ptr) +#else +#define AesEncrypt_preFetchOpt(aes, inBlock, outBlock, prefetch_ptr) \ + wc_AesEncrypt(aes, inBlock, outBlock) static WARN_UNUSED_RESULT int wc_AesEncrypt( Aes* aes, const byte* inBlock, byte* outBlock) + WC_ALL_ARGS_NOT_NULL +#endif { #if defined(MAX3266X_AES) word32 keySize; @@ -3153,7 +3187,11 @@ static WARN_UNUSED_RESULT int wc_AesEncrypt( } #endif +#ifdef WC_AES_HAVE_PREFETCH_ARG + AesEncrypt_C(aes, inBlock, outBlock, r, prefetch_ptr); +#else AesEncrypt_C(aes, inBlock, outBlock, r); +#endif return 0; } /* wc_AesEncrypt */ @@ -3211,8 +3249,14 @@ static WARN_UNUSED_RESULT WC_INLINE word32 PreFetchTd4(void) * @param [out] outBlock Encrypted block. * @param [in] r Rounds divided by 2. */ +#ifndef WC_AES_HAVE_PREFETCH_ARG + #define WC_AES_HAVE_PREFETCH_ARG + static int always_prefetch = 0; + WC_MAYBE_UNUSED static int never_prefetch = 1; +#endif +WC_ARGS_NOT_NULL((1, 2, 3, 5)) static void AesDecrypt_C(Aes* aes, const byte* inBlock, byte* outBlock, - word32 r) + word32 r, int *prefetch_ptr) { word32 s0 = 0, s1 = 0, s2 = 0, s3 = 0; word32 t0 = 0, t1 = 0, t2 = 0, t3 = 0; @@ -3246,8 +3290,16 @@ static void AesDecrypt_C(Aes* aes, const byte* inBlock, byte* outBlock, s3 ^= rk[3]; #ifndef WOLFSSL_AES_SMALL_TABLES + #ifndef WC_NO_CACHE_RESISTANT - s0 |= PreFetchTd(); + if (*prefetch_ptr == 0) { + s0 |= PreFetchTd(); + /* don't set the prefetched flag here -- PreFetchTd4() is called + * below. + */ + } +#else + (void)prefetch_ptr; #endif #ifndef WOLFSSL_AES_TOUCH_LINES @@ -3330,7 +3382,13 @@ static void AesDecrypt_C(Aes* aes, const byte* inBlock, byte* outBlock, */ #ifndef WC_NO_CACHE_RESISTANT - t0 |= PreFetchTd4(); + if (*prefetch_ptr == 0) { + t0 |= PreFetchTd4(); + if (prefetch_ptr != &always_prefetch) + *prefetch_ptr = 1; + } +#else + (void)prefetch_ptr; #endif s0 = GetTable8_4(Td4, GETBYTE(t0, 3), GETBYTE(t3, 2), @@ -3341,9 +3399,17 @@ static void AesDecrypt_C(Aes* aes, const byte* inBlock, byte* outBlock, GETBYTE(t0, 1), GETBYTE(t3, 0)) ^ rk[2]; s3 = GetTable8_4(Td4, GETBYTE(t3, 3), GETBYTE(t2, 2), GETBYTE(t1, 1), GETBYTE(t0, 0)) ^ rk[3]; -#else + +#else /* WOLFSSL_AES_SMALL_TABLES */ + #ifndef WC_NO_CACHE_RESISTANT - s0 |= PreFetchTd4(); + if (*prefetch_ptr == 0) { + s0 |= PreFetchTd4(); + if (prefetch_ptr != &always_prefetch) + *prefetch_ptr = 1; + } +#else + (void)prefetch_ptr; #endif r *= 2; @@ -3419,7 +3485,8 @@ static void AesDecrypt_C(Aes* aes, const byte* inBlock, byte* outBlock, s1 = t1 ^ rk[1]; s2 = t2 ^ rk[2]; s3 = t3 ^ rk[3]; -#endif + +#endif /* WOLFSSL_AES_SMALL_TABLES */ /* write out */ #ifdef LITTLE_ENDIAN_ORDER @@ -3450,9 +3517,10 @@ static void AesDecrypt_C(Aes* aes, const byte* inBlock, byte* outBlock, static void AesDecryptBlocks_C(Aes* aes, const byte* in, byte* out, word32 sz) { word32 i; + int did_prefetches = 0; for (i = 0; i < sz; i += WC_AES_BLOCK_SIZE) { - AesDecrypt_C(aes, in, out, aes->rounds >> 1); + AesDecrypt_C(aes, in, out, aes->rounds >> 1, &did_prefetches); in += WC_AES_BLOCK_SIZE; out += WC_AES_BLOCK_SIZE; } @@ -3808,8 +3876,18 @@ static void AesDecryptBlocks_C(Aes* aes, const byte* in, byte* out, word32 sz) #if defined(__aarch64__) || !defined(WOLFSSL_ARMASM) #if !defined(WC_AES_BITSLICED) || defined(WOLFSSL_AES_DIRECT) /* Software AES - ECB Decrypt */ -static WARN_UNUSED_RESULT int wc_AesDecrypt( + +#ifdef WC_AES_HAVE_PREFETCH_ARG +#define wc_AesDecrypt(aes, inBlock, outBlock) \ + AesDecrypt_preFetchOpt(aes, inBlock, outBlock, &always_prefetch) +WC_ALL_ARGS_NOT_NULL static WARN_UNUSED_RESULT int AesDecrypt_preFetchOpt( + Aes* aes, const byte* inBlock, byte* outBlock, int *prefetch_ptr) +#else +#define AesDecrypt_preFetchOpt(aes, inBlock, outBlock, prefetch_ptr) \ + wc_AesDecrypt(aes, inBlock, outBlock) +WC_ALL_ARGS_NOT_NULL static WARN_UNUSED_RESULT int wc_AesDecrypt( Aes* aes, const byte* inBlock, byte* outBlock) +#endif { #if defined(MAX3266X_AES) word32 keySize; @@ -3935,7 +4013,11 @@ static WARN_UNUSED_RESULT int wc_AesDecrypt( } #endif +#ifdef WC_AES_HAVE_PREFETCH_ARG + AesDecrypt_C(aes, inBlock, outBlock, r, prefetch_ptr); +#else AesDecrypt_C(aes, inBlock, outBlock, r); +#endif return 0; } /* wc_AesDecrypt[_SW]() */ @@ -3946,7 +4028,16 @@ static WARN_UNUSED_RESULT int wc_AesDecrypt( #endif /* NEED_AES_TABLES */ - +#ifndef WC_AES_HAVE_PREFETCH_ARG + #ifndef AesEncrypt_preFetchOpt + #define AesEncrypt_preFetchOpt(aes, inBlock, outBlock, do_preFetch) \ + wc_AesEncrypt(aes, inBlock, outBlock) + #endif + #ifndef AesDecrypt_preFetchOpt + #define AesDecrypt_preFetchOpt(aes, inBlock, outBlock, do_preFetch) \ + wc_AesDecrypt(aes, inBlock, outBlock) + #endif +#endif /* wc_AesSetKey */ #if defined(STM32_CRYPTO) @@ -5335,6 +5426,7 @@ int wc_AesSetIV(Aes* aes, const byte* iv) #else /* Allow direct access to one block encrypt */ + /* Note, the in and out args are swapped compared to wc_AesEncrypt(). */ int wc_AesEncryptDirect(Aes* aes, byte* out, const byte* in) { int ret; @@ -5355,6 +5447,7 @@ int wc_AesSetIV(Aes* aes, const byte* iv) #ifdef HAVE_AES_DECRYPT /* Allow direct access to one block decrypt */ + /* Note, the in and out args are swapped compared to wc_AesDecrypt(). */ int wc_AesDecryptDirect(Aes* aes, byte* out, const byte* in) { int ret; @@ -6097,7 +6190,6 @@ int wc_AesSetIV(Aes* aes, const byte* iv) offset += WC_AES_BLOCK_SIZE; } - return 0; } #endif /* HAVE_AES_DECRYPT */ @@ -6471,10 +6563,15 @@ int wc_AesCbcEncrypt(Aes* aes, byte* out, const byte* in, word32 sz) else #endif { +#ifdef WC_AES_HAVE_PREFETCH_ARG + int did_prefetches = 0; +#endif ret = 0; while (blocks--) { xorbuf((byte*)aes->reg, in, WC_AES_BLOCK_SIZE); - ret = wc_AesEncrypt(aes, (byte*)aes->reg, (byte*)aes->reg); + ret = AesEncrypt_preFetchOpt(aes, (byte*)aes->reg, + (byte*)aes->reg, + &did_prefetches); if (ret != 0) break; XMEMCPY(out, aes->reg, WC_AES_BLOCK_SIZE); @@ -6713,9 +6810,13 @@ int wc_AesCbcEncrypt(Aes* aes, byte* out, const byte* in, word32 sz) } } #else +#ifdef WC_AES_HAVE_PREFETCH_ARG + { + int did_prefetches = 0; +#endif while (blocks--) { XMEMCPY(aes->tmp, in, WC_AES_BLOCK_SIZE); - ret = wc_AesDecrypt(aes, in, out); + ret = AesDecrypt_preFetchOpt(aes, in, out, &did_prefetches); if (ret != 0) return ret; xorbuf(out, (byte*)aes->reg, WC_AES_BLOCK_SIZE); @@ -6725,6 +6826,9 @@ int wc_AesCbcEncrypt(Aes* aes, byte* out, const byte* in, word32 sz) out += WC_AES_BLOCK_SIZE; in += WC_AES_BLOCK_SIZE; } +#ifdef WC_AES_HAVE_PREFETCH_ARG + } +#endif #endif } @@ -6967,6 +7071,9 @@ int wc_AesCbcEncrypt(Aes* aes, byte* out, const byte* in, word32 sz) int ret = 0; #endif word32 processed; +#ifdef WC_AES_HAVE_PREFETCH_ARG + int did_prefetches = 0; +#endif #if !(!defined(__aarch64__) && defined(WOLFSSL_ARMASM) && \ !defined(WOLFSSL_ARMASM_NO_HW_CRYPTO)) @@ -7118,7 +7225,9 @@ int wc_AesCbcEncrypt(Aes* aes, byte* out, const byte* in, word32 sz) #ifdef XTRANSFORM_AESCTRBLOCK XTRANSFORM_AESCTRBLOCK(aes, out, in); #else - ret = wc_AesEncrypt(aes, (byte*)aes->reg, scratch); + ret = AesEncrypt_preFetchOpt(aes, (byte*)aes->reg, + scratch, + &did_prefetches); if (ret != 0) break; xorbuf(scratch, in, WC_AES_BLOCK_SIZE); @@ -7136,7 +7245,9 @@ int wc_AesCbcEncrypt(Aes* aes, byte* out, const byte* in, word32 sz) /* handle non block size remaining and store unused byte count in left */ if ((ret == 0) && sz) { - ret = wc_AesEncrypt(aes, (byte*)aes->reg, (byte*)aes->tmp); + ret = AesEncrypt_preFetchOpt(aes, (byte*)aes->reg, + (byte*)aes->tmp, + &did_prefetches); if (ret == 0) { IncrementAesCounter((byte*)aes->reg); aes->left = WC_AES_BLOCK_SIZE - sz; @@ -7173,8 +7284,26 @@ int wc_AesCbcEncrypt(Aes* aes, byte* out, const byte* in, word32 sz) #endif /* NEED_AES_CTR_SOFT */ #endif /* WOLFSSL_AES_COUNTER */ -#endif /* !WOLFSSL_RISCV_ASM */ +#ifndef WC_AES_HAVE_PREFETCH_ARG + #ifndef AesEncrypt_preFetchOpt + #define AesEncrypt_preFetchOpt(aes, inBlock, outBlock, do_preFetch) \ + wc_AesEncrypt(aes, inBlock, outBlock) + #endif + #ifndef AesDecrypt_preFetchOpt + #define AesDecrypt_preFetchOpt(aes, inBlock, outBlock, do_preFetch) \ + wc_AesDecrypt(aes, inBlock, outBlock) + #endif +#endif + +#else /* WOLFSSL_RISCV_ASM */ + +#define AesEncrypt_preFetchOpt(aes, inBlock, outBlock, do_preFetch) \ + wc_AesEncryptDirect(aes, outBlock, inBlock) +#define AesDecrypt_preFetchOpt(aes, inBlock, outBlock, do_preFetch) \ + wc_AesDecryptDirect(aes, outBlock, inBlock) + +#endif /* WOLFSSL_RISCV_ASM */ /* * The IV for AES GCM and CCM, stored in struct Aes's member reg, is comprised @@ -9616,6 +9745,9 @@ WARN_UNUSED_RESULT int AES_GCM_encrypt_C( ALIGN16 byte counter[WC_AES_BLOCK_SIZE]; ALIGN16 byte initialCounter[WC_AES_BLOCK_SIZE]; ALIGN16 byte scratch[WC_AES_BLOCK_SIZE]; +#ifdef WC_AES_HAVE_PREFETCH_ARG + int did_prefetches = 0; +#endif if (ivSz == GCM_NONCE_MID_SZ) { /* Counter is IV with bottom 4 bytes set to: 0x00,0x00,0x00,0x01. */ @@ -9674,7 +9806,8 @@ WARN_UNUSED_RESULT int AES_GCM_encrypt_C( while (blocks--) { IncrementGcmCounter(counter); #if !defined(WOLFSSL_PIC32MZ_CRYPT) - ret = wc_AesEncrypt(aes, counter, scratch); + ret = AesEncrypt_preFetchOpt(aes, counter, scratch, + &did_prefetches); if (ret != 0) return ret; xorbufout(c, scratch, p, WC_AES_BLOCK_SIZE); @@ -9686,14 +9819,15 @@ WARN_UNUSED_RESULT int AES_GCM_encrypt_C( if (partial != 0) { IncrementGcmCounter(counter); - ret = wc_AesEncrypt(aes, counter, scratch); + ret = AesEncrypt_preFetchOpt(aes, counter, scratch, &did_prefetches); if (ret != 0) return ret; xorbufout(c, scratch, p, partial); } if (authTag) { GHASH(&aes->gcm, authIn, authInSz, out, sz, authTag, authTagSz); - ret = wc_AesEncrypt(aes, initialCounter, scratch); + ret = AesEncrypt_preFetchOpt(aes, initialCounter, scratch, + &did_prefetches); if (ret != 0) return ret; xorbuf(authTag, scratch, authTagSz); @@ -12814,7 +12948,11 @@ static WARN_UNUSED_RESULT int roll_x( in += WC_AES_BLOCK_SIZE; inSz -= WC_AES_BLOCK_SIZE; - ret = wc_AesEncrypt(aes, out, out); + /* wc_AesCcmEncrypt(), wc_AesCcmDecrypt(), and roll_auth() only call + * roll_x() after the AES cache lines are already hot -- no need to + * absorb additional prefetch overhead here. + */ + ret = AesEncrypt_preFetchOpt(aes, out, out, &never_prefetch); if (ret != 0) return ret; } @@ -12822,7 +12960,11 @@ static WARN_UNUSED_RESULT int roll_x( /* process remainder of the data */ if (inSz > 0) { xorbuf(out, in, inSz); - ret = wc_AesEncrypt(aes, out, out); + /* wc_AesCcmEncrypt(), wc_AesCcmDecrypt(), and roll_auth() only call + * roll_x() after the AES cache lines are already hot -- no need to + * absorb additional prefetch overhead here. + */ + ret = AesEncrypt_preFetchOpt(aes, out, out, &never_prefetch); if (ret != 0) return ret; } @@ -12870,7 +13012,11 @@ static WARN_UNUSED_RESULT int roll_auth( xorbuf(out + authLenSz, in, inSz); inSz = 0; } - ret = wc_AesEncrypt(aes, out, out); + /* wc_AesCcmEncrypt() and wc_AesCcmDecrypt() only call roll_auth() after the + * AES cache lines are already hot -- no need to absorb additional prefetch + * overhead here. + */ + ret = AesEncrypt_preFetchOpt(aes, out, out, &never_prefetch); if ((ret == 0) && (inSz > 0)) { ret = roll_x(aes, in, inSz, out); @@ -12996,6 +13142,9 @@ int wc_AesCcmEncrypt(Aes* aes, byte* out, const byte* in, word32 inSz, #endif VECTOR_REGISTERS_PUSH; + /* note this wc_AesEncrypt() will perform cache prefetches if needed, so + * that the later encrypt ops don't need to. + */ ret = wc_AesEncrypt(aes, B, A); #ifdef WOLFSSL_CHECK_MEM_ZERO if (ret == 0) @@ -13014,7 +13163,7 @@ int wc_AesCcmEncrypt(Aes* aes, byte* out, const byte* in, word32 inSz, B[0] = (byte)(lenSz - 1U); for (i = 0; i < lenSz; i++) B[WC_AES_BLOCK_SIZE - 1 - i] = 0; - ret = wc_AesEncrypt(aes, B, A); + ret = AesEncrypt_preFetchOpt(aes, B, A, &never_prefetch); } if (ret == 0) { @@ -13042,7 +13191,7 @@ int wc_AesCcmEncrypt(Aes* aes, byte* out, const byte* in, word32 inSz, #endif if (ret == 0) { while (inSz >= WC_AES_BLOCK_SIZE) { - ret = wc_AesEncrypt(aes, B, A); + ret = AesEncrypt_preFetchOpt(aes, B, A, &never_prefetch); if (ret != 0) break; xorbuf(A, in, WC_AES_BLOCK_SIZE); @@ -13055,7 +13204,7 @@ int wc_AesCcmEncrypt(Aes* aes, byte* out, const byte* in, word32 inSz, } } if ((ret == 0) && (inSz > 0)) { - ret = wc_AesEncrypt(aes, B, A); + ret = AesEncrypt_preFetchOpt(aes, B, A, &never_prefetch); } if ((ret == 0) && (inSz > 0)) { xorbuf(A, in, inSz); @@ -13095,6 +13244,9 @@ int wc_AesCcmDecrypt(Aes* aes, byte* out, const byte* in, word32 inSz, byte mask = 0xFF; const word32 wordSz = (word32)sizeof(word32); int ret = 0; +#ifdef WC_AES_HAVE_PREFETCH_ARG + int did_prefetches = 0; +#endif /* sanity check on arguments */ if (aes == NULL || (inSz != 0 && (in == NULL || out == NULL)) || @@ -13165,7 +13317,7 @@ int wc_AesCcmDecrypt(Aes* aes, byte* out, const byte* in, word32 inSz, #endif while (oSz >= WC_AES_BLOCK_SIZE) { - ret = wc_AesEncrypt(aes, B, A); + ret = AesEncrypt_preFetchOpt(aes, B, A, &did_prefetches); if (ret != 0) break; xorbuf(A, in, WC_AES_BLOCK_SIZE); @@ -13177,14 +13329,14 @@ int wc_AesCcmDecrypt(Aes* aes, byte* out, const byte* in, word32 inSz, } if ((ret == 0) && (inSz > 0)) - ret = wc_AesEncrypt(aes, B, A); + ret = AesEncrypt_preFetchOpt(aes, B, A, &did_prefetches); if ((ret == 0) && (inSz > 0)) { xorbuf(A, in, oSz); XMEMCPY(o, A, oSz); for (i = 0; i < lenSz; i++) B[WC_AES_BLOCK_SIZE - 1 - i] = 0; - ret = wc_AesEncrypt(aes, B, A); + ret = AesEncrypt_preFetchOpt(aes, B, A, &did_prefetches); } if (ret == 0) { @@ -13200,7 +13352,7 @@ int wc_AesCcmDecrypt(Aes* aes, byte* out, const byte* in, word32 inSz, B[WC_AES_BLOCK_SIZE - 1 - i] = (byte)((inSz >> ((8 * i) & mask)) & mask); } - ret = wc_AesEncrypt(aes, B, A); + ret = AesEncrypt_preFetchOpt(aes, B, A, &did_prefetches); } if (ret == 0) { @@ -13214,7 +13366,7 @@ int wc_AesCcmDecrypt(Aes* aes, byte* out, const byte* in, word32 inSz, B[0] = (byte)(lenSz - 1U); for (i = 0; i < lenSz; i++) B[WC_AES_BLOCK_SIZE - 1 - i] = 0; - ret = wc_AesEncrypt(aes, B, B); + ret = AesEncrypt_preFetchOpt(aes, B, B, &did_prefetches); } if (ret == 0) @@ -13772,9 +13924,12 @@ static WARN_UNUSED_RESULT int _AesEcbEncrypt( AesEncryptBlocks_C(aes, in, out, sz); #else word32 i; +#ifdef WC_AES_HAVE_PREFETCH_ARG + int did_prefetches = 0; +#endif for (i = 0; i < sz; i += WC_AES_BLOCK_SIZE) { - ret = wc_AesEncryptDirect(aes, out, in); + ret = AesEncrypt_preFetchOpt(aes, in, out, &did_prefetches); if (ret != 0) break; in += WC_AES_BLOCK_SIZE; @@ -13936,6 +14091,9 @@ static WARN_UNUSED_RESULT int AesCfbEncrypt_C(Aes* aes, byte* out, { int ret = 0; word32 processed; +#ifdef WC_AES_HAVE_PREFETCH_ARG + int did_prefetches = 0; +#endif if ((aes == NULL) || (out == NULL) || (in == NULL)) { return BAD_FUNC_ARG; @@ -13960,7 +14118,8 @@ static WARN_UNUSED_RESULT int AesCfbEncrypt_C(Aes* aes, byte* out, VECTOR_REGISTERS_PUSH; while (sz >= WC_AES_BLOCK_SIZE) { - ret = wc_AesEncryptDirect(aes, (byte*)aes->reg, (byte*)aes->reg); + ret = AesEncrypt_preFetchOpt(aes, (byte*)aes->reg, (byte*)aes->reg, + &did_prefetches); if (ret != 0) { break; } @@ -13973,7 +14132,8 @@ static WARN_UNUSED_RESULT int AesCfbEncrypt_C(Aes* aes, byte* out, /* encrypt left over data */ if ((ret == 0) && sz) { - ret = wc_AesEncryptDirect(aes, (byte*)aes->tmp, (byte*)aes->reg); + ret = AesEncrypt_preFetchOpt(aes, (byte*)aes->reg, (byte*)aes->tmp, + &did_prefetches); if (ret == 0) { xorbufout(out, in, aes->tmp, sz); XMEMCPY(aes->reg, out, sz); @@ -14004,6 +14164,9 @@ static WARN_UNUSED_RESULT int AesCfbDecrypt_C(Aes* aes, byte* out, { int ret = 0; word32 processed; +#ifdef WC_AES_HAVE_PREFETCH_ARG + int did_prefetches = 0; +#endif (void)mode; @@ -14050,7 +14213,8 @@ static WARN_UNUSED_RESULT int AesCfbDecrypt_C(Aes* aes, byte* out, } #endif while (sz >= WC_AES_BLOCK_SIZE) { - ret = wc_AesEncryptDirect(aes, (byte*)aes->tmp, (byte*)aes->reg); + ret = AesEncrypt_preFetchOpt(aes, (byte*)aes->reg, (byte*)aes->tmp, + &did_prefetches); if (ret != 0) { break; } @@ -14063,7 +14227,8 @@ static WARN_UNUSED_RESULT int AesCfbDecrypt_C(Aes* aes, byte* out, /* decrypt left over data */ if ((ret == 0) && sz) { - ret = wc_AesEncryptDirect(aes, (byte*)aes->tmp, (byte*)aes->reg); + ret = AesEncrypt_preFetchOpt(aes, (byte*)aes->reg, (byte*)aes->tmp, + &did_prefetches); if (ret == 0) { XMEMCPY(aes->reg, in, sz); xorbufout(out, in, aes->tmp, sz); @@ -14144,6 +14309,9 @@ static WARN_UNUSED_RESULT int wc_AesFeedbackCFB8( { byte *pt; int ret = 0; +#ifdef WC_AES_HAVE_PREFETCH_ARG + int did_prefetches = 0; +#endif if (aes == NULL || out == NULL || in == NULL) { return BAD_FUNC_ARG; @@ -14156,7 +14324,8 @@ static WARN_UNUSED_RESULT int wc_AesFeedbackCFB8( VECTOR_REGISTERS_PUSH; while (sz > 0) { - ret = wc_AesEncryptDirect(aes, (byte*)aes->tmp, (byte*)aes->reg); + ret = AesEncrypt_preFetchOpt(aes, (byte*)aes->reg, (byte*)aes->tmp, + &did_prefetches); if (ret != 0) break; if (dir == AES_DECRYPTION) { @@ -14200,6 +14369,9 @@ static WARN_UNUSED_RESULT int wc_AesFeedbackCFB1( byte* pt; int bit = 7; int ret = 0; +#ifdef WC_AES_HAVE_PREFETCH_ARG + int did_prefetches = 0; +#endif if (aes == NULL || out == NULL || in == NULL) { return BAD_FUNC_ARG; @@ -14212,7 +14384,8 @@ static WARN_UNUSED_RESULT int wc_AesFeedbackCFB1( VECTOR_REGISTERS_PUSH; while (sz > 0) { - ret = wc_AesEncryptDirect(aes, (byte*)aes->tmp, (byte*)aes->reg); + ret = AesEncrypt_preFetchOpt(aes, (byte*)aes->reg, (byte*)aes->tmp, + &did_prefetches); if (ret != 0) break; if (dir == AES_DECRYPTION) { @@ -14351,6 +14524,9 @@ static WARN_UNUSED_RESULT int AesOfbCrypt_C(Aes* aes, byte* out, const byte* in, { int ret = 0; word32 processed; +#ifdef WC_AES_HAVE_PREFETCH_ARG + int did_prefetches = 0; +#endif if ((aes == NULL) || (out == NULL) || (in == NULL)) { return BAD_FUNC_ARG; @@ -14373,7 +14549,8 @@ static WARN_UNUSED_RESULT int AesOfbCrypt_C(Aes* aes, byte* out, const byte* in, VECTOR_REGISTERS_PUSH; while (sz >= WC_AES_BLOCK_SIZE) { - ret = wc_AesEncryptDirect(aes, (byte*)aes->reg, (byte*)aes->reg); + ret = AesEncrypt_preFetchOpt(aes, (byte*)aes->reg, (byte*)aes->reg, + &did_prefetches); if (ret != 0) { break; } @@ -14385,7 +14562,8 @@ static WARN_UNUSED_RESULT int AesOfbCrypt_C(Aes* aes, byte* out, const byte* in, /* encrypt left over data */ if ((ret == 0) && sz) { - ret = wc_AesEncryptDirect(aes, (byte*)aes->tmp, (byte*)aes->reg); + ret = AesEncrypt_preFetchOpt(aes, (byte*)aes->reg, (byte*)aes->tmp, + &did_prefetches); if (ret == 0) { XMEMCPY(aes->reg, aes->tmp, WC_AES_BLOCK_SIZE); xorbufout(out, in, aes->tmp, sz); @@ -14643,7 +14821,7 @@ int wc_AesKeyUnWrap_ex(Aes *aes, const byte* in, word32 inSz, byte* out, return ret; /* verify IV */ - if (XMEMCMP(tmp, expIv, KEYWRAP_BLOCK_SIZE) != 0) + if (ConstantCompare(tmp, expIv, KEYWRAP_BLOCK_SIZE) != 0) return BAD_KEYWRAP_IV_E; return (int)(inSz - KEYWRAP_BLOCK_SIZE); @@ -16096,6 +16274,45 @@ int wc_AesXtsDecryptConsecutiveSectors(XtsAes* aes, byte* out, const byte* in, #endif /* HAVE_AES_DECRYPT */ #endif /* WOLFSSL_AES_XTS */ +#ifdef WOLFSSL_CMAC + +int wc_local_CmacUpdateAes(struct Cmac *cmac, const byte* in, word32 inSz) { + int ret = 0; + Aes *aes = &cmac->aes; +#ifdef WC_AES_HAVE_PREFETCH_ARG + int did_prefetches = 0; +#endif + + VECTOR_REGISTERS_PUSH; + + while ((ret == 0) && (inSz != 0)) { + word32 add = min(inSz, WC_AES_BLOCK_SIZE - cmac->bufferSz); + XMEMCPY(&cmac->buffer[cmac->bufferSz], in, add); + + cmac->bufferSz += add; + inSz -= add; + in += add; + + if (cmac->bufferSz == WC_AES_BLOCK_SIZE && inSz != 0) { + if (cmac->totalSz != 0) { + xorbuf(cmac->buffer, cmac->digest, WC_AES_BLOCK_SIZE); + } + ret = AesEncrypt_preFetchOpt(aes, cmac->buffer, + cmac->digest, &did_prefetches); + if (ret == 0) { + cmac->totalSz += WC_AES_BLOCK_SIZE; + cmac->bufferSz = 0; + } + } + } + + VECTOR_REGISTERS_POP; + + return ret; +} + +#endif /* WOLFSSL_CMAC */ + #ifdef WOLFSSL_AES_SIV /* @@ -16303,7 +16520,7 @@ static WARN_UNUSED_RESULT int AesSivCipher( WOLFSSL_MSG("S2V failed."); } - if (XMEMCMP(siv, sivTmp, WC_AES_BLOCK_SIZE) != 0) { + if (ConstantCompare(siv, sivTmp, WC_AES_BLOCK_SIZE) != 0) { WOLFSSL_MSG("Computed SIV doesn't match received SIV."); ret = AES_SIV_AUTH_E; } @@ -17147,5 +17364,4 @@ int wc_AesCtsDecryptFinal(Aes* aes, byte* out, word32* outSz) #endif /* WOLFSSL_AES_CTS */ - #endif /* !NO_AES */ diff --git a/wolfcrypt/src/asn.c b/wolfcrypt/src/asn.c index 3be5e33fb0..231d250b6e 100644 --- a/wolfcrypt/src/asn.c +++ b/wolfcrypt/src/asn.c @@ -487,7 +487,7 @@ static word32 SizeASNLength(word32 length) #define ALLOC_ASNSETDATA(name, cnt, err, heap) \ do { \ if ((err) == 0) { \ - (name) = (ASNSetData*)XMALLOC(sizeof(ASNGetData) * (cnt), (heap), \ + (name) = (ASNSetData*)XMALLOC(sizeof(ASNSetData) * (cnt), (heap), \ DYNAMIC_TYPE_TMP_BUFFER); \ if ((name) == NULL) { \ (err) = MEMORY_E; \ @@ -2539,7 +2539,7 @@ int GetASNHeader(const byte* input, byte tag, word32* inOutIdx, int* len, return GetASNHeader_ex(input, tag, inOutIdx, len, maxIdx, 1); } -#ifndef WOLFSSL_ASN_TEMPLATE +#if !defined(WOLFSSL_ASN_TEMPLATE) static int GetHeader(const byte* input, byte* tag, word32* inOutIdx, int* len, word32 maxIdx, int check) { @@ -2894,7 +2894,9 @@ static int GetInteger7Bit(const byte* input, word32* inOutIdx, word32 maxIdx) return b; } #endif /* !NO_CERTS */ +#endif /* !WOLFSSL_ASN_TEMPLATE */ +#if !defined(WOLFSSL_ASN_TEMPLATE) #if ((defined(WC_RSA_PSS) && !defined(NO_RSA)) || !defined(NO_CERTS)) /* Get the DER/BER encoding of an ASN.1 INTEGER that has a value of no more than * 16 bits. @@ -2932,7 +2934,7 @@ static int GetInteger16Bit(const byte* input, word32* inOutIdx, word32 maxIdx) return ASN_PARSE_E; } n = input[idx++]; - n = (n << 8) | input[idx++]; + n = (word16)((n << 8) | input[idx++]); } else return ASN_PARSE_E; @@ -2940,7 +2942,7 @@ static int GetInteger16Bit(const byte* input, word32* inOutIdx, word32 maxIdx) *inOutIdx = idx; return n; } -#endif /* WC_RSA_PSS && !NO_RSA */ +#endif /* WC_RSA_PSS || !NO_CERTS */ #endif /* !WOLFSSL_ASN_TEMPLATE */ #if !defined(NO_DSA) && !defined(NO_SHA) @@ -3132,10 +3134,12 @@ const char* GetSigName(int oid) { #if !defined(WOLFSSL_ASN_TEMPLATE) || defined(HAVE_PKCS7) || \ - defined(OPENSSL_EXTRA) || defined(WOLFSSL_CERT_GEN) + defined(OPENSSL_EXTRA) || defined(WOLFSSL_CERT_GEN) || defined(WC_RSA_PSS) #if !defined(NO_DSA) || defined(HAVE_ECC) || !defined(NO_CERTS) || \ defined(WOLFSSL_CERT_GEN) || \ - (!defined(NO_RSA) && (defined(WOLFSSL_KEY_GEN) || defined(OPENSSL_EXTRA))) + (!defined(NO_RSA) && \ + (defined(WOLFSSL_KEY_GEN) || defined(OPENSSL_EXTRA))) || \ + (defined(WC_RSA_PSS) && !defined(NO_RSA)) /* Set the DER/BER encoding of the ASN.1 INTEGER header. * * When output is NULL, calculate the header length only. @@ -3146,7 +3150,7 @@ const char* GetSigName(int oid) { * @param [out] output Buffer to write into. * @return Number of bytes added to the buffer. */ -int SetASNInt(int len, byte firstByte, byte* output) +WOLFSSL_LOCAL int SetASNInt(int len, byte firstByte, byte* output) { int idx = 0; @@ -7452,6 +7456,9 @@ static int RsaPssHashOidToSigOid(word32 oid, word32* sigOid) } #endif +/* RSASSA-PSS-params context-specific tags. + * RFC 4055, 3.1 + */ #ifdef WOLFSSL_ASN_TEMPLATE /* ASN tag for hashAlgorithm. */ #define ASN_TAG_RSA_PSS_HASH (ASN_CONTEXT_SPECIFIC | 0) @@ -7498,7 +7505,7 @@ enum { RSAPSSPARAMSASN_IDX_TRAILERINT }; -/* Number of items in ASN.1 template for an algorithm identifier. */ +/* Number of items in ASN.1 template for RSA PSS parameters. */ #define rsaPssParamsASN_Length (sizeof(rsaPssParamsASN) / sizeof(ASNItem)) #else /* ASN tag for hashAlgorithm. */ @@ -7525,122 +7532,44 @@ enum { static int DecodeRsaPssParams(const byte* params, word32 sz, enum wc_HashType* hash, int* mgf, int* saltLen) { -#ifndef WOLFSSL_ASN_TEMPLATE - int ret = 0; - word32 idx = 0; - int len = 0; - word32 oid = 0; - byte tag; - int length; + if (params == NULL) + return BAD_FUNC_ARG; - if (params == NULL) { - ret = BAD_FUNC_ARG; + /* Empty or NULL-tag parameters mean all defaults per RFC 4055 */ + if (sz == 0) { + *hash = WC_HASH_TYPE_SHA; + *mgf = WC_MGF1SHA1; + *saltLen = 20; + return 0; } - if ((ret == 0) && (GetSequence_ex(params, &idx, &len, sz, 1) < 0)) { - ret = ASN_PARSE_E; - } - if (ret == 0) { - if ((idx < sz) && (params[idx] == ASN_TAG_RSA_PSS_HASH)) { - /* Hash algorithm to use on message. */ - if (GetHeader(params, &tag, &idx, &length, sz, 0) < 0) { - ret = ASN_PARSE_E; - } - if (ret == 0) { - if (GetAlgoId(params, &idx, &oid, oidHashType, sz) < 0) { - ret = ASN_PARSE_E; - } - } - if (ret == 0) { - ret = RsaPssHashOidToType(oid, hash); - } - } - else { - /* Default hash algorithm. */ + if (params[0] == ASN_TAG_NULL) { + if (sz >= 2 && params[1] == 0) { *hash = WC_HASH_TYPE_SHA; - } - } - if (ret == 0) { - if ((idx < sz) && (params[idx] == ASN_TAG_RSA_PSS_MGF)) { - /* MGF and hash algorithm to use with padding. */ - if (GetHeader(params, &tag, &idx, &length, sz, 0) < 0) { - ret = ASN_PARSE_E; - } - if (ret == 0) { - if (GetAlgoId(params, &idx, &oid, oidIgnoreType, sz) < 0) { - ret = ASN_PARSE_E; - } - } - if ((ret == 0) && (oid != MGF1_OID)) { - ret = ASN_PARSE_E; - } - if (ret == 0) { - ret = GetAlgoId(params, &idx, &oid, oidHashType, sz); - if (ret == 0) { - ret = RsaPssHashOidToMgf1(oid, mgf); - } - } - } - else { - /* Default MGF/Hash algorithm. */ *mgf = WC_MGF1SHA1; - } - } - if (ret == 0) { - if ((idx < sz) && (params[idx] == ASN_TAG_RSA_PSS_SALTLEN)) { - /* Salt length to use with padding. */ - if (GetHeader(params, &tag, &idx, &length, sz, 0) < 0) { - ret = ASN_PARSE_E; - } - if (ret == 0) { - ret = GetInteger16Bit(params, &idx, sz); - if (ret >= 0) { - *saltLen = ret; - ret = 0; - } - } - } - else { - /* Default salt length. */ *saltLen = 20; + return 0; } + return ASN_PARSE_E; } - if (ret == 0) { - if ((idx < sz) && (params[idx] == ASN_TAG_RSA_PSS_TRAILER)) { - /* Unused - trialerField. */ - if (GetHeader(params, &tag, &idx, &length, sz, 0) < 0) { - ret = ASN_PARSE_E; - } - if (ret == 0) { - ret = GetInteger16Bit(params, &idx, sz); - if (ret > 0) { - ret = 0; - } - } - } - } - if ((ret == 0) && (idx != sz)) { - ret = ASN_PARSE_E; - } + if (params[0] != (ASN_SEQUENCE | ASN_CONSTRUCTED)) + return ASN_PARSE_E; - return ret; -#else +#ifdef WOLFSSL_ASN_TEMPLATE +{ DECL_ASNGETDATA(dataASN, rsaPssParamsASN_Length); int ret = 0; word16 sLen = 20; - if (params == NULL) { - ret = BAD_FUNC_ARG; - } + /* Default values. */ + *hash = WC_HASH_TYPE_SHA; + *mgf = WC_MGF1SHA1; CALLOC_ASNGETDATA(dataASN, rsaPssParamsASN_Length, ret, NULL); if (ret == 0) { word32 inOutIdx = 0; - /* Default values. */ - *hash = WC_HASH_TYPE_SHA; - *mgf = WC_MGF1SHA1; - /* Set OID type expected. */ GetASN_OID(&dataASN[RSAPSSPARAMSASN_IDX_HASHOID], oidHashType); + GetASN_OID(&dataASN[RSAPSSPARAMSASN_IDX_MGFOID], oidIgnoreType); GetASN_OID(&dataASN[RSAPSSPARAMSASN_IDX_MGFHOID], oidHashType); /* Place the salt length into 16-bit var sLen. */ GetASN_Int16Bit(&dataASN[RSAPSSPARAMSASN_IDX_SALTLENINT], &sLen); @@ -7652,6 +7581,10 @@ static int DecodeRsaPssParams(const byte* params, word32 sz, word32 oid = dataASN[RSAPSSPARAMSASN_IDX_HASHOID].data.oid.sum; ret = RsaPssHashOidToType(oid, hash); } + if ((ret == 0) && (dataASN[RSAPSSPARAMSASN_IDX_MGFOID].tag != 0)) { + if (dataASN[RSAPSSPARAMSASN_IDX_MGFOID].data.oid.sum != MGF1_OID) + ret = ASN_PARSE_E; + } if ((ret == 0) && (dataASN[RSAPSSPARAMSASN_IDX_MGFHOID].tag != 0)) { word32 oid = dataASN[RSAPSSPARAMSASN_IDX_MGFHOID].data.oid.sum; ret = RsaPssHashOidToMgf1(oid, mgf); @@ -7662,8 +7595,317 @@ static int DecodeRsaPssParams(const byte* params, word32 sz, FREE_ASNGETDATA(dataASN, NULL); return ret; +} +#else /* !WOLFSSL_ASN_TEMPLATE */ +{ + int ret = 0; + word32 idx = 0; + int len = 0; + word32 oid = 0; + byte tag; + int length; + + /* Decode RSASSA-PSS-params SEQUENCE content. */ + if (GetSequence_ex(params, &idx, &len, sz, 1) < 0) { + WOLFSSL_MSG("DecodeRsaPssParams: fail at sequence"); + return ASN_PARSE_E; + } + + /* [0] hashAlgorithm */ + if (ret == 0) { + if ((idx < sz) && (params[idx] == ASN_TAG_RSA_PSS_HASH)) { + /* Hash algorithm to use on message. */ + if (GetHeader(params, &tag, &idx, &length, sz, 0) < 0) { + WOLFSSL_MSG("DecodeRsaPssParams: fail at hash_header"); + ret = ASN_PARSE_E; + } + if (ret == 0) { + if (GetAlgoId(params, &idx, &oid, oidHashType, sz) < 0) { + WOLFSSL_MSG("DecodeRsaPssParams: fail at hash_algo"); + ret = ASN_PARSE_E; + } + } + if (ret == 0) { + ret = RsaPssHashOidToType(oid, hash); + if (ret != 0) { + WOLFSSL_MSG("DecodeRsaPssParams: fail at hash_oid"); + } + } + } + else { + /* Default hash algorithm. */ + *hash = WC_HASH_TYPE_SHA; + } + } + + /* [1] maskGenAlgorithm -- AlgorithmIdentifier { OID id-mgf1, hash AlgoId } + * Parse manually: read the MGF SEQUENCE + OID, then the hash AlgoId + * parameter, because GetAlgoId consumes the entire AlgorithmIdentifier + * including the hash parameter inside it. */ + if (ret == 0) { + if ((idx < sz) && (params[idx] == ASN_TAG_RSA_PSS_MGF)) { + int mgfSeqLen = 0; + word32 mgfEnd; + + if (GetHeader(params, &tag, &idx, &length, sz, 0) < 0) { + WOLFSSL_MSG("DecodeRsaPssParams: fail at mgf_header"); + ret = ASN_PARSE_E; + } + /* Read MGF AlgorithmIdentifier SEQUENCE header */ + if (ret == 0) { + if (GetSequence(params, &idx, &mgfSeqLen, sz) < 0) { + WOLFSSL_MSG("DecodeRsaPssParams: fail at mgf_seq"); + ret = ASN_PARSE_E; + } + } + /* Bound subsequent reads to the MGF SEQUENCE content */ + mgfEnd = idx + (word32)mgfSeqLen; + if (mgfEnd > sz) { + WOLFSSL_MSG("DecodeRsaPssParams: mgf_seq overflows buffer"); + ret = ASN_PARSE_E; + } + /* Read MGF OID (id-mgf1) directly */ + if (ret == 0) { + if (GetObjectId(params, &idx, &oid, oidIgnoreType, + mgfEnd) < 0) { + WOLFSSL_MSG("DecodeRsaPssParams: fail at mgf_oid"); + ret = ASN_PARSE_E; + } + } + if ((ret == 0) && (oid != MGF1_OID)) { + WOLFSSL_MSG("DecodeRsaPssParams: fail at mgf_oid_value"); + ret = ASN_PARSE_E; + } + /* Read hash AlgorithmIdentifier (parameter of MGF1) */ + if (ret == 0) { + ret = GetAlgoId(params, &idx, &oid, oidHashType, mgfEnd); + if (ret == 0) { + ret = RsaPssHashOidToMgf1(oid, mgf); + if (ret != 0) { + WOLFSSL_MSG("DecodeRsaPssParams: fail at mgf_hash_oid"); + } + } + else { + WOLFSSL_MSG("DecodeRsaPssParams: fail at mgf_hash_algo"); + } + } + if ((ret == 0) && (idx != mgfEnd)) { + WOLFSSL_MSG("DecodeRsaPssParams: extra data in mgf_seq"); + ret = ASN_PARSE_E; + } + } + else { + /* Default MGF/Hash algorithm. */ + *mgf = WC_MGF1SHA1; + } + } + + /* [2] saltLength */ + if (ret == 0) { + if ((idx < sz) && (params[idx] == ASN_TAG_RSA_PSS_SALTLEN)) { + /* Salt length to use with padding. */ + if (GetHeader(params, &tag, &idx, &length, sz, 0) < 0) { + WOLFSSL_MSG("DecodeRsaPssParams: fail at saltlen_header"); + ret = ASN_PARSE_E; + } + if (ret == 0) { + ret = GetInteger16Bit(params, &idx, sz); + if (ret >= 0) { + *saltLen = ret; + ret = 0; + } + else { + WOLFSSL_MSG("DecodeRsaPssParams: fail at saltlen_value"); + } + } + } + else { + /* Default salt length. */ + *saltLen = 20; + } + } + + /* [3] trailerField */ + if (ret == 0) { + if ((idx < sz) && (params[idx] == ASN_TAG_RSA_PSS_TRAILER)) { + /* Unused - trailerField. */ + if (GetHeader(params, &tag, &idx, &length, sz, 0) < 0) { + WOLFSSL_MSG("DecodeRsaPssParams: fail at trailer_header"); + ret = ASN_PARSE_E; + } + if (ret == 0) { + ret = GetInteger16Bit(params, &idx, sz); + if (ret > 0) { + ret = 0; + } + else if (ret != 0) { + WOLFSSL_MSG("DecodeRsaPssParams: fail at trailer_value"); + } + } + } + } + + if ((ret == 0) && (idx != sz)) { + WOLFSSL_MSG("DecodeRsaPssParams: fail at extra_data"); + ret = ASN_PARSE_E; + } + + return ret; +} #endif /* WOLFSSL_ASN_TEMPLATE */ } + +WOLFSSL_TEST_VIS int wc_DecodeRsaPssParams(const byte* params, word32 sz, + enum wc_HashType* hash, int* mgf, int* saltLen) +{ + return DecodeRsaPssParams(params, sz, hash, mgf, saltLen); +} + +/* Encode AlgorithmIdentifier for id-RSASSA-PSS with full RSASSA-PSS-params. + * hashOID: e.g. SHA256h; saltLen: e.g. 32; trailerField is encoded as 1. + * When out is NULL returns required length only. + * Returns encoded length on success, 0 on error. + * Note: saltLength is encoded as a single-byte INTEGER; values >255 would + * require multi-byte encoding (not implemented; CMS profiles use hash-length + * salts, e.g. 20-64 bytes). */ +/* Scratch buffer for building RSA-PSS AlgorithmIdentifier sub-encodings. + * Must hold the largest single sub-encoding (hash AlgoId, typically ~15 bytes) + * plus room for integer TLVs. 64 bytes is sufficient; 128 gives margin. */ +#define RSA_PSS_ALGOID_TMPBUF_SZ 128 + +word32 wc_EncodeRsaPssAlgoId(int hashOID, int saltLen, byte* out, word32 outSz) +{ + word32 idx = 0; + word32 hashAlgSz, mgf1ParamSz, tag0Sz, tag1Sz, tag2Sz, tag3Sz; + word32 paramsSz, outerSz; + const byte* rsapssOid = sigRsaSsaPssOid; + word32 rsapssOidSz = sizeof(sigRsaSsaPssOid); + /* MGF1 OID 1.2.840.113549.1.1.8 */ + static const byte mgf1Oid[] = { 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x08 }; + int setIntRet; +#ifdef WOLFSSL_SMALL_STACK + byte* tmpBuf; +#else + byte tmpBuf[RSA_PSS_ALGOID_TMPBUF_SZ]; +#endif + + if (saltLen < 0 || saltLen > 255) { + WOLFSSL_MSG("Salt length must be 0-255 for single-byte encoding"); + return 0; + } + +#ifdef WOLFSSL_SMALL_STACK + tmpBuf = (byte*)XMALLOC(RSA_PSS_ALGOID_TMPBUF_SZ, NULL, + DYNAMIC_TYPE_TMP_BUFFER); + if (tmpBuf == NULL) + return 0; +#endif + + hashAlgSz = SetAlgoID(hashOID, out ? tmpBuf : NULL, oidHashType, 0); + if (hashAlgSz == 0) { + idx = 0; goto pss_algoid_done; + } + mgf1ParamSz = hashAlgSz; + tag0Sz = SetExplicit(0, hashAlgSz, NULL, 0) + hashAlgSz; + { + /* MGF AlgorithmIdentifier: SEQUENCE { OID id-mgf1, hash AlgoId } + * The hash AlgoId (from SetAlgoID) is the parameter of MGF1. */ + word32 mgf1OidLen = (word32)SetObjectId((int)sizeof(mgf1Oid), NULL) + (word32)sizeof(mgf1Oid); + word32 mgf1SeqContent = mgf1OidLen + mgf1ParamSz; + word32 mgf1SeqSz = SetSequence(mgf1SeqContent, NULL) + mgf1SeqContent; + tag1Sz = SetExplicit(1, mgf1SeqSz, NULL, 0) + mgf1SeqSz; + } + /* SetASNInt writes only tag+length (+ optional leading 0); value byte(s) appended by caller. */ + setIntRet = SetASNInt(1, (byte)(saltLen & 0xff), NULL); + if (setIntRet <= 0) { + idx = 0; goto pss_algoid_done; + } + { + word32 saltIntSz = (word32)setIntRet + 1; /* header + one value byte (two if high bit set) */ + tag2Sz = SetExplicit(2, saltIntSz, NULL, 0) + saltIntSz; + } + setIntRet = SetASNInt(1, 0x01, NULL); + if (setIntRet <= 0) { + idx = 0; goto pss_algoid_done; + } + { + word32 trailerIntSz = (word32)setIntRet + 1; + tag3Sz = SetExplicit(3, trailerIntSz, NULL, 0) + trailerIntSz; + } + + paramsSz = tag0Sz + tag1Sz + tag2Sz + tag3Sz; + { + word32 idPart = (word32)SetObjectId((int)rsapssOidSz, NULL) + rsapssOidSz; + word32 seqPart = SetSequence(paramsSz, NULL) + paramsSz; + outerSz = SetSequence(idPart + seqPart, NULL) + idPart + seqPart; + } + + if (out == NULL) { + idx = outerSz; goto pss_algoid_done; + } + if (outSz < outerSz) { + idx = 0; goto pss_algoid_done; + } + + { + word32 idPart = (word32)SetObjectId((int)rsapssOidSz, NULL) + rsapssOidSz; + word32 seqPart = SetSequence(paramsSz, NULL) + paramsSz; + idx += SetSequence(idPart + seqPart, out + idx); + } + idx += (word32)SetObjectId((int)rsapssOidSz, out + idx); + XMEMCPY(out + idx, rsapssOid, rsapssOidSz); + idx += rsapssOidSz; + idx += SetSequence(paramsSz, out + idx); + + idx += SetExplicit(0, hashAlgSz, out + idx, 0); + XMEMCPY(out + idx, tmpBuf, hashAlgSz); + idx += hashAlgSz; + + { + /* [1] EXPLICIT { SEQUENCE { OID id-mgf1, hash AlgoId } } */ + word32 mgf1OidLen = (word32)SetObjectId((int)sizeof(mgf1Oid), NULL) + (word32)sizeof(mgf1Oid); + word32 mgf1SeqContent = mgf1OidLen + mgf1ParamSz; + word32 mgf1SeqSz = SetSequence(mgf1SeqContent, NULL) + mgf1SeqContent; + idx += SetExplicit(1, mgf1SeqSz, out + idx, 0); + idx += SetSequence(mgf1SeqContent, out + idx); + idx += (word32)SetObjectId((int)sizeof(mgf1Oid), out + idx); + XMEMCPY(out + idx, mgf1Oid, sizeof(mgf1Oid)); + idx += (word32)sizeof(mgf1Oid); + XMEMCPY(out + idx, tmpBuf, hashAlgSz); + idx += hashAlgSz; + } + + /* INTEGER saltLength (single byte; see function comment for >255 limitation). */ + setIntRet = SetASNInt(1, (byte)(saltLen & 0xff), tmpBuf); + if (setIntRet > 0) { + tmpBuf[setIntRet] = (byte)(saltLen & 0xff); + } + { + word32 saltIntSz = (word32)setIntRet + 1; + idx += SetExplicit(2, saltIntSz, out + idx, 0); + XMEMCPY(out + idx, tmpBuf, saltIntSz); + idx += saltIntSz; + } + + /* INTEGER trailerField (1): same pattern. */ + setIntRet = SetASNInt(1, 0x01, tmpBuf); + if (setIntRet > 0) { + tmpBuf[setIntRet] = 0x01; + } + { + word32 trailerIntSz = (word32)setIntRet + 1; + idx += SetExplicit(3, trailerIntSz, out + idx, 0); + XMEMCPY(out + idx, tmpBuf, trailerIntSz); + idx += trailerIntSz; + } + +pss_algoid_done: +#ifdef WOLFSSL_SMALL_STACK + XFREE(tmpBuf, NULL, DYNAMIC_TYPE_TMP_BUFFER); +#endif + return idx; +} + #endif /* WC_RSA_PSS */ #if defined(WOLFSSL_ASN_TEMPLATE) || (!defined(NO_CERTS) && \ @@ -11113,6 +11355,7 @@ int wc_RsaPublicKeyDecode_ex(const byte* input, word32* inOutIdx, word32 inSz, #else DECL_ASNGETDATA(dataASN, rsaPublicKeyASN_Length); int ret = 0; + word32 startIdx = (inOutIdx != NULL) ? *inOutIdx : 0; #ifdef WC_RSA_PSS word32 oid = RSAk; #endif @@ -11131,7 +11374,10 @@ int wc_RsaPublicKeyDecode_ex(const byte* input, word32* inOutIdx, word32 inSz, (int)(rsaPublicKeyASN_Length - RSAPUBLICKEYASN_IDX_PUBKEY_RSA_SEQ), 0, input, inOutIdx, inSz); if (ret != 0) { - /* Didn't work - try whole SubjectKeyInfo instead. */ + /* Didn't work - try whole SubjectKeyInfo instead. Reset index + * to caller's start since the previous attempt advanced it. */ + if (inOutIdx != NULL) + *inOutIdx = startIdx; #ifdef WC_RSA_PSS /* Could be RSA or RSA PSS key. */ GetASN_OID(&dataASN[RSAPUBLICKEYASN_IDX_ALGOID_OID], oidKeyType); @@ -17041,8 +17287,17 @@ static int GetSigAlg(DecodedCert* cert, word32* sigOid, word32 maxIdx) if (cert->srcIdx != endSeqIdx) { #ifdef WC_RSA_PSS if (*sigOid == CTC_RSASSAPSS) { - cert->sigParamsIndex = cert->srcIdx; - cert->sigParamsLength = endSeqIdx - cert->srcIdx; + /* cert->srcIdx is at start of parameters TLV (NULL or SEQUENCE) */ + word32 tmpIdx = cert->srcIdx; + byte tag; + int len; + + WOLFSSL_MSG("Cert sigAlg is RSASSA-PSS; decoding params"); + if (GetHeader(cert->source, &tag, &tmpIdx, &len, endSeqIdx, 0) < 0) { + return ASN_PARSE_E; + } + cert->sigParamsIndex = cert->srcIdx; + cert->sigParamsLength = (word32)((tmpIdx - cert->srcIdx) + len); } else #endif @@ -24082,10 +24337,17 @@ static int DecodeCertInternal(DecodedCert* cert, int verify, int* criticalExt, } } if (ret == 0) { - /* Store parameters for use in signature verification. */ - cert->sigParamsIndex = - dataASN[X509CERTASN_IDX_SIGALGO_PARAMS].offset; - cert->sigParamsLength = sigAlgParamsSz; + /* Store parameters for use in signature verification. + * Use full TLV length: tag (1) + length bytes + content. + * GetASNItem_Length can be content-only when buffer.data unset. */ + word32 off = dataASN[X509CERTASN_IDX_SIGALGO_PARAMS].offset; + word32 contentLen = + (word32)dataASN[X509CERTASN_IDX_SIGALGO_PARAMS].length; + cert->sigParamsIndex = off; + cert->sigParamsLength = (contentLen <= 127) + ? (2 + contentLen) + : GetASNItem_Length( + dataASN[X509CERTASN_IDX_SIGALGO_PARAMS], cert->source); } } #endif @@ -24119,8 +24381,6 @@ static int DecodeCertInternal(DecodedCert* cert, int verify, int* criticalExt, cert->extensionsSz = (int)GetASNItem_Length( dataASN[X509CERTASN_IDX_TBS_EXT], cert->source); cert->extensionsIdx = dataASN[X509CERTASN_IDX_TBS_EXT].offset; - /* Advance past extensions. */ - cert->srcIdx = dataASN[X509CERTASN_IDX_SIGALGO_SEQ].offset; } } @@ -27452,6 +27712,27 @@ int PemToDer(const unsigned char* buff, long longSz, int type, footer = END_X509_CRL; } #endif + else if (type == CERT_TYPE + || type == CA_TYPE + || type == CHAIN_CERT_TYPE + || type == TRUSTED_PEER_TYPE) { + if (header == BEGIN_CERT) { + header = BEGIN_TRUSTED_CERT; + footer = END_TRUSTED_CERT; + } + else { + break; + } + } + else if (type == TRUSTED_CERT_TYPE) { + if (header == BEGIN_TRUSTED_CERT) { + header = BEGIN_CERT; + footer = END_CERT; + } + else { + break; + } + } else { break; } diff --git a/wolfcrypt/src/chacha20_poly1305.c b/wolfcrypt/src/chacha20_poly1305.c index f788c2ed64..8911b4d53d 100644 --- a/wolfcrypt/src/chacha20_poly1305.c +++ b/wolfcrypt/src/chacha20_poly1305.c @@ -187,6 +187,8 @@ int wc_ChaCha20Poly1305_Init(ChaChaPoly_Aead* aead, aead->state = CHACHA20_POLY1305_STATE_READY; } + ForceZero(authKey, sizeof(authKey)); + return ret; } @@ -332,25 +334,30 @@ int wc_XChaCha20Poly1305_Init( /* Create the Poly1305 key */ if ((ret = wc_Chacha_Process(&aead->chacha, authKey, authKey, (word32)sizeof authKey)) < 0) - return ret; + goto out; /* advance to start of the next ChaCha block. */ wc_Chacha_purge_current_block(&aead->chacha); /* Initialize Poly1305 context */ if ((ret = wc_Poly1305SetKey(&aead->poly, authKey, (word32)sizeof authKey)) < 0) - return ret; + goto out; if ((ret = wc_Poly1305Update(&aead->poly, ad, (word32)ad_len)) < 0) - return ret; + goto out; if ((ret = wc_Poly1305_Pad(&aead->poly, (word32)ad_len)) < 0) - return ret; + goto out; aead->isEncrypt = isEncrypt ? 1 : 0; aead->state = CHACHA20_POLY1305_STATE_AAD; - return 0; + ret = 0; + +out: + ForceZero(authKey, sizeof(authKey)); + + return ret; } static WC_INLINE int wc_XChaCha20Poly1305_crypt_oneshot( diff --git a/wolfcrypt/src/cmac.c b/wolfcrypt/src/cmac.c index ac9a14811e..66e45f9247 100644 --- a/wolfcrypt/src/cmac.c +++ b/wolfcrypt/src/cmac.c @@ -228,6 +228,7 @@ int wc_CmacUpdate(Cmac* cmac, const byte* in, word32 inSz) #if !defined(NO_AES) && defined(WOLFSSL_AES_DIRECT) case WC_CMAC_AES: { +#ifdef HAVE_SELFTEST while ((ret == 0) && (inSz != 0)) { word32 add = min(inSz, WC_AES_BLOCK_SIZE - cmac->bufferSz); XMEMCPY(&cmac->buffer[cmac->bufferSz], in, add); @@ -240,21 +241,16 @@ int wc_CmacUpdate(Cmac* cmac, const byte* in, word32 inSz) if (cmac->totalSz != 0) { xorbuf(cmac->buffer, cmac->digest, WC_AES_BLOCK_SIZE); } -#ifndef HAVE_SELFTEST - ret = wc_AesEncryptDirect(&cmac->aes, cmac->digest, - cmac->buffer); - if (ret == 0) { - cmac->totalSz += WC_AES_BLOCK_SIZE; - cmac->bufferSz = 0; - } -#else wc_AesEncryptDirect(&cmac->aes, cmac->digest, cmac->buffer); cmac->totalSz += WC_AES_BLOCK_SIZE; cmac->bufferSz = 0; -#endif } } +#else + (void)ret; + ret = wc_local_CmacUpdateAes(cmac, in, inSz); +#endif }; break; #endif /* !NO_AES && WOLFSSL_AES_DIRECT */ default: diff --git a/wolfcrypt/src/ecc.c b/wolfcrypt/src/ecc.c index d7acd0bdaf..721c52a1d5 100644 --- a/wolfcrypt/src/ecc.c +++ b/wolfcrypt/src/ecc.c @@ -14484,6 +14484,8 @@ int wc_ecc_encrypt_ex(ecc_key* privKey, ecc_key* pubKey, const byte* msg, RESTORE_VECTOR_REGISTERS(); + ForceZero(sharedSecret, sharedSz); + ForceZero(keys, (word32)keysLen); WC_FREE_VAR_EX(sharedSecret, ctx->heap, DYNAMIC_TYPE_ECC_BUFFER); WC_FREE_VAR_EX(keys, ctx->heap, DYNAMIC_TYPE_ECC_BUFFER); @@ -14778,8 +14780,8 @@ int wc_ecc_decrypt(ecc_key* privKey, ecc_key* pubKey, const byte* msg, if (ret == 0) ret = wc_HmacFinal(hmac, verify); - if ((ret == 0) && (XMEMCMP(verify, msg + msgSz - digestSz, - digestSz) != 0)) { + if ((ret == 0) && (ConstantCompare(verify, msg + msgSz - digestSz, + (int)digestSz) != 0)) { ret = HASH_TYPE_E; WOLFSSL_MSG("ECC Decrypt HMAC Check failed!"); } @@ -14882,6 +14884,8 @@ int wc_ecc_decrypt(ecc_key* privKey, ecc_key* pubKey, const byte* msg, if (pubKey == peerKey) wc_ecc_free(peerKey); #endif + ForceZero(sharedSecret, sharedSz); + ForceZero(keys, (word32)keysLen); #ifdef WOLFSSL_SMALL_STACK #ifndef WOLFSSL_ECIES_OLD XFREE(peerKey, ctx->heap, DYNAMIC_TYPE_ECC_BUFFER); diff --git a/wolfcrypt/src/evp.c b/wolfcrypt/src/evp.c index dc14d4fe66..cc2bb3a73b 100644 --- a/wolfcrypt/src/evp.c +++ b/wolfcrypt/src/evp.c @@ -4952,7 +4952,7 @@ int wolfSSL_EVP_DigestVerifyFinal(WOLFSSL_EVP_MD_CTX *ctx, hashLen = wolfssl_mac_len(ctx->hash.hmac.macType); - if (siglen > hashLen) + if (siglen > hashLen || siglen > INT_MAX) return WOLFSSL_FAILURE; /* May be a truncated signature. */ } @@ -4962,7 +4962,7 @@ int wolfSSL_EVP_DigestVerifyFinal(WOLFSSL_EVP_MD_CTX *ctx, if (ctx->isHMAC) { /* Check HMAC result matches the signature. */ - if (XMEMCMP(sig, digest, (size_t)siglen) == 0) + if (ConstantCompare(sig, digest, (int)siglen) == 0) return WOLFSSL_SUCCESS; return WOLFSSL_FAILURE; } diff --git a/wolfcrypt/src/hpke.c b/wolfcrypt/src/hpke.c index 9f99f190ba..e7b15db0a4 100644 --- a/wolfcrypt/src/hpke.c +++ b/wolfcrypt/src/hpke.c @@ -796,6 +796,8 @@ static int wc_HpkeEncap(Hpke* hpke, void* ephemeralKey, void* receiverKey, hpke->Npk * 2, sharedSecret); } + ForceZero(dh, hpke->Ndh); + ForceZero(kemContext, hpke->Npk * 2); WC_FREE_VAR_EX(dh, hpke->heap, DYNAMIC_TYPE_TMP_BUFFER); WC_FREE_VAR_EX(kemContext, hpke->heap, DYNAMIC_TYPE_TMP_BUFFER); @@ -816,6 +818,9 @@ static int wc_HpkeSetupBaseSender(Hpke* hpke, HpkeBaseContext* context, #ifdef WOLFSSL_SMALL_STACK sharedSecret = (byte*)XMALLOC(hpke->Nsecret, hpke->heap, DYNAMIC_TYPE_TMP_BUFFER); + if (sharedSecret == NULL) { + return MEMORY_E; + } #endif /* encap */ @@ -827,6 +832,7 @@ static int wc_HpkeSetupBaseSender(Hpke* hpke, HpkeBaseContext* context, infoSz); } + ForceZero(sharedSecret, hpke->Nsecret); WC_FREE_VAR_EX(sharedSecret, hpke->heap, DYNAMIC_TYPE_TMP_BUFFER); return ret; @@ -914,6 +920,7 @@ int wc_HpkeSealBase(Hpke* hpke, void* ephemeralKey, void* receiverKey, PRIVATE_KEY_LOCK(); + ForceZero(context, sizeof(HpkeBaseContext)); WC_FREE_VAR_EX(context, hpke->heap, DYNAMIC_TYPE_TMP_BUFFER); return ret; @@ -1032,6 +1039,8 @@ static int wc_HpkeDecap(Hpke* hpke, void* receiverKey, const byte* pubKey, hpke->Npk * 2, sharedSecret); } + ForceZero(dh, hpke->Ndh); + ForceZero(kemContext, hpke->Npk * 2); WC_FREE_VAR_EX(dh, hpke->heap, DYNAMIC_TYPE_TMP_BUFFER); WC_FREE_VAR_EX(kemContext, hpke->heap, DYNAMIC_TYPE_TMP_BUFFER); @@ -1058,6 +1067,7 @@ static int wc_HpkeSetupBaseReceiver(Hpke* hpke, HpkeBaseContext* context, infoSz); } + ForceZero(sharedSecret, hpke->Nsecret); WC_FREE_VAR_EX(sharedSecret, hpke->heap, DYNAMIC_TYPE_TMP_BUFFER); return ret; @@ -1144,6 +1154,7 @@ int wc_HpkeOpenBase(Hpke* hpke, void* receiverKey, const byte* pubKey, PRIVATE_KEY_LOCK(); + ForceZero(context, sizeof(HpkeBaseContext)); WC_FREE_VAR_EX(context, hpke->heap, DYNAMIC_TYPE_TMP_BUFFER); return ret; diff --git a/wolfcrypt/src/pkcs12.c b/wolfcrypt/src/pkcs12.c index 93066a5e3f..3edfd36830 100644 --- a/wolfcrypt/src/pkcs12.c +++ b/wolfcrypt/src/pkcs12.c @@ -637,7 +637,13 @@ static int wc_PKCS12_verify(WC_PKCS12* pkcs12, byte* data, word32 dataSz, } #endif - return XMEMCMP(digest, mac->digest, mac->digestSz); + if (ConstantCompare(digest, mac->digest, (int)mac->digestSz) != 0) { + ForceZero(digest, sizeof(digest)); + return MAC_CMP_FAILED_E; + } + + ForceZero(digest, sizeof(digest)); + return 0; } int wc_PKCS12_verify_ex(WC_PKCS12* pkcs12, const byte* psw, word32 pswSz) diff --git a/wolfcrypt/src/pkcs7.c b/wolfcrypt/src/pkcs7.c index 75b53d4bfe..135163dd25 100644 --- a/wolfcrypt/src/pkcs7.c +++ b/wolfcrypt/src/pkcs7.c @@ -28,6 +28,26 @@ #ifndef NO_RSA #include #endif +#ifndef RSA_PSS_SALT_LEN_DEFAULT + #define RSA_PSS_SALT_LEN_DEFAULT (-1) +#endif +#if defined(WC_RSA_PSS) && !defined(NO_RSA) + #if (defined(HAVE_FIPS) && \ + (!defined(HAVE_FIPS_VERSION) || (HAVE_FIPS_VERSION < 2))) || \ + (defined(HAVE_SELFTEST) && \ + (!defined(HAVE_SELFTEST_VERSION) || (HAVE_SELFTEST_VERSION < 2))) + #ifndef WC_MGF1NONE + #define WC_MGF1NONE 0 + #define WC_MGF1SHA1 26 + #define WC_MGF1SHA224 4 + #define WC_MGF1SHA256 1 + #define WC_MGF1SHA384 2 + #define WC_MGF1SHA512 3 + #define WC_MGF1SHA512_224 5 + #define WC_MGF1SHA512_256 6 + #endif + #endif +#endif #ifdef HAVE_ECC #include #endif @@ -1019,6 +1039,11 @@ static int wc_PKCS7_CheckPublicKeyDer(wc_PKCS7* pkcs7, int keyOID, switch (keyOID) { #ifndef NO_RSA + #ifdef WC_RSA_PSS + case RSAPSSk: + /* RSA-PSS cert: public key is same RSA format as RSAk */ + FALL_THROUGH; + #endif case RSAk: ret = wc_InitRsaKey_ex(rsa, pkcs7->heap, pkcs7->devId); if (ret != 0) { @@ -1173,6 +1198,10 @@ int wc_PKCS7_InitWithCert(wc_PKCS7* pkcs7, byte* derCert, word32 derCertSz) XMEMCPY(pkcs7->publicKey, dCert->publicKey, dCert->pubKeySize); pkcs7->publicKeySz = dCert->pubKeySize; pkcs7->publicKeyOID = dCert->keyOID; + /* Do not derive publicKeyOID from cert signatureOID: the cert's + * signature is how the cert was signed by its issuer; the signer + * chooses digestEncryptionAlgorithm (e.g. RSASSA-PSS vs PKCS#1 v1.5) + * via API / pkcs7->publicKeyOID set by the application. */ XMEMCPY(pkcs7->issuerHash, dCert->issuerHash, KEYID_SIZE); pkcs7->issuer = dCert->issuerRaw; pkcs7->issuerSz = (word32)dCert->issuerRawLen; @@ -1533,7 +1562,11 @@ typedef struct ESD { byte issuerSKIDSeq[MAX_SEQ_SZ]; byte issuerSKID[MAX_OCTET_STR_SZ]; byte signerDigAlgoId[MAX_ALGO_SZ]; +#if defined(WC_RSA_PSS) + byte digEncAlgoId[128]; /* RSASSA-PSS needs full params */ +#else byte digEncAlgoId[MAX_ALGO_SZ]; +#endif byte signedAttribSet[MAX_SET_SZ]; EncodedAttrib signedAttribs[7]; byte signerDigest[MAX_OCTET_STR_SZ]; @@ -1945,6 +1978,131 @@ static int wc_PKCS7_EcdsaSign(wc_PKCS7* pkcs7, byte* in, word32 inSz, ESD* esd) #endif /* HAVE_ECC */ +#if defined(WC_RSA_PSS) && !defined(NO_RSA) +/* Map hash type to MGF1 identifier; local copy so PKCS7 does not depend on + * rsa.c when RSA is in a separate FIPS module (e.g. CAVP build). */ +static int pkcs7_hash2mgf(enum wc_HashType hType) +{ + switch (hType) { + case WC_HASH_TYPE_NONE: + return WC_MGF1NONE; + case WC_HASH_TYPE_SHA: +#ifndef NO_SHA + return WC_MGF1SHA1; +#else + break; +#endif + case WC_HASH_TYPE_SHA224: +#ifdef WOLFSSL_SHA224 + return WC_MGF1SHA224; +#else + break; +#endif + case WC_HASH_TYPE_SHA256: +#ifndef NO_SHA256 + return WC_MGF1SHA256; +#else + break; +#endif + case WC_HASH_TYPE_SHA384: +#ifdef WOLFSSL_SHA384 + return WC_MGF1SHA384; +#else + break; +#endif + case WC_HASH_TYPE_SHA512: +#ifdef WOLFSSL_SHA512 + return WC_MGF1SHA512; +#else + break; +#endif + /* MGF1 only supports SHA-1 and SHA-2; other hashes fall through to WC_MGF1NONE */ + case WC_HASH_TYPE_MD2: + case WC_HASH_TYPE_MD4: + case WC_HASH_TYPE_MD5: + case WC_HASH_TYPE_MD5_SHA: + case WC_HASH_TYPE_SHA3_224: + case WC_HASH_TYPE_SHA3_256: + case WC_HASH_TYPE_SHA3_384: + case WC_HASH_TYPE_SHA3_512: + case WC_HASH_TYPE_BLAKE2B: + case WC_HASH_TYPE_BLAKE2S: +#ifndef WOLFSSL_NOSHA512_224 + case WC_HASH_TYPE_SHA512_224: +#endif +#ifndef WOLFSSL_NOSHA512_256 + case WC_HASH_TYPE_SHA512_256: +#endif +#ifdef WOLFSSL_SHAKE128 + case WC_HASH_TYPE_SHAKE128: +#endif +#ifdef WOLFSSL_SHAKE256 + case WC_HASH_TYPE_SHAKE256: +#endif +#ifdef WOLFSSL_SM3 + case WC_HASH_TYPE_SM3: +#endif + default: + break; + } + return WC_MGF1NONE; +} + +/* returns size of signature put into esd->encContentDigest, negative on error. + * Signs the digest (contentAttribsDigest) with RSA-PSS padding, like ECDSA. */ +static int wc_PKCS7_RsaPssSign(wc_PKCS7* pkcs7, byte* digest, word32 digestSz, + ESD* esd) +{ + int ret; + word32 outSz; + WC_DECLARE_VAR(privKey, RsaKey, 1, 0); + + if (pkcs7 == NULL || pkcs7->rng == NULL || digest == NULL || esd == NULL) { + return BAD_FUNC_ARG; + } + + WC_ALLOC_VAR_EX(privKey, RsaKey, 1, pkcs7->heap, DYNAMIC_TYPE_TMP_BUFFER, + return MEMORY_E); + + ret = wc_PKCS7_ImportRSA(pkcs7, privKey); + if (ret == 0) { + outSz = sizeof(esd->encContentDigest); +#ifdef WOLFSSL_ASYNC_CRYPT + do { + ret = wc_AsyncWait(ret, &privKey->asyncDev, + WC_ASYNC_FLAG_CALL_AGAIN); + if (ret >= 0) +#endif + { + /* Salt length policy: use hash digest length (RFC 4055 typical). + * RFC 3447 allows arbitrary salt lengths, but hash-length is the + * most interoperable choice and matches OpenSSL's default. + * Must agree with the saltLen encoded in + * SignerInfo.signatureAlgorithm params above. */ + int saltLen = wc_HashGetDigestSize(wc_OidGetHash(pkcs7->hashOID)); + if (saltLen < 0) { + ret = saltLen; + } + else { + ret = wc_RsaPSS_Sign_ex(digest, digestSz, + esd->encContentDigest, outSz, + esd->hashType, pkcs7_hash2mgf(esd->hashType), + saltLen, privKey, pkcs7->rng); + } + } +#ifdef WOLFSSL_ASYNC_CRYPT + } while (ret == WC_NO_ERR_TRACE(WC_PENDING_E)); +#endif + /* wc_RsaPSS_Sign_ex returns signature length on success */ + } + + wc_FreeRsaKey(privKey); + WC_FREE_VAR_EX(privKey, pkcs7->heap, DYNAMIC_TYPE_TMP_BUFFER); + + return ret; +} +#endif /* WC_RSA_PSS && !NO_RSA */ + /* returns encContentDigestSz based on the signature set to be used */ static int wc_PKCS7_GetSignSize(wc_PKCS7* pkcs7) { @@ -1954,6 +2112,9 @@ static int wc_PKCS7_GetSignSize(wc_PKCS7* pkcs7) #ifndef NO_RSA case RSAk: + #ifdef WC_RSA_PSS + case RSAPSSk: + #endif { #ifndef WOLFSSL_SMALL_STACK RsaKey privKey[1]; @@ -2244,6 +2405,20 @@ static int wc_PKCS7_SignedDataGetEncAlgoId(wc_PKCS7* pkcs7, int* digEncAlgoId, } #endif /* HAVE_ECC */ +#ifdef WC_RSA_PSS + else if (pkcs7->publicKeyOID == RSAPSSk) { + algoType = oidSigType; + algoId = CTC_RSASSAPSS; + /* Hash/MGF/salt conveyed via PSS params in AlgorithmIdentifier */ + } +#endif +#ifndef WC_RSA_PSS + else if (pkcs7->publicKeyOID == RSAPSSk) { + WOLFSSL_MSG("RSA-PSS requested but WC_RSA_PSS not compiled in"); + return NOT_COMPILED_IN; + } +#endif + if (algoId == 0) { WOLFSSL_MSG("Invalid signature algorithm type"); return BAD_FUNC_ARG; @@ -2359,7 +2534,8 @@ static int wc_PKCS7_SignedDataBuildSignature(wc_PKCS7* pkcs7, { int ret = 0; #if defined(HAVE_ECC) || \ - (defined(HAVE_PKCS7_RSA_RAW_SIGN_CALLBACK) && !defined(NO_RSA)) + (defined(HAVE_PKCS7_RSA_RAW_SIGN_CALLBACK) && !defined(NO_RSA)) || \ + (defined(WC_RSA_PSS) && !defined(NO_RSA)) int hashSz = 0; #endif #if defined(HAVE_PKCS7_RSA_RAW_SIGN_CALLBACK) && !defined(NO_RSA) @@ -2384,7 +2560,8 @@ static int wc_PKCS7_SignedDataBuildSignature(wc_PKCS7* pkcs7, } #if defined(HAVE_ECC) || \ - (defined(HAVE_PKCS7_RSA_RAW_SIGN_CALLBACK) && !defined(NO_RSA)) + (defined(HAVE_PKCS7_RSA_RAW_SIGN_CALLBACK) && !defined(NO_RSA)) || \ + (defined(WC_RSA_PSS) && !defined(NO_RSA)) /* get digest size from hash type */ hashSz = wc_HashGetDigestSize(esd->hashType); if (hashSz < 0) { @@ -2447,6 +2624,14 @@ static int wc_PKCS7_SignedDataBuildSignature(wc_PKCS7* pkcs7, break; #endif +#if defined(WC_RSA_PSS) && !defined(NO_RSA) + case RSAPSSk: + /* RSA-PSS signs the digest directly (no DigestInfo), like ECDSA */ + ret = wc_PKCS7_RsaPssSign(pkcs7, esd->contentAttribsDigest, + (word32)hashSz, esd); + break; +#endif + default: WOLFSSL_MSG("Unsupported public key type"); ret = BAD_FUNC_ARG; @@ -2801,14 +2986,19 @@ static int PKCS7_EncodeSigned(wc_PKCS7* pkcs7, return BAD_FUNC_ARG; } - /* signature size varies with ECDSA, with a varying sign size the content - * hash must be known in order to create the surrounding ASN1 syntax - * properly before writing out the content and generating the hash on the - * fly and then creating the signature */ - if (hashBuf == NULL && pkcs7->publicKeyOID == ECDSAk) { + /* signature size varies with ECDSA; RSA-PSS signs digest directly like + * ECDSA. For both, content hash must be known to build ASN.1 before signing */ +#if defined(HAVE_ECC) || defined(WC_RSA_PSS) + if (hashBuf == NULL && + (pkcs7->publicKeyOID == ECDSAk +#ifdef WC_RSA_PSS + || pkcs7->publicKeyOID == RSAPSSk +#endif + )) { WOLFSSL_MSG("Pre-calculated content hash is needed in this case"); return BAD_FUNC_ARG; } +#endif #if defined(WOLFSSL_SM2) && defined(WOLFSSL_SM3) keyIdSize = wc_HashGetDigestSize(wc_HashTypeConvert(HashIdAlg( @@ -2965,8 +3155,31 @@ static int PKCS7_EncodeSigned(wc_PKCS7* pkcs7, idx = ret; goto out; } - esd->digEncAlgoIdSz = SetAlgoIDEx(digEncAlgoId, esd->digEncAlgoId, - digEncAlgoType, 0, pkcs7->hashParamsAbsent); +#if defined(WC_RSA_PSS) + if (digEncAlgoId == CTC_RSASSAPSS) { + /* Salt length policy: always encode as hash digest length. + * This is the common CMS/RFC 4055 profile and matches OpenSSL + * defaults. The decoder (pssSaltLen) handles arbitrary values + * from external blobs. A future pkcs7->pssSaltLen override for + * encode could be added here if custom salt lengths are needed. */ + int saltLen = wc_HashGetDigestSize(wc_OidGetHash(pkcs7->hashOID)); + if (saltLen < 0) { + idx = saltLen; + goto out; + } + esd->digEncAlgoIdSz = wc_EncodeRsaPssAlgoId(pkcs7->hashOID, + (int)(word32)saltLen, esd->digEncAlgoId, + (word32)sizeof(esd->digEncAlgoId)); + if (esd->digEncAlgoIdSz == 0) { + idx = ASN_PARSE_E; + goto out; + } + } else +#endif + { + esd->digEncAlgoIdSz = SetAlgoIDEx(digEncAlgoId, esd->digEncAlgoId, + digEncAlgoType, 0, pkcs7->hashParamsAbsent); + } signerInfoSz += esd->digEncAlgoIdSz; /* build up signed attributes, include contentType, signingTime, and @@ -3567,8 +3780,12 @@ int wc_PKCS7_EncodeSignedData(wc_PKCS7* pkcs7, byte* output, word32 outputSz) return BAD_FUNC_ARG; } - /* pre-calculate hash for ECC signatures */ - if (pkcs7->publicKeyOID == ECDSAk) { + /* pre-calculate content hash for ECDSA and RSA-PSS (both sign digest directly) */ + if (pkcs7->publicKeyOID == ECDSAk +#ifdef WC_RSA_PSS + || pkcs7->publicKeyOID == RSAPSSk +#endif + ) { int hashSz; enum wc_HashType hashType; byte hashBuf[WC_MAX_DIGEST_SIZE]; @@ -4071,8 +4288,7 @@ static int wc_PKCS7_RsaVerify(wc_PKCS7* pkcs7, byte* sig, int sigSz, byte digest[MAX_PKCS7_DIGEST_SZ]; #endif RsaKey key[1]; - DecodedCert stack_dCert; - DecodedCert* dCert = &stack_dCert; + DecodedCert dCert[1]; #endif if (pkcs7 == NULL || sig == NULL || hash == NULL) { @@ -4175,6 +4391,144 @@ static int wc_PKCS7_RsaVerify(wc_PKCS7* pkcs7, byte* sig, int sigSz, return ret; } +#if defined(WC_RSA_PSS) +/* returns 0 when signature verifies, negative on error */ +static int wc_PKCS7_RsaPssVerify(wc_PKCS7* pkcs7, byte* sig, int sigSz, + byte* hash, word32 hashSz) +{ + int ret = 0, i; + word32 scratch = 0, verified = 0; + word32 pkSz; + int saltLen, verify; + enum wc_HashType hashType; + int hashDigSz, mgf; + /* wc_RsaPSS_Verify_ex output is PSS-encoded message (RSA_PSS_PAD_SZ + + * saltLen + 2*hLen bytes), which can exceed MAX_PKCS7_DIGEST_SZ. + * Use MAX_ENCRYPTED_KEY_SZ (512) to cover RSA keys up to 4096-bit. */ + WC_DECLARE_VAR(digest, byte, MAX_ENCRYPTED_KEY_SZ, 0); + WC_DECLARE_VAR(key, RsaKey, 1, 0); + WC_DECLARE_VAR(dCert, DecodedCert, 1, 0); + + if (pkcs7 == NULL || sig == NULL || hash == NULL) + return BAD_FUNC_ARG; + + /* Use SignerInfo.signatureAlgorithm params when decoded; else digestAlgo */ + if (pkcs7->pssParamsPresent) + hashType = (enum wc_HashType)pkcs7->pssHashType; + else + hashType = wc_OidGetHash(pkcs7->hashOID); + hashDigSz = wc_HashGetDigestSize(hashType); + if (hashDigSz < 0) + return ASN_PARSE_E; + if (hashSz != (word32)hashDigSz) + return ASN_PARSE_E; + mgf = pkcs7->pssParamsPresent + ? pkcs7->pssMgf : pkcs7_hash2mgf(hashType); + if (mgf == WC_MGF1NONE) + return ASN_PARSE_E; + + WC_ALLOC_VAR_EX(digest, byte, MAX_ENCRYPTED_KEY_SZ, pkcs7->heap, + DYNAMIC_TYPE_TMP_BUFFER, return MEMORY_E); + WC_ALLOC_VAR_EX(key, RsaKey, 1, pkcs7->heap, DYNAMIC_TYPE_TMP_BUFFER, + { WC_FREE_VAR_EX(digest, pkcs7->heap, DYNAMIC_TYPE_TMP_BUFFER); + return MEMORY_E; }); + WC_ALLOC_VAR_EX(dCert, DecodedCert, 1, pkcs7->heap, DYNAMIC_TYPE_DCERT, + { WC_FREE_VAR_EX(digest, pkcs7->heap, DYNAMIC_TYPE_TMP_BUFFER); + WC_FREE_VAR_EX(key, pkcs7->heap, DYNAMIC_TYPE_TMP_BUFFER); + return MEMORY_E; }); + + XMEMSET(digest, 0, MAX_ENCRYPTED_KEY_SZ); + + for (i = 0; i < MAX_PKCS7_CERTS; i++) { + if (pkcs7->certSz[i] == 0) + continue; + + scratch = 0; + ret = wc_InitRsaKey_ex(key, pkcs7->heap, pkcs7->devId); + if (ret != 0) + break; + + InitDecodedCert(dCert, pkcs7->cert[i], pkcs7->certSz[i], pkcs7->heap); + ret = ParseCert(dCert, CA_TYPE, NO_VERIFY, 0); + if (ret < 0) { + FreeDecodedCert(dCert); + wc_FreeRsaKey(key); + continue; + } + + pkSz = dCert->pubKeySize; + if (pkSz > (MAX_RSA_INT_SZ + MAX_RSA_E_SZ)) + pkSz = (MAX_RSA_INT_SZ + MAX_RSA_E_SZ); + + if (wc_RsaPublicKeyDecode(dCert->publicKey, &scratch, key, + pkSz) < 0) { + FreeDecodedCert(dCert); + wc_FreeRsaKey(key); + continue; + } + + saltLen = pkcs7->pssParamsPresent + ? pkcs7->pssSaltLen : RSA_PSS_SALT_LEN_DEFAULT; + + /* wc_RsaPSS_Verify_ex returns PSS encoded message length, not + * the recovered hash. Must then call wc_RsaPSS_CheckPadding_ex + * to verify the PSS padding against the expected hash. */ + verify = wc_RsaPSS_Verify_ex(sig, (word32)sigSz, digest, + MAX_ENCRYPTED_KEY_SZ, + hashType, mgf, saltLen, key); + if (verify > 0) { + /* wc_RsaPSS_CheckPadding_ex signature varies across + * FIPS / selftest versions; match the pattern in asn.c */ + #if (defined(HAVE_SELFTEST) && \ + (!defined(HAVE_SELFTEST_VERSION) || \ + (HAVE_SELFTEST_VERSION < 2))) || \ + (defined(HAVE_FIPS) && defined(HAVE_FIPS_VERSION) && \ + (HAVE_FIPS_VERSION < 2)) + ret = wc_RsaPSS_CheckPadding_ex(hash, hashSz, digest, + (word32)verify, hashType, + saltLen); + #elif (defined(HAVE_SELFTEST) && \ + (HAVE_SELFTEST_VERSION == 2)) || \ + (defined(HAVE_FIPS) && defined(HAVE_FIPS_VERSION) && \ + (HAVE_FIPS_VERSION == 2)) + ret = wc_RsaPSS_CheckPadding_ex(hash, hashSz, digest, + (word32)verify, hashType, + saltLen, 0); + #else + ret = wc_RsaPSS_CheckPadding_ex2(hash, hashSz, digest, + (word32)verify, hashType, + saltLen, + mp_count_bits(&key->n), + pkcs7->heap); + #endif + } + else { + ret = verify; + } + + FreeDecodedCert(dCert); + wc_FreeRsaKey(key); + + if (ret == 0) { + verified = 1; + pkcs7->verifyCert = pkcs7->cert[i]; + pkcs7->verifyCertSz = pkcs7->certSz[i]; + break; + } + ret = 0; + } + + if (verified == 0) + ret = SIG_VERIFY_E; + + WC_FREE_VAR_EX(digest, pkcs7->heap, DYNAMIC_TYPE_TMP_BUFFER); + WC_FREE_VAR_EX(key, pkcs7->heap, DYNAMIC_TYPE_TMP_BUFFER); + WC_FREE_VAR_EX(dCert, pkcs7->heap, DYNAMIC_TYPE_DCERT); + + return ret; +} +#endif /* WC_RSA_PSS */ + #endif /* NO_RSA */ @@ -4194,8 +4548,7 @@ static int wc_PKCS7_EcdsaVerify(wc_PKCS7* pkcs7, byte* sig, int sigSz, #else byte digest[MAX_PKCS7_DIGEST_SZ]; ecc_key key[1]; - DecodedCert stack_dCert; - DecodedCert* dCert = &stack_dCert; + DecodedCert dCert[1]; #endif word32 idx = 0; @@ -4695,6 +5048,14 @@ static int wc_PKCS7_SignedDataVerifySignature(wc_PKCS7* pkcs7, byte* sig, plainDigestSz); } break; + + #ifdef WC_RSA_PSS + /* RSA-PSS signs the raw hash (plainDigest), not DigestInfo */ + case RSAPSSk: + ret = wc_PKCS7_RsaPssVerify(pkcs7, sig, (int)sigSz, plainDigest, + plainDigestSz); + break; + #endif #endif #ifdef HAVE_ECC @@ -4741,6 +5102,13 @@ static int wc_PKCS7_SetPublicKeyOID(wc_PKCS7* pkcs7, int sigOID) pkcs7->publicKeyOID = RSAk; break; + #ifdef WC_RSA_PSS + /* CTC_RSASSAPSS and RSAPSSk are the same OID value */ + case CTC_RSASSAPSS: + pkcs7->publicKeyOID = RSAPSSk; + break; + #endif + /* if sigOID is already RSAk */ case RSAk: pkcs7->publicKeyOID = (word32)sigOID; @@ -5050,9 +5418,62 @@ static int wc_PKCS7_ParseSignerInfo(wc_PKCS7* pkcs7, byte* in, word32 inSz, idx += (word32)length; } - /* Get digestEncryptionAlgorithm - key type or signature type */ - if (ret == 0 && GetAlgoId(in, &idx, &sigOID, oidIgnoreType, inSz) < 0) { - ret = ASN_PARSE_E; + /* Get digestEncryptionAlgorithm (signatureAlgorithm) - parse manually + * so we can decode id-RSASSA-PSS parameters in all builds. */ +#if defined(WC_RSA_PSS) && !defined(NO_RSA) + pkcs7->pssParamsPresent = 0; +#endif + if (ret == 0) { + int algoSeqLen = 0; + word32 algoContentStart = 0; + if (GetSequence(in, &idx, &algoSeqLen, (word32)inSz) < 0) { + ret = ASN_PARSE_E; + } + else { + algoContentStart = idx; /* first byte of AlgorithmIdentifier content */ + if (GetObjectId(in, &idx, &sigOID, oidSigType, inSz) < 0) { + ret = ASN_PARSE_E; + } + /* Only parse params when still inside the AlgorithmIdentifier; + * when optional params are absent, idx is already past the sequence. */ + else if (algoContentStart + (word32)algoSeqLen > idx) { + word32 paramsStart = idx; + byte paramTag; + int paramLen = 0; + if (GetASNTag(in, &idx, ¶mTag, inSz) != 0 || + GetLength(in, &idx, ¶mLen, inSz) < 0) { + ret = ASN_PARSE_E; + } + else { +#if defined(WC_RSA_PSS) && !defined(NO_RSA) + if ((word32)sigOID == (word32)CTC_RSASSAPSS && + paramTag == (ASN_SEQUENCE | ASN_CONSTRUCTED)) { + word32 tlvLen = (idx - paramsStart) + + (word32)paramLen; + enum wc_HashType pssHash = WC_HASH_TYPE_SHA; + int pssMgfVal = 0, pssSalt = 0; + if (paramsStart + tlvLen > (word32)inSz) { + return ASN_PARSE_E; + } + ret = wc_DecodeRsaPssParams(in + paramsStart, tlvLen, + &pssHash, &pssMgfVal, + &pssSalt); + if (ret == 0) { + pkcs7->pssSaltLen = pssSalt; + pkcs7->pssHashType = (int)pssHash; + pkcs7->pssMgf = pssMgfVal; + pkcs7->pssParamsPresent = 1; + } + else { + WOLFSSL_MSG("RSASSA-PSS parameters invalid - failing parse"); + return ASN_PARSE_E; + } + } +#endif + idx += (word32)paramLen; + } + } + } } /* store public key type based on digestEncryptionAlgorithm */ diff --git a/wolfcrypt/src/pwdbased.c b/wolfcrypt/src/pwdbased.c index d575eaa362..5e212b5d6c 100644 --- a/wolfcrypt/src/pwdbased.c +++ b/wolfcrypt/src/pwdbased.c @@ -152,6 +152,8 @@ int wc_PBKDF1_ex(byte* key, int keyLen, byte* iv, int ivLen, WC_FREE_VAR_EX(hash, heap, DYNAMIC_TYPE_HASHCTX); + ForceZero(digest, sizeof(digest)); + if (err != 0) return err; @@ -294,6 +296,7 @@ int wc_PBKDF2_ex(byte* output, const byte* passwd, int pLen, const byte* salt, wc_HmacFree(hmac); } + ForceZero(buffer, (word32)hLen); WC_FREE_VAR_EX(buffer, heap, DYNAMIC_TYPE_TMP_BUFFER); WC_FREE_VAR_EX(hmac, heap, DYNAMIC_TYPE_HMAC); diff --git a/wolfcrypt/src/sakke.c b/wolfcrypt/src/sakke.c index e59943506b..97de612b8e 100644 --- a/wolfcrypt/src/sakke.c +++ b/wolfcrypt/src/sakke.c @@ -6941,7 +6941,8 @@ int wc_DeriveSakkeSSV(SakkeKey* key, enum wc_HashType hashType, byte* ssv, err = sakke_compute_point_r(key, key->id, key->idSz, ri, n, test); } - if ((err == 0) && (XMEMCMP(auth, test, (size_t)(2 * n + 1)) != 0)) { + /* n is word16, so 2*n+1 always fits in int */ + if ((err == 0) && (ConstantCompare(auth, test, (int)(2 * n + 1)) != 0)) { err = SAKKE_VERIFY_FAIL_E; } diff --git a/wolfcrypt/src/srp.c b/wolfcrypt/src/srp.c index 9f2b416f13..5f51d4f5ee 100644 --- a/wolfcrypt/src/srp.c +++ b/wolfcrypt/src/srp.c @@ -982,7 +982,7 @@ int wc_SrpVerifyPeersProof(Srp* srp, byte* proof, word32 size) if (hashSize < 0) return ALGO_ID_E; - if (size != (word32)hashSize) + if (size != (word32)hashSize || size > INT_MAX) return BUFFER_E; r = SrpHashFinal(srp->side == SRP_CLIENT_SIDE ? &srp->server_proof @@ -994,9 +994,11 @@ int wc_SrpVerifyPeersProof(Srp* srp, byte* proof, word32 size) if (!r) r = SrpHashUpdate(&srp->server_proof, srp->key, srp->keySz); } - if (!r && XMEMCMP(proof, digest, size) != 0) + if (!r && ConstantCompare(proof, digest, (int)size) != 0) r = SRP_VERIFY_E; + ForceZero(digest, sizeof(digest)); + return r; } diff --git a/wolfcrypt/src/wc_mlkem.c b/wolfcrypt/src/wc_mlkem.c index 60a0850dc5..2c43bbabec 100644 --- a/wolfcrypt/src/wc_mlkem.c +++ b/wolfcrypt/src/wc_mlkem.c @@ -950,6 +950,55 @@ static int mlkemkey_encapsulate(MlKemKey* key, const byte* m, byte* r, byte* c) } #endif +#if !defined(WOLFSSL_MLKEM_NO_ENCAPSULATE) || \ + !defined(WOLFSSL_MLKEM_NO_DECAPSULATE) +static int wc_mlkemkey_check_h(MlKemKey* key) +{ + int ret = 0; + + /* If public hash (h) is not stored against key, calculate it + * (fields set explicitly instead of using decode). + * Step 1: ... H(ek)... + */ + if ((key->flags & MLKEM_FLAG_H_SET) == 0) { + #ifndef WOLFSSL_NO_MALLOC + byte* pubKey = NULL; + word32 pubKeyLen; + #else + byte pubKey[WC_ML_KEM_MAX_PUBLIC_KEY_SIZE]; + word32 pubKeyLen; + #endif + + /* Determine how big an encoded public key will be. */ + ret = wc_KyberKey_PublicKeySize(key, &pubKeyLen); + if (ret == 0) { + #ifndef WOLFSSL_NO_MALLOC + /* Allocate dynamic memory for encoded public key. */ + pubKey = (byte*)XMALLOC(pubKeyLen, key->heap, + DYNAMIC_TYPE_TMP_BUFFER); + if (pubKey == NULL) { + ret = MEMORY_E; + } + } + if (ret == 0) { + #endif + /* Encode public key - h is hash of encoded public key. */ + ret = wc_KyberKey_EncodePublicKey(key, pubKey, pubKeyLen); + } + #ifndef WOLFSSL_NO_MALLOC + /* Dispose of encoded public key. */ + XFREE(pubKey, key->heap, DYNAMIC_TYPE_TMP_BUFFER); + #endif + } + if ((ret == 0) && ((key->flags & MLKEM_FLAG_H_SET) == 0)) { + /* Implementation issue if h not cached and flag set. */ + ret = BAD_STATE_E; + } + + return ret; +} +#endif + #ifndef WOLFSSL_MLKEM_NO_ENCAPSULATE /** * Encapsulate with random number generator and derive secret. @@ -1084,43 +1133,8 @@ int wc_MlKemKey_EncapsulateWithRandom(MlKemKey* key, unsigned char* c, } #endif - /* If public hash (h) is not stored against key, calculate it - * (fields set explicitly instead of using decode). - * Step 1: ... H(ek)... - */ - if ((ret == 0) && ((key->flags & MLKEM_FLAG_H_SET) == 0)) { - #ifndef WOLFSSL_NO_MALLOC - byte* pubKey = NULL; - word32 pubKeyLen; - #else - byte pubKey[WC_ML_KEM_MAX_PUBLIC_KEY_SIZE]; - word32 pubKeyLen = WC_ML_KEM_MAX_PUBLIC_KEY_SIZE; - #endif - - #ifndef WOLFSSL_NO_MALLOC - /* Determine how big an encoded public key will be. */ - ret = wc_KyberKey_PublicKeySize(key, &pubKeyLen); - if (ret == 0) { - /* Allocate dynamic memory for encoded public key. */ - pubKey = (byte*)XMALLOC(pubKeyLen, key->heap, - DYNAMIC_TYPE_TMP_BUFFER); - if (pubKey == NULL) { - ret = MEMORY_E; - } - } - if (ret == 0) { - #endif - /* Encode public key - h is hash of encoded public key. */ - ret = wc_KyberKey_EncodePublicKey(key, pubKey, pubKeyLen); - #ifndef WOLFSSL_NO_MALLOC - } - /* Dispose of encoded public key. */ - XFREE(pubKey, key->heap, DYNAMIC_TYPE_TMP_BUFFER); - #endif - } - if ((ret == 0) && ((key->flags & MLKEM_FLAG_H_SET) == 0)) { - /* Implementation issue if h not cached and flag set. */ - ret = BAD_STATE_E; + if (ret == 0) { + ret = wc_mlkemkey_check_h(key); } #ifdef WOLFSSL_MLKEM_KYBER @@ -1205,6 +1219,8 @@ int wc_MlKemKey_EncapsulateWithRandom(MlKemKey* key, unsigned char* c, } #endif + ForceZero(kr, sizeof(kr)); + return ret; } #endif /* !WOLFSSL_MLKEM_NO_ENCAPSULATE */ @@ -1487,6 +1503,10 @@ int wc_MlKemKey_Decapsulate(MlKemKey* key, unsigned char* ss, /* Decapsulate the cipher text. */ ret = mlkemkey_decapsulate(key, msg, ct); } + if (ret == 0) { + /* Check we have H, hash of public, set. */ + ret = wc_mlkemkey_check_h(key); + } if (ret == 0) { /* Hash message into seed buffer. */ ret = MLKEM_HASH_G(&key->hash, msg, WC_ML_KEM_SYM_SZ, key->h, @@ -1541,6 +1561,9 @@ int wc_MlKemKey_Decapsulate(MlKemKey* key, unsigned char* ss, } #endif + ForceZero(msg, sizeof(msg)); + ForceZero(kr, sizeof(kr)); + return ret; } #endif /* WOLFSSL_MLKEM_NO_DECAPSULATE */ diff --git a/wolfcrypt/test/test.c b/wolfcrypt/test/test.c index a98e826af2..6a8ca6798f 100644 --- a/wolfcrypt/test/test.c +++ b/wolfcrypt/test/test.c @@ -20574,7 +20574,7 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t random_bank_test(void) #ifdef WC_DRBG_BANKREF WC_ALLOC_VAR_EX(rng, WC_RNG, 1, HEAP_HINT, DYNAMIC_TYPE_TMP_BUFFER, - return WC_TEST_RET_ENC_EC(MEMORY_E)); + ERROR_OUT(WC_TEST_RET_ENC_EC(MEMORY_E), out)); XMEMSET(rng, 0, sizeof(*rng)); #endif @@ -21039,7 +21039,6 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t memory_test(void) { wc_test_ret_t ret = 0; word32 j = 0; /* used in embedded const pointer test */ - WOLFSSL_ENTER("memory_test"); #if defined(COMPLEX_MEM_TEST) || defined(WOLFSSL_STATIC_MEMORY) int i; @@ -21053,6 +21052,8 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t memory_test(void) * alignment when tests are ran */ #endif + WOLFSSL_ENTER("memory_test"); + #ifdef WOLFSSL_STATIC_MEMORY /* check macro settings */ if (sizeof(size)/sizeof(word32) != WOLFMEM_DEF_BUCKETS) { @@ -42961,6 +42962,29 @@ static wc_test_ret_t mlkem512_kat(void) if (XMEMCMP(ss_dec, ml_kem_512_ss, sizeof(ml_kem_512_ss)) != 0) ERROR_OUT(WC_TEST_RET_ENC_NC, out); + +#ifndef WOLFSSL_MLKEM_NO_MAKE_KEY + wc_MlKemKey_Free(key); + XMEMSET(key, 0, sizeof(MlKemKey)); + key_inited = 0; + ret = wc_MlKemKey_Init(key, WC_ML_KEM_512, HEAP_HINT, devId); + if (ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); + else + key_inited = 1; + ret = wc_MlKemKey_MakeKeyWithRandom(key, kyber512_rand, + sizeof(kyber512_rand)); + if (ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); + + ret = wc_MlKemKey_Decapsulate(key, ss_dec, ml_kem_512_ct, + sizeof(ml_kem_512_ct)); + if (ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); + + if (XMEMCMP(ss_dec, ml_kem_512_ss, sizeof(ml_kem_512_ss)) != 0) + ERROR_OUT(WC_TEST_RET_ENC_NC, out); +#endif #else (void)ml_kem_512_ct; (void)ml_kem_512_ss; diff --git a/wolfssl/internal.h b/wolfssl/internal.h index 7ebe3d7b6c..18d7d10213 100644 --- a/wolfssl/internal.h +++ b/wolfssl/internal.h @@ -1279,16 +1279,6 @@ enum { #endif #endif /* NO_DH */ -#ifndef MAX_PSK_ID_LEN - /* max psk identity/hint supported */ - #if defined(WOLFSSL_TLS13) - /* OpenSSL has a 1472 byte session ticket */ - #define MAX_PSK_ID_LEN 1536 - #else - #define MAX_PSK_ID_LEN 128 - #endif -#endif - #ifndef MAX_PSK_KEY_LEN #define MAX_PSK_KEY_LEN 64 #endif @@ -2046,6 +2036,7 @@ WOLFSSL_LOCAL int NamedGroupIsPqcHybrid(int group); #define MAX_ENCRYPT_SZ ENCRYPT_LEN #define WOLFSSL_ASSERT_EQ(x, y) wc_static_assert((x) == (y)) +#define WOLFSSL_ASSERT_GE(x, y) wc_static_assert((x) >= (y)) #define WOLFSSL_ASSERT_SIZEOF_GE(x, y) wc_static_assert(sizeof(x) >= sizeof(y)) @@ -3169,7 +3160,10 @@ struct TLSX { struct TLSX* next; /* List Behavior */ }; -WOLFSSL_LOCAL TLSX* TLSX_Find(TLSX* list, TLSX_Type type); +#ifdef WOLFSSL_API_PREFIX_MAP + #define TLSX_Find wolfSSL_TLSX_Find +#endif +WOLFSSL_TEST_VIS TLSX* TLSX_Find(TLSX* list, TLSX_Type type); WOLFSSL_LOCAL void TLSX_Remove(TLSX** list, TLSX_Type type, void* heap); WOLFSSL_LOCAL void TLSX_FreeAll(TLSX* list, void* heap); WOLFSSL_LOCAL int TLSX_SupportExtensions(WOLFSSL* ssl); @@ -3237,6 +3231,10 @@ WOLFSSL_LOCAL int TLSX_UseSNI(TLSX** extensions, byte type, const void* data, WOLFSSL_LOCAL byte TLSX_SNI_Status(TLSX* extensions, byte type); WOLFSSL_LOCAL word16 TLSX_SNI_GetRequest(TLSX* extensions, byte type, void** data, byte ignoreStatus); +#ifdef WOLFSSL_API_PREFIX_MAP + #define TLSX_SNI_GetSize wolfSSL_TLSX_SNI_GetSize +#endif +WOLFSSL_TEST_VIS word16 TLSX_SNI_GetSize(SNI* list); #ifndef NO_WOLFSSL_SERVER WOLFSSL_LOCAL void TLSX_SNI_SetOptions(TLSX* extensions, byte type, @@ -3463,6 +3461,11 @@ WOLFSSL_LOCAL int TLSX_AddEmptyRenegotiationInfo(TLSX** extensions, void* heap); #endif /* HAVE_SECURE_RENEGOTIATION */ #ifdef HAVE_SESSION_TICKET +/* Max peer cert size for ticket: 2KB is reasonable for most RSA/ECC certs */ +#ifndef MAX_TICKET_PEER_CERT_SZ +#define MAX_TICKET_PEER_CERT_SZ 2048 +#endif + /* Our ticket format. All members need to be a byte or array of byte to * avoid alignment issues */ typedef struct InternalTicket { @@ -3488,21 +3491,40 @@ typedef struct InternalTicket { byte sessionCtxSz; /* sessionCtx length */ byte sessionCtx[ID_LEN]; /* app specific context id */ #endif /* OPENSSL_EXTRA */ +#if defined(OPENSSL_ALL) && defined(KEEP_PEER_CERT) && \ + !defined(NO_CERT_IN_TICKET) + byte peerCertLen[OPAQUE16_LEN]; /* peer cert length */ + byte peerCert[]; /* peer certificate DER - variable length */ +#endif } InternalTicket; +/* Base size of InternalTicket without the variable-length peerCert field */ +#define WOLFSSL_INTERNAL_TICKET_BASE_SZ (sizeof(InternalTicket)) + +/* Minimum internal ticket length (no peer cert) */ #ifndef WOLFSSL_TICKET_ENC_CBC_HMAC - #define WOLFSSL_INTERNAL_TICKET_LEN sizeof(InternalTicket) + #define WOLFSSL_INTERNAL_TICKET_LEN WOLFSSL_INTERNAL_TICKET_BASE_SZ #else #define WOLFSSL_INTERNAL_TICKET_LEN \ - (((sizeof(InternalTicket) + 15) / 16) * 16) + (((WOLFSSL_INTERNAL_TICKET_BASE_SZ + 15) / 16) * 16) +#endif + +/* Maximum internal ticket length (with max peer cert) */ +#if defined(OPENSSL_ALL) && defined(KEEP_PEER_CERT) && \ + !defined(NO_CERT_IN_TICKET) + #define WOLFSSL_INTERNAL_TICKET_MAX_SZ \ + (WOLFSSL_INTERNAL_TICKET_BASE_SZ + MAX_TICKET_PEER_CERT_SZ) +#else + #define WOLFSSL_INTERNAL_TICKET_MAX_SZ WOLFSSL_INTERNAL_TICKET_BASE_SZ #endif #ifndef WOLFSSL_TICKET_EXTRA_PADDING_SZ #define WOLFSSL_TICKET_EXTRA_PADDING_SZ 32 #endif +/* Maximum encrypted ticket size */ #define WOLFSSL_TICKET_ENC_SZ \ - (sizeof(InternalTicket) + WOLFSSL_TICKET_EXTRA_PADDING_SZ) + (WOLFSSL_INTERNAL_TICKET_MAX_SZ + WOLFSSL_TICKET_EXTRA_PADDING_SZ) /* RFC 5077 defines this for session tickets. All members need to be a byte or * array of byte to avoid alignment issues */ @@ -3510,14 +3532,18 @@ typedef struct ExternalTicket { byte key_name[WOLFSSL_TICKET_NAME_SZ]; /* key context name - 16 */ byte iv[WOLFSSL_TICKET_IV_SZ]; /* this ticket's iv - 16 */ byte enc_len[OPAQUE16_LEN]; /* encrypted length - 2 */ - byte enc_ticket[WOLFSSL_TICKET_ENC_SZ]; - /* encrypted internal ticket */ - byte mac[WOLFSSL_TICKET_MAC_SZ]; /* total mac - 32 */ + byte enc_ticket[]; /* encrypted ticket - var length + * + total mac - 32 */ } ExternalTicket; -/* Cast to int to reduce amount of casts in code */ -#define SESSION_TICKET_LEN ((int)sizeof(ExternalTicket)) -#define WOLFSSL_TICKET_FIXED_SZ (SESSION_TICKET_LEN - WOLFSSL_TICKET_ENC_SZ) +/* Fixed portion of external ticket (key_name + iv + enc_len) */ +#define WOLFSSL_TICKET_FIXED_SZ \ + (WOLFSSL_TICKET_NAME_SZ + WOLFSSL_TICKET_IV_SZ + OPAQUE16_LEN + \ + WOLFSSL_TICKET_MAC_SZ) + +/* Maximum session ticket length */ +#define SESSION_TICKET_LEN \ + ((int)(WOLFSSL_TICKET_FIXED_SZ + WOLFSSL_TICKET_ENC_SZ)) typedef struct SessionTicket { word32 lifetime; @@ -3559,6 +3585,20 @@ WOLFSSL_LOCAL void TLSX_SessionTicket_Free(SessionTicket* ticket, void* heap); #endif /* HAVE_SESSION_TICKET */ +#ifndef MAX_PSK_ID_LEN + /* max psk identity/hint supported */ + #if defined(WOLFSSL_TLS13) + #ifdef SESSION_TICKET_LEN + #define MAX_PSK_ID_LEN SESSION_TICKET_LEN + #else + /* Previous value. Use as fallback for when tickets are disabled. */ + #define MAX_PSK_ID_LEN 1536 + #endif + #else + #define MAX_PSK_ID_LEN 128 + #endif +#endif + #if defined(HAVE_ENCRYPT_THEN_MAC) && !defined(WOLFSSL_AEAD_ONLY) int TLSX_EncryptThenMac_Respond(WOLFSSL* ssl); #endif @@ -6411,12 +6451,20 @@ struct SystemCryptoPolicy { * for the caller to find so we clear the last error. */ #if defined(OPENSSL_EXTRA) && defined(WOLFSSL_HAVE_ERROR_QUEUE) -#define CLEAR_ASN_NO_PEM_HEADER_ERROR(err) \ - (err) = wolfSSL_ERR_peek_last_error(); \ - if (wolfSSL_ERR_GET_LIB(err) == WOLFSSL_ERR_LIB_PEM && \ - wolfSSL_ERR_GET_REASON(err) == -WOLFSSL_PEM_R_NO_START_LINE_E) { \ - wc_RemoveErrorNode(-1); \ - } +#define CLEAR_ASN_NO_PEM_HEADER_ERROR(err) \ +do { \ + (err) = wolfSSL_ERR_peek_last_error(); \ + if (wolfSSL_ERR_GET_LIB(err) == WOLFSSL_ERR_LIB_PEM && \ + wolfSSL_ERR_GET_REASON(err) == -WOLFSSL_PEM_R_NO_START_LINE_E) { \ + unsigned long peekErr; \ + do { \ + wc_RemoveErrorNode(-1); \ + peekErr = wolfSSL_ERR_peek_last_error(); \ + } while (wolfSSL_ERR_GET_LIB(peekErr) == WOLFSSL_ERR_LIB_PEM && \ + wolfSSL_ERR_GET_REASON(peekErr) == \ + -WOLFSSL_PEM_R_NO_START_LINE_E); \ + } \ +} while(0) #else #define CLEAR_ASN_NO_PEM_HEADER_ERROR(err) (void)(err); #endif diff --git a/wolfssl/openssl/ec.h b/wolfssl/openssl/ec.h index 80674e1a0b..c706761e08 100644 --- a/wolfssl/openssl/ec.h +++ b/wolfssl/openssl/ec.h @@ -379,6 +379,9 @@ int wolfSSL_EC_POINT_cmp(const WOLFSSL_EC_GROUP *group, WOLFSSL_API int wolfSSL_EC_POINT_copy(WOLFSSL_EC_POINT *dest, const WOLFSSL_EC_POINT *src); WOLFSSL_API +WOLFSSL_EC_POINT *wolfSSL_EC_POINT_dup(const WOLFSSL_EC_POINT *src, + const WOLFSSL_EC_GROUP *group); +WOLFSSL_API void wolfSSL_EC_POINT_free(WOLFSSL_EC_POINT *point); WOLFSSL_API int wolfSSL_EC_POINT_is_at_infinity(const WOLFSSL_EC_GROUP *group, @@ -479,6 +482,7 @@ typedef WOLFSSL_EC_KEY_METHOD EC_KEY_METHOD; #define EC_POINT_clear_free wolfSSL_EC_POINT_clear_free #define EC_POINT_cmp wolfSSL_EC_POINT_cmp #define EC_POINT_copy wolfSSL_EC_POINT_copy +#define EC_POINT_dup wolfSSL_EC_POINT_dup #define EC_POINT_is_at_infinity wolfSSL_EC_POINT_is_at_infinity #define EC_get_builtin_curves wolfSSL_EC_get_builtin_curves diff --git a/wolfssl/openssl/ssl.h b/wolfssl/openssl/ssl.h index 2222430839..aa954cd6b1 100644 --- a/wolfssl/openssl/ssl.h +++ b/wolfssl/openssl/ssl.h @@ -632,16 +632,18 @@ typedef STACK_OF(ACCESS_DESCRIPTION) AUTHORITY_INFO_ACCESS; #define sk_X509_push wolfSSL_sk_X509_push #define sk_X509_pop wolfSSL_sk_X509_pop #define sk_X509_pop_free wolfSSL_sk_X509_pop_free -#define sk_X509_dup wolfSSL_sk_dup +#define sk_X509_dup wolfSSL_shallow_sk_dup #define sk_X509_free wolfSSL_sk_X509_free #define X509_chain_up_ref wolfSSL_X509_chain_up_ref #define sk_X509_CRL_new wolfSSL_sk_X509_CRL_new +#define sk_X509_CRL_new_null wolfSSL_sk_X509_CRL_new_null #define sk_X509_CRL_pop_free wolfSSL_sk_X509_CRL_pop_free #define sk_X509_CRL_free wolfSSL_sk_X509_CRL_free #define sk_X509_CRL_push wolfSSL_sk_X509_CRL_push #define sk_X509_CRL_value wolfSSL_sk_X509_CRL_value #define sk_X509_CRL_num wolfSSL_sk_X509_CRL_num +#define sk_X509_CRL_dup wolfSSL_shallow_sk_dup #define sk_X509_OBJECT_new wolfSSL_sk_X509_OBJECT_new #define sk_X509_OBJECT_free wolfSSL_sk_X509_OBJECT_free @@ -824,6 +826,7 @@ wolfSSL_X509_STORE_set_verify_cb((WOLFSSL_X509_STORE *)(s), (WOLFSSL_X509_STORE_ #define X509_CRL_new wolfSSL_X509_CRL_new #define X509_CRL_dup wolfSSL_X509_CRL_dup +#define X509_CRL_up_ref wolfSSL_X509_CRL_up_ref #define X509_CRL_free wolfSSL_X509_CRL_free #define X509_CRL_sign wolfSSL_X509_CRL_sign #define X509_CRL_get_lastUpdate wolfSSL_X509_CRL_get_lastUpdate @@ -1333,11 +1336,13 @@ typedef WOLFSSL_SRTP_PROTECTION_PROFILE SRTP_PROTECTION_PROFILE; #define SSL_SESSION_get_id wolfSSL_SESSION_get_id #define SSL_get_cipher_bits(s,np) \ wolfSSL_CIPHER_get_bits(SSL_get_current_cipher(s),np) +#define SSL_get_cipher_version(s) \ + wolfSSL_CIPHER_get_version(SSL_get_current_cipher(s)) #define sk_SSL_CIPHER_num wolfSSL_sk_SSL_CIPHER_num #define sk_SSL_COMP_zero wolfSSL_sk_SSL_COMP_zero #define sk_SSL_CIPHER_value wolfSSL_sk_SSL_CIPHER_value #endif /* OPENSSL_ALL || WOLFSSL_HAPROXY */ -#define sk_SSL_CIPHER_dup wolfSSL_sk_dup +#define sk_SSL_CIPHER_dup wolfSSL_shallow_sk_dup #define sk_SSL_CIPHER_free wolfSSL_sk_SSL_CIPHER_free #define sk_SSL_CIPHER_find wolfSSL_sk_SSL_CIPHER_find diff --git a/wolfssl/ssl.h b/wolfssl/ssl.h index 8dd8acde1c..2fdbfa4a94 100644 --- a/wolfssl/ssl.h +++ b/wolfssl/ssl.h @@ -1952,6 +1952,7 @@ WOLFSSL_API WOLFSSL_X509* wolfSSL_sk_X509_pop(WOLF_STACK_OF(WOLFSSL_X509)* sk); WOLFSSL_API void wolfSSL_sk_X509_free(WOLF_STACK_OF(WOLFSSL_X509)* sk); WOLFSSL_API WOLFSSL_STACK* wolfSSL_sk_X509_CRL_new(void); +WOLFSSL_API WOLFSSL_STACK* wolfSSL_sk_X509_CRL_new_null(void); WOLFSSL_API void wolfSSL_sk_X509_CRL_pop_free(WOLF_STACK_OF(WOLFSSL_X509_CRL)* sk, void (*f) (WOLFSSL_X509_CRL*)); WOLFSSL_API void wolfSSL_sk_X509_CRL_free(WOLF_STACK_OF(WOLFSSL_X509_CRL)* sk); @@ -3517,6 +3518,7 @@ WOLFSSL_API int wolfSSL_X509_REVOKED_get_serial_number(RevokedCert* rev, #endif #if defined(HAVE_CRL) && (defined(OPENSSL_EXTRA) || defined(WOLFSSL_WPAS_SMALL)) WOLFSSL_API WOLFSSL_X509_CRL* wolfSSL_X509_CRL_dup(const WOLFSSL_X509_CRL* crl); +WOLFSSL_API int wolfSSL_X509_CRL_up_ref(WOLFSSL_X509_CRL* crl); WOLFSSL_API void wolfSSL_X509_CRL_free(WOLFSSL_X509_CRL *crl); #endif #if defined(HAVE_CRL) && defined(OPENSSL_EXTRA) diff --git a/wolfssl/wolfcrypt/aes.h b/wolfssl/wolfcrypt/aes.h index da27ccc709..3c4a7db5eb 100644 --- a/wolfssl/wolfcrypt/aes.h +++ b/wolfssl/wolfcrypt/aes.h @@ -806,6 +806,13 @@ int wc_AesSivDecrypt_ex(const byte* key, word32 keySz, const AesSivAssoc* assoc, const byte* in, word32 inSz, byte* siv, byte* out); #endif +#ifdef WOLFSSL_CMAC +/* forward declaration, in case aes.h is being included by cmac.h */ +struct Cmac; +WOLFSSL_LOCAL int wc_local_CmacUpdateAes(struct Cmac *cmac, const byte* in, + word32 inSz); +#endif + #ifdef WOLFSSL_AES_EAX /* Because of the circular dependency between AES and CMAC, we need to prevent diff --git a/wolfssl/wolfcrypt/asn.h b/wolfssl/wolfcrypt/asn.h index 69e8f04bae..340f189ec5 100644 --- a/wolfssl/wolfcrypt/asn.h +++ b/wolfssl/wolfcrypt/asn.h @@ -2506,6 +2506,12 @@ WOLFSSL_LOCAL word32 SetSet(word32 len, byte* output); WOLFSSL_API word32 SetAlgoID(int algoOID, byte* output, int type, int curveSz); WOLFSSL_LOCAL word32 SetAlgoIDEx(int algoOID, byte* output, int type, int curveSz, byte absentParams); +#if defined(WC_RSA_PSS) && !defined(NO_RSA) +WOLFSSL_LOCAL word32 wc_EncodeRsaPssAlgoId(int hashOID, int saltLen, byte* out, + word32 outSz); +WOLFSSL_TEST_VIS int wc_DecodeRsaPssParams(const byte* params, word32 sz, + enum wc_HashType* hash, int* mgf, int* saltLen); +#endif WOLFSSL_LOCAL int SetMyVersion(word32 version, byte* output, int header); WOLFSSL_LOCAL int SetSerialNumber(const byte* sn, word32 snSz, byte* output, word32 outputSz, int maxSnSz); diff --git a/wolfssl/wolfcrypt/pkcs7.h b/wolfssl/wolfcrypt/pkcs7.h index 1a13ae0277..93c9c6a5d7 100644 --- a/wolfssl/wolfcrypt/pkcs7.h +++ b/wolfssl/wolfcrypt/pkcs7.h @@ -388,6 +388,13 @@ struct wc_PKCS7 { CallbackEccSignRawDigest eccSignRawDigestCb; #endif +#if defined(WC_RSA_PSS) && !defined(NO_RSA) + int pssSaltLen; /* RSASSA-PSS params from SignerInfo; valid when */ + int pssHashType; /* pssParamsPresent == 1; else verify path uses */ + int pssMgf; /* RSA_PSS_SALT_LEN_DEFAULT / digest algo defaults */ + byte pssParamsPresent; +#endif + /* !! NEW DATA MEMBERS MUST BE ADDED AT END !! */ }; diff --git a/wolfssl/wolfcrypt/sha256.h b/wolfssl/wolfcrypt/sha256.h index 398722955f..1cc27b070a 100644 --- a/wolfssl/wolfcrypt/sha256.h +++ b/wolfssl/wolfcrypt/sha256.h @@ -102,13 +102,7 @@ #define WOLFSSL_NO_HASH_RAW #endif -#if defined(_MSC_VER) - #define SHA256_NOINLINE __declspec(noinline) -#elif defined(__IAR_SYSTEMS_ICC__) || defined(__GNUC__) - #define SHA256_NOINLINE __attribute__((noinline)) -#else - #define SHA256_NOINLINE -#endif +#define SHA256_NOINLINE WC_NO_INLINE #if !defined(NO_OLD_SHA_NAMES) #define SHA256 WC_SHA256 diff --git a/wolfssl/wolfcrypt/sha512.h b/wolfssl/wolfcrypt/sha512.h index 7d9724e5f1..a6e906f1c8 100644 --- a/wolfssl/wolfcrypt/sha512.h +++ b/wolfssl/wolfcrypt/sha512.h @@ -80,13 +80,7 @@ #include #endif -#if defined(_MSC_VER) - #define SHA512_NOINLINE __declspec(noinline) -#elif defined(__IAR_SYSTEMS_ICC__) || defined(__GNUC__) - #define SHA512_NOINLINE __attribute__((noinline)) -#else - #define SHA512_NOINLINE -#endif +#define SHA512_NOINLINE WC_NO_INLINE #ifdef WOLFSSL_SHA512 diff --git a/wolfssl/wolfcrypt/sp.h b/wolfssl/wolfcrypt/sp.h index 1ebd39efad..c0f028a9b3 100644 --- a/wolfssl/wolfcrypt/sp.h +++ b/wolfssl/wolfcrypt/sp.h @@ -48,18 +48,7 @@ #undef WOLFSSL_HAVE_SP_ECC #endif -#ifdef noinline - #define SP_NOINLINE noinline -#elif defined(_MSC_VER) - #define SP_NOINLINE __declspec(noinline) -#elif defined(__ICCARM__) || defined(__IAR_SYSTEMS_ICC__) - #define SP_NOINLINE _Pragma("inline = never") -#elif defined(__GNUC__) || defined(__KEIL__) || defined(__DCC__) - #define SP_NOINLINE __attribute__((noinline)) -#else - #define SP_NOINLINE -#endif - +#define SP_NOINLINE WC_NO_INLINE #ifdef __cplusplus extern "C" { diff --git a/wolfssl/wolfcrypt/types.h b/wolfssl/wolfcrypt/types.h index 28bb12fc17..af7d6b6224 100644 --- a/wolfssl/wolfcrypt/types.h +++ b/wolfssl/wolfcrypt/types.h @@ -2056,6 +2056,33 @@ WOLFSSL_API word32 CheckRunTimeSettings(void); #define WC_NORETURN #endif +#ifdef __has_attribute +#if __has_attribute(nonnull) + #ifndef WC_ARG_NOT_NULL + #define WC_ARG_NOT_NULL(a) __attribute__((nonnull(a))) + #endif + #ifndef WC_ARGS_NOT_NULL + /* double-parenthesize, a la WC_ARGS_NOT_NULL((1, 2)) -- this approach + * maintains compatibility with WOLF_NO_VARIADIC_MACROS. + */ + #define WC_ARGS_NOT_NULL(p_a) __attribute__((nonnull p_a)) + #endif + #ifndef WC_ALL_ARGS_NOT_NULL + #define WC_ALL_ARGS_NOT_NULL __attribute__((nonnull)) + #endif +#endif /* __has_attribute(nonnull) */ +#endif /* defined(__has_attribute) */ + +#ifndef WC_ARG_NOT_NULL + #define WC_ARG_NOT_NULL(a) /* null expansion */ +#endif +#ifndef WC_ARGS_NOT_NULL + #define WC_ARGS_NOT_NULL(p_a) /* null expansion */ +#endif +#ifndef WC_ALL_ARGS_NOT_NULL + #define WC_ALL_ARGS_NOT_NULL +#endif + #if defined(WOLFSSL_KEY_GEN) || defined(HAVE_COMP_KEY) || \ defined(WOLFSSL_DEBUG_MATH) || defined(DEBUG_WOLFSSL) || \ defined(WOLFSSL_PUBLIC_MP) || defined(OPENSSL_EXTRA) || \ diff --git a/wolfssl/wolfcrypt/wc_mlkem.h b/wolfssl/wolfcrypt/wc_mlkem.h index 1ee4225787..27f12264c3 100644 --- a/wolfssl/wolfcrypt/wc_mlkem.h +++ b/wolfssl/wolfcrypt/wc_mlkem.h @@ -44,15 +44,7 @@ #define WOLFSSL_MLKEM_NO_DECAPSULATE #endif -#ifdef noinline - #define MLKEM_NOINLINE noinline -#elif defined(_MSC_VER) - #define MLKEM_NOINLINE __declspec(noinline) -#elif defined(__GNUC__) - #define MLKEM_NOINLINE __attribute__((noinline)) -#else - #define MLKEM_NOINLINE -#endif +#define MLKEM_NOINLINE WC_NO_INLINE enum { /* Flags of Kyber keys. */ diff --git a/wolfssl/wolfcrypt/wc_port.h b/wolfssl/wolfcrypt/wc_port.h index 76cb11b5b1..cb0f35f1e2 100644 --- a/wolfssl/wolfcrypt/wc_port.h +++ b/wolfssl/wolfcrypt/wc_port.h @@ -104,7 +104,7 @@ #else #define WC_DEPRECATED(msg) /* null expansion */ #endif -#endif /* !WC_MAYBE_UNUSED */ +#endif /* !WC_DEPRECATED */ /* use inlining if compiler allows */ #ifndef WC_INLINE @@ -143,6 +143,20 @@ #endif #endif +#ifndef WC_NO_INLINE + #ifdef noinline + #define WC_NO_INLINE noinline + #elif defined(_MSC_VER) + #define WC_NO_INLINE __declspec(noinline) + #elif defined(__ICCARM__) || defined(__IAR_SYSTEMS_ICC__) + #define WC_NO_INLINE _Pragma("inline = never") + #elif defined(__GNUC__) || defined(__KEIL__) || defined(__DCC__) + #define WC_NO_INLINE __attribute__((noinline)) + #else + #define WC_NO_INLINE + #endif +#endif + #ifndef WC_OMIT_FRAME_POINTER #if defined(__GNUC__) #define WC_OMIT_FRAME_POINTER \ diff --git a/wrapper/rust/wolfssl-wolfcrypt/build.rs b/wrapper/rust/wolfssl-wolfcrypt/build.rs index 0f9985acba..63e098b0ae 100644 --- a/wrapper/rust/wolfssl-wolfcrypt/build.rs +++ b/wrapper/rust/wolfssl-wolfcrypt/build.rs @@ -312,6 +312,22 @@ fn scan_cfg() -> Result<()> { println!("cargo:rustc-cfg=rsa_const_api"); } + /* dilithium / ML-DSA */ + check_cfg(&binding, "wc_dilithium_init", "dilithium"); + check_cfg(&binding, "wc_dilithium_make_key", "dilithium_make_key"); + check_cfg(&binding, "wc_dilithium_make_key_from_seed", "dilithium_make_key_from_seed"); + check_cfg(&binding, "wc_dilithium_sign_msg", "dilithium_sign"); + check_cfg(&binding, "wc_dilithium_sign_msg_with_seed", "dilithium_sign_with_seed"); + check_cfg(&binding, "wc_dilithium_verify_msg", "dilithium_verify"); + check_cfg(&binding, "wc_dilithium_import_public", "dilithium_import"); + check_cfg(&binding, "wc_dilithium_export_public", "dilithium_export"); + check_cfg(&binding, "wc_dilithium_check_key", "dilithium_check_key"); + check_cfg(&binding, "DILITHIUM_LEVEL2_KEY_SIZE", "dilithium_level2"); + check_cfg(&binding, "DILITHIUM_LEVEL3_KEY_SIZE", "dilithium_level3"); + check_cfg(&binding, "DILITHIUM_LEVEL5_KEY_SIZE", "dilithium_level5"); + check_cfg(&binding, "DILITHIUM_SEED_SZ", "dilithium_make_key_seed_sz"); + check_cfg(&binding, "DILITHIUM_RND_SZ", "dilithium_rnd_sz"); + /* sha */ check_cfg(&binding, "wc_InitSha", "sha"); check_cfg(&binding, "wc_InitSha224", "sha224"); diff --git a/wrapper/rust/wolfssl-wolfcrypt/headers.h b/wrapper/rust/wolfssl-wolfcrypt/headers.h index da521fa703..ee1038bf80 100644 --- a/wrapper/rust/wolfssl-wolfcrypt/headers.h +++ b/wrapper/rust/wolfssl-wolfcrypt/headers.h @@ -19,3 +19,4 @@ #include "wolfssl/wolfcrypt/logging.h" #include "wolfssl/wolfcrypt/aes.h" #include "wolfssl/wolfcrypt/pwdbased.h" +#include "wolfssl/wolfcrypt/dilithium.h" diff --git a/wrapper/rust/wolfssl-wolfcrypt/src/dilithium.rs b/wrapper/rust/wolfssl-wolfcrypt/src/dilithium.rs new file mode 100644 index 0000000000..0dcf920043 --- /dev/null +++ b/wrapper/rust/wolfssl-wolfcrypt/src/dilithium.rs @@ -0,0 +1,1313 @@ +/* + * Copyright (C) 2006-2026 wolfSSL Inc. + * + * This file is part of wolfSSL. + * + * wolfSSL is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * wolfSSL is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA + */ + +/*! +This module provides a Rust wrapper for the wolfCrypt library's ML-DSA +(Dilithium) post-quantum digital signature functionality. + +The primary component is the [`Dilithium`] struct, which manages the lifecycle +of a wolfSSL `dilithium_key` object. It ensures proper initialization and +deallocation. + +Three security parameter sets are supported, selected via +[`Dilithium::set_level()`]: + +| Constant | Level | NIST PQC Level | +|-----------------|-------|----------------| +| [`Dilithium::LEVEL_44`] | 2 | 2 (ML-DSA-44) | +| [`Dilithium::LEVEL_65`] | 3 | 3 (ML-DSA-65) | +| [`Dilithium::LEVEL_87`] | 5 | 5 (ML-DSA-87) | + +# Examples + +```rust +#[cfg(all(dilithium, dilithium_make_key, dilithium_sign, dilithium_verify, random))] +{ +use wolfssl_wolfcrypt::random::RNG; +use wolfssl_wolfcrypt::dilithium::Dilithium; +let mut rng = RNG::new().expect("RNG creation failed"); +let mut key = Dilithium::generate(Dilithium::LEVEL_44, &mut rng) + .expect("Key generation failed"); +let message = b"Hello, ML-DSA!"; +let mut sig = vec![0u8; key.sig_size().expect("sig_size failed")]; +let sig_len = key.sign_msg(message, &mut sig, &mut rng) + .expect("Signing failed"); +let valid = key.verify_msg(&sig[..sig_len], message) + .expect("Verification failed"); +assert!(valid); +} +``` +*/ + +#![cfg(dilithium)] + +use crate::sys; +#[cfg(all(random, any(dilithium_make_key, dilithium_sign)))] +use crate::random::RNG; +use core::mem::MaybeUninit; + +/// Rust wrapper for a wolfSSL `dilithium_key` object. +/// +/// Manages the lifecycle of the underlying key, including initialization and +/// deallocation via the [`Drop`] trait. +/// +/// An instance is created with [`Dilithium::generate()`], +/// [`Dilithium::generate_from_seed()`], or [`Dilithium::new()`]. +pub struct Dilithium { + ws_key: sys::dilithium_key, +} + +impl Dilithium { + /// ML-DSA-44 security parameter set (NIST Level 2). + pub const LEVEL_44: u8 = sys::WC_ML_DSA_44 as u8; + /// ML-DSA-65 security parameter set (NIST Level 3). + pub const LEVEL_65: u8 = sys::WC_ML_DSA_65 as u8; + /// ML-DSA-87 security parameter set (NIST Level 5). + pub const LEVEL_87: u8 = sys::WC_ML_DSA_87 as u8; + + /// Required size in bytes of the seed passed to + /// [`Dilithium::generate_from_seed()`] (`DILITHIUM_SEED_SZ`). + #[cfg(dilithium_make_key_seed_sz)] + pub const DILITHIUM_SEED_SZ: usize = sys::DILITHIUM_SEED_SZ as usize; + + /// Required size in bytes of the seed passed to signing-with-seed + /// functions such as [`Dilithium::sign_msg_with_seed()`] + /// (`DILITHIUM_RND_SZ`). + #[cfg(dilithium_rnd_sz)] + pub const SIGN_SEED_SIZE: usize = sys::DILITHIUM_RND_SZ as usize; + + /// Private (secret) key size in bytes for ML-DSA-44. + #[cfg(dilithium_level2)] + pub const LEVEL2_KEY_SIZE: usize = sys::DILITHIUM_LEVEL2_KEY_SIZE as usize; + /// Signature size in bytes for ML-DSA-44. + #[cfg(dilithium_level2)] + pub const LEVEL2_SIG_SIZE: usize = sys::DILITHIUM_LEVEL2_SIG_SIZE as usize; + /// Public key size in bytes for ML-DSA-44. + #[cfg(dilithium_level2)] + pub const LEVEL2_PUB_KEY_SIZE: usize = sys::DILITHIUM_LEVEL2_PUB_KEY_SIZE as usize; + /// Combined private-plus-public key size in bytes for ML-DSA-44. + #[cfg(dilithium_level2)] + pub const LEVEL2_PRV_KEY_SIZE: usize = + sys::DILITHIUM_LEVEL2_PUB_KEY_SIZE as usize + sys::DILITHIUM_LEVEL2_KEY_SIZE as usize; + + /// Private (secret) key size in bytes for ML-DSA-65. + #[cfg(dilithium_level3)] + pub const LEVEL3_KEY_SIZE: usize = sys::DILITHIUM_LEVEL3_KEY_SIZE as usize; + /// Signature size in bytes for ML-DSA-65. + #[cfg(dilithium_level3)] + pub const LEVEL3_SIG_SIZE: usize = sys::DILITHIUM_LEVEL3_SIG_SIZE as usize; + /// Public key size in bytes for ML-DSA-65. + #[cfg(dilithium_level3)] + pub const LEVEL3_PUB_KEY_SIZE: usize = sys::DILITHIUM_LEVEL3_PUB_KEY_SIZE as usize; + /// Combined private-plus-public key size in bytes for ML-DSA-65. + #[cfg(dilithium_level3)] + pub const LEVEL3_PRV_KEY_SIZE: usize = + sys::DILITHIUM_LEVEL3_PUB_KEY_SIZE as usize + sys::DILITHIUM_LEVEL3_KEY_SIZE as usize; + + /// Private (secret) key size in bytes for ML-DSA-87. + #[cfg(dilithium_level5)] + pub const LEVEL5_KEY_SIZE: usize = sys::DILITHIUM_LEVEL5_KEY_SIZE as usize; + /// Signature size in bytes for ML-DSA-87. + #[cfg(dilithium_level5)] + pub const LEVEL5_SIG_SIZE: usize = sys::DILITHIUM_LEVEL5_SIG_SIZE as usize; + /// Public key size in bytes for ML-DSA-87. + #[cfg(dilithium_level5)] + pub const LEVEL5_PUB_KEY_SIZE: usize = sys::DILITHIUM_LEVEL5_PUB_KEY_SIZE as usize; + /// Combined private-plus-public key size in bytes for ML-DSA-87. + #[cfg(dilithium_level5)] + pub const LEVEL5_PRV_KEY_SIZE: usize = + sys::DILITHIUM_LEVEL5_PUB_KEY_SIZE as usize + sys::DILITHIUM_LEVEL5_KEY_SIZE as usize; + + /// Generate a new Dilithium key pair using a random number generator. + /// + /// # Parameters + /// + /// * `level`: Security parameter set. One of [`Dilithium::LEVEL_44`], + /// [`Dilithium::LEVEL_65`], or [`Dilithium::LEVEL_87`]. + /// * `rng`: `RNG` instance to use for random number generation. + /// + /// # Returns + /// + /// Returns either Ok(Dilithium) containing the key instance or Err(e) + /// containing the wolfSSL library error code value. + /// + /// # Example + /// + /// ```rust + /// #[cfg(all(dilithium, dilithium_make_key, random))] + /// { + /// use wolfssl_wolfcrypt::random::RNG; + /// use wolfssl_wolfcrypt::dilithium::Dilithium; + /// let mut rng = RNG::new().expect("Error creating RNG"); + /// let key = Dilithium::generate(Dilithium::LEVEL_44, &mut rng) + /// .expect("Error with generate()"); + /// } + /// ``` + #[cfg(all(dilithium_make_key, random))] + pub fn generate(level: u8, rng: &mut RNG) -> Result { + Self::generate_ex(level, rng, None, None) + } + + /// Generate a new Dilithium key pair with optional heap hint and device ID. + /// + /// # Parameters + /// + /// * `level`: Security parameter set. One of [`Dilithium::LEVEL_44`], + /// [`Dilithium::LEVEL_65`], or [`Dilithium::LEVEL_87`]. + /// * `rng`: `RNG` instance to use for random number generation. + /// * `heap`: Optional heap hint. + /// * `dev_id`: Optional device ID for crypto callbacks or async hardware. + /// + /// # Returns + /// + /// Returns either Ok(Dilithium) containing the key instance or Err(e) + /// containing the wolfSSL library error code value. + /// + /// # Example + /// + /// ```rust + /// #[cfg(all(dilithium, dilithium_make_key, random))] + /// { + /// use wolfssl_wolfcrypt::random::RNG; + /// use wolfssl_wolfcrypt::dilithium::Dilithium; + /// let mut rng = RNG::new().expect("Error creating RNG"); + /// let key = Dilithium::generate_ex(Dilithium::LEVEL_44, &mut rng, None, None) + /// .expect("Error with generate_ex()"); + /// } + /// ``` + #[cfg(all(dilithium_make_key, random))] + pub fn generate_ex( + level: u8, + rng: &mut RNG, + heap: Option<*mut core::ffi::c_void>, + dev_id: Option, + ) -> Result { + let mut key = Self::new_ex(heap, dev_id)?; + let rc = unsafe { sys::wc_dilithium_set_level(&mut key.ws_key, level) }; + if rc != 0 { + return Err(rc); + } + let rc = unsafe { sys::wc_dilithium_make_key(&mut key.ws_key, &mut rng.wc_rng) }; + if rc != 0 { + return Err(rc); + } + Ok(key) + } + + /// Generate a Dilithium key pair from a fixed seed. + /// + /// Produces the same key pair for a given `(level, seed)` pair, enabling + /// deterministic key generation. + /// + /// # Parameters + /// + /// * `level`: Security parameter set. One of [`Dilithium::LEVEL_44`], + /// [`Dilithium::LEVEL_65`], or [`Dilithium::LEVEL_87`]. + /// * `seed`: Seed bytes. Must be `DILITHIUM_SEED_SZ` (32) bytes. + /// + /// # Returns + /// + /// Returns either Ok(Dilithium) containing the key instance or Err(e) + /// containing the wolfSSL library error code value. + /// + /// # Example + /// + /// ```rust + /// #[cfg(all(dilithium, dilithium_make_key_from_seed))] + /// { + /// use wolfssl_wolfcrypt::dilithium::Dilithium; + /// let seed = [0x42u8; 32]; + /// let key = Dilithium::generate_from_seed(Dilithium::LEVEL_44, &seed) + /// .expect("Error with generate_from_seed()"); + /// } + /// ``` + #[cfg(dilithium_make_key_from_seed)] + pub fn generate_from_seed(level: u8, seed: &[u8]) -> Result { + Self::generate_from_seed_ex(level, seed, None, None) + } + + /// Generate a Dilithium key pair from a fixed seed with optional heap hint + /// and device ID. + /// + /// # Parameters + /// + /// * `level`: Security parameter set. One of [`Dilithium::LEVEL_44`], + /// [`Dilithium::LEVEL_65`], or [`Dilithium::LEVEL_87`]. + /// * `seed`: Seed bytes. Must be `DILITHIUM_SEED_SZ` (32) bytes. + /// * `heap`: Optional heap hint. + /// * `dev_id`: Optional device ID for crypto callbacks or async hardware. + /// + /// # Returns + /// + /// Returns either Ok(Dilithium) containing the key instance or Err(e) + /// containing the wolfSSL library error code value. + /// + /// # Example + /// + /// ```rust + /// #[cfg(all(dilithium, dilithium_make_key_from_seed))] + /// { + /// use wolfssl_wolfcrypt::dilithium::Dilithium; + /// let seed = [0x42u8; 32]; + /// let key = Dilithium::generate_from_seed_ex(Dilithium::LEVEL_44, &seed, None, None) + /// .expect("Error with generate_from_seed_ex()"); + /// } + /// ``` + #[cfg(dilithium_make_key_from_seed)] + pub fn generate_from_seed_ex( + level: u8, + seed: &[u8], + heap: Option<*mut core::ffi::c_void>, + dev_id: Option, + ) -> Result { + #[cfg(dilithium_make_key_seed_sz)] + if seed.len() != Self::DILITHIUM_SEED_SZ { + return Err(sys::wolfCrypt_ErrorCodes_BUFFER_E); + } + let mut key = Self::new_ex(heap, dev_id)?; + let rc = unsafe { sys::wc_dilithium_set_level(&mut key.ws_key, level) }; + if rc != 0 { + return Err(rc); + } + let rc = unsafe { + sys::wc_dilithium_make_key_from_seed(&mut key.ws_key, seed.as_ptr()) + }; + if rc != 0 { + return Err(rc); + } + Ok(key) + } + + /// Create and initialize a new Dilithium key instance without a key. + /// + /// The security level and key material can be set afterwards using + /// [`Dilithium::set_level()`] and one of the import functions. + /// + /// # Returns + /// + /// Returns either Ok(Dilithium) containing the key instance or Err(e) + /// containing the wolfSSL library error code value. + /// + /// # Example + /// + /// ```rust + /// #[cfg(dilithium)] + /// { + /// use wolfssl_wolfcrypt::dilithium::Dilithium; + /// let key = Dilithium::new().expect("Error with new()"); + /// } + /// ``` + pub fn new() -> Result { + Self::new_ex(None, None) + } + + /// Create and initialize a new Dilithium key instance with optional heap + /// hint and device ID. + /// + /// # Parameters + /// + /// * `heap`: Optional heap hint. + /// * `dev_id`: Optional device ID for crypto callbacks or async hardware. + /// + /// # Returns + /// + /// Returns either Ok(Dilithium) containing the key instance or Err(e) + /// containing the wolfSSL library error code value. + /// + /// # Example + /// + /// ```rust + /// #[cfg(dilithium)] + /// { + /// use wolfssl_wolfcrypt::dilithium::Dilithium; + /// let key = Dilithium::new_ex(None, None).expect("Error with new_ex()"); + /// } + /// ``` + pub fn new_ex( + heap: Option<*mut core::ffi::c_void>, + dev_id: Option, + ) -> Result { + let mut ws_key: MaybeUninit = MaybeUninit::uninit(); + let heap = match heap { + Some(h) => h, + None => core::ptr::null_mut(), + }; + let dev_id = match dev_id { + Some(id) => id, + None => sys::INVALID_DEVID, + }; + let rc = unsafe { sys::wc_dilithium_init_ex(ws_key.as_mut_ptr(), heap, dev_id) }; + if rc != 0 { + return Err(rc); + } + let ws_key = unsafe { ws_key.assume_init() }; + Ok(Dilithium { ws_key }) + } + + /// Set the security parameter level for this key. + /// + /// Must be called before generating or importing key material. Use one of + /// the level constants: [`Dilithium::LEVEL_44`], [`Dilithium::LEVEL_65`], + /// or [`Dilithium::LEVEL_87`]. + /// + /// # Parameters + /// + /// * `level`: Security level (2 = ML-DSA-44, 3 = ML-DSA-65, 5 = ML-DSA-87). + /// + /// # Returns + /// + /// Returns either Ok(()) on success or Err(e) containing the wolfSSL + /// library error code value. + /// + /// # Example + /// + /// ```rust + /// #[cfg(dilithium)] + /// { + /// use wolfssl_wolfcrypt::dilithium::Dilithium; + /// let mut key = Dilithium::new().expect("Error with new()"); + /// key.set_level(Dilithium::LEVEL_65).expect("Error with set_level()"); + /// } + /// ``` + pub fn set_level(&mut self, level: u8) -> Result<(), i32> { + let rc = unsafe { sys::wc_dilithium_set_level(&mut self.ws_key, level) }; + if rc != 0 { + return Err(rc); + } + Ok(()) + } + + /// Get the security parameter level of this key. + /// + /// # Returns + /// + /// Returns either Ok(level) containing the current security level or + /// Err(e) containing the wolfSSL library error code value. + /// + /// # Example + /// + /// ```rust + /// #[cfg(dilithium)] + /// { + /// use wolfssl_wolfcrypt::dilithium::Dilithium; + /// let mut key = Dilithium::new().expect("Error with new()"); + /// key.set_level(Dilithium::LEVEL_87).expect("Error with set_level()"); + /// let level = key.get_level().expect("Error with get_level()"); + /// assert_eq!(level, Dilithium::LEVEL_87); + /// } + /// ``` + pub fn get_level(&mut self) -> Result { + let mut level = 0u8; + let rc = unsafe { sys::wc_dilithium_get_level(&mut self.ws_key, &mut level) }; + if rc != 0 { + return Err(rc); + } + Ok(level) + } + + /// Get the private (secret) key size in bytes for the current level. + /// + /// # Returns + /// + /// Returns either Ok(size) containing the private key size or Err(e) + /// containing the wolfSSL library error code value. + /// + /// # Example + /// + /// ```rust + /// #[cfg(all(dilithium, dilithium_make_key, random))] + /// { + /// use wolfssl_wolfcrypt::random::RNG; + /// use wolfssl_wolfcrypt::dilithium::Dilithium; + /// let mut rng = RNG::new().expect("Error creating RNG"); + /// let mut key = Dilithium::generate(Dilithium::LEVEL_44, &mut rng) + /// .expect("Error with generate()"); + /// let sz = key.size().expect("Error with size()"); + /// assert_eq!(sz, Dilithium::LEVEL2_KEY_SIZE); + /// } + /// ``` + pub fn size(&mut self) -> Result { + let rc = unsafe { sys::wc_dilithium_size(&mut self.ws_key) }; + if rc < 0 { + return Err(rc); + } + Ok(rc as usize) + } + + /// Get the combined private-plus-public key size in bytes for the current + /// level. + /// + /// # Returns + /// + /// Returns either Ok(size) or Err(e) containing the wolfSSL library error + /// code value. + /// + /// # Example + /// + /// ```rust + /// #[cfg(all(dilithium, dilithium_make_key, random))] + /// { + /// use wolfssl_wolfcrypt::random::RNG; + /// use wolfssl_wolfcrypt::dilithium::Dilithium; + /// let mut rng = RNG::new().expect("Error creating RNG"); + /// let mut key = Dilithium::generate(Dilithium::LEVEL_44, &mut rng) + /// .expect("Error with generate()"); + /// let sz = key.priv_size().expect("Error with priv_size()"); + /// assert_eq!(sz, Dilithium::LEVEL2_PRV_KEY_SIZE); + /// } + /// ``` + pub fn priv_size(&mut self) -> Result { + let rc = unsafe { sys::wc_dilithium_priv_size(&mut self.ws_key) }; + if rc < 0 { + return Err(rc); + } + Ok(rc as usize) + } + + /// Get the public key size in bytes for the current level. + /// + /// # Returns + /// + /// Returns either Ok(size) or Err(e) containing the wolfSSL library error + /// code value. + /// + /// # Example + /// + /// ```rust + /// #[cfg(all(dilithium, dilithium_make_key, random))] + /// { + /// use wolfssl_wolfcrypt::random::RNG; + /// use wolfssl_wolfcrypt::dilithium::Dilithium; + /// let mut rng = RNG::new().expect("Error creating RNG"); + /// let mut key = Dilithium::generate(Dilithium::LEVEL_44, &mut rng) + /// .expect("Error with generate()"); + /// let sz = key.pub_size().expect("Error with pub_size()"); + /// assert_eq!(sz, Dilithium::LEVEL2_PUB_KEY_SIZE); + /// } + /// ``` + pub fn pub_size(&mut self) -> Result { + let rc = unsafe { sys::wc_dilithium_pub_size(&mut self.ws_key) }; + if rc < 0 { + return Err(rc); + } + Ok(rc as usize) + } + + /// Get the signature size in bytes for the current level. + /// + /// # Returns + /// + /// Returns either Ok(size) or Err(e) containing the wolfSSL library error + /// code value. + /// + /// # Example + /// + /// ```rust + /// #[cfg(all(dilithium, dilithium_make_key, random))] + /// { + /// use wolfssl_wolfcrypt::random::RNG; + /// use wolfssl_wolfcrypt::dilithium::Dilithium; + /// let mut rng = RNG::new().expect("Error creating RNG"); + /// let mut key = Dilithium::generate(Dilithium::LEVEL_44, &mut rng) + /// .expect("Error with generate()"); + /// let sz = key.sig_size().expect("Error with sig_size()"); + /// assert_eq!(sz, Dilithium::LEVEL2_SIG_SIZE); + /// } + /// ``` + pub fn sig_size(&mut self) -> Result { + let rc = unsafe { sys::wc_dilithium_sig_size(&mut self.ws_key) }; + if rc < 0 { + return Err(rc); + } + Ok(rc as usize) + } + + /// Check that the key pair is valid (public key matches private key). + /// + /// # Returns + /// + /// Returns either Ok(()) on success or Err(e) containing the wolfSSL + /// library error code value. + /// + /// # Example + /// + /// ```rust + /// #[cfg(all(dilithium, dilithium_make_key, dilithium_check_key, random))] + /// { + /// use wolfssl_wolfcrypt::random::RNG; + /// use wolfssl_wolfcrypt::dilithium::Dilithium; + /// let mut rng = RNG::new().expect("Error creating RNG"); + /// let mut key = Dilithium::generate(Dilithium::LEVEL_44, &mut rng) + /// .expect("Error with generate()"); + /// key.check_key().expect("Error with check_key()"); + /// } + /// ``` + #[cfg(dilithium_check_key)] + pub fn check_key(&mut self) -> Result<(), i32> { + let rc = unsafe { sys::wc_dilithium_check_key(&mut self.ws_key) }; + if rc != 0 { + return Err(rc); + } + Ok(()) + } + + /// Import a public key from a raw byte buffer. + /// + /// # Parameters + /// + /// * `public`: Input buffer containing the raw public key bytes. + /// + /// # Returns + /// + /// Returns either Ok(()) on success or Err(e) containing the wolfSSL + /// library error code value. + /// + /// # Example + /// + /// ```rust + /// #[cfg(all(dilithium, dilithium_make_key, dilithium_import, dilithium_export, random))] + /// { + /// use wolfssl_wolfcrypt::random::RNG; + /// use wolfssl_wolfcrypt::dilithium::Dilithium; + /// let mut rng = RNG::new().expect("Error creating RNG"); + /// let mut key = Dilithium::generate(Dilithium::LEVEL_44, &mut rng) + /// .expect("Error with generate()"); + /// let mut pub_buf = vec![0u8; key.pub_size().unwrap()]; + /// key.export_public(&mut pub_buf).expect("Error with export_public()"); + /// let mut key2 = Dilithium::new().expect("Error with new()"); + /// key2.set_level(Dilithium::LEVEL_44).expect("Error with set_level()"); + /// key2.import_public(&pub_buf).expect("Error with import_public()"); + /// } + /// ``` + #[cfg(dilithium_import)] + pub fn import_public(&mut self, public: &[u8]) -> Result<(), i32> { + let public_size = public.len() as u32; + let rc = unsafe { + sys::wc_dilithium_import_public(public.as_ptr(), public_size, &mut self.ws_key) + }; + if rc != 0 { + return Err(rc); + } + Ok(()) + } + + /// Import a private (secret) key from a raw byte buffer. + /// + /// The buffer should contain the raw private key bytes only + /// (size = `LEVEL*_KEY_SIZE`). + /// + /// # Parameters + /// + /// * `private`: Input buffer containing the raw private key bytes. + /// + /// # Returns + /// + /// Returns either Ok(()) on success or Err(e) containing the wolfSSL + /// library error code value. + /// + /// # Example + /// + /// ```rust + /// #[cfg(all(dilithium, dilithium_make_key, dilithium_import, dilithium_export, random))] + /// { + /// use wolfssl_wolfcrypt::random::RNG; + /// use wolfssl_wolfcrypt::dilithium::Dilithium; + /// let mut rng = RNG::new().expect("Error creating RNG"); + /// let mut key = Dilithium::generate(Dilithium::LEVEL_44, &mut rng) + /// .expect("Error with generate()"); + /// let mut priv_buf = vec![0u8; key.size().unwrap()]; + /// key.export_private(&mut priv_buf).expect("Error with export_private()"); + /// let mut key2 = Dilithium::new().expect("Error with new()"); + /// key2.set_level(Dilithium::LEVEL_44).expect("Error with set_level()"); + /// key2.import_private(&priv_buf).expect("Error with import_private()"); + /// } + /// ``` + #[cfg(dilithium_import)] + pub fn import_private(&mut self, private: &[u8]) -> Result<(), i32> { + let private_size = private.len() as u32; + let rc = unsafe { + sys::wc_dilithium_import_private(private.as_ptr(), private_size, &mut self.ws_key) + }; + if rc != 0 { + return Err(rc); + } + Ok(()) + } + + /// Import both private and public key material from raw byte buffers. + /// + /// # Parameters + /// + /// * `private`: Input buffer containing the raw private key bytes. + /// * `public`: Input buffer containing the raw public key bytes. + /// + /// # Returns + /// + /// Returns either Ok(()) on success or Err(e) containing the wolfSSL + /// library error code value. + /// + /// # Example + /// + /// ```rust + /// #[cfg(all(dilithium, dilithium_make_key, dilithium_import, dilithium_export, random))] + /// { + /// use wolfssl_wolfcrypt::random::RNG; + /// use wolfssl_wolfcrypt::dilithium::Dilithium; + /// let mut rng = RNG::new().expect("Error creating RNG"); + /// let mut key = Dilithium::generate(Dilithium::LEVEL_44, &mut rng) + /// .expect("Error with generate()"); + /// let mut priv_buf = vec![0u8; key.size().unwrap()]; + /// let mut pub_buf = vec![0u8; key.pub_size().unwrap()]; + /// key.export_key(&mut priv_buf, &mut pub_buf).expect("Error with export_key()"); + /// let mut key2 = Dilithium::new().expect("Error with new()"); + /// key2.set_level(Dilithium::LEVEL_44).expect("Error with set_level()"); + /// key2.import_key(&priv_buf, &pub_buf).expect("Error with import_key()"); + /// } + /// ``` + #[cfg(dilithium_import)] + pub fn import_key(&mut self, private: &[u8], public: &[u8]) -> Result<(), i32> { + let private_size = private.len() as u32; + let public_size = public.len() as u32; + let rc = unsafe { + sys::wc_dilithium_import_key( + private.as_ptr(), private_size, + public.as_ptr(), public_size, + &mut self.ws_key, + ) + }; + if rc != 0 { + return Err(rc); + } + Ok(()) + } + + /// Export the public key to a raw byte buffer. + /// + /// # Parameters + /// + /// * `public`: Output buffer to receive the public key. Must be at least + /// `pub_size()` bytes. + /// + /// # Returns + /// + /// Returns either Ok(size) containing the number of bytes written or Err(e) + /// containing the wolfSSL library error code value. + /// + /// # Example + /// + /// ```rust + /// #[cfg(all(dilithium, dilithium_make_key, dilithium_export, random))] + /// { + /// use wolfssl_wolfcrypt::random::RNG; + /// use wolfssl_wolfcrypt::dilithium::Dilithium; + /// let mut rng = RNG::new().expect("Error creating RNG"); + /// let mut key = Dilithium::generate(Dilithium::LEVEL_44, &mut rng) + /// .expect("Error with generate()"); + /// let mut pub_buf = vec![0u8; key.pub_size().unwrap()]; + /// let written = key.export_public(&mut pub_buf).expect("Error with export_public()"); + /// assert_eq!(written, Dilithium::LEVEL2_PUB_KEY_SIZE); + /// } + /// ``` + #[cfg(dilithium_export)] + pub fn export_public(&mut self, public: &mut [u8]) -> Result { + let mut public_size = public.len() as u32; + let rc = unsafe { + sys::wc_dilithium_export_public(&mut self.ws_key, public.as_mut_ptr(), &mut public_size) + }; + if rc != 0 { + return Err(rc); + } + Ok(public_size as usize) + } + + /// Export the private (secret) key to a raw byte buffer. + /// + /// # Parameters + /// + /// * `private`: Output buffer to receive the private key. Must be at + /// least `size()` bytes. + /// + /// # Returns + /// + /// Returns either Ok(size) containing the number of bytes written or Err(e) + /// containing the wolfSSL library error code value. + /// + /// # Example + /// + /// ```rust + /// #[cfg(all(dilithium, dilithium_make_key, dilithium_export, random))] + /// { + /// use wolfssl_wolfcrypt::random::RNG; + /// use wolfssl_wolfcrypt::dilithium::Dilithium; + /// let mut rng = RNG::new().expect("Error creating RNG"); + /// let mut key = Dilithium::generate(Dilithium::LEVEL_44, &mut rng) + /// .expect("Error with generate()"); + /// let mut priv_buf = vec![0u8; key.size().unwrap()]; + /// let written = key.export_private(&mut priv_buf).expect("Error with export_private()"); + /// assert_eq!(written, Dilithium::LEVEL2_KEY_SIZE); + /// } + /// ``` + #[cfg(dilithium_export)] + pub fn export_private(&mut self, private: &mut [u8]) -> Result { + let mut private_size = private.len() as u32; + let rc = unsafe { + sys::wc_dilithium_export_private( + &mut self.ws_key, private.as_mut_ptr(), &mut private_size, + ) + }; + if rc != 0 { + return Err(rc); + } + Ok(private_size as usize) + } + + /// Export both private and public key material to separate raw byte + /// buffers. + /// + /// # Parameters + /// + /// * `private`: Output buffer for the private key. Must be at least + /// `size()` bytes. + /// * `public`: Output buffer for the public key. Must be at least + /// `pub_size()` bytes. + /// + /// # Returns + /// + /// Returns either Ok(()) on success or Err(e) containing the wolfSSL + /// library error code value. + /// + /// # Example + /// + /// ```rust + /// #[cfg(all(dilithium, dilithium_make_key, dilithium_export, random))] + /// { + /// use wolfssl_wolfcrypt::random::RNG; + /// use wolfssl_wolfcrypt::dilithium::Dilithium; + /// let mut rng = RNG::new().expect("Error creating RNG"); + /// let mut key = Dilithium::generate(Dilithium::LEVEL_44, &mut rng) + /// .expect("Error with generate()"); + /// let mut priv_buf = vec![0u8; key.size().unwrap()]; + /// let mut pub_buf = vec![0u8; key.pub_size().unwrap()]; + /// key.export_key(&mut priv_buf, &mut pub_buf).expect("Error with export_key()"); + /// } + /// ``` + #[cfg(dilithium_export)] + pub fn export_key(&mut self, private: &mut [u8], public: &mut [u8]) -> Result<(), i32> { + let mut private_size = private.len() as u32; + let mut public_size = public.len() as u32; + let rc = unsafe { + sys::wc_dilithium_export_key( + &mut self.ws_key, + private.as_mut_ptr(), &mut private_size, + public.as_mut_ptr(), &mut public_size, + ) + }; + if rc != 0 { + return Err(rc); + } + Ok(()) + } + + /// Sign a message and write the signature to `sig`. + /// + /// # Parameters + /// + /// * `msg`: Message to sign. + /// * `sig`: Output buffer to hold the signature. Must be at least + /// `sig_size()` bytes. + /// * `rng`: RNG instance for hedged signing. For deterministic signing, + /// use [`Dilithium::sign_msg_with_seed()`] instead. + /// + /// # Returns + /// + /// Returns either Ok(size) containing the number of bytes written to `sig` + /// on success or Err(e) containing the wolfSSL library error code value. + /// + /// # Example + /// + /// ```rust + /// #[cfg(all(dilithium, dilithium_make_key, dilithium_sign, random))] + /// { + /// use wolfssl_wolfcrypt::random::RNG; + /// use wolfssl_wolfcrypt::dilithium::Dilithium; + /// let mut rng = RNG::new().expect("Error creating RNG"); + /// let mut key = Dilithium::generate(Dilithium::LEVEL_44, &mut rng) + /// .expect("Error with generate()"); + /// let message = b"Hello, ML-DSA!"; + /// let mut sig = vec![0u8; key.sig_size().unwrap()]; + /// let sig_len = key.sign_msg(message, &mut sig, &mut rng) + /// .expect("Error with sign_msg()"); + /// assert_eq!(sig_len, Dilithium::LEVEL2_SIG_SIZE); + /// } + /// ``` + #[cfg(all(dilithium_sign, random))] + pub fn sign_msg( + &mut self, + msg: &[u8], + sig: &mut [u8], + rng: &mut RNG, + ) -> Result { + let msg_len = msg.len() as u32; + let mut sig_len = sig.len() as u32; + let rc = unsafe { + sys::wc_dilithium_sign_msg( + msg.as_ptr(), msg_len, + sig.as_mut_ptr(), &mut sig_len, + &mut self.ws_key, + &mut rng.wc_rng, + ) + }; + if rc != 0 { + return Err(rc); + } + Ok(sig_len as usize) + } + + /// Sign a message with a context string and write the signature to `sig`. + /// + /// # Parameters + /// + /// * `ctx`: Context string (at most 255 bytes). + /// * `msg`: Message to sign. + /// * `sig`: Output buffer to hold the signature. Must be at least + /// `sig_size()` bytes. + /// * `rng`: RNG instance for hedged signing. For deterministic signing, + /// use [`Dilithium::sign_ctx_msg_with_seed()`] instead. + /// + /// # Returns + /// + /// Returns either Ok(size) containing the number of bytes written to `sig` + /// on success or Err(e) containing the wolfSSL library error code value. + /// + /// # Example + /// + /// ```rust + /// #[cfg(all(dilithium, dilithium_make_key, dilithium_sign, random))] + /// { + /// use wolfssl_wolfcrypt::random::RNG; + /// use wolfssl_wolfcrypt::dilithium::Dilithium; + /// let mut rng = RNG::new().expect("Error creating RNG"); + /// let mut key = Dilithium::generate(Dilithium::LEVEL_44, &mut rng) + /// .expect("Error with generate()"); + /// let message = b"Hello, ML-DSA!"; + /// let ctx = b"my context"; + /// let mut sig = vec![0u8; key.sig_size().unwrap()]; + /// key.sign_ctx_msg(ctx, message, &mut sig, &mut rng) + /// .expect("Error with sign_ctx_msg()"); + /// } + /// ``` + #[cfg(all(dilithium_sign, random))] + pub fn sign_ctx_msg( + &mut self, + ctx: &[u8], + msg: &[u8], + sig: &mut [u8], + rng: &mut RNG, + ) -> Result { + if ctx.len() > 255 { + return Err(sys::wolfCrypt_ErrorCodes_BUFFER_E); + } + let ctx_len = ctx.len() as u8; + let msg_len = msg.len() as u32; + let mut sig_len = sig.len() as u32; + let rc = unsafe { + sys::wc_dilithium_sign_ctx_msg( + ctx.as_ptr(), ctx_len, + msg.as_ptr(), msg_len, + sig.as_mut_ptr(), &mut sig_len, + &mut self.ws_key, + &mut rng.wc_rng, + ) + }; + if rc != 0 { + return Err(rc); + } + Ok(sig_len as usize) + } + + /// Sign a pre-hashed message with a context string. + /// + /// This is the HashML-DSA variant: the message is supplied as a hash + /// digest along with the hash algorithm identifier. + /// + /// # Parameters + /// + /// * `ctx`: Context string (at most 255 bytes). + /// * `hash_alg`: Hash algorithm identifier (e.g. `WC_HASH_TYPE_SHA256`). + /// * `hash`: Hash digest of the message to sign. + /// * `sig`: Output buffer to hold the signature. Must be at least + /// `sig_size()` bytes. + /// * `rng`: RNG instance for hedged signing. For deterministic signing, + /// use [`Dilithium::sign_ctx_hash_with_seed()`] instead. + /// + /// # Returns + /// + /// Returns either Ok(size) containing the number of bytes written to `sig` + /// on success or Err(e) containing the wolfSSL library error code value. + #[cfg(all(dilithium_sign, random))] + pub fn sign_ctx_hash( + &mut self, + ctx: &[u8], + hash_alg: i32, + hash: &[u8], + sig: &mut [u8], + rng: &mut RNG, + ) -> Result { + if ctx.len() > 255 { + return Err(sys::wolfCrypt_ErrorCodes_BUFFER_E); + } + let ctx_len = ctx.len() as u8; + let hash_len = hash.len() as u32; + let mut sig_len = sig.len() as u32; + let rc = unsafe { + sys::wc_dilithium_sign_ctx_hash( + ctx.as_ptr(), ctx_len, + hash_alg, + hash.as_ptr(), hash_len, + sig.as_mut_ptr(), &mut sig_len, + &mut self.ws_key, + &mut rng.wc_rng, + ) + }; + if rc != 0 { + return Err(rc); + } + Ok(sig_len as usize) + } + + /// Sign a message using a fixed random seed instead of an RNG. + /// + /// Produces a deterministic signature for a given `(key, msg, seed)` + /// triple. + /// + /// # Parameters + /// + /// * `msg`: Message to sign. + /// * `sig`: Output buffer to hold the signature. + /// * `seed`: Random seed bytes (`DILITHIUM_RND_SZ` = 32 bytes). + /// + /// # Returns + /// + /// Returns either Ok(size) containing the number of bytes written to `sig` + /// on success or Err(e) containing the wolfSSL library error code value. + /// + /// # Example + /// + /// ```rust + /// #[cfg(all(dilithium, dilithium_make_key_from_seed, dilithium_sign_with_seed))] + /// { + /// use wolfssl_wolfcrypt::dilithium::Dilithium; + /// let key_seed = [0x42u8; 32]; + /// let mut key = Dilithium::generate_from_seed(Dilithium::LEVEL_44, &key_seed) + /// .expect("Error with generate_from_seed()"); + /// let message = b"Hello, ML-DSA!"; + /// let sign_seed = [0x55u8; 32]; + /// let mut sig = vec![0u8; key.sig_size().unwrap()]; + /// key.sign_msg_with_seed(message, &mut sig, &sign_seed) + /// .expect("Error with sign_msg_with_seed()"); + /// } + /// ``` + #[cfg(dilithium_sign_with_seed)] + pub fn sign_msg_with_seed( + &mut self, + msg: &[u8], + sig: &mut [u8], + seed: &[u8], + ) -> Result { + #[cfg(dilithium_rnd_sz)] + if seed.len() != sys::DILITHIUM_RND_SZ as usize { + return Err(sys::wolfCrypt_ErrorCodes_BUFFER_E); + } + let msg_len = msg.len() as u32; + let mut sig_len = sig.len() as u32; + let rc = unsafe { + sys::wc_dilithium_sign_msg_with_seed( + msg.as_ptr(), msg_len, + sig.as_mut_ptr(), &mut sig_len, + &mut self.ws_key, + seed.as_ptr(), + ) + }; + if rc != 0 { + return Err(rc); + } + Ok(sig_len as usize) + } + + /// Sign a message with a context string using a fixed random seed. + /// + /// # Parameters + /// + /// * `ctx`: Context string (at most 255 bytes). + /// * `msg`: Message to sign. + /// * `sig`: Output buffer to hold the signature. + /// * `seed`: Random seed bytes (`DILITHIUM_RND_SZ` = 32 bytes). + /// + /// # Returns + /// + /// Returns either Ok(size) containing the number of bytes written to `sig` + /// on success or Err(e) containing the wolfSSL library error code value. + #[cfg(dilithium_sign_with_seed)] + pub fn sign_ctx_msg_with_seed( + &mut self, + ctx: &[u8], + msg: &[u8], + sig: &mut [u8], + seed: &[u8], + ) -> Result { + if ctx.len() > 255 { + return Err(sys::wolfCrypt_ErrorCodes_BUFFER_E); + } + #[cfg(dilithium_rnd_sz)] + if seed.len() != sys::DILITHIUM_RND_SZ as usize { + return Err(sys::wolfCrypt_ErrorCodes_BUFFER_E); + } + let ctx_len = ctx.len() as u8; + let msg_len = msg.len() as u32; + let mut sig_len = sig.len() as u32; + let rc = unsafe { + sys::wc_dilithium_sign_ctx_msg_with_seed( + ctx.as_ptr(), ctx_len, + msg.as_ptr(), msg_len, + sig.as_mut_ptr(), &mut sig_len, + &mut self.ws_key, + seed.as_ptr(), + ) + }; + if rc != 0 { + return Err(rc); + } + Ok(sig_len as usize) + } + + /// Sign a pre-hashed message with a context string using a fixed random + /// seed. + /// + /// # Parameters + /// + /// * `ctx`: Context string (at most 255 bytes). + /// * `hash_alg`: Hash algorithm identifier (e.g. `WC_HASH_TYPE_SHA256`). + /// * `hash`: Hash digest of the message to sign. + /// * `sig`: Output buffer to hold the signature. + /// * `seed`: Random seed bytes (`DILITHIUM_RND_SZ` = 32 bytes). + /// + /// # Returns + /// + /// Returns either Ok(size) containing the number of bytes written to `sig` + /// on success or Err(e) containing the wolfSSL library error code value. + #[cfg(dilithium_sign_with_seed)] + pub fn sign_ctx_hash_with_seed( + &mut self, + ctx: &[u8], + hash_alg: i32, + hash: &[u8], + sig: &mut [u8], + seed: &[u8], + ) -> Result { + if ctx.len() > 255 { + return Err(sys::wolfCrypt_ErrorCodes_BUFFER_E); + } + #[cfg(dilithium_rnd_sz)] + if seed.len() != sys::DILITHIUM_RND_SZ as usize { + return Err(sys::wolfCrypt_ErrorCodes_BUFFER_E); + } + let ctx_len = ctx.len() as u8; + let hash_len = hash.len() as u32; + let mut sig_len = sig.len() as u32; + let rc = unsafe { + sys::wc_dilithium_sign_ctx_hash_with_seed( + ctx.as_ptr(), ctx_len, + hash_alg, + hash.as_ptr(), hash_len, + sig.as_mut_ptr(), &mut sig_len, + &mut self.ws_key, + seed.as_ptr(), + ) + }; + if rc != 0 { + return Err(rc); + } + Ok(sig_len as usize) + } + + /// Verify a message signature. + /// + /// # Parameters + /// + /// * `sig`: Signature to verify. + /// * `msg`: Message the signature was created over. + /// + /// # Returns + /// + /// Returns either Ok(true) if the signature is valid, Ok(false) if it is + /// invalid, or Err(e) containing the wolfSSL library error code value. + /// + /// # Example + /// + /// ```rust + /// #[cfg(all(dilithium, dilithium_make_key, dilithium_sign, dilithium_verify, random))] + /// { + /// use wolfssl_wolfcrypt::random::RNG; + /// use wolfssl_wolfcrypt::dilithium::Dilithium; + /// let mut rng = RNG::new().expect("Error creating RNG"); + /// let mut key = Dilithium::generate(Dilithium::LEVEL_44, &mut rng) + /// .expect("Error with generate()"); + /// let message = b"Hello, ML-DSA!"; + /// let mut sig = vec![0u8; key.sig_size().unwrap()]; + /// let sig_len = key.sign_msg(message, &mut sig, &mut rng) + /// .expect("Error with sign_msg()"); + /// let valid = key.verify_msg(&sig[..sig_len], message) + /// .expect("Error with verify_msg()"); + /// assert!(valid); + /// } + /// ``` + #[cfg(dilithium_verify)] + pub fn verify_msg(&mut self, sig: &[u8], msg: &[u8]) -> Result { + let sig_len = sig.len() as u32; + let msg_len = msg.len() as u32; + let mut res = 0i32; + let rc = unsafe { + sys::wc_dilithium_verify_msg( + sig.as_ptr(), sig_len, + msg.as_ptr(), msg_len, + &mut res, + &mut self.ws_key, + ) + }; + if rc != 0 { + return Err(rc); + } + Ok(res == 1) + } + + /// Verify a message signature with a context string. + /// + /// # Parameters + /// + /// * `sig`: Signature to verify. + /// * `ctx`: Context string used when signing. + /// * `msg`: Message the signature was created over. + /// + /// # Returns + /// + /// Returns either Ok(true) if the signature is valid, Ok(false) if it is + /// invalid, or Err(e) containing the wolfSSL library error code value. + /// + /// # Example + /// + /// ```rust + /// #[cfg(all(dilithium, dilithium_make_key, dilithium_sign, dilithium_verify, random))] + /// { + /// use wolfssl_wolfcrypt::random::RNG; + /// use wolfssl_wolfcrypt::dilithium::Dilithium; + /// let mut rng = RNG::new().expect("Error creating RNG"); + /// let mut key = Dilithium::generate(Dilithium::LEVEL_44, &mut rng) + /// .expect("Error with generate()"); + /// let message = b"Hello, ML-DSA!"; + /// let ctx = b"my context"; + /// let mut sig = vec![0u8; key.sig_size().unwrap()]; + /// let sig_len = key.sign_ctx_msg(ctx, message, &mut sig, &mut rng) + /// .expect("Error with sign_ctx_msg()"); + /// let valid = key.verify_ctx_msg(&sig[..sig_len], ctx, message) + /// .expect("Error with verify_ctx_msg()"); + /// assert!(valid); + /// } + /// ``` + #[cfg(dilithium_verify)] + pub fn verify_ctx_msg(&mut self, sig: &[u8], ctx: &[u8], msg: &[u8]) -> Result { + if ctx.len() > 255 { + return Err(sys::wolfCrypt_ErrorCodes_BUFFER_E); + } + let sig_len = sig.len() as u32; + let ctx_len = ctx.len() as u32; + let msg_len = msg.len() as u32; + let mut res = 0i32; + let rc = unsafe { + sys::wc_dilithium_verify_ctx_msg( + sig.as_ptr(), sig_len, + ctx.as_ptr(), ctx_len, + msg.as_ptr(), msg_len, + &mut res, + &mut self.ws_key, + ) + }; + if rc != 0 { + return Err(rc); + } + Ok(res == 1) + } + + /// Verify a pre-hashed message signature with a context string. + /// + /// This is the HashML-DSA variant: the message is supplied as a hash + /// digest along with the hash algorithm identifier. + /// + /// # Parameters + /// + /// * `sig`: Signature to verify. + /// * `ctx`: Context string used when signing. + /// * `hash_alg`: Hash algorithm identifier (e.g. `WC_HASH_TYPE_SHA256`). + /// * `hash`: Hash digest of the message to verify. + /// + /// # Returns + /// + /// Returns either Ok(true) if the signature is valid, Ok(false) if it is + /// invalid, or Err(e) containing the wolfSSL library error code value. + #[cfg(dilithium_verify)] + pub fn verify_ctx_hash( + &mut self, + sig: &[u8], + ctx: &[u8], + hash_alg: i32, + hash: &[u8], + ) -> Result { + if ctx.len() > 255 { + return Err(sys::wolfCrypt_ErrorCodes_BUFFER_E); + } + let sig_len = sig.len() as u32; + let ctx_len = ctx.len() as u32; + let hash_len = hash.len() as u32; + let mut res = 0i32; + let rc = unsafe { + sys::wc_dilithium_verify_ctx_hash( + sig.as_ptr(), sig_len, + ctx.as_ptr(), ctx_len, + hash_alg, + hash.as_ptr(), hash_len, + &mut res, + &mut self.ws_key, + ) + }; + if rc != 0 { + return Err(rc); + } + Ok(res == 1) + } +} + +impl Drop for Dilithium { + /// Safely free the underlying wolfSSL Dilithium key context. + /// + /// This calls `wc_dilithium_free()`. The Rust Drop trait guarantees this + /// is called when the `Dilithium` struct goes out of scope. + fn drop(&mut self) { + unsafe { sys::wc_dilithium_free(&mut self.ws_key); } + } +} diff --git a/wrapper/rust/wolfssl-wolfcrypt/src/lib.rs b/wrapper/rust/wolfssl-wolfcrypt/src/lib.rs index a073e8289e..9bbc7562f3 100644 --- a/wrapper/rust/wolfssl-wolfcrypt/src/lib.rs +++ b/wrapper/rust/wolfssl-wolfcrypt/src/lib.rs @@ -29,6 +29,7 @@ pub mod chacha20_poly1305; pub mod cmac; pub mod curve25519; pub mod dh; +pub mod dilithium; pub mod ecc; pub mod ed25519; pub mod ed448; diff --git a/wrapper/rust/wolfssl-wolfcrypt/tests/common/mod.rs b/wrapper/rust/wolfssl-wolfcrypt/tests/common/mod.rs index 8d191ed97b..38fbdd907b 100644 --- a/wrapper/rust/wolfssl-wolfcrypt/tests/common/mod.rs +++ b/wrapper/rust/wolfssl-wolfcrypt/tests/common/mod.rs @@ -5,6 +5,7 @@ fn setup_fips() fips::set_private_key_read_enable(1).expect("Error with set_private_key_read_enable()"); } +#[allow(dead_code)] pub fn setup() { #[cfg(fips)] diff --git a/wrapper/rust/wolfssl-wolfcrypt/tests/test_dilithium.rs b/wrapper/rust/wolfssl-wolfcrypt/tests/test_dilithium.rs new file mode 100644 index 0000000000..d8b3b452d0 --- /dev/null +++ b/wrapper/rust/wolfssl-wolfcrypt/tests/test_dilithium.rs @@ -0,0 +1,437 @@ +/* + * Copyright (C) 2006-2026 wolfSSL Inc. + * + * This file is part of wolfSSL. + * + * wolfSSL is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * wolfSSL is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA + */ + +#![cfg(dilithium)] + +mod common; + +use wolfssl_wolfcrypt::dilithium::Dilithium; +#[cfg(all(random, any(dilithium_make_key, dilithium_sign)))] +use wolfssl_wolfcrypt::random::RNG; + +/// Verify the level constants have the correct numeric values required by +/// the wolfCrypt API. +#[test] +fn test_level_constants() { + assert_eq!(Dilithium::LEVEL_44, 2); + assert_eq!(Dilithium::LEVEL_65, 3); + assert_eq!(Dilithium::LEVEL_87, 5); +} + +/// Verify `new()` + `set_level()` + `get_level()` for all three parameter sets. +#[test] +fn test_new_and_level() { + common::setup(); + + let mut key = Dilithium::new().expect("Error with new()"); + + key.set_level(Dilithium::LEVEL_44).expect("Error with set_level()"); + assert_eq!(key.get_level().expect("Error with get_level()"), Dilithium::LEVEL_44); + + key.set_level(Dilithium::LEVEL_65).expect("Error with set_level()"); + assert_eq!(key.get_level().expect("Error with get_level()"), Dilithium::LEVEL_65); + + key.set_level(Dilithium::LEVEL_87).expect("Error with set_level()"); + assert_eq!(key.get_level().expect("Error with get_level()"), Dilithium::LEVEL_87); +} + +/// Verify that `new_ex()` accepts the optional heap and device ID parameters. +#[test] +fn test_new_ex() { + common::setup(); + let mut key = Dilithium::new_ex(None, None).expect("Error with new_ex()"); + key.set_level(Dilithium::LEVEL_44).expect("Error with set_level()"); + assert_eq!(key.get_level().expect("Error with get_level()"), Dilithium::LEVEL_44); +} + +/// Verify the runtime size queries match the compile-time constants for +/// ML-DSA-44. +#[test] +#[cfg(all(dilithium_make_key, dilithium_level2))] +fn test_sizes_level44() { + common::setup(); + let mut rng = RNG::new().expect("Error creating RNG"); + let mut key = Dilithium::generate(Dilithium::LEVEL_44, &mut rng) + .expect("Error with generate()"); + assert_eq!(key.size().expect("Error with size()"), Dilithium::LEVEL2_KEY_SIZE); + assert_eq!(key.priv_size().expect("Error with priv_size()"), Dilithium::LEVEL2_PRV_KEY_SIZE); + assert_eq!(key.pub_size().expect("Error with pub_size()"), Dilithium::LEVEL2_PUB_KEY_SIZE); + assert_eq!(key.sig_size().expect("Error with sig_size()"), Dilithium::LEVEL2_SIG_SIZE); +} + +/// Verify the runtime size queries match the compile-time constants for +/// ML-DSA-65. +#[test] +#[cfg(all(dilithium_make_key, dilithium_level3))] +fn test_sizes_level65() { + common::setup(); + let mut rng = RNG::new().expect("Error creating RNG"); + let mut key = Dilithium::generate(Dilithium::LEVEL_65, &mut rng) + .expect("Error with generate()"); + assert_eq!(key.size().expect("Error with size()"), Dilithium::LEVEL3_KEY_SIZE); + assert_eq!(key.priv_size().expect("Error with priv_size()"), Dilithium::LEVEL3_PRV_KEY_SIZE); + assert_eq!(key.pub_size().expect("Error with pub_size()"), Dilithium::LEVEL3_PUB_KEY_SIZE); + assert_eq!(key.sig_size().expect("Error with sig_size()"), Dilithium::LEVEL3_SIG_SIZE); +} + +/// Verify the runtime size queries match the compile-time constants for +/// ML-DSA-87. +#[test] +#[cfg(all(dilithium_make_key, dilithium_level5))] +fn test_sizes_level87() { + common::setup(); + let mut rng = RNG::new().expect("Error creating RNG"); + let mut key = Dilithium::generate(Dilithium::LEVEL_87, &mut rng) + .expect("Error with generate()"); + assert_eq!(key.size().expect("Error with size()"), Dilithium::LEVEL5_KEY_SIZE); + assert_eq!(key.priv_size().expect("Error with priv_size()"), Dilithium::LEVEL5_PRV_KEY_SIZE); + assert_eq!(key.pub_size().expect("Error with pub_size()"), Dilithium::LEVEL5_PUB_KEY_SIZE); + assert_eq!(key.sig_size().expect("Error with sig_size()"), Dilithium::LEVEL5_SIG_SIZE); +} + +/// Verify that `check_key()` accepts a freshly generated ML-DSA-44 key pair. +#[test] +#[cfg(all(dilithium_make_key, dilithium_check_key))] +fn test_check_key_level44() { + common::setup(); + let mut rng = RNG::new().expect("Error creating RNG"); + let mut key = Dilithium::generate(Dilithium::LEVEL_44, &mut rng) + .expect("Error with generate()"); + key.check_key().expect("Error with check_key()"); +} + +/// Verify that `check_key()` accepts a freshly generated ML-DSA-65 key pair. +#[test] +#[cfg(all(dilithium_make_key, dilithium_check_key))] +fn test_check_key_level65() { + common::setup(); + let mut rng = RNG::new().expect("Error creating RNG"); + let mut key = Dilithium::generate(Dilithium::LEVEL_65, &mut rng) + .expect("Error with generate()"); + key.check_key().expect("Error with check_key()"); +} + +/// Verify that `check_key()` accepts a freshly generated ML-DSA-87 key pair. +#[test] +#[cfg(all(dilithium_make_key, dilithium_check_key))] +fn test_check_key_level87() { + common::setup(); + let mut rng = RNG::new().expect("Error creating RNG"); + let mut key = Dilithium::generate(Dilithium::LEVEL_87, &mut rng) + .expect("Error with generate()"); + key.check_key().expect("Error with check_key()"); +} + +/// Sign and verify a message round-trip using ML-DSA-44. +/// +/// Also verifies that a tampered message or signature produces a +/// verification failure rather than an error. +#[test] +#[cfg(all(dilithium_make_key, dilithium_sign, dilithium_verify))] +fn test_sign_verify_level44() { + common::setup(); + let mut rng = RNG::new().expect("Error creating RNG"); + let mut key = Dilithium::generate(Dilithium::LEVEL_44, &mut rng) + .expect("Error with generate()"); + let message = b"Hello, ML-DSA-44!"; + let mut sig = vec![0u8; key.sig_size().expect("Error with sig_size()")]; + + let sig_len = key.sign_msg(message, &mut sig, &mut rng) + .expect("Error with sign_msg()"); + assert_eq!(sig_len, sig.len()); + + let valid = key.verify_msg(&sig, message).expect("Error with verify_msg()"); + assert!(valid, "Valid signature should verify"); + + // A different message must not verify with the original signature. + let valid = key.verify_msg(&sig, b"Tampered message") + .expect("Error with verify_msg() on tampered message"); + assert!(!valid, "Tampered message should not verify"); +} + +/// Sign and verify a message round-trip using ML-DSA-65. +#[test] +#[cfg(all(dilithium_make_key, dilithium_sign, dilithium_verify))] +fn test_sign_verify_level65() { + common::setup(); + let mut rng = RNG::new().expect("Error creating RNG"); + let mut key = Dilithium::generate(Dilithium::LEVEL_65, &mut rng) + .expect("Error with generate()"); + let message = b"Hello, ML-DSA-65!"; + let mut sig = vec![0u8; key.sig_size().expect("Error with sig_size()")]; + + let sig_len = key.sign_msg(message, &mut sig, &mut rng) + .expect("Error with sign_msg()"); + assert_eq!(sig_len, sig.len()); + + let valid = key.verify_msg(&sig, message).expect("Error with verify_msg()"); + assert!(valid, "Valid signature should verify"); +} + +/// Sign and verify a message round-trip using ML-DSA-87. +#[test] +#[cfg(all(dilithium_make_key, dilithium_sign, dilithium_verify))] +fn test_sign_verify_level87() { + common::setup(); + let mut rng = RNG::new().expect("Error creating RNG"); + let mut key = Dilithium::generate(Dilithium::LEVEL_87, &mut rng) + .expect("Error with generate()"); + let message = b"Hello, ML-DSA-87!"; + let mut sig = vec![0u8; key.sig_size().expect("Error with sig_size()")]; + + let sig_len = key.sign_msg(message, &mut sig, &mut rng) + .expect("Error with sign_msg()"); + assert_eq!(sig_len, sig.len()); + + let valid = key.verify_msg(&sig, message).expect("Error with verify_msg()"); + assert!(valid, "Valid signature should verify"); +} + +/// Sign with a context string and verify using ML-DSA-44. +/// +/// Also verifies that a mismatched context causes verification to fail. +#[test] +#[cfg(all(dilithium_make_key, dilithium_sign, dilithium_verify))] +fn test_sign_ctx_verify_level44() { + common::setup(); + let mut rng = RNG::new().expect("Error creating RNG"); + let mut key = Dilithium::generate(Dilithium::LEVEL_44, &mut rng) + .expect("Error with generate()"); + let message = b"Context-bound message"; + let ctx = b"my context"; + let mut sig = vec![0u8; key.sig_size().expect("Error with sig_size()")]; + + let sig_len = key.sign_ctx_msg(ctx, message, &mut sig, &mut rng) + .expect("Error with sign_ctx_msg()"); + + let valid = key.verify_ctx_msg(&sig[..sig_len], ctx, message) + .expect("Error with verify_ctx_msg()"); + assert!(valid, "Valid context signature should verify"); + + // Wrong context must not verify. + let valid = key.verify_ctx_msg(&sig[..sig_len], b"wrong context", message) + .expect("Error with verify_ctx_msg() with wrong context"); + assert!(!valid, "Wrong context should not verify"); +} + +/// Export both keys, re-import them separately, and verify that: +/// - a signature from the original key is accepted by a public-key-only +/// import, and +/// - the re-imported private key can sign messages that verify with the +/// original public key. +#[test] +#[cfg(all(dilithium_make_key, dilithium_import, dilithium_export, dilithium_sign, dilithium_verify))] +fn test_import_export_level44() { + common::setup(); + let mut rng = RNG::new().expect("Error creating RNG"); + let mut key = Dilithium::generate(Dilithium::LEVEL_44, &mut rng) + .expect("Error with generate()"); + + let priv_size = key.size().expect("Error with size()"); + let pub_size = key.pub_size().expect("Error with pub_size()"); + let sig_size = key.sig_size().expect("Error with sig_size()"); + + let mut priv_buf = vec![0u8; priv_size]; + let mut pub_buf = vec![0u8; pub_size]; + key.export_key(&mut priv_buf, &mut pub_buf).expect("Error with export_key()"); + + // Verify export_public and export_private return the same bytes. + let mut pub_buf2 = vec![0u8; pub_size]; + let pub_written = key.export_public(&mut pub_buf2).expect("Error with export_public()"); + assert_eq!(pub_written, pub_size); + assert_eq!(pub_buf2, pub_buf); + + let mut priv_buf2 = vec![0u8; priv_size]; + let priv_written = key.export_private(&mut priv_buf2).expect("Error with export_private()"); + assert_eq!(priv_written, priv_size); + assert_eq!(priv_buf2, priv_buf); + + // Sign with the original key. + let message = b"Import/export test message"; + let mut sig = vec![0u8; sig_size]; + let sig_len = key.sign_msg(message, &mut sig, &mut rng) + .expect("Error with sign_msg()"); + + // Re-import public key only and verify. + let mut pub_key = Dilithium::new().expect("Error with new()"); + pub_key.set_level(Dilithium::LEVEL_44).expect("Error with set_level()"); + pub_key.import_public(&pub_buf).expect("Error with import_public()"); + let valid = pub_key.verify_msg(&sig[..sig_len], message) + .expect("Error with verify_msg() via imported public key"); + assert!(valid, "Imported public key should accept original signature"); + + // Re-import private key, sign a message, and verify with the original key. + let mut priv_key = Dilithium::new().expect("Error with new()"); + priv_key.set_level(Dilithium::LEVEL_44).expect("Error with set_level()"); + priv_key.import_private(&priv_buf).expect("Error with import_private()"); + let mut sig2 = vec![0u8; sig_size]; + let sig2_len = priv_key.sign_msg(message, &mut sig2, &mut rng) + .expect("Error with sign_msg() from imported private key"); + let valid = key.verify_msg(&sig2[..sig2_len], message) + .expect("Error with verify_msg() after import_private"); + assert!(valid, "Signature from re-imported private key should verify"); +} + +/// Export both keys, import them together via `import_key()`, then sign and +/// verify using the re-imported key pair. +#[test] +#[cfg(all(dilithium_make_key, dilithium_import, dilithium_export, dilithium_sign, dilithium_verify))] +fn test_import_key_level44() { + common::setup(); + let mut rng = RNG::new().expect("Error creating RNG"); + let mut key = Dilithium::generate(Dilithium::LEVEL_44, &mut rng) + .expect("Error with generate()"); + + let priv_size = key.size().expect("Error with size()"); + let pub_size = key.pub_size().expect("Error with pub_size()"); + let sig_size = key.sig_size().expect("Error with sig_size()"); + + let mut priv_buf = vec![0u8; priv_size]; + let mut pub_buf = vec![0u8; pub_size]; + key.export_key(&mut priv_buf, &mut pub_buf).expect("Error with export_key()"); + + let mut key2 = Dilithium::new().expect("Error with new()"); + key2.set_level(Dilithium::LEVEL_44).expect("Error with set_level()"); + key2.import_key(&priv_buf, &pub_buf).expect("Error with import_key()"); + + let message = b"import_key round-trip"; + let mut sig = vec![0u8; sig_size]; + let sig_len = key2.sign_msg(message, &mut sig, &mut rng) + .expect("Error with sign_msg() from imported key pair"); + let valid = key.verify_msg(&sig[..sig_len], message) + .expect("Error with verify_msg()"); + assert!(valid, "Imported key pair should produce valid signatures"); +} + +/// Verify that `generate_from_seed()` is deterministic: the same seed +/// produces the same key pair on repeated calls. +#[test] +#[cfg(all(dilithium_make_key_from_seed, dilithium_export))] +fn test_generate_from_seed_determinism() { + common::setup(); + // DILITHIUM_SEED_SZ = 32 bytes + let seed = [0x42u8; 32]; + + let mut key1 = Dilithium::generate_from_seed(Dilithium::LEVEL_44, &seed) + .expect("Error with generate_from_seed() first call"); + let mut key2 = Dilithium::generate_from_seed(Dilithium::LEVEL_44, &seed) + .expect("Error with generate_from_seed() second call"); + + let pub_size = key1.pub_size().expect("Error with pub_size()"); + let mut pub1 = vec![0u8; pub_size]; + let mut pub2 = vec![0u8; pub_size]; + key1.export_public(&mut pub1).expect("Error with export_public() key1"); + key2.export_public(&mut pub2).expect("Error with export_public() key2"); + assert_eq!(pub1, pub2, "Same seed must yield same public key"); + + let priv_size = key1.size().expect("Error with size()"); + let mut priv1 = vec![0u8; priv_size]; + let mut priv2 = vec![0u8; priv_size]; + key1.export_private(&mut priv1).expect("Error with export_private() key1"); + key2.export_private(&mut priv2).expect("Error with export_private() key2"); + assert_eq!(priv1, priv2, "Same seed must yield same private key"); +} + +/// Verify that `sign_msg_with_seed()` is deterministic: the same key, +/// message, and signing seed always produce the same signature bytes, and +/// the signature verifies correctly. +#[test] +#[cfg(all(dilithium_make_key_from_seed, dilithium_sign_with_seed, dilithium_verify))] +fn test_sign_with_seed_determinism() { + common::setup(); + // DILITHIUM_SEED_SZ = 32 bytes + let key_seed = [0x42u8; 32]; + // DILITHIUM_RND_SZ = 32 bytes + let sign_seed = [0x55u8; 32]; + let message = b"Deterministic ML-DSA signing test"; + + let mut key = Dilithium::generate_from_seed(Dilithium::LEVEL_44, &key_seed) + .expect("Error with generate_from_seed()"); + + let sig_size = key.sig_size().expect("Error with sig_size()"); + let mut sig1 = vec![0u8; sig_size]; + let mut sig2 = vec![0u8; sig_size]; + + let len1 = key.sign_msg_with_seed(message, &mut sig1, &sign_seed) + .expect("Error with sign_msg_with_seed() first call"); + let len2 = key.sign_msg_with_seed(message, &mut sig2, &sign_seed) + .expect("Error with sign_msg_with_seed() second call"); + + assert_eq!(len1, len2, "Signature lengths must match"); + assert_eq!(sig1[..len1], sig2[..len2], "Same inputs must yield same signature"); + + let valid = key.verify_msg(&sig1[..len1], message) + .expect("Error with verify_msg()"); + assert!(valid, "Deterministically signed message should verify"); +} + +/// Verify that `sign_ctx_msg_with_seed()` is deterministic and that the +/// produced signature verifies with `verify_ctx_msg()`. +#[test] +#[cfg(all(dilithium_make_key_from_seed, dilithium_sign_with_seed, dilithium_verify))] +fn test_sign_ctx_with_seed_determinism() { + common::setup(); + let key_seed = [0x11u8; 32]; + let sign_seed = [0x22u8; 32]; + let message = b"Context deterministic signing test"; + let ctx = b"test-context"; + + let mut key = Dilithium::generate_from_seed(Dilithium::LEVEL_44, &key_seed) + .expect("Error with generate_from_seed()"); + + let sig_size = key.sig_size().expect("Error with sig_size()"); + let mut sig1 = vec![0u8; sig_size]; + let mut sig2 = vec![0u8; sig_size]; + + let len1 = key.sign_ctx_msg_with_seed(ctx, message, &mut sig1, &sign_seed) + .expect("Error with sign_ctx_msg_with_seed() first call"); + let len2 = key.sign_ctx_msg_with_seed(ctx, message, &mut sig2, &sign_seed) + .expect("Error with sign_ctx_msg_with_seed() second call"); + + assert_eq!(len1, len2); + assert_eq!(sig1[..len1], sig2[..len2], "Same inputs must yield same signature"); + + let valid = key.verify_ctx_msg(&sig1[..len1], ctx, message) + .expect("Error with verify_ctx_msg()"); + assert!(valid, "Context-signed message should verify"); +} + +/// Verify that `generate_from_seed()` + `sign_msg_with_seed()` + +/// `verify_msg()` work across all three security levels. +#[test] +#[cfg(all(dilithium_make_key_from_seed, dilithium_sign_with_seed, dilithium_verify))] +fn test_seed_sign_verify_all_levels() { + common::setup(); + let key_seed = [0xABu8; 32]; + let sign_seed = [0xCDu8; 32]; + let message = b"All-levels seed sign/verify test"; + + for level in [Dilithium::LEVEL_44, Dilithium::LEVEL_65, Dilithium::LEVEL_87] { + let mut key = Dilithium::generate_from_seed(level, &key_seed) + .expect("Error with generate_from_seed()"); + let sig_size = key.sig_size().expect("Error with sig_size()"); + let mut sig = vec![0u8; sig_size]; + let sig_len = key.sign_msg_with_seed(message, &mut sig, &sign_seed) + .expect("Error with sign_msg_with_seed()"); + let valid = key.verify_msg(&sig[..sig_len], message) + .expect("Error with verify_msg()"); + assert!(valid, "Level {} seed-signed message should verify", level); + } +}