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);
+ }
+}