diff -pruN 1.4.0-1/.gitignore 2.5.0-0ubuntu1/.gitignore
--- 1.4.0-1/.gitignore	2017-09-08 17:54:06.000000000 +0000
+++ 2.5.0-0ubuntu1/.gitignore	2025-04-16 08:09:24.000000000 +0000
@@ -7,8 +7,11 @@ autom4te.cache
 *.la
 *.o
 *.tar.gz
+ibmca-engine-opensslconfig
+ibmca_mechaList_test
 Makefile
 Makefile.in
+Makefile.linux
 config.status
 config.log
 configure
diff -pruN 1.4.0-1/.travis.yml 2.5.0-0ubuntu1/.travis.yml
--- 1.4.0-1/.travis.yml	1970-01-01 00:00:00.000000000 +0000
+++ 2.5.0-0ubuntu1/.travis.yml	2025-04-16 08:09:24.000000000 +0000
@@ -0,0 +1,44 @@
+os: linux
+dist: focal
+
+language: c
+
+before_install:
+    - sudo apt-get -qq update
+    - sudo apt-get install -y libica3 libica-dev
+
+jobs:
+    include:
+       - name: "linux-s390x-gcc"
+         os: linux
+         arch: s390x
+         compiler: gcc
+         env: CONFIG_OPTS="--enable-engine --enable-provider"
+ 
+before_script:
+    - git clone https://github.com/openssl/openssl.git
+    - pushd openssl
+    - git checkout master
+    - ./config -w
+    - make -j 5 -s
+    - export OPENSSL_DIR=$PWD
+    - export PATH=$OPENSSL_DIR/apps/:$PATH
+    - export LD_LIBRARY_PATH=$OPENSSL_DIR/:$LD_LIBRARY_PATH
+    - popd
+    - openssl version
+    - git clone https://github.com/opencryptoki/libica.git
+    - pushd libica
+    - ./bootstrap.sh
+    - ./configure CFLAGS="-I$OPENSSL_DIR/include -L$OPENSSL_DIR"
+    - make -j 5 V=0
+    - export LIBICA_DIR=$PWD
+    - export LD_LIBRARY_PATH=$LIBICA_DIR/src/.libs:$LD_LIBRARY_PATH
+    - popd
+    - $LIBICA_DIR/src/icainfo
+
+script:
+    - ./bootstrap.sh
+    - ./configure CFLAGS="-I$OPENSSL_DIR/include -L$OPENSSL_DIR -I$LIBICA_DIR/include -L$LIBICA_DIR/src/.libs" $CONFIG_OPTS
+    - make -j 5 V=0
+    - make check V=0
+
diff -pruN 1.4.0-1/ChangeLog 2.5.0-0ubuntu1/ChangeLog
--- 1.4.0-1/ChangeLog	2017-09-08 17:54:06.000000000 +0000
+++ 2.5.0-0ubuntu1/ChangeLog	2025-04-16 08:09:24.000000000 +0000
@@ -1,3 +1,89 @@
+* openssl-ibmca 2.5.0
+- Provider: Add support for OSSL_PKEY_PARAM_RSA_DERIVE_FROM_PQ on import
+- Provider: Add support for SignMessage and VerifyMessage API for ECDSA and RSA
+- Provider: Allow the DHKEM-IKM option for EC keygen, but use fallback provider
+- Provider: Allow ECDSA deterministic signatures, but use fallback
+- Engine: Enable external AES-GCM IV when libica is in FIPS mode
+- Bug fixes
+
+* openssl-ibmca 2.4.1
+- Provider: Change the default log directory to /tmp
+- Bug fixes
+
+* openssl-ibmca 2.4.0
+- Provider: Adjustments for OpenSSL versions 3.1 and 3.2 
+- Provider: Support RSA blinding
+- Provider: Constant-time fixes for RSA PKCS#1 v1.5 and OAEP padding
+- Provider: Support "implicit rejection" option for RSA PKCS#1 v1.5 padding
+- Provider: Adjustments in OpenSSL config generator and example configs
+- Engine: EC: Cache ICA key in EC_KEY object (performance improvement)
+- Engine: Enable RSA blinding
+
+* openssl-ibmca 2.3.1
+- Adjustments for libica 4.1.0
+
+* openssl-ibmca 2.3.0
+- First version including the provider
+- Fix for engine build without OpenSSL 3.0 sources
+
+* openssl-ibmca 2.2.3
+- Fix PKEY segfault with OpenSSL 3.0
+
+* openssl-ibmca 2.2.2
+- Fix tests with OpenSSL 3.0
+- Build against libica 4.0
+
+* openssl-ibmca 2.2.1
+- Bug fixes
+
+* openssl-ibmca 2.2.0
+- Implement fallbacks based on OpenSSL
+- Disable software fallbacks from libica
+- Allow to specify default library (libica vs. libica-cex) to use
+- Provide "libica" engine ctrl to switch library at load time
+- Update README.md
+- Remove libica link dependency
+- Generate sample configuration files from system configuration
+- Restructure registration global data
+
+* openssl-ibmca 2.1.3
+- Bug fix
+
+* openssl-ibmca 2.1.2
+- Bug fixes
+
+* openssl-ibmca 2.1.1
+- Bug fixes
+
+* openssl-ibmca 2.1.0
+- Add MSA9 CPACF support for X25519, X448, Ed25519 and Ed448
+
+* openssl-ibmca 2.0.3
+- Add MSA9 CPACF support for ECDSA sign/verify
+
+* openssl-ibmca 2.0.2
+- Fix doing rsa-me, altough rsa-crt would be possible.
+
+* openssl-ibmca 2.0.1
+- Dont fail when a libica symbol cannot be resolved.
+
+* openssl-ibmca 2.0.0
+- Add ECC support.
+- Add check and distcheck make-targets.
+- Project cleanup, code was broken into multiple files and coding style cleanup.
+- Improvements to compat macros for openssl.
+- Don't disable libica sw fallbacks.
+- Fix dlclose logic.
+
+* openssl-ibmca 1.4.1
+- Fix structure size for aes-256-ecb/cbc/cfb/ofb
+- Update man page
+- Switch to ibmca.so filename to allow standalone use
+- Switch off Libica fallback mode if available
+- Make sure ibmca_init only runs once
+- Provide simple macro for DEBUG_PRINTF possibility
+- Cleanup and slight rework of function set_supported_meths
+
 * openssl-ibmca 1.4.0
 - Re-license to Apache License v2.0
 - Fix aes_gcm initialization.
diff -pruN 1.4.0-1/Makefile.am 2.5.0-0ubuntu1/Makefile.am
--- 1.4.0-1/Makefile.am	2017-09-08 17:54:06.000000000 +0000
+++ 2.5.0-0ubuntu1/Makefile.am	2025-04-16 08:09:24.000000000 +0000
@@ -1,4 +1,4 @@
 ACLOCAL_AMFLAGS = -I m4
-SUBDIRS = src
+SUBDIRS = src test
 
-EXTRA_DIST =  openssl-ibmca.spec bootstrap.sh cleanup.sh
+EXTRA_DIST = openssl-ibmca.spec openssl-ibmca-provider.spec bootstrap.sh cleanup.sh
diff -pruN 1.4.0-1/README.md 2.5.0-0ubuntu1/README.md
--- 1.4.0-1/README.md	2017-09-08 17:54:06.000000000 +0000
+++ 2.5.0-0ubuntu1/README.md	2025-04-16 08:09:24.000000000 +0000
@@ -1,75 +1,97 @@
 # OpenSSL-ibmca
 
-OpenSSL engine that uses the libica library under s390x to accelerate
-cryptographic operations.
+OpenSSL engine and provider that uses the libica library under s390x to
+accelerate cryptographic operations.
 
 
 ## Requirements
 
 The build requirements are:
  * openssl-devel >= 0.9.8
- * libica-devel >= 3.1.1
+ * openssl-devel >= 3.0.0 for building the IBMCA provider
+ * libica-devel >= 3.3.0
+ * libica-devel >= 3.6.0 or >= 4.0.1 for building the IBMCA provider
  * autoconf
  * automake
  * libtool
+ * openssl
+ * perl
 
 The runtime requirements are:
  * openssl >= 0.9.8
- * libica >= 3.1.1
+ * openssl-devel >= 3.0.0 for using the IBMCA provider
+ * libica >= 3.3.0
+ * libica >= 3.6.0 or >= 4.0.1 for using the IBMCA provider
 
 
 ## Installing
 
 ```
-$ ./configure [--enable-debug]
+$ ./configure [--enable-debug] [--disable-engine] [--disable-provider]
 $ make
 $ sudo make install
 ```
 
 This will configure, build and install the package in a default location,
-which is `/usr/local/lib`. It means that the libibmca.so will be installed in
-`/usr/local/lib/libibmca.so` by default. If you want to install it anywhere
-else, run "configure" passing the new location via prefix argument, for
-example:
+which is `/usr/local/lib`. It means that the engine ibmca.so and the provider
+ibmca-provider.so will be installed in `/usr/local/lib/` by default.
+If you want to install it anywhere else, run "configure" passing the new
+location via prefix argument, for example:
 
 ```
-$ ./configure --prefix=/usr --libdir=/usr/lib64/openssl/engines
+$ ./configure --prefix=/usr --disable-provider --libdir=/usr/lib64/openssl/engines
 ```
 
-## Enabling IBMCA
+or
 
-Included in this package there is a sample `openssl.cnf` file
-(`openssl.cnf.sample`), which can be used to turn on use of the IBMCA engine in
-apps where OpenSSL config support is compiled in.
+```
+$ ./configure --prefix=/usr --disable-engine --libdir=/usr/lib64/ossl-modules
+```
 
-In order to enable IBMCA, use the following instructions to apply the
-configurations from `openssl.cnf.sample` to the `openssl.cnf` file installed
-in the host by the OpenSSL package. **WARNING:** you may want to save the
-original `openssl.cnf` file before changing it.
+Additionally, at configure time, you can specify to build the engine against the
+libica-cex version via the `--with-libica-cex` feature switch.  If
+this switch is not specified, the engine will use the full version of
+libica by default.
 
-In `openssl.cnf.sample`, the *dynamic_path* variable is set to the default
-location, which is `/usr/local/lib/libibmca.so` by default. However, if the
-libibmca.so library has been installed anywhere else, then update the
-*dynamic_path* variable.
+To specify the version of libica for the engine, use
+`--with-libica-version=<version>`.  The default version is version 4
+of libica.  To build the engine against version 3 of libica, specify
+`--with-libica-version=3` at configure time.
 
-Locate where the `openssl.cnf` file has been installed in the host and append
-the content of the `openssl.cnf.sample` file to it.
+The provider uses the libica-cex version of libica by default. To build
+the provider against the full version of libica specify the
+`--with-provider-libica-full` feature switch. There is no functional
+difference when the provider is built against the full version of libica.
+The provider requires libica version 4, it can not be built with an older
+libica version.
 
-```
-$ rpm -ql openssl | grep openssl.cnf
-$ cat openssl.cnf.sample >> /path/to/openssl.cnf
-```
+The provider requires OpenSSL 3.0 or later to be built. If OpenSSL 3.0 is
+not availale, then the provider is automatically disabled.
+When OpenSSL 3.0 is available, by default both, the engine as well as the
+provider are built. You can disable the engine or the provider with the
+`----disable-engine` or `----disable-provider` switch.
 
-In `openssl.cnf` file, move the *openssl_conf* variable from the bottom to the
-top of the file, such as in the example below:
+There are 2 RPM spec files contained in this package:
+`openssl-ibmca.spec` and `openssl-ibmca-provider.spec`. The first one builds
+only the engine and installs it into OpenSSL's engine directory. The second
+one builds only the provider and install it into OpenSSL's modules directory.
 
-```
-HOME = .
-RANDFILE = $ENV::HOME/.rnd
-openssl_conf = openssl_def
-```
+We leave it to the distributions to produce an RPM that contains both, engine
+and provider, if wanted. You can only specify one installation directory with
+the `--libdir` configure option, but providers and engines need to be installed
+into different locations. To achieve this, the engine and provider shared objects
+must be moved by subsequent commands to the correct location after
+`make install` has been performed.
+
+
+## Enabling IBMCA
+
+Apps with compiled-in OpenSSL config support can enable the engine or provider
+via an OpenSSL configuration file. Refer to config(5). Sample OpenSSL
+configuration files (`openssl.cnf.sample` and `openssl.cnf.provider.sample`)
+are included in this package.
 
-Finally, check if the IBMCA is now enabled. The command below should return the
+If the engine is configured properly, the command below should return the
 IBMCA engine and all the supported cryptographic methods.
 
 ```
@@ -83,6 +105,81 @@ $ openssl engine -c
 $
 ```
 
+If the provider is configured properly, the command below should return the
+IBMCA provider.
+
+```
+$ openssl list -providers
+Providers:
+  default
+    name: OpenSSL Default Provider
+    version: 3.1.0
+    status: active
+  ibmca
+    name: ibmca
+    version: 1.1.0
+    status: active
+$
+```
+
+You can list the cryptographic methods implemented by the activated providers
+as follows:
+
+```
+$ openssl list -key-managers
+...
+  Name: IBMCA RSA implementation
+    Type: Provider Algorithm
+    IDs: { 1.2.840.113549.1.1.1, 2.5.8.1.1, RSA, rsaEncryption } @ ibmca
+  Name: IBMCA DH implementation
+    Type: Provider Algorithm
+    IDs: { 1.2.840.113549.1.3.1, DH, dhKeyAgreement } @ ibmca
+  Name: IBMCA EC implementation
+    Type: Provider Algorithm
+    IDs: { 1.2.840.10045.2.1, EC, id-ecPublicKey } @ ibmca
+  Name: IBMCA RSA-PSS implementation
+    Type: Provider Algorithm
+    IDs: { 1.2.840.113549.1.1.10, RSA-PSS, RSASSA-PSS, rsassaPss } @ ibmca
+  Name: IBMCA DHX implementation
+    Type: Provider Algorithm
+    IDs: { 1.2.840.10046.2.1, dhpublicnumber, DHX, X9.42 DH } @ ibmca
+...
+$ openssl list -signature-algorithms
+...
+  { 1.2.840.113549.1.1.1, 2.5.8.1.1, RSA, rsaEncryption } @ ibmca
+  ECDSA @ ibmca
+...
+$ openssl list -asymcipher-algorithms
+...
+  { 1.2.840.113549.1.1.1, 2.5.8.1.1, RSA, rsaEncryption } @ ibmca
+...
+$ openssl list -key-exchange-algorithms
+...
+  { 1.2.840.113549.1.3.1, DH, dhKeyAgreement } @ ibmca
+  ECDH @ ibmca
+....
+$
+```
+
+## Configuring OpenSSL-ibmca
+
+Since libica 3.8.0, libica provides two libraries.  The basic
+libica.so.3 contains all the features listed above and is the default
+library unless the `configure` switch `--with-libica-cex` is provided.
+In that case, libica-cex.so.4 becomes the default library.  If both
+versions of the library are installed on a system, OpenSSL-ibmca can
+be configured to use either of these two.  To use `libica.so.4`, with
+OpenSSL-ibmca, simply add the directive `libica = libica.so.4` to your
+OpenSSL configuration file in the engine section before `init = 1`.
+Similarly, to use `libica-cex.so.4`, add the line
+`libica = libica-cex.so.4`.
+
+The build process of OpenSSL-ibmca will produce the scripts
+`ibmca-engine-opensslconfig` and `ibmca-provider-opensslconfig` which can be
+used to update an existing OpenSSL configuration to enable the OpenSSL-ibmca
+engine or provider.  By default, these scripts are not installed.  We leave it
+to the distributions to find the correct place for these scripts (or not use
+them at all).
 
 ## Support
 
@@ -94,15 +191,18 @@ To report a bug please submit a
 * distro release
 * openssl-ibmca package version
 * libica package version
+* OpenSSL package version
 * steps to reproduce the bug
 
-Regarding technical or usage questions, send email to
- [opencryptoki-tech](
-    https://sourceforge.net/p/opencryptoki/mailman/opencryptoki-tech) or
- [opencryptoki-users](
-    https://sourceforge.net/p/opencryptoki/mailman/opencryptoki-users)
- mailing list respectively.
+Regarding technical or usage questions, also submit a
+ [ticket](https://github.com/opencryptoki/openssl-ibmca/issues).
+
+## Limitations
 
+The ibmca engine's cipher and digest implementations do not
+support the processing of messages in arbitrary chunk sizes.
+All chunks, except the final one, are required to be a multiple
+of the primitive's block size.
 
 ## Contributing
 
diff -pruN 1.4.0-1/bootstrap.sh 2.5.0-0ubuntu1/bootstrap.sh
--- 1.4.0-1/bootstrap.sh	2017-09-08 17:54:06.000000000 +0000
+++ 2.5.0-0ubuntu1/bootstrap.sh	2025-04-16 08:09:24.000000000 +0000
@@ -15,4 +15,4 @@
 # limitations under the License.
 #
 
-autoreconf -iv
+autoreconf --force --install --verbose --warnings=all
diff -pruN 1.4.0-1/configure.ac 2.5.0-0ubuntu1/configure.ac
--- 1.4.0-1/configure.ac	2017-09-08 17:54:06.000000000 +0000
+++ 2.5.0-0ubuntu1/configure.ac	2025-04-16 08:09:24.000000000 +0000
@@ -2,12 +2,17 @@
 # Process this file with autoconf to produce a configure script.
 # See autoconf and autoscan online documentation for details.
 
-AC_INIT([openssl-ibmca], [1.4.0], [opencryptoki-users@lists.sf.net])
-AC_CONFIG_SRCDIR([src/e_ibmca.c]) # sanity check
+AC_INIT([openssl-ibmca], [2.5.0], [https://github.com/opencryptoki/openssl-ibmca/issues],[],[https://github.com/opencryptoki/openssl-ibmca])
+AC_CONFIG_SRCDIR([src/engine/e_ibmca.c]) # sanity check
 AC_CONFIG_MACRO_DIR([m4])
 AC_CONFIG_AUX_DIR([build-aux])
 AM_INIT_AUTOMAKE([foreign])
 
+AC_PATH_PROG([CHMOD], [chmod], [/bin/chmod])
+
+logdir=/tmp
+AC_SUBST(logdir)
+
 # Cmdline arguments.
 AC_ARG_ENABLE([debug],
 		[AS_HELP_STRING([--enable-debug], [turn on debugging flags (default is off)])],
@@ -20,19 +25,56 @@ else
 	CFLAGS="$CFLAGS -O2 -Wall"
 fi
 
+AC_ARG_ENABLE([sanitizer],
+		AS_HELP_STRING([--enable-sanitizer],[enable sanitizer build (may not work in all environments) @<:@default=no@:>@]),
+		[],
+		[enable_sanitizer=no])
+
+AC_ARG_ENABLE([engine],
+		[AS_HELP_STRING([--enable-engine], [build IBMCA engine (OpenSSL 1.1.1, default is yes)])],
+		[if test "x$enableval" = "xyes" ; then
+			enable_engine="yes"
+		 else
+		 	enable_engine="no"
+		 fi],
+		[enable_engine="yes"])
+
+AC_ARG_ENABLE([provider],
+		[AS_HELP_STRING([--enable-provider], [build IBMCA provider (OpenSSL >= 3.0, default is yes if built against OpenSSL 3.0 or later, else the default is false)])],
+		[if test "x$enableval" = "xyes" ; then
+			enable_provider="yes"
+		 else
+		 	enable_provider="no"
+		 fi],
+		[enable_provider="check"])
+
 # Checks for programs.
 AC_DISABLE_STATIC
 AC_PROG_CC
-AC_PROG_LIBTOOL
+LT_INIT
+
+dnl --- check for perl
+AC_PATH_PROG(PERL, perl)
+if test -z "$PERL" ; then
+	AC_MSG_ERROR([Please install perl])
+fi
+
+AC_MSG_CHECKING([if perl module 'FindBin' is installed])
+(echo "use FindBin;" ; echo "exit(0);") | $PERL > /dev/null 2>&1
+if test $? != 0 ; then
+	AC_MSG_RESULT(no)
+	AC_MSG_ERROR([Please install perl-FindBin])
+fi
+AC_MSG_RESULT(yes)
 
 # Checks for libraries.
-AC_CHECK_LIB([crypto], [RAND_add], [], AC_MSG_ERROR([*** openssl >= 0.9.8 is required ***]))
-AC_CHECK_LIB([ica], [ica_get_functionlist], [], AC_MSG_ERROR([*** libica >= 2.4.0 is required ***]))
+AC_CHECK_LIB([crypto], [RAND_add], [], AC_MSG_ERROR([*** openssl >= 1.1.1 is required ***]))
+AC_CHECK_LIB([crypto], [OSSL_LIB_CTX_new], [openssl_3_0="yes"], [openssl_3_0="no"])
 
 # Checks for header files.
 AC_CHECK_HEADERS([arpa/inet.h fcntl.h malloc.h netdb.h netinet/in.h stddef.h stdlib.h \
                  string.h strings.h sys/ioctl.h sys/param.h sys/socket.h sys/time.h unistd.h])
-AC_CHECK_HEADER([ica_api.h], [], AC_MSG_ERROR([*** libica-devel >= 2.4.0 is required ***]))
+AC_CHECK_HEADER([ica_api.h], [], AC_MSG_ERROR([*** libica-devel >= 3.6.0 is required ***]))
 
 
 # Checks for typedefs, structures, and compiler characteristics.
@@ -43,15 +85,111 @@ AC_TYPE_SSIZE_T
 
 # Checks for library functions.
 AC_CHECK_FUNCS([gethostbyaddr gethostbyname memset strcasecmp strncasecmp strstr malloc])
-AC_CHECK_DECLS([ICA_FLAG_DHW,ica_get_functionlist,ica_open_adapter,DES_ECB], [],
-		AC_MSG_ERROR([*** libica >= 2.4.0 and libica-devel >= 2.4.0 are required ***]),
+AC_CHECK_DECLS([ICA_FLAG_DHW,DES_ECB], [],
+		AC_MSG_ERROR([*** libica-devel >= 3.6.0 are required ***]),
+		[#include <ica_api.h>])
+AC_CHECK_DECLS([OSSL_ASYM_CIPHER_PARAM_IMPLICIT_REJECTION],
+		[openssl_implicit_rejection="yes"], [openssl_implicit_rejection="no"],
+		[#include <openssl/core_names.h>])
+AM_CONDITIONAL([OPENSSL_IMPLICIT_REJECTION], [test "x$openssl_implicit_rejection" = xyes])
+
+AC_ARG_WITH([libica-cex],
+	[AS_HELP_STRING([--with-libica-cex],[Use libica-cex as default library for the IBMCA engine])],
+	[usecexonly=${withval}],
+	[])
+
+AC_ARG_WITH([libica-version],
+	[AS_HELP_STRING([--with-libica-version],[Use specified libica major version (defaults to 4)])],
+	[libicaversion=${withval}],
+	[libicaversion=4])
+
+if test "x$usecexonly" = xyes; then
+	defaultlib="libica-cex.so.$libicaversion"
+	ica="ica-cex"
+else
+	defaultlib="libica.so.$libicaversion"
+	ica="ica"
+fi
+# In cex-only mode, testing the ciphers does not make any sense since
+# they will fall back to OpenSSL without the engine.  So remove these
+# tests from that build.
+AM_CONDITIONAL([FULL_LIBICA], [test "x$usecexonly" != xyes])
+
+AC_ARG_WITH([provider-libica-full],
+	[AS_HELP_STRING([--with-provider-libica-full],[Use the full libica as library for the IBMCA provider])],
+	[useproviderfulllibica=${withval}],
+	[])
+AM_CONDITIONAL([PROVIDER_FULL_LIBICA], [test "x$useproviderfulllibica" = xyes])
+
+AC_SUBST(libicaversion, "$libicaversion")
+
+# If compiled against OpenSSL 3.0 or later, build the provider unless
+# explicitely disabled. 
+# If build against OpenSSL 1.1.1, we can not build the provider.
+if test "x$openssl_3_0" = xyes; then
+	if test "x$enable_provider" != xno; then
+		enable_provider=yes
+	fi
+else
+	if test "x$enable_provider" = xyes; then
+		AC_MSG_ERROR([*** openssl >= 3.0 is required to build the IBMCA provider ***]);
+	fi
+	enable_provider=no
+fi
+
+AM_CONDITIONAL([IBMCA_ENGINE], [test "x$enable_engine" == xyes])
+AM_CONDITIONAL([IBMCA_PROVIDER], [test "x$enable_provider" == xyes])
+
+
+AC_DEFINE_UNQUOTED([LIBICA_SHARED_LIB],["$defaultlib"])
+AC_SUBST([ICA],["$ica"])
+
+AC_CHECK_PROG([openssl_var],[openssl],[yes],[no])
+if test "x$openssl_var" != xyes; then
+        AC_MSG_ERROR([openssl binary required]);
+fi
+
+if test "x$enable_provider" = xyes; then
+	AC_CHECK_DECLS([ica_fips_status], [],
+		AC_MSG_ERROR([*** libica >= 4.0 is required to build the IBMCA provider ***]),
 		[#include <ica_api.h>])
+fi
+
+AC_CHECK_DECLS([ica_cleanup],,,[#include <ica_api.h>])
+
+if test "x$enable_sanitizer" = "xyes"; then
+	AC_CHECK_LIB([asan], [strcpy], [LDFLAGS="-lasan $LDFLAGS"],
+			[AC_MSG_ERROR(['libasan' library is missing on your system. Please install 'libasan'.])])
+	AC_CHECK_LIB([ubsan], [strcpy], [LDFLAGS="-lubsan $LDFLAGS"],
+			[AC_MSG_ERROR(['libubsan' library is missing on your system. Please install 'libubsan'.])])
+	if test "x$enable_debug" = "xyes"; then
+		CFLAGS="$CFLAGS -O2 -g3 -DDEBUG"
+	fi
+	CFLAGS="$CFLAGS -fstack-protector-all -fsanitize=address,signed-integer-overflow,undefined -Wformat -Wformat-security -Werror=format-security -Warray-bounds -Werror=array-bounds -D_FORTIFY_SOURCE=2"
+	AC_DEFINE([WITH_SANITIZER])
+	AC_MSG_RESULT([*** Enabling sanitizer at user request ***])
+fi
 
 AC_CONFIG_FILES([
 	Makefile
 	src/Makefile
-	src/doc/Makefile])
+	src/engine/Makefile
+	src/engine/test/Makefile.linux
+	src/engine/doc/Makefile
+	src/provider/Makefile
+	src/provider/doc/Makefile
+	test/Makefile
+	test/engine/Makefile
+	test/provider/Makefile])
 
 AC_OUTPUT
 
 echo "CFLAGS=$CFLAGS"
+echo "IBMCA engine:      $enable_engine"
+echo "  default library: $defaultlib"
+echo "IBMCA provider:    $enable_provider"
+if test "x$useproviderfulllibica" = xyes; then
+	echo "  libica library:  libica"
+else
+	echo "  libica library:  libica-cex"
+fi
diff -pruN 1.4.0-1/debian/README.Debian 2.5.0-0ubuntu1/debian/README.Debian
--- 1.4.0-1/debian/README.Debian	2017-09-20 14:18:57.000000000 +0000
+++ 2.5.0-0ubuntu1/debian/README.Debian	1970-01-01 00:00:00.000000000 +0000
@@ -1,42 +0,0 @@
-openssl-ibmca for Debian
------------------------
-
-In order to enable IBMCA, use the following instructions to apply the
-configurations from `openssl.cnf.sample` to the `openssl.cnf` file installed
-in the host by the OpenSSL package. **WARNING:** you may want to save the
-original `openssl.cnf` file before changing it.
-
-In `openssl.cnf.sample`, the *dynamic_path* variable is set to the default
-location in Debian, which is
-/usr/lib/s390x-linux-gnu/openssl-1.0.2/engine/libibmca.so
-
-Append the `openssl.cnf.sample` file to it `/etc/ssl/openssl.cnf` file;
-
-```
-$ cat /usr/share/doc/openssl-ibmca/examples/openssl.cnf.sample >> /etc/ssl/openssl.cnf
-```
-
-In `openssl.cnf` file, move the *openssl_conf* variable from the bottom to the
-top of the file, such as in the example below:
-
-```
-HOME = .
-RANDFILE = $ENV::HOME/.rnd
-openssl_conf = openssl_def
-```
-
-Finally, check if the IBMCA is now enabled. The command below should return the
-IBMCA engine and all the supported cryptographic methods.
-
-```
-$ openssl engine -c
-(dynamic) Dynamic engine loading support
-(ibmca) Ibmca hardware engine support
-[RAND, DES-ECB, DES-CBC, DES-OFB, DES-CFB, DES-EDE3, DES-EDE3-CBC, DES-EDE3-OFB,
- DES-EDE3-CFB, AES-128-ECB, AES-192-ECB, AES-256-ECB, AES-128-CBC, AES-192-CBC,
- AES-256-CBC, AES-128-OFB, AES-192-OFB, AES-256-OFB, AES-128-CFB, AES-192-CFB,
- AES-256-CFB, id-aes128-GCM, id-aes192-GCM, id-aes256-GCM, SHA1, SHA256, SHA512]
-$
-```
-
- -- Paulo Vital <pvital@gmail.com>  Wed, 20 Sep 2017 10:47:45 -0300
diff -pruN 1.4.0-1/debian/README.source 2.5.0-0ubuntu1/debian/README.source
--- 1.4.0-1/debian/README.source	2017-09-20 14:18:57.000000000 +0000
+++ 2.5.0-0ubuntu1/debian/README.source	1970-01-01 00:00:00.000000000 +0000
@@ -1,64 +0,0 @@
-# OpenSSL-ibmca
-
-OpenSSL engine that uses the libica library under s390x to accelerate
-cryptographic operations.
-
-
-## Requirements
-
-The build requirements are:
- * openssl-devel >= 0.9.8
- * libica-devel >= 3.1.1
- * autoconf
- * automake
- * libtool
-
-The runtime requirements are:
- * openssl >= 0.9.8
- * libica >= 3.1.1
-
-
-## Installing
-
-```
-$ ./configure [--enable-debug]
-$ make
-$ sudo make install
-```
-
-This will configure, build and install the package in a default location,
-which is `/usr/local/lib`. It means that the libibmca.so will be installed in
-`/usr/local/lib/libibmca.so` by default. If you want to install it anywhere
-else, run "configure" passing the new location via prefix argument, for
-example:
-
-```
-$ ./configure --prefix=/usr --libdir=/usr/lib64/openssl/engines
-```
-
-
-## Support
-
-To report a bug please submit a
- [ticket](https://github.com/opencryptoki/openssl-ibmca/issues) including the
- following information in the issue description:
-
-* bug description
-* distro release
-* openssl-ibmca package version
-* libica package version
-* steps to reproduce the bug
-
-Regarding technical or usage questions, send email to
- [opencryptoki-tech](
-    https://sourceforge.net/p/opencryptoki/mailman/opencryptoki-tech) or
- [opencryptoki-users](
-    https://sourceforge.net/p/opencryptoki/mailman/opencryptoki-users)
- mailing list respectively.
-
-
-## Contributing
-
-See [CONTRIBUTING.md](https://github.com/opencryptoki/openssl-ibmca/blob/master/CONTRIBUTING.md).
-
- -- Paulo Vital <pvital@gmail.com>  Wed, 20 Sep 2017 11:10:45 -0300
diff -pruN 1.4.0-1/debian/changelog 2.5.0-0ubuntu1/debian/changelog
--- 1.4.0-1/debian/changelog	2017-09-20 14:18:57.000000000 +0000
+++ 2.5.0-0ubuntu1/debian/changelog	2025-08-07 17:11:25.000000000 +0000
@@ -1,5 +1,259 @@
-openssl-ibmca (1.4.0-1) unstable; urgency=medium
+openssl-ibmca (2.5.0-0ubuntu1) questing; urgency=medium
 
-  * Initial release. Closes: #813772
+  * New upstream release. LP: #2116709
+  * d/control: Upgrade debhelper-compat to 13 and remove d/compat.
 
- -- Paulo Vital <pvital@gmail.com>  Wed, 20 sep 2017 11:18:57 -0300
+ -- Alexandre Erwin Ittner <alexandre.ittner@canonical.com>  Thu, 07 Aug 2025 17:11:25 +0000
+
+openssl-ibmca (2.4.1-0ubuntu1) noble; urgency=medium
+
+  * New upstream release. LP: #2050025
+    - Adjust d/p/testconf-openssl3.patch due to slightly different context.
+    - Remove 5 patches d/p/lp-2027809-* since they are incl. in upstream 2.4.1.
+    - Update d/control Standards-Version field to latest 4.6.2
+
+ -- Frank Heimes <frank.heimes@canonical.com>  Mon, 22 Jan 2024 19:51:21 +0100
+
+openssl-ibmca (2.4.0-0ubuntu2) mantic; urgency=medium
+
+  * Add selected commits/patches as requested here: LP: #2027809
+    - d/p/lp-2027809-engine-Only-register-those-algos-specified-with-defa.patch
+      To set the ENGINE_FLAGS_NO_REGISTER_ALL flag during IBMCA engine
+      initialization to avoid unconditional registration of all algorithms.
+    - d/p/lp-2027809-provider-rsa-Check-RSA-keys-with-p-q-at-key-generati.patch
+      To check and correct RSA keys where p < q (privileged form) right after
+      key generation or during import, so that p > q is assured whenever the key
+      is used afterwards, and no ica_rsa_crt() correction is applied later on.
+    - d/p/lp-2027809-provider-Support-importing-of-RSA-keys-with-just-ME-.patch
+      To let an RSA key also contain the private key components in ME format,
+      and use ica_rsa_mod_expo() only if the ME components are available.
+    - d/p/lp-2027809-provider-RSA-Fix-get_params-to-retrieve-max-size-bit.patch
+      To ensure (and fix) that the RSA key management's get_params() function
+      is able to return the values for max-size, bits, and security-bits (if
+      at least the public key is available).
+    - d/p/lp-2027809-provider-Default-debug-directory-to-tmp-but-make-it-.patch
+      To change the default log directory from /var/log/ibmca/ to /tmp which is
+      world-writable anyway, and to avoid making /var/log/ibmca/ world-
+      writable, which can cause security issues, since it's not known under
+      which user an application runs that uses the provider.
+      With that a world-writable directory under /var is avoided.
+
+ -- Frank Heimes <frank.heimes@canonical.com>  Thu, 27 Jul 2023 16:38:43 +0200
+
+openssl-ibmca (2.4.0-0ubuntu1) lunar; urgency=medium
+
+  * New upstream release. LP: #2015333 and LP: #2015454
+
+ -- Frank Heimes <frank.heimes@canonical.com>  Tue, 11 Apr 2023 19:51:12 +0200
+
+openssl-ibmca (2.3.1-0ubuntu1) lunar; urgency=medium
+
+  * New upstream release. LP: #2004529
+  * Remove patch d/p/lp-1959763-Adjust-to-new-libica.patch
+    because it's now included in upstream v2.3.1.
+  * Remove patch d/p/lp-1959763-Support-tests-in-remote-builds.patch
+    because it's now included in upstream v2.3.1.
+  * Remove patch
+    d/p/lp-1959763-provider-Adapt-keymgmt_match-implementations.patch
+    because it's now included in upstream v2.3.1.
+  * Remove patch
+    d/p/lp-1959763-tests-skip-tests-if-libica-does-not-support.patch
+    because it's now included in upstream v2.3.1.
+  * Remove patch d/p/lp-1959763-Provider-Fix-parallel-test-runs.patch
+    because it's now included in upstream v2.3.1.
+  * Remove no longer needed dh-autoreconf at line Build-Depends:
+    in d/control to solve lintian warning:
+    openssl-ibmca source: useless-autoreconf-build-depends
+    (does not need to satisfy dh-autoreconf:any)
+
+ -- Frank Heimes <frank.heimes@canonical.com>  Wed, 01 Feb 2023 17:23:55 +0100
+
+openssl-ibmca (2.3.0-0ubuntu1) kinetic; urgency=medium
+
+  * New upstream release. LP: #1959763
+    - update d/p/openssl-config.patch
+      since code moved from src to src/engine
+    - update d/p/testconf-openssl3.patch
+      since code moved from test to test/engine
+      and context adjustment in test/engine/Makefile.am
+    - remove d/p/e59cce5-Fix-compilation-for-OpenSSL-3.0.patch
+      since this patch/commit is incl. in the new upstream version
+    - due to the refactoring (engine/provider) path adjustments needed in
+      d/rules and d/openssl-ibmca.install
+    - add ibmca-provider-opensslconfig to
+      d/rules and d/openssl-ibmca.install
+    - modify d/rules to configure for engine and provider using full libica
+    - expand d/examples with openssl.cnf.provider.sample
+    - add d/p/lp-1959763-Adjust-to-new-libica.patch
+      to be compliiant with latest libica
+    - add d/p/lp-1959763-Support-tests-in-remote-builds.patch
+      to make tests more decent
+    - add d/p/lp-1959763-provider-Adapt-keymgmt_match-implementations.patch
+      to adapt the provider's match functions
+    - add d/p/lp-1959763-tests-skip-tests-if-libica-does-not-support.patch
+      skip tests if unsupported by libica
+    - add d/p/lp-1959763-Provider-Fix-parallel-test-runs.patch
+      without this esp. the provider tests will not properly work
+
+ -- Frank Heimes <frank.heimes@canonical.com>  Fri, 05 Aug 2022 16:37:13 +0200
+
+openssl-ibmca (2.2.3-0ubuntu1) jammy; urgency=medium
+
+  * New upstream release. LP: #1967141
+  * The difference between 2.2.2 and 2.2.3 includes just these two fixes:
+    - "PKEY: Fix usage of ECX keys"
+    - "use correct libica for ibmca_mechaList_test"
+    Rather than adding these as quilt patches, raising the package to the
+    bugfix-only version that incl. them is preferable.
+  * For "PKEY: Fix usage of ECX keys" a backport of
+    "Fix compilation for OpenSSL 3.0" was needed:
+    d/p/e59cce5-Fix-compilation-for-OpenSSL-3.0.patch
+  * For convenience reasons a generated sample config is now included in
+    the package, but also the optional configuration generator Perl script
+    'ibmca-engine-opensslconfig'.
+  * d/control: add dh-autoreconf to Build-Depends to work around a Lintian
+    regression on missing-build-dependency-for-dh-addon
+
+ -- Frank Heimes <frank.heimes@canonical.com>  Wed, 30 Mar 2022 19:19:00 +0100
+
+openssl-ibmca (2.2.2-0ubuntu1) jammy; urgency=medium
+
+  * New upstream release. LP: #1960004
+  * compatibility to libica4 is now included
+  * d/control modified to depend on libica4
+  * removed d/p/disable-3des-ecb-test.patch since the tests got fixed upstream
+  * d/p/openssl-witness.cnf needed to be refreshed
+  * d/p/testconf-openssl3.patch added initialization and refreshed
+
+ -- Frank Heimes <frank.heimes@canonical.com>  Fri, 04 Feb 2022 09:54:15 +0100
+
+openssl-ibmca (2.2.1-0ubuntu1) jammy; urgency=medium
+
+  * New upstream release. LP: #1958419 
+
+  [Simon Chopin]
+  * d/p/testconf-openssl3.patch: fix the test suite against OpenSSL 3.0
+  * d/p/disable-3des-ecb-test.patch: Temporarily disable a failing test
+  * d/rules: make the build fail if the tests fail
+
+ -- Frank Heimes <frank.heimes@canonical.com>  Thu, 20 Jan 2022 15:44:47 +0100
+
+openssl-ibmca (2.2.0-0ubuntu2) jammy; urgency=medium
+
+  * No-change rebuild against openssl3
+
+ -- Simon Chopin <simon.chopin@canonical.com>  Tue, 07 Dec 2021 15:45:01 +0100
+
+openssl-ibmca (2.2.0-0ubuntu1) impish; urgency=medium
+
+  * New upstream release. LP: #1929052
+
+ -- Matthieu Clemenceau <matthieu.clemenceau@canonical.com>  Fri, 18 Jun 2021 12:02:24 -0500
+
+openssl-ibmca (2.1.2-0ubuntu1) impish; urgency=medium
+
+  * New upstream release.  LP: #1926584.
+
+ -- Steve Langasek <steve.langasek@ubuntu.com>  Thu, 29 Apr 2021 08:41:11 -0700
+
+openssl-ibmca (2.1.1-0ubuntu1) groovy; urgency=medium
+
+  * New upstream release. LP: #1884763
+
+ -- Dimitri John Ledkov <xnox@ubuntu.com>  Wed, 26 Aug 2020 20:14:28 +0100
+
+openssl-ibmca (2.1.0-0ubuntu1) eoan; urgency=medium
+
+  * New upstream release LP: #1836865
+
+ -- Dimitri John Ledkov <xnox@ubuntu.com>  Mon, 07 Oct 2019 11:30:34 +0100
+
+openssl-ibmca (2.0.3-0ubuntu1) eoan; urgency=medium
+
+  * New upstream release LP: #1826198
+
+ -- Dimitri John Ledkov <xnox@ubuntu.com>  Tue, 30 Apr 2019 12:34:27 +0100
+
+openssl-ibmca (2.0.2-0ubuntu2) disco; urgency=medium
+
+  * Rework error string init and exit. LP: #1819487
+
+ -- Dimitri John Ledkov <xnox@ubuntu.com>  Mon, 18 Mar 2019 15:03:08 +0000
+
+openssl-ibmca (2.0.2-0ubuntu1) disco; urgency=medium
+
+  * New upstream release LP: #1804233 LP: #1806483
+  * Drop dlopen-soname.patch, applied upstream.
+  * Update watch file to github.com.
+
+ -- Dimitri John Ledkov <xnox@ubuntu.com>  Mon, 10 Dec 2018 11:21:56 +1100
+
+openssl-ibmca (2.0.0-0ubuntu2) cosmic; urgency=medium
+
+  * Disable test-suite, as it appears to fail on launchpad builders, yet
+    passes locally when uncontained.
+
+ -- Dimitri John Ledkov ðŸŒˆ <xnox@ubuntu.com>  Fri, 15 Jun 2018 12:44:40 +0100
+
+openssl-ibmca (2.0.0-0ubuntu1) cosmic; urgency=medium
+
+  * New upstream release. LP: #1776209
+  * Update debian/copyright to Apache-2
+
+ -- Dimitri John Ledkov ðŸŒˆ <xnox@ubuntu.com>  Thu, 14 Jun 2018 12:10:32 +0100
+
+openssl-ibmca (1.4.1-0ubuntu1) bionic; urgency=medium
+
+  * New upstream release
+  * Update watch file to point at github
+  * Build against openssl1.1 with openssl1.1 engine paths LP: #1747626
+
+ -- Dimitri John Ledkov <xnox@ubuntu.com>  Fri, 23 Feb 2018 18:06:36 +0000
+
+openssl-ibmca (1.4.0-0ubuntu2) bionic; urgency=high
+
+  * No change rebuild against openssl1.1.
+
+ -- Dimitri John Ledkov <xnox@ubuntu.com>  Tue, 06 Feb 2018 17:54:51 +0000
+
+openssl-ibmca (1.4.0-0ubuntu1) artful; urgency=medium
+
+  * New upstream release
+  * Drop patches applied upstream
+
+ -- Dimitri John Ledkov <xnox@ubuntu.com>  Thu, 28 Sep 2017 11:13:14 -0400
+
+openssl-ibmca (1.3.0-0ubuntu5) artful; urgency=medium
+
+  * Apply upstream patch to resolve crashes when libssl attempts to
+    initialise engine a few times too many. LP: #1543455
+
+ -- Dimitri John Ledkov <xnox@ubuntu.com>  Wed, 26 Jul 2017 08:48:51 +0100
+
+openssl-ibmca (1.3.0-0ubuntu4) zesty; urgency=medium
+
+  * Build against libica.so.3.
+
+ -- Dimitri John Ledkov <xnox@ubuntu.com>  Wed, 30 Nov 2016 10:24:29 +0000
+
+openssl-ibmca (1.3.0-0ubuntu3) zesty; urgency=medium
+
+  * Attempt to dlopen libica.so.2, if libica.so (or ctrl provided one)
+    fails. LP: #1605511
+  * Add depends on libica2.
+
+ -- Dimitri John Ledkov <xnox@ubuntu.com>  Tue, 04 Oct 2016 15:25:59 +0100
+
+openssl-ibmca (1.3.0-0ubuntu2) xenial; urgency=medium
+
+  * Correct license information. LP: 1543682
+  * Add watch file.
+  * Resolves LP: #1538864
+
+ -- Dimitri John Ledkov <xnox@ubuntu.com>  Mon, 15 Feb 2016 16:32:05 +0000
+
+openssl-ibmca (1.3.0-0ubuntu1) xenial; urgency=medium
+
+  * Initial release.
+
+ -- Dimitri John Ledkov <xnox@ubuntu.com>  Fri, 05 Feb 2016 06:16:50 +0000
diff -pruN 1.4.0-1/debian/compat 2.5.0-0ubuntu1/debian/compat
--- 1.4.0-1/debian/compat	2017-09-20 14:18:57.000000000 +0000
+++ 2.5.0-0ubuntu1/debian/compat	1970-01-01 00:00:00.000000000 +0000
@@ -1 +0,0 @@
-10
diff -pruN 1.4.0-1/debian/control 2.5.0-0ubuntu1/debian/control
--- 1.4.0-1/debian/control	2017-09-20 14:18:57.000000000 +0000
+++ 2.5.0-0ubuntu1/debian/control	2025-08-07 17:11:25.000000000 +0000
@@ -1,17 +1,16 @@
 Source: openssl-ibmca
 Priority: optional
-Maintainer: Paulo Vital <pvital@gmail.com>
-Build-Depends: debhelper (>= 10), dh-autoreconf, libica-dev, libssl-dev
-Standards-Version: 4.0.0
+Maintainer: Ubuntu Developers <ubuntu-devel-discuss@lists.ubuntu.com>
+XSBC-Original-Maintainer: Dimitri John Ledkov <xnox@ubuntu.com>
+Build-Depends: debhelper-compat (= 13), libica-dev, libssl-dev
+Standards-Version: 4.6.2
 Section: libs
-Homepage: https://github.com/opencryptoki/openssl-ibmca
+Homepage: http://sourceforge.net/projects/opencryptoki/files/libica%20OpenSSL%20Engine
 
 Package: openssl-ibmca
 Architecture: s390 s390x
-Depends: libica3, ${shlibs:Depends}, ${misc:Depends}
-Description: libica engine for OpenSSL
- This package provides an OpenSSL engine to enable hardware acceleration
- of cryptographic functions in OpenSSL, and all applications that use
- OpenSSL.
- .
- This package is specific for s390x architecture.
+Depends: libica4, ${shlibs:Depends}, ${misc:Depends}
+Description: libica based hardware acceleration engine for OpenSSL
+ This package provides an OpenSSL engine to enable hardware
+ acceleration of cryptographic functions in OpenSSL, and all
+ applications that use OpenSSL.
diff -pruN 1.4.0-1/debian/copyright 2.5.0-0ubuntu1/debian/copyright
--- 1.4.0-1/debian/copyright	2017-09-20 14:18:57.000000000 +0000
+++ 2.5.0-0ubuntu1/debian/copyright	2025-08-07 17:11:25.000000000 +0000
@@ -1,45 +1,37 @@
 Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
 Upstream-Name: openssl-ibmca
-Upstream-Contact: Paulo Vital <pvital@gmail.com>
-Source: https://github.com/opencryptoki/openssl-ibmca
+Source: https://github.com/opencryptoki/openssl-ibmca/releases
 
 Files: *
-Copyright: 2001 International Business Machines Corp.
-License: Apache-2.0
- Licensed to the Apache Software Foundation (ASF) under one or more
- contributor license agreements.  See the NOTICE file distributed with
- this work for additional information regarding copyright ownership.
- The ASF licenses this file to You under the Apache License, Version 2.0
- (the "License"); you may not use this file except in compliance with
- the License.  You may obtain a copy of the License at
- .
- 	 http://www.apache.org/licenses/LICENSE-2.0
- .
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
- .
- On Debian systems, the full text of the Apache Software License version 2 can
- be found in the file `/usr/share/common-licenses/Apache-2.0'.
- .
+Copyright: 2015-2018 International Business Machines Corp.
+License: Apache-2
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ On Debian/Ubuntu systems, the full text of the Apache License,
+ Version 2.0 can be found in `/usr/share/common-licenses/Apache-2.0'
+
 
 Files: debian/*
-Copyright: 2017 Paulo Vital <pvital@gmail.com>
-License: GPL-2+
- This package 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 2 of the License, or
- (at your option) any later version.
+Copyright: 2016 Canonical LTD
+License: GPL-3
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License version 3 as
+ published by the Free Software Foundation.
  .
- This package is distributed in the hope that it will be useful,
+ This program 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, see <https://www.gnu.org/licenses/>
- .
- On Debian systems, the complete text of the GNU General
- Public License version 2 can be found in "/usr/share/common-licenses/GPL-2".
+ On Debian/Ubuntu systems, the full text of the GPL v3 can be found in
+ `/usr/share/common-licenses/GPL-3'
diff -pruN 1.4.0-1/debian/dirs 2.5.0-0ubuntu1/debian/dirs
--- 1.4.0-1/debian/dirs	2017-09-20 14:18:57.000000000 +0000
+++ 2.5.0-0ubuntu1/debian/dirs	1970-01-01 00:00:00.000000000 +0000
@@ -1 +0,0 @@
-usr/lib
diff -pruN 1.4.0-1/debian/docs 2.5.0-0ubuntu1/debian/docs
--- 1.4.0-1/debian/docs	2017-09-20 14:18:57.000000000 +0000
+++ 2.5.0-0ubuntu1/debian/docs	1970-01-01 00:00:00.000000000 +0000
@@ -1,2 +0,0 @@
-debian/README.source
-debian/README.Debian
diff -pruN 1.4.0-1/debian/examples 2.5.0-0ubuntu1/debian/examples
--- 1.4.0-1/debian/examples	2017-09-20 14:18:57.000000000 +0000
+++ 2.5.0-0ubuntu1/debian/examples	2025-08-07 17:11:25.000000000 +0000
@@ -1 +1,2 @@
- src/openssl.cnf.sample
+src/engine/openssl.cnf.sample
+src/provider/openssl.cnf.provider.sample
diff -pruN 1.4.0-1/debian/openssl-ibmca.install 2.5.0-0ubuntu1/debian/openssl-ibmca.install
--- 1.4.0-1/debian/openssl-ibmca.install	1970-01-01 00:00:00.000000000 +0000
+++ 2.5.0-0ubuntu1/debian/openssl-ibmca.install	2025-08-07 17:11:25.000000000 +0000
@@ -0,0 +1,7 @@
+# add the configuration generator scripts 'ibmca-engine-opensslconfig'
+# and 'ibmca-provider-opensslconfig' to the package
+src/engine/ibmca-engine-opensslconfig /usr/bin/
+src/provider/ibmca-provider-opensslconfig /usr/bin/
+
+src/engine/.libs/ibmca.so usr/lib/s390x-linux-gnu/engines-1.1/
+src/provider/.libs/ibmca-provider.so /usr/lib/s390x-linux-gnu/ossl-modules/
diff -pruN 1.4.0-1/debian/patches/libica_soname.patch 2.5.0-0ubuntu1/debian/patches/libica_soname.patch
--- 1.4.0-1/debian/patches/libica_soname.patch	2017-09-20 14:18:57.000000000 +0000
+++ 2.5.0-0ubuntu1/debian/patches/libica_soname.patch	1970-01-01 00:00:00.000000000 +0000
@@ -1,15 +0,0 @@
-Description: Setting libica so name to libica.so.3
-Author: Paulo Vital <pvital@gmail.com>
-Last-Update: 2017-09-20
-
---- a/src/e_ibmca.c
-+++ b/src/e_ibmca.c
-@@ -46,7 +46,7 @@
- #include "e_ibmca_err.h"
- 
- #define IBMCA_LIB_NAME "ibmca engine"
--#define LIBICA_SHARED_LIB "libica.so"
-+#define LIBICA_SHARED_LIB "libica.so.3"
- 
- #define AP_PATH "/sys/devices/ap"
- 
diff -pruN 1.4.0-1/debian/patches/openssl-config.patch 2.5.0-0ubuntu1/debian/patches/openssl-config.patch
--- 1.4.0-1/debian/patches/openssl-config.patch	2017-09-20 14:18:57.000000000 +0000
+++ 2.5.0-0ubuntu1/debian/patches/openssl-config.patch	2025-08-07 17:11:25.000000000 +0000
@@ -1,15 +1,14 @@
-Description: correct engine location to the multiarch location
-Author: Paulo Vital <pvital@gmail.com>
-Last-Update: 2017-09-20
-
---- a/src/openssl.cnf.sample
-+++ b/src/openssl.cnf.sample
-@@ -23,7 +23,7 @@
- # The openssl engine path for libibmca.so.
- # Set the dynamic_path to where the libibmca.so engine
+Description: correct engine location to the multiarch locationIndex: openssl-ibmca-1.3.0/src/openssl.cnf.sample
+===================================================================
+--- a/src/engine/openssl.cnf.sample
++++ b/src/engine/openssl.cnf.sample
+@@ -20,7 +20,8 @@
+ # The openssl engine path for ibmca.so.
+ # Set the dynamic_path to where the ibmca.so engine
  # resides on the system.
--dynamic_path = /usr/local/lib/libibmca.so
-+dynamic_path = /usr/lib/s390x-linux-gnu/openssl-1.0.2/engines/libibmca.so
+-dynamic_path = /usr/local/lib/ibmca.so
++dynamic_path = /usr/lib/s390x-linux-gnu/engines-1.1/ibmca.so
++
  engine_id = ibmca
  init = 1
  
diff -pruN 1.4.0-1/debian/patches/series 2.5.0-0ubuntu1/debian/patches/series
--- 1.4.0-1/debian/patches/series	2017-09-20 13:40:30.000000000 +0000
+++ 2.5.0-0ubuntu1/debian/patches/series	2025-08-07 17:11:25.000000000 +0000
@@ -1,2 +1,2 @@
 openssl-config.patch
-libica_soname.patch
+testconf-openssl3.patch
diff -pruN 1.4.0-1/debian/patches/testconf-openssl3.patch 2.5.0-0ubuntu1/debian/patches/testconf-openssl3.patch
--- 1.4.0-1/debian/patches/testconf-openssl3.patch	1970-01-01 00:00:00.000000000 +0000
+++ 2.5.0-0ubuntu1/debian/patches/testconf-openssl3.patch	2025-08-07 17:11:25.000000000 +0000
@@ -0,0 +1,66 @@
+Description: Enable the legacy provider in the test config
+ This provider is needed for the tests against 3DES and DES which have been removed from
+ the default algorithms in OpenSSL 3.0
+Author: Simon Chopin <simon.chopin@canonical.com>
+Bug-Ubuntu: https://bugs.launchpad.net/ubuntu/+source/openssl-ibmca/+bug/1958419
+Last-Update: 2022-01-24
+--- a/test/engine/Makefile.am
++++ b/test/engine/Makefile.am
+@@ -61,7 +61,7 @@
+ enginectrl_SOURCES = enginectrl.c
+ enginectrl_LDADD = -lcrypto -ldl
+ 
+-AM_TESTS_ENVIRONMENT = export IBMCA_TEST_PATH=${top_builddir}/src/engine/.libs/ibmca.so IBMCA_OPENSSL_TEST_CONF=${srcdir}/openssl-test.cnf IBMCA_OPENSSL_TEST_NOINIT_CONF=${srcdir}/openssl-test-noinit.cnf PERL5LIB=${srcdir};
++AM_TESTS_ENVIRONMENT = export IBMCA_TEST_PATH=${top_builddir}/src/engine/.libs/ibmca.so IBMCA_OPENSSL_TEST_CONF=${srcdir}/openssl-test.cnf IBMCA_OPENSSL_WITNESS_CONF=${srcdir}/openssl-witness.cnf  IBMCA_OPENSSL_TEST_NOINIT_CONF=${srcdir}/openssl-test-noinit.cnf PERL5LIB=${srcdir};
+ EXTRA_DIST = ${TESTS_PERL} ${TESTS_CIPHERS} test.pm openssl-test.cnf 	\
+ 	openssl-test-noinit.cnf dsa2k.key dsa2k_pub.key dsa4k.key	\
+ 	dsa4k_pub.key dsa8k.key dsa8k_pub.key dsamax.key dsamax_pub.key	\
+--- /dev/null
++++ b/test/engine/openssl-witness.cnf
+@@ -0,0 +1,14 @@
++openssl_conf = openssl_def
++
++[openssl_def]
++providers = provider_sect
++
++[provider_sect]
++default = default_sect
++legacy = legacy_sect
++
++[default_sect]
++activate = 1
++init = 1
++
++[legacy_sect]
++activate = 1
+--- a/test/engine/test.pm
++++ b/test/engine/test.pm
+@@ -21,6 +21,7 @@
+ 	my $tests = 50;
+ 	my $max_file_size = 1024;
+ 	my $eng = "OPENSSL_CONF=$ENV{IBMCA_OPENSSL_TEST_CONF}";
++	my $noeng = "OPENSSL_CONF=$ENV{IBMCA_OPENSSL_WITNESS_CONF}";
+ 	my @hex = ("a".."f", "0".."9");
+ 
+ 	my ($cipher,$keylen,$ivlen) = @_;
+@@ -39,16 +40,16 @@
+ 		}
+ 
+ 		# engine enc, no-engine dec
+-		`openssl rand $bytes > ${cipher}.${i}.data.in`;
++		`$noeng openssl rand $bytes > ${cipher}.${i}.data.in`;
+ 		`$eng openssl $cipher -e -K $key $iv -in ${cipher}.${i}.data.in -out ${cipher}.${i}.data.enc`;
+-		`openssl $cipher -d -K $key $iv -in ${cipher}.${i}.data.enc -out ${cipher}.${i}.data.dec`;
++		`$noeng openssl $cipher -d -K $key $iv -in ${cipher}.${i}.data.enc -out ${cipher}.${i}.data.dec`;
+ 		`cmp ${cipher}.${i}.data.in ${cipher}.${i}.data.dec`;
+ 		exit(99) if ($?);
+ 		`rm -f ${cipher}.${i}.data.in ${cipher}.${i}.data.enc ${cipher}.${i}.data.dec`;
+ 
+ 		# no-engine enc, engine dec
+-		`openssl rand $bytes > ${cipher}.${i}.data.in`;
+-		`openssl $cipher -e -K $key $iv -in ${cipher}.${i}.data.in -out ${cipher}.${i}.data.enc`;
++		`$noeng openssl rand $bytes > ${cipher}.${i}.data.in`;
++		`$noeng openssl $cipher -e -K $key $iv -in ${cipher}.${i}.data.in -out ${cipher}.${i}.data.enc`;
+ 		`$eng openssl $cipher -d -K $key $iv -in ${cipher}.${i}.data.enc -out ${cipher}.${i}.data.dec`;
+ 		`cmp ${cipher}.${i}.data.in ${cipher}.${i}.data.dec`;
+ 		exit(99) if ($?);
diff -pruN 1.4.0-1/debian/rules 2.5.0-0ubuntu1/debian/rules
--- 1.4.0-1/debian/rules	2017-09-20 14:18:57.000000000 +0000
+++ 2.5.0-0ubuntu1/debian/rules	2025-08-07 17:11:25.000000000 +0000
@@ -1,31 +1,25 @@
 #!/usr/bin/make -f
-# See debhelper(7) (uncomment to enable)
-# output every command that modifies files on the build system.
-#export DH_VERBOSE = 1
-
-# see FEATURE AREAS in dpkg-buildflags(1)
 export DEB_BUILD_MAINT_OPTIONS = hardening=+all
 
-# see ENVIRONMENT in dpkg-buildflags(1)
-# package maintainers to append CFLAGS
-#export DEB_CFLAGS_MAINT_APPEND  = -Wall -pedantic
-# package maintainers to append LDFLAGS
-#export DEB_LDFLAGS_MAINT_APPEND = -Wl,--as-needed
-
 %:
-	dh $@
-
-# dh_make generated override targets
-# This is example for Cmake (See https://bugs.debian.org/641051 )
-#override_dh_auto_configure:
-#	dh_auto_configure -- #	-DCMAKE_LIBRARY_PATH=$(DEB_HOST_MULTIARCH)
+	dh $@  --with autoreconf
 
 override_dh_auto_configure:
-	dh_auto_configure -- --libdir=/usr/lib/$(DEB_HOST_MULTIARCH)/openssl-1.0.2/engines/
+	# by default engine and provider are created
+	pwd
+	dh_auto_configure -- --libdir=/usr/lib/$(DEB_HOST_MULTIARCH)/engines-1.1 --with-provider-libica-full
+
+override_dh_auto_build:
+	dh_auto_build
+	src/engine/ibmca-engine-opensslconfig
+	src/provider/ibmca-provider-opensslconfig
 
 override_dh_auto_install:
 	dh_auto_install
-
-	# Remove useless files
 	find debian -name '*.la' -delete
+	# remove the provider 'so' from the engine folder:
+	rm -f ./debian/openssl-ibmca/usr/lib/s390x-linux-gnu/engines-1.1/ibmca-provider.so
+	# since it needs to be installed to /usr/lib/s390x-linux-gnu/ossl-modules - this is done in openssl-ibmca.install
 
+override_dh_auto_test:
+	dh_auto_test
diff -pruN 1.4.0-1/debian/watch 2.5.0-0ubuntu1/debian/watch
--- 1.4.0-1/debian/watch	2017-09-20 14:18:57.000000000 +0000
+++ 2.5.0-0ubuntu1/debian/watch	2025-08-07 17:11:25.000000000 +0000
@@ -1,4 +1,4 @@
 version=4
-opts="mode=git, pgpmode=none" \
-https://github.com/opencryptoki/openssl-ibmca.git refs/tags/v?(.*) \
-debian /bin/sh uupdate
+opts="filenamemangle=s%(?:.*?)?v?(\d[\d.]*)\.tar\.gz%openssl-ibmca-$1.tar.gz%" \
+   https://github.com/opencryptoki/openssl-ibmca/tags \
+   (?:.*?/)?v?(\d[\d.]*)\.tar\.gz debian uupdate
diff -pruN 1.4.0-1/openssl-ibmca-provider.spec 2.5.0-0ubuntu1/openssl-ibmca-provider.spec
--- 1.4.0-1/openssl-ibmca-provider.spec	1970-01-01 00:00:00.000000000 +0000
+++ 2.5.0-0ubuntu1/openssl-ibmca-provider.spec	2025-04-16 08:09:24.000000000 +0000
@@ -0,0 +1,125 @@
+%global modulesdir %(openssl version -m | grep -o '".*"' | tr -d '"')
+# Above can be replaced by the following once OpenSSL commit 
+# https://github.com/openssl/openssl/commit/7fde39de848f062d6db45bf9e69439db2100b9bb
+# has been included into the distribution:
+# %global modulesdir %(pkg-config --variable=modulesdir libcrypto)
+
+Name:       openssl-ibmca
+Version:    2.5.0
+Release:    1%{?dist}
+Summary:    An IBMCA OpenSSL dynamic provider
+
+License:    ASL 2.0
+URL:        https://github.com/opencryptoki/openssl-ibmca
+Source0:    https://github.com/opencryptoki/%{name}/archive/v%{version}/%{name}-%{version}.tar.gz
+
+Requires:       openssl >= 3.0.0 libica >= 4.0.1
+BuildRequires:  openssl-devel >= 3.0.0 libica-devel >= 4.0.1 openssl >= 3.0.0
+BuildRequires:  autoconf automake libtool perl
+
+ExclusiveArch: s390 s390x
+
+%description
+This package contains a shared object OpenSSL dynamic provider which interfaces 
+to libica-cex, a library enabling the IBM s390x crypto instructions.
+
+%prep
+%setup -q -n %{name}-%{version}
+
+./bootstrap.sh
+
+%build
+%configure --libdir=%{modulesdir} --disable-engine --enable-provider
+%make_build
+
+%install
+%make_install
+rm -f $RPM_BUILD_ROOT%{modulesdir}/ibmca-provider.la
+mv -f src/provider/openssl.cnf.sample src/provider/openssl.cnf.sample.%{_arch}
+
+%files
+%license LICENSE
+%doc ChangeLog README.md src/provider/openssl.cnf.sample.%{_arch} src/provider/ibmca-provider-opensslconfig
+%{modulesdir}/ibmca-provider.so
+%{_mandir}/man5/ibmca-provider.5*
+%dir %attr(777,root,root) %{_localstatedir}/log/ibmca
+
+%changelog
+* Thu Mar 30 2023 Ingo Franzki <ifranzki@linux.ibm.com> 2.4.0
+- Update Version
+
+* Fri Sep 30 2022 Juergen Christ <jchrist@linux.ibm.com> 2.3.1
+- Adjust to libica 4.1.0
+
+* Fri Mar 25 2022 Juergen Christ <jchrist@linux.ibm.com> 2.3.0
+- First version including the provider
+- Fix for engine build without OpenSSL 3.0 sources
+
+* Wed March 3 2022 Ingo Franzki <ifranzki@linux.ibm.com>
+- Add provider support
+
+* Thu Mar 10 2022 Juergen Christ <jchrist@linux.ibm.com> 2.2.3
+- Update Version
+* Thu Jan 27 2022 Juergen Christ <jchrist@linux.ibm.com> 2.2.2
+- Update Version
+
+* Mon Sep 13 2021 Juergen Christ <jchrist@linux.ibm.com> 2.2.1
+- Update Version
+
+* Wed May 19 2021 Juergen Christ <jchrist@linux.ibm.com> 2.2.0
+- Update Version
+
+* Wed May 19 2021 Juergen Christ <jchrist@linux.ibm.com> 2.1.3
+- Update Version
+
+* Wed Apr 28 2021 Juergen Christ <jchrist@linux.ibm.com> 2.1.2
+- Update Version
+
+* Tue May 05 2020 Patrick Steuer <patrick.steuer@de.ibm.com> 2.1.1
+- Update Version
+
+* Mon Sep 09 2019 Patrick Steuer <patrick.steuer@de.ibm.com> 2.1.0
+- Update Version
+
+* Tue Apr 23 2019 Patrick Steuer <patrick.steuer@de.ibm.com> 2.0.3
+- Update Version
+
+* Tue Nov 27 2018 Patrick Steuer <patrick.steuer@de.ibm.com> 2.0.2
+- Update Version
+
+* Thu Nov 08 2018 Patrick Steuer <patrick.steuer@de.ibm.com> 2.0.1
+- Update Version
+
+* Wed Jun 06 2018 Eduardo Barretto <ebarretto@linux.vnet.ibm.com> 2.0.0
+- Update Version
+- Update libica version required for building ibmca
+
+* Wed Feb 21 2018 Eduardo Barretto <ebarretto@linux.vnet.ibm.com> 1.4.1
+- Updated to 1.4.1
+
+* Thu Jan 25 2018 Eduardo Barretto <ebarretto@linux.vnet.ibm.com>
+- Update engine filename
+- Spec cleanup
+
+* Thu Oct 26 2017 Patrick Steuer <patrick.steuer@de.ibm.com>
+- Fix build warning about comma and newlines
+- Remove INSTALL file from doc
+- Fix README name on doc
+
+* Fri Sep 8 2017 Paulo Vital <pvital@linux.vnet.ibm.com> 1.4.0
+- Update new License
+- Update Source and URL pointing to GitHub
+- Added support to AES-GCM
+- Fix bugs/issues
+
+* Fri Feb 17 2017 Paulo Vital <pvital@linux.vnet.ibm.com> 1.3.1
+- Support OpenSSL-1.1 and older versions
+
+* Tue Dec 1 2015 Claudio Carvalho <cclaudio@br.ibm.com> 1.3.0
+- openssl-ibmca-1.3.0 release
+
+* Mon May 2 2011 Kent Yoder <yoder1@us.ibm.com> 1.2.0
+- updates for s390 MSA4 features, engine version 1.2
+
+* Fri Mar 17 2006 Michael A. Halcrow <mhalcrow@us.ibm.com> 1.0.0
+- initial version
diff -pruN 1.4.0-1/openssl-ibmca.spec 2.5.0-0ubuntu1/openssl-ibmca.spec
--- 1.4.0-1/openssl-ibmca.spec	2017-09-08 17:54:06.000000000 +0000
+++ 2.5.0-0ubuntu1/openssl-ibmca.spec	2025-04-16 08:09:24.000000000 +0000
@@ -1,19 +1,17 @@
+%global enginesdir %(pkg-config --variable=enginesdir libcrypto)
+
 Name:       openssl-ibmca
-Version:    1.4.0
-Release:    0
+Version:    2.5.0
+Release:    1%{?dist}
 Summary:    An IBMCA OpenSSL dynamic engine
 
-Group:      Hardware/Other
 License:    ASL 2.0
-Source:     https://github.com/opencryptoki/%{name}/archive/v%{version}.tar.gz
+URL:        https://github.com/opencryptoki/openssl-ibmca
+Source0:    https://github.com/opencryptoki/%{name}/archive/v%{version}/%{name}-%{version}.tar.gz
 
-BuildRequires:  openssl-devel >= 0.9.8,
-                libica-devel >= 3.1.1,
-                autoconf,
-                automake,
-                libtool
-Requires:       openssl >= 0.9.8,
-                libica >= 3.1.1
+Requires:       openssl >= 1.1.1 libica >= 3.6.0
+BuildRequires:  openssl-devel >= 1.1.1 libica-devel >= 3.6.0 openssl >= 1.1.1
+BuildRequires:  autoconf automake libtool perl
 
 ExclusiveArch: s390 s390x
 
@@ -22,28 +20,89 @@ This package contains a shared object Op
 to libica, a library enabling the IBM s390/x CPACF crypto instructions.
 
 %prep
-%setup -q
+%setup -q -n %{name}-%{version}
+
+./bootstrap.sh
 
 %build
-%configure
-make
+%configure --libdir=%{enginesdir} --disable-provider
+%make_build
 
 %install
-%makeinstall
-rm -f $RPM_BUILD_ROOT%{_libdir}/libibmca.la
-mkdir -p $RPM_BUILD_ROOT%{_libdir}/openssl/engines
-mv $RPM_BUILD_ROOT%{_libdir}/lib* $RPM_BUILD_ROOT%{_libdir}/openssl/engines
+%make_install
+rm -f $RPM_BUILD_ROOT%{enginesdir}/ibmca.la
 
-%post -p /sbin/ldconfig
+pushd src/engine
+sed -e 's|/usr/local/lib|%{_libdir}/openssl/engines|' openssl.cnf.sample > openssl.cnf.sample.%{_arch}
+popd
 
-%postun -p /sbin/ldconfig
 
 %files
-%doc README INSTALL src/openssl.cnf.sample
-%{_mandir}/man5/*
-%{_libdir}/openssl/engines/*
+%license LICENSE
+%doc ChangeLog README.md src/engine/openssl.cnf.sample.%{_arch} src/engine/ibmca-engine-opensslconfig
+%{enginesdir}/ibmca.so
+%{_mandir}/man5/ibmca.5*
 
 %changelog
+* Thu Mar 30 2023 Ingo Franzki <ifranzki@linux.ibm.com> 2.4.0
+- Update Version
+
+* Fri Sep 30 2022 Juergen Christ <jchrist@linux.ibm.com> 2.3.1
+- Adjust to libica 4.1.0
+
+* Fri Mar 25 2022 Juergen Christ <jchrist@linux.ibm.com> 2.3.0
+- First version including the provider
+- Fix for engine build without OpenSSL 3.0 sources
+
+* Thu Mar 10 2022 Juergen Christ <jchrist@linux.ibm.com> 2.2.3
+- Update Version
+
+* Thu Jan 27 2022 Juergen Christ <jchrist@linux.ibm.com> 2.2.2
+- Update Version
+
+* Mon Sep 13 2021 Juergen Christ <jchrist@linux.ibm.com> 2.2.1
+- Update Version
+
+* Wed May 19 2021 Juergen Christ <jchrist@linux.ibm.com> 2.2.0
+- Update Version
+
+* Wed May 19 2021 Juergen Christ <jchrist@linux.ibm.com> 2.1.3
+- Update Version
+
+* Wed Apr 28 2021 Juergen Christ <jchrist@linux.ibm.com> 2.1.2
+- Update Version
+
+* Tue May 05 2020 Patrick Steuer <patrick.steuer@de.ibm.com> 2.1.1
+- Update Version
+
+* Mon Sep 09 2019 Patrick Steuer <patrick.steuer@de.ibm.com> 2.1.0
+- Update Version
+
+* Tue Apr 23 2019 Patrick Steuer <patrick.steuer@de.ibm.com> 2.0.3
+- Update Version
+
+* Tue Nov 27 2018 Patrick Steuer <patrick.steuer@de.ibm.com> 2.0.2
+- Update Version
+
+* Thu Nov 08 2018 Patrick Steuer <patrick.steuer@de.ibm.com> 2.0.1
+- Update Version
+
+* Wed Jun 06 2018 Eduardo Barretto <ebarretto@linux.vnet.ibm.com> 2.0.0
+- Update Version
+- Update libica version required for building ibmca
+
+* Wed Feb 21 2018 Eduardo Barretto <ebarretto@linux.vnet.ibm.com> 1.4.1
+- Updated to 1.4.1
+
+* Thu Jan 25 2018 Eduardo Barretto <ebarretto@linux.vnet.ibm.com>
+- Update engine filename
+- Spec cleanup
+
+* Thu Oct 26 2017 Patrick Steuer <patrick.steuer@de.ibm.com>
+- Fix build warning about comma and newlines
+- Remove INSTALL file from doc
+- Fix README name on doc
+
 * Fri Sep 8 2017 Paulo Vital <pvital@linux.vnet.ibm.com> 1.4.0
 - Update new License
 - Update Source and URL pointing to GitHub
diff -pruN 1.4.0-1/src/Makefile.am 2.5.0-0ubuntu1/src/Makefile.am
--- 1.4.0-1/src/Makefile.am	2017-09-08 17:54:06.000000000 +0000
+++ 2.5.0-0ubuntu1/src/Makefile.am	2025-04-16 08:09:24.000000000 +0000
@@ -1,11 +1,7 @@
-lib_LTLIBRARIES=libibmca.la
-
-libibmca_la_SOURCES=e_ibmca.c e_ibmca_err.c
-libibmca_la_LIBADD=-ldl
-libibmca_la_LDFLAGS=-module -version-info 0:2:0 -shared -no-undefined -avoid-version
-
-dist_libibmca_la_SOURCES=e_ibmca_err.h e_os.h cryptlib.h
-EXTRA_DIST = openssl.cnf.sample
-
-ACLOCAL_AMFLAGS = -I m4
-SUBDIRS = doc
+SUBDIRS=
+if IBMCA_ENGINE
+SUBDIRS += engine
+endif
+if IBMCA_PROVIDER
+SUBDIRS += provider
+endif
diff -pruN 1.4.0-1/src/doc/Makefile.am 2.5.0-0ubuntu1/src/doc/Makefile.am
--- 1.4.0-1/src/doc/Makefile.am	2017-09-08 17:54:06.000000000 +0000
+++ 2.5.0-0ubuntu1/src/doc/Makefile.am	1970-01-01 00:00:00.000000000 +0000
@@ -1,2 +0,0 @@
-man5_MANS = ibmca.man
-dist_man5_MANS = $(man5_MANS)
diff -pruN 1.4.0-1/src/doc/ibmca.man 2.5.0-0ubuntu1/src/doc/ibmca.man
--- 1.4.0-1/src/doc/ibmca.man	2017-09-08 17:54:06.000000000 +0000
+++ 2.5.0-0ubuntu1/src/doc/ibmca.man	1970-01-01 00:00:00.000000000 +0000
@@ -1,65 +0,0 @@
-.\" Process this file with
-.\" groff -man -Tascii ibmca.5
-.TH IBMCA 5 2017-08-24 IBM "IBMCA user manual"
-.SH NAME
-IBMCA \- IBMCA is an OpenSSL engine that uses the libica library under s390x to
-accelerate cryptographic operations.
-
-.SH DESCRIPTION
-IBMCA accelerates cryptographic operations of applications that use OpenSSL.
-The engine can be configured by the IBMCA configuration file. The OpenSSL
-configuration file is only needed to attach the engine.
-
-.SS openssl.cnf
-The OpenSSL configuration file can have an IBMCA section. This section includes
-only OpenSSL configuration options for the IBMCA engine.
-
-.SS Control Commands
-Applications that load an OpenSSL engine can optionally send control commands
-to the engine. Control Commands are key value pairs. The value can be a string,
-a numeric integer or be null. See the engine(3) manpage for a mechanism to
-discover control commands.
-
-.SH OPTIONS
-.SS openssl.cnf
-Options for the IBMCA section in openssl.cnf:
-.PP
-dynamic_path =
-.I /path/to/libibmca.so
-.RS
-Set the path to the IBMCA shared object file allowing OpenSSL to find the file.
-.RE
-.PP
-engine_id =
-.I name
-.RS
-Set the name of the engine. The default name is "ibmca".
-.RE
-.IP "init = 0 | 1"
-OpenSSL will try to initialize the engine if this option is set to 1.
-If set to 0, OpenSSL will not try to initialize the engine.
-.PP
-default_algorithms = ALL |
-.I mechanisms
-.RS
-Redirect all cryptographic operations through the engine or disable types of
-mechanisms that the engine supports.
-If ALL is not used, the default_algorithms consists of a comma separated list
-of
-.I mechanisms
-:
-.B CIPHERS | DIGESTS | RSA | DH | DSA.
-.PP
-Only all CIPHERS and/or DIGESTS can be
-de/activated. Algorithms like AES can not be de/activated independently.
-.SS Control Command
-IBMCA does support one optional control command:
-.PP
-SO_PATH:
-.I /path/to/libica.so
-.RS
-Replaces the current libica library by an libica library located at SO_PATH.
-.RE
-
-.SH SEE ALSO
-.B engine(3)
diff -pruN 1.4.0-1/src/e_ibmca.c 2.5.0-0ubuntu1/src/e_ibmca.c
--- 1.4.0-1/src/e_ibmca.c	2017-09-08 17:54:06.000000000 +0000
+++ 2.5.0-0ubuntu1/src/e_ibmca.c	1970-01-01 00:00:00.000000000 +0000
@@ -1,3741 +0,0 @@
-/*
- * Copyright [2005-2017] International Business Machines Corp.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
-
-/*
- * Digest and Cipher support added by Robert H Burroughs (burrough@us.ibm.com).
- *
- * DES/3DES/AES-CFB/OFB support added by Kent Yoder (yoder1@us.ibm.com)
- */
-
-#include <stdint.h>
-#include <stdio.h>
-#include <sys/types.h>
-#include <dirent.h>
-#include <dlfcn.h>
-#include <string.h>
-#include <openssl/crypto.h>
-#include <openssl/engine.h>
-#include <openssl/evp.h>
-#include <openssl/objects.h>
-#include <openssl/sha.h>
-#include <openssl/obj_mac.h>
-#include <openssl/aes.h>
-
-#ifndef OPENSSL_NO_HW
-#ifndef OPENSSL_NO_HW_IBMCA
-
-#if OPENSSL_VERSION_NUMBER < 0x10100000L
- #define OLDER_OPENSSL
-#endif
-
-#include <ica_api.h>
-#include "e_ibmca_err.h"
-
-#define IBMCA_LIB_NAME "ibmca engine"
-#define LIBICA_SHARED_LIB "libica.so"
-
-#define AP_PATH "/sys/devices/ap"
-
-/* COMPAT MACROS */
-#ifdef OLDER_OPENSSL
- #define EVP_CIPHER_CTX_get_cipher_data(ctx)	((ctx)->cipher_data)
- #define EVP_CIPHER_CTX_original_iv(ctx)	((ctx)->oiv)
- #define EVP_CIPHER_CTX_iv_noconst(ctx)		((ctx)->iv)
- #define EVP_CIPHER_CTX_encrypting(ctx)		((ctx)->encrypt)
- #define EVP_CIPHER_CTX_buf_noconst(ctx)	((ctx)->buf)
-#else
- #define EVP_CTRL_GCM_SET_IVLEN			EVP_CTRL_AEAD_SET_IVLEN
- #define EVP_CTRL_GCM_SET_TAG			EVP_CTRL_AEAD_SET_TAG
- #define EVP_CTRL_GCM_GET_TAG			EVP_CTRL_AEAD_GET_TAG
-#endif
-
-#if !defined(NID_aes_128_gcm) || \
-    !defined(NID_aes_192_gcm) || \
-    !defined(NID_aes_256_gcm)
- #ifndef OPENSSL_NO_AES_GCM
-  #define OPENSSL_NO_AES_GCM
- #endif
-#endif
-#ifndef EVP_AEAD_TLS1_AAD_LEN
- #define EVP_AEAD_TLS1_AAD_LEN			13
-#endif
-#ifndef EVP_MD_FLAG_PKEY_METHOD_SIGNATURE
- #define EVP_MD_FLAG_PKEY_METHOD_SIGNATURE	0
-#endif
-
-typedef struct ibmca_des_context {
-	unsigned char key[sizeof(ica_des_key_triple_t)];
-} ICA_DES_CTX;
-
-typedef struct ibmca_aes_128_context {
-	unsigned char key[sizeof(ica_aes_key_len_128_t)];
-} ICA_AES_128_CTX;
-
-typedef struct ibmca_aes_192_context {
-	unsigned char key[sizeof(ica_aes_key_len_192_t)];
-} ICA_AES_192_CTX;
-
-typedef struct ibmca_aes_256_context {
-	unsigned char key[sizeof(ica_aes_key_len_256_t)];
-} ICA_AES_256_CTX;
-
-typedef struct ibmca_aes_gcm_context {
-	unsigned char key[32];
-	int key_set;
-	int iv_set;
-
-	unsigned char tag[16];
-	unsigned char subkey[16];
-	unsigned char icb[16];
-	unsigned char ucb[16];
-	unsigned long long ptlen;
-	unsigned long long aadlen;
-
-	unsigned char *iv;
-	int ivlen;
-	int taglen;
-	int iv_gen;
-	int tls_aadlen;
-
-} ICA_AES_GCM_CTX;
-
-#ifndef OPENSSL_NO_SHA1
-#define SHA_BLOCK_SIZE 64
-typedef struct ibmca_sha1_ctx {
-	sha_context_t c;
-	unsigned char tail[SHA_BLOCK_SIZE];
-	unsigned int tail_len;
-} IBMCA_SHA_CTX;
-#endif
-
-#ifndef OPENSSL_NO_SHA256
-#define SHA256_BLOCK_SIZE 64
-typedef struct ibmca_sha256_ctx {
-	sha256_context_t c;
-	unsigned char tail[SHA256_BLOCK_SIZE];
-	unsigned int tail_len;
-} IBMCA_SHA256_CTX;
-#endif
-
-#ifndef OPENSSL_NO_SHA512
-#define SHA512_BLOCK_SIZE 128
-typedef struct ibmca_sha512_ctx {
-	sha512_context_t c;
-	unsigned char tail[SHA512_BLOCK_SIZE];
-	unsigned int tail_len;
-} IBMCA_SHA512_CTX;
-#endif
-
-static const char *LIBICA_NAME = "ica";
-
-#if defined(NID_aes_128_cfb128) && ! defined (NID_aes_128_cfb)
-#define NID_aes_128_cfb NID_aes_128_cfb128
-#endif
-
-#if defined(NID_aes_128_ofb128) && ! defined (NID_aes_128_ofb)
-#define NID_aes_128_ofb NID_aes_128_ofb128
-#endif
-
-#if defined(NID_aes_192_cfb128) && ! defined (NID_aes_192_cfb)
-#define NID_aes_192_cfb NID_aes_192_cfb128
-#endif
-
-#if defined(NID_aes_192_ofb128) && ! defined (NID_aes_192_ofb)
-#define NID_aes_192_ofb NID_aes_192_ofb128
-#endif
-
-#if defined(NID_aes_256_cfb128) && ! defined (NID_aes_256_cfb)
-#define NID_aes_256_cfb NID_aes_256_cfb128
-#endif
-
-#if defined(NID_aes_256_ofb128) && ! defined (NID_aes_256_ofb)
-#define NID_aes_256_ofb NID_aes_256_ofb128
-#endif
-
-#if defined(NID_des_ofb64) && ! defined (NID_des_ofb)
-#define NID_des_ofb NID_des_ofb64
-#endif
-
-#if defined(NID_des_ede3_ofb64) && ! defined (NID_des_ede3_ofb)
-#define NID_des_ede3_ofb NID_des_ede3_ofb64
-#endif
-
-#if defined(NID_des_cfb64) && ! defined (NID_des_cfb)
-#define NID_des_cfb NID_des_cfb64
-#endif
-
-#if defined(NID_des_ede3_cfb64) && ! defined (NID_des_ede3_cfb)
-#define NID_des_ede3_cfb NID_des_ede3_cfb64
-#endif
-
-/*
- * ibmca_crypto_algos lists the supported crypto algos by ibmca.
- * This list is matched against all algo support by libica. Only if
- * the algo is in this list it is activated in ibmca.
- * The defines can be found in the libica header file.
- */
-static int ibmca_crypto_algos[] = {
-        SHA1,
-        SHA256,
-        SHA512,
-        P_RNG,
-        RSA_ME,
-        RSA_CRT,
-        DES_ECB,
-        DES_CBC,
-        DES_OFB,
-        DES_CFB,
-        DES3_ECB,
-        DES3_CBC,
-        DES3_OFB,
-        DES3_CFB,
-        DES3_CTR,
-        AES_ECB,
-        AES_CBC,
-        AES_OFB,
-        AES_CFB,
-	AES_GCM_KMA,
-        0
-};
-
-
-#define MAX_CIPHER_NIDS sizeof(ibmca_crypto_algos)
-/*
- * This struct maps one NID to one crypto algo.
- * So we can tell OpenSSL thsi NID maps to this function.
- */
-struct crypto_pair
-{
-        int nids[MAX_CIPHER_NIDS];
-        const void *crypto_meths[MAX_CIPHER_NIDS];
-};
-
-/* We can not say how much crypto algos are
- * supported by libica. We can only say the
- * size is not greater as the supported
- * crypto algos by ibmca.
- * The actual number of supported crypto algos
- * is saved to the size_****_nid variabes
- */
-static size_t size_cipher_list = 0;
-static size_t size_digest_list = 0;
-
-static struct crypto_pair ibmca_cipher_lists;
-static struct crypto_pair ibmca_digest_lists;
-
-
-static int ibmca_destroy(ENGINE * e);
-static int ibmca_init(ENGINE * e);
-static int ibmca_finish(ENGINE * e);
-static int ibmca_ctrl(ENGINE * e, int cmd, long i, void *p, void (*f) ());
-
-static ica_adapter_handle_t ibmca_handle = 0;
-
-/* BIGNUM stuff */
-static int ibmca_mod_exp(BIGNUM * r, const BIGNUM * a, const BIGNUM * p,
-			 const BIGNUM * m, BN_CTX * ctx);
-
-static int ibmca_mod_exp_crt(BIGNUM * r, const BIGNUM * a,
-			     const BIGNUM * p, const BIGNUM * q,
-			     const BIGNUM * dmp1, const BIGNUM * dmq1,
-			     const BIGNUM * iqmp, BN_CTX * ctx);
-
-#ifndef OPENSSL_NO_RSA
-/* RSA stuff */
-static int ibmca_rsa_mod_exp(BIGNUM * r0, const BIGNUM * I, RSA * rsa,
-                             BN_CTX *ctx);
-
-static int ibmca_rsa_init(RSA *rsa);
-#endif
-
-/* This function is aliased to mod_exp (with the mont stuff dropped). */
-static int ibmca_mod_exp_mont(BIGNUM * r, const BIGNUM * a,
-			      const BIGNUM * p, const BIGNUM * m,
-			      BN_CTX * ctx, BN_MONT_CTX * m_ctx);
-
-#ifndef OPENSSL_NO_DSA
-/* DSA stuff */
-#ifdef OLDER_OPENSSL
-static int ibmca_dsa_mod_exp(DSA * dsa, BIGNUM * rr, BIGNUM * a1,
-			     BIGNUM * p1, BIGNUM * a2, BIGNUM * p2,
-			     BIGNUM * m, BN_CTX * ctx,
-			     BN_MONT_CTX * in_mont);
-static int ibmca_mod_exp_dsa(DSA * dsa, BIGNUM * r, BIGNUM * a,
-			     const BIGNUM * p, const BIGNUM * m,
-			     BN_CTX * ctx, BN_MONT_CTX * m_ctx);
-#else
-static int ibmca_dsa_mod_exp(DSA * dsa, BIGNUM * rr, const BIGNUM * a1,
-			     const BIGNUM * p1, const BIGNUM * a2,
-			     const BIGNUM * p2, const BIGNUM * m,
-			     BN_CTX * ctx, BN_MONT_CTX * in_mont);
-static int ibmca_mod_exp_dsa(DSA * dsa, BIGNUM * r, const BIGNUM * a,
-			     const BIGNUM * p, const BIGNUM * m,
-			     BN_CTX * ctx, BN_MONT_CTX * m_ctx);
-#endif
-#endif
-
-#ifndef OPENSSL_NO_DH
-/* DH stuff */
-/* This function is alised to mod_exp (with the DH and mont dropped). */
-static int ibmca_mod_exp_dh(const DH * dh, BIGNUM * r,
-			    const BIGNUM * a, const BIGNUM * p,
-			    const BIGNUM * m, BN_CTX * ctx,
-			    BN_MONT_CTX * m_ctx);
-#endif
-
-/* RAND stuff */
-static int ibmca_rand_bytes(unsigned char *buf, int num);
-static int ibmca_rand_status(void);
-
-/* DES, TDES, AES declarations */
-static int ibmca_usable_ciphers(const int **nids);
-
-static int ibmca_engine_ciphers(ENGINE * e, const EVP_CIPHER ** cipher,
-				const int **nids, int nid);
-
-static int ibmca_init_key(EVP_CIPHER_CTX * ctx, const unsigned char *key,
-			  const unsigned char *iv, int enc);
-
-static int ibmca_des_cipher(EVP_CIPHER_CTX * ctx, unsigned char *out,
-			    const unsigned char *in, size_t inlen);
-
-static int ibmca_tdes_cipher(EVP_CIPHER_CTX * ctx, unsigned char *out,
-			     const unsigned char *in, size_t inlen);
-
-static int ibmca_aes_128_cipher(EVP_CIPHER_CTX *ctx, unsigned char *out,
-				const unsigned char *in, size_t inlen);
-
-static int ibmca_aes_192_cipher(EVP_CIPHER_CTX *ctx, unsigned char *out,
-				const unsigned char *in, size_t inlen);
-
-static int ibmca_aes_256_cipher(EVP_CIPHER_CTX *ctx, unsigned char *out,
-				const unsigned char *in, size_t inlen);
-
-static int ibmca_cipher_cleanup(EVP_CIPHER_CTX * ctx);
-
-#ifndef OPENSSL_NO_AES_GCM
-static int ibmca_aes_gcm_init_key(EVP_CIPHER_CTX *ctx,
-                                  const unsigned char *key,
-                                  const unsigned char *iv, int enc);
-static int ibmca_aes_gcm_tls_cipher(EVP_CIPHER_CTX *ctx, unsigned char *out,
-                                    const unsigned char *in, size_t len);
-static int ibmca_gcm_aad(ICA_AES_GCM_CTX *ctx, const unsigned char *aad,
-			 size_t len, int enc, int keylen);
-static int ibmca_aes_gcm_cipher(EVP_CIPHER_CTX *ctx, unsigned char *out,
-                                const unsigned char *in, size_t len);
-static int ibmca_aes_gcm(ICA_AES_GCM_CTX *ctx, const unsigned char *in,
-                         unsigned char *out, size_t len, int enc, int keylen);
-static int ibmca_aes_gcm_ctrl(EVP_CIPHER_CTX *ctx, int type, int arg,
-			      void *ptr);
-static int ibmca_aes_gcm_setiv(EVP_CIPHER_CTX *c);
-static int ibmca_gcm_tag(EVP_CIPHER_CTX *ctx, unsigned char *out,
-			 const unsigned char *in, int taglen);
-#endif
-
-/* Sha1 stuff */
-static int ibmca_usable_digests(const int **nids);
-
-static int ibmca_engine_digests(ENGINE * e, const EVP_MD ** digest,
-				const int **nids, int nid);
-
-#ifndef OPENSSL_NO_SHA1
-static int ibmca_sha1_init(EVP_MD_CTX * ctx);
-
-static int ibmca_sha1_update(EVP_MD_CTX * ctx, const void *data,
-			     unsigned long count);
-
-static int ibmca_sha1_final(EVP_MD_CTX * ctx, unsigned char *md);
-
-static int ibmca_sha1_cleanup(EVP_MD_CTX * ctx);
-#endif
-
-#ifndef OPENSSL_NO_SHA256
-static int ibmca_sha256_init(EVP_MD_CTX * ctx);
-
-static int ibmca_sha256_update(EVP_MD_CTX * ctx, const void *data,
-			       unsigned long count);
-
-static int ibmca_sha256_final(EVP_MD_CTX * ctx, unsigned char *md);
-
-static int ibmca_sha256_cleanup(EVP_MD_CTX * ctx);
-#endif
-
-#ifndef OPENSSL_NO_SHA512
-static int ibmca_sha512_init(EVP_MD_CTX * ctx);
-
-static int ibmca_sha512_update(EVP_MD_CTX * ctx, const void *data,
-			       unsigned long count);
-
-static int ibmca_sha512_final(EVP_MD_CTX * ctx, unsigned char *md);
-
-static int ibmca_sha512_cleanup(EVP_MD_CTX * ctx);
-#endif
-
-/* WJH - check for more commands, like in nuron */
-
-/* The definitions for control commands specific to this engine */
-#define IBMCA_CMD_SO_PATH		ENGINE_CMD_BASE
-static const ENGINE_CMD_DEFN ibmca_cmd_defns[] = {
-	{IBMCA_CMD_SO_PATH,
-	 "SO_PATH",
-	 "Specifies the path to the 'atasi' shared library",
-	 ENGINE_CMD_FLAG_STRING},
-	{0, NULL, NULL, 0}
-};
-
-#ifndef OPENSSL_NO_RSA
-/* Our internal RSA_METHOD that we provide pointers to */
-#ifdef OLDER_OPENSSL
-static RSA_METHOD ibmca_rsa = {
-	"Ibmca RSA method",      /* name */
-	NULL,                    /* rsa_pub_enc */
-	NULL,                    /* rsa_pub_dec */
-	NULL,                    /* rsa_priv_enc */
-	NULL,                    /* rsa_priv_dec */
-	ibmca_rsa_mod_exp,       /* rsa_mod_exp */
-	ibmca_mod_exp_mont,      /* bn_mod_exp */
-	ibmca_rsa_init,          /* init */
-	NULL,                    /* finish */
-	0,                       /* flags */
-	NULL,                    /* app_data */
-	NULL,                    /* rsa_sign */
-	NULL,                    /* rsa_verify */
-	NULL                     /* rsa_keygen */
-};
-#else
-static RSA_METHOD *ibmca_rsa = NULL;
-#endif
-#endif
-
-#ifndef OPENSSL_NO_DSA
-/* Our internal DSA_METHOD that we provide pointers to */
-#ifdef OLDER_OPENSSL
-static DSA_METHOD ibmca_dsa = {
-	"Ibmca DSA method",     /* name */
-	NULL,			/* dsa_do_sign */
-	NULL,			/* dsa_sign_setup */
-	NULL,			/* dsa_do_verify */
-	ibmca_dsa_mod_exp,	/* dsa_mod_exp */
-	ibmca_mod_exp_dsa,	/* bn_mod_exp */
-	NULL,			/* init */
-	NULL,			/* finish */
-	0,			/* flags */
-	NULL			/* app_data */
-};
-#else
-static DSA_METHOD *ibmca_dsa = NULL;
-#endif
-#endif
-
-#ifndef OPENSSL_NO_DH
-/* Our internal DH_METHOD that we provide pointers to */
-#ifdef OLDER_OPENSSL
-static DH_METHOD ibmca_dh = {
-	"Ibmca DH method",     /* name */
-	NULL,                  /* generate_key */
-	NULL,                  /* compute_key */
-	ibmca_mod_exp_dh,      /* bn_mod_exp */
-	NULL,                  /* init */
-	NULL,                  /* finish */
-	0,                     /* flags */
-	NULL                   /* app_data */
-};
-#else
-static DH_METHOD *ibmca_dh = NULL;
-#endif
-#endif
-
-static RAND_METHOD ibmca_rand = {
-	/* "IBMCA RAND method", */
-	NULL,                  /* seed */
-	ibmca_rand_bytes,      /* bytes */
-	NULL,                  /* cleanup */
-	NULL,                  /* add */
-	ibmca_rand_bytes,      /* pseudorand */
-	ibmca_rand_status,     /* status */
-};
-
-#ifdef OLDER_OPENSSL
-/* DES ECB EVP */
-const EVP_CIPHER ibmca_des_ecb = {
-	NID_des_ecb,                  /* nid */
-	sizeof(ica_des_vector_t),     /* block_size */
-	sizeof(ica_des_key_single_t), /* key_len */
-	sizeof(ica_des_vector_t),     /* iv_len */
-	EVP_CIPH_ECB_MODE,            /* flags */
-	ibmca_init_key,               /* init */
-	ibmca_des_cipher,             /* do_cipher */
-	ibmca_cipher_cleanup,         /* cleanup */
-	sizeof(struct ibmca_des_context), /* ctx_size */
-	EVP_CIPHER_set_asn1_iv,       /* set_asn1_parameters */
-	EVP_CIPHER_get_asn1_iv,       /* get_asn1_parameters */
-	NULL,                         /* ctrl */
-	NULL                          /* app_data */
-};
-
-/* DES CBC EVP */
-const EVP_CIPHER ibmca_des_cbc = {
-	NID_des_cbc,
-	sizeof(ica_des_vector_t),
-	sizeof(ica_des_key_single_t),
-	sizeof(ica_des_vector_t),
-	EVP_CIPH_CBC_MODE,
-	ibmca_init_key,
-	ibmca_des_cipher,
-	ibmca_cipher_cleanup,
-	sizeof(struct ibmca_des_context),
-	EVP_CIPHER_set_asn1_iv,
-	EVP_CIPHER_get_asn1_iv,
-	NULL,
-	NULL
-};
-
-/* DES OFB EVP */
-const EVP_CIPHER ibmca_des_ofb = {
-	NID_des_ofb,
-	1, // stream cipher needs blocksize set to 1
-	sizeof(ica_des_key_single_t),
-	sizeof(ica_des_vector_t),
-	EVP_CIPH_OFB_MODE,
-	ibmca_init_key, /* XXX check me */
-	ibmca_des_cipher,
-	ibmca_cipher_cleanup,
-	sizeof(struct ibmca_des_context),
-	EVP_CIPHER_set_asn1_iv,
-	EVP_CIPHER_get_asn1_iv,
-	NULL,
-	NULL
-};
-
-/* DES CFB EVP */
-const EVP_CIPHER ibmca_des_cfb = {
-	NID_des_cfb,
-	1, // stream cipher needs blocksize set to 1
-	sizeof(ica_des_key_single_t),
-	sizeof(ica_des_vector_t),
-	EVP_CIPH_CFB_MODE,
-	ibmca_init_key, /* XXX check me */
-	ibmca_des_cipher,
-	ibmca_cipher_cleanup,
-	sizeof(struct ibmca_des_context),
-	EVP_CIPHER_set_asn1_iv,
-	EVP_CIPHER_get_asn1_iv,
-	NULL,
-	NULL
-};
-#else
-#define EVP_CIPHER_block_size_ECB       sizeof(ica_des_vector_t)
-#define EVP_CIPHER_block_size_CBC       sizeof(ica_des_vector_t)
-#define EVP_CIPHER_block_size_OFB       1
-#define EVP_CIPHER_block_size_CFB	1
-
-#define DECLARE_DES_EVP(lmode,umode)								\
-static EVP_CIPHER *des_##lmode = NULL;								\
-static const EVP_CIPHER *ibmca_des_##lmode(void)						\
-{												\
-	if (des_##lmode == NULL) {								\
-		EVP_CIPHER *cipher;								\
-		if (( cipher = EVP_CIPHER_meth_new(NID_des_##lmode,				\
-						EVP_CIPHER_block_size_##umode,      	   	\
-						sizeof(ica_des_key_single_t))) == NULL  	\
-		   || !EVP_CIPHER_meth_set_iv_length(cipher, sizeof(ica_des_vector_t))		\
-		   || !EVP_CIPHER_meth_set_flags(cipher,EVP_CIPH_##umode##_MODE)		\
-		   || !EVP_CIPHER_meth_set_init(cipher, ibmca_init_key)				\
-		   || !EVP_CIPHER_meth_set_do_cipher(cipher, ibmca_des_cipher)			\
-		   || !EVP_CIPHER_meth_set_cleanup(cipher, ibmca_cipher_cleanup)		\
-		   || !EVP_CIPHER_meth_set_impl_ctx_size(cipher,				\
-							sizeof(struct ibmca_des_context))	\
-		   || !EVP_CIPHER_meth_set_set_asn1_params(cipher, EVP_CIPHER_set_asn1_iv) 	\
-		   || !EVP_CIPHER_meth_set_get_asn1_params(cipher, EVP_CIPHER_get_asn1_iv)) {	\
-			EVP_CIPHER_meth_free(cipher);					        \
-			cipher = NULL;                           				\
-		}										\
-		des_##lmode = cipher;								\
-	}											\
-	return des_##lmode;									\
-}												\
-												\
-static void ibmca_des_##lmode##_destroy(void)							\
-{												\
-	EVP_CIPHER_meth_free(des_##lmode);							\
-	des_##lmode = NULL;									\
-}
-
-DECLARE_DES_EVP(ecb, ECB)
-DECLARE_DES_EVP(cbc, CBC)
-DECLARE_DES_EVP(ofb, OFB)
-DECLARE_DES_EVP(cfb, CFB)
-#endif
-
-#ifdef OLDER_OPENSSL
-/* 3DES ECB EVP	*/
-const EVP_CIPHER ibmca_tdes_ecb = {
-	NID_des_ede3_ecb,
-	sizeof(ica_des_vector_t),
-	sizeof(ica_des_key_triple_t),
-	sizeof(ica_des_vector_t),
-	EVP_CIPH_ECB_MODE,
-	ibmca_init_key,
-	ibmca_tdes_cipher,
-	ibmca_cipher_cleanup,
-	sizeof(struct ibmca_des_context),
-	EVP_CIPHER_set_asn1_iv,
-	EVP_CIPHER_get_asn1_iv,
-	NULL,
-	NULL
-};
-
-/* 3DES CBC EVP	*/
-const EVP_CIPHER ibmca_tdes_cbc = {
-	NID_des_ede3_cbc,
-	sizeof(ica_des_vector_t),
-	sizeof(ica_des_key_triple_t),
-	sizeof(ica_des_vector_t),
-	EVP_CIPH_CBC_MODE,
-	ibmca_init_key,
-	ibmca_tdes_cipher,
-	ibmca_cipher_cleanup,
-	sizeof(struct ibmca_des_context),
-	EVP_CIPHER_set_asn1_iv,
-	EVP_CIPHER_get_asn1_iv,
-	NULL,
-	NULL
-};
-
-/* 3DES OFB EVP */
-const EVP_CIPHER ibmca_tdes_ofb = {
-	NID_des_ede3_ofb,
-	1, // stream cipher needs blocksize set to 1
-	sizeof(ica_des_key_triple_t),
-	sizeof(ica_des_vector_t),
-	EVP_CIPH_OFB_MODE,
-	ibmca_init_key, /* XXX check me */
-	ibmca_tdes_cipher,
-	ibmca_cipher_cleanup,
-	sizeof(struct ibmca_des_context),
-	EVP_CIPHER_set_asn1_iv,
-	EVP_CIPHER_get_asn1_iv,
-	NULL,
-	NULL
-};
-
-/* 3DES CFB EVP */
-const EVP_CIPHER ibmca_tdes_cfb = {
-	NID_des_ede3_cfb,
-	1, // stream cipher needs blocksize set to 1
-	sizeof(ica_des_key_triple_t),
-	sizeof(ica_des_vector_t),
-	EVP_CIPH_CFB_MODE,
-	ibmca_init_key, /* XXX check me */
-	ibmca_tdes_cipher,
-	ibmca_cipher_cleanup,
-	sizeof(struct ibmca_des_context),
-	EVP_CIPHER_set_asn1_iv,
-	EVP_CIPHER_get_asn1_iv,
-	NULL,
-	NULL
-};
-#else
-#define DECLARE_TDES_EVP(lmode,umode)								\
-static EVP_CIPHER *tdes_##lmode = NULL;								\
-static const EVP_CIPHER *ibmca_tdes_##lmode(void)						\
-{												\
-	if (tdes_##lmode == NULL) {								\
-		EVP_CIPHER *cipher;								\
-		if (( cipher = EVP_CIPHER_meth_new(NID_des_ede3_##lmode,			\
-						EVP_CIPHER_block_size_##umode,      	   	\
-						sizeof(ica_des_key_triple_t))) == NULL  	\
-		   || !EVP_CIPHER_meth_set_iv_length(cipher, sizeof(ica_des_vector_t))		\
-		   || !EVP_CIPHER_meth_set_flags(cipher,EVP_CIPH_##umode##_MODE)		\
-		   || !EVP_CIPHER_meth_set_init(cipher, ibmca_init_key)				\
-		   || !EVP_CIPHER_meth_set_do_cipher(cipher, ibmca_tdes_cipher)			\
-		   || !EVP_CIPHER_meth_set_cleanup(cipher, ibmca_cipher_cleanup)		\
-		   || !EVP_CIPHER_meth_set_impl_ctx_size(cipher,				\
-							   sizeof(struct ibmca_des_context))	\
-		   || !EVP_CIPHER_meth_set_set_asn1_params(cipher, EVP_CIPHER_set_asn1_iv) 	\
-		   || !EVP_CIPHER_meth_set_get_asn1_params(cipher, EVP_CIPHER_get_asn1_iv)) {	\
-			EVP_CIPHER_meth_free(cipher);					        \
-			cipher = NULL;                           				\
-		}										\
-		tdes_##lmode = cipher;								\
-	}											\
-	return tdes_##lmode;									\
-}												\
-												\
-static void ibmca_tdes_##lmode##_destroy(void)							\
-{												\
-	EVP_CIPHER_meth_free(tdes_##lmode);							\
-	tdes_##lmode = NULL;									\
-}
-
-DECLARE_TDES_EVP(ecb, ECB)
-DECLARE_TDES_EVP(cbc, CBC)
-DECLARE_TDES_EVP(ofb, OFB)
-DECLARE_TDES_EVP(cfb, CFB)
-#endif
-
-#ifdef OLDER_OPENSSL
-# define DECLARE_AES_EVP(kbits, mode, block_size, key_len, iv_len,	\
-			flags, ctx_size, init, do_cipher, cleanup,	\
-			set_asn1_parameters, get_asn1_parameters, ctrl)	\
-const EVP_CIPHER aes_##kbits##_##mode = {				\
-	NID_aes_##kbits##_##mode,					\
-	block_size,							\
-	key_len,							\
-	iv_len,								\
-	flags,								\
-	init,								\
-	do_cipher,							\
-	cleanup,							\
-	ctx_size,							\
-	set_asn1_parameters,						\
-	get_asn1_parameters,						\
-	ctrl,								\
-	NULL								\
-};									\
-static const EVP_CIPHER *ibmca_aes_##kbits##_##mode(void)		\
-{									\
-	return &aes_##kbits##_##mode;					\
-}
-#else
-# define DECLARE_AES_EVP(kbits, mode, block_size, key_len, iv_len,	\
-			 flags,	ctx_size, init, do_cipher, cleanup,	\
-			 set_asn1_parameters, get_asn1_parameters, ctrl)\
-static EVP_CIPHER *aes_##kbits##_##mode = NULL;				\
-static const EVP_CIPHER *ibmca_aes_##kbits##_##mode(void)		\
-{									\
-	EVP_CIPHER *cipher;						\
-									\
-	if (aes_##kbits##_##mode != NULL)				\
-		goto done;						\
-									\
-	if ((cipher = EVP_CIPHER_meth_new(NID_aes_##kbits##_##mode,	\
-					  block_size, key_len)) == NULL	\
-	   || !EVP_CIPHER_meth_set_iv_length(cipher, iv_len)		\
-	   || !EVP_CIPHER_meth_set_flags(cipher, flags)			\
-	   || !EVP_CIPHER_meth_set_init(cipher, init)			\
-	   || !EVP_CIPHER_meth_set_do_cipher(cipher, do_cipher)		\
-	   || !EVP_CIPHER_meth_set_cleanup(cipher, cleanup)		\
-	   || !EVP_CIPHER_meth_set_impl_ctx_size(cipher, ctx_size)	\
-	   || !EVP_CIPHER_meth_set_set_asn1_params(cipher,		\
-						   set_asn1_parameters)	\
-	   || !EVP_CIPHER_meth_set_get_asn1_params(cipher,		\
-						   get_asn1_parameters)	\
-	   || !EVP_CIPHER_meth_set_ctrl(cipher, ctrl)) {		\
-		EVP_CIPHER_meth_free(cipher);				\
-		cipher = NULL;                           		\
-	}								\
-	aes_##kbits##_##mode = cipher;					\
-done:									\
-	return aes_##kbits##_##mode;					\
-}									\
-									\
-static void ibmca_aes_##kbits##_##mode##_destroy(void)			\
-{									\
-	EVP_CIPHER_meth_free(aes_##kbits##_##mode);			\
-	aes_##kbits##_##mode = NULL;					\
-}
-#endif
-
-DECLARE_AES_EVP(128, ecb, sizeof(ica_aes_vector_t),
-		sizeof(ica_aes_key_len_128_t), sizeof(ica_aes_vector_t),
-		EVP_CIPH_ECB_MODE, sizeof(ICA_AES_128_CTX),
-		ibmca_init_key, ibmca_aes_128_cipher, ibmca_cipher_cleanup,
-		EVP_CIPHER_set_asn1_iv, EVP_CIPHER_get_asn1_iv, NULL)
-DECLARE_AES_EVP(128, cbc, sizeof(ica_aes_vector_t),
-		sizeof(ica_aes_key_len_128_t), sizeof(ica_aes_vector_t),
-		EVP_CIPH_CBC_MODE, sizeof(ICA_AES_128_CTX),
-		ibmca_init_key, ibmca_aes_128_cipher, ibmca_cipher_cleanup,
-		EVP_CIPHER_set_asn1_iv, EVP_CIPHER_get_asn1_iv, NULL)
-DECLARE_AES_EVP(128, ofb, 1, sizeof(ica_aes_key_len_128_t),
-		sizeof(ica_aes_vector_t), EVP_CIPH_OFB_MODE,
-		sizeof(ICA_AES_128_CTX), ibmca_init_key,
-		ibmca_aes_128_cipher, ibmca_cipher_cleanup,
-		EVP_CIPHER_set_asn1_iv, EVP_CIPHER_get_asn1_iv, NULL)
-DECLARE_AES_EVP(128, cfb, 1, sizeof(ica_aes_key_len_128_t),
-		sizeof(ica_aes_vector_t), EVP_CIPH_CFB_MODE,
-		sizeof(ICA_AES_128_CTX), ibmca_init_key,
-		ibmca_aes_128_cipher, ibmca_cipher_cleanup,
-		EVP_CIPHER_set_asn1_iv, EVP_CIPHER_get_asn1_iv, NULL)
-#ifndef OPENSSL_NO_AES_GCM
-DECLARE_AES_EVP(128, gcm, 1, sizeof(ica_aes_key_len_128_t),
-		sizeof(ica_aes_vector_t) - sizeof(uint32_t),
-		EVP_CIPH_GCM_MODE | EVP_CIPH_FLAG_DEFAULT_ASN1
-		| EVP_CIPH_CUSTOM_IV | EVP_CIPH_FLAG_CUSTOM_CIPHER
-		| EVP_CIPH_ALWAYS_CALL_INIT | EVP_CIPH_CTRL_INIT
-		| EVP_CIPH_CUSTOM_COPY | EVP_CIPH_FLAG_AEAD_CIPHER,
-		sizeof(ICA_AES_GCM_CTX),
-		ibmca_aes_gcm_init_key, ibmca_aes_gcm_cipher, NULL, NULL,
-		NULL, ibmca_aes_gcm_ctrl)
-#endif
-
-DECLARE_AES_EVP(192, ecb, sizeof(ica_aes_vector_t),
-		sizeof(ica_aes_key_len_192_t), sizeof(ica_aes_vector_t),
-		EVP_CIPH_ECB_MODE, sizeof(ICA_AES_192_CTX),
-		ibmca_init_key, ibmca_aes_192_cipher, ibmca_cipher_cleanup,
-		EVP_CIPHER_set_asn1_iv, EVP_CIPHER_get_asn1_iv, NULL)
-DECLARE_AES_EVP(192, cbc, sizeof(ica_aes_vector_t),
-		sizeof(ica_aes_key_len_192_t), sizeof(ica_aes_vector_t),
-		EVP_CIPH_CBC_MODE, sizeof(ICA_AES_192_CTX),
-		ibmca_init_key, ibmca_aes_192_cipher, ibmca_cipher_cleanup,
-		EVP_CIPHER_set_asn1_iv, EVP_CIPHER_get_asn1_iv, NULL)
-DECLARE_AES_EVP(192, ofb, 1, sizeof(ica_aes_key_len_192_t),
-		sizeof(ica_aes_vector_t), EVP_CIPH_OFB_MODE,
-		sizeof(ICA_AES_192_CTX), ibmca_init_key,
-		ibmca_aes_192_cipher, ibmca_cipher_cleanup,
-		EVP_CIPHER_set_asn1_iv, EVP_CIPHER_get_asn1_iv, NULL)
-DECLARE_AES_EVP(192, cfb, 1, sizeof(ica_aes_key_len_192_t),
-		sizeof(ica_aes_vector_t), EVP_CIPH_CFB_MODE,
-		sizeof(ICA_AES_192_CTX), ibmca_init_key,
-		ibmca_aes_192_cipher, ibmca_cipher_cleanup,
-		EVP_CIPHER_set_asn1_iv, EVP_CIPHER_get_asn1_iv, NULL)
-#ifndef OPENSSL_NO_AES_GCM
-DECLARE_AES_EVP(192, gcm, 1, sizeof(ica_aes_key_len_192_t),
-		sizeof(ica_aes_vector_t) - sizeof(uint32_t),
-		EVP_CIPH_GCM_MODE | EVP_CIPH_FLAG_DEFAULT_ASN1
-		| EVP_CIPH_CUSTOM_IV | EVP_CIPH_FLAG_CUSTOM_CIPHER
-		| EVP_CIPH_ALWAYS_CALL_INIT | EVP_CIPH_CTRL_INIT
-		| EVP_CIPH_CUSTOM_COPY | EVP_CIPH_FLAG_AEAD_CIPHER,
-		sizeof(ICA_AES_GCM_CTX),
-		ibmca_aes_gcm_init_key, ibmca_aes_gcm_cipher, NULL, NULL,
-		NULL, ibmca_aes_gcm_ctrl)
-#endif
-
-DECLARE_AES_EVP(256, ecb, sizeof(ica_aes_vector_t),
-		sizeof(ica_aes_key_len_256_t), sizeof(ica_aes_vector_t),
-		EVP_CIPH_ECB_MODE, sizeof(ICA_AES_GCM_CTX),
-		ibmca_init_key, ibmca_aes_256_cipher, ibmca_cipher_cleanup,
-		EVP_CIPHER_set_asn1_iv, EVP_CIPHER_get_asn1_iv, NULL)
-DECLARE_AES_EVP(256, cbc, sizeof(ica_aes_vector_t),
-		sizeof(ica_aes_key_len_256_t), sizeof(ica_aes_vector_t),
-		EVP_CIPH_CBC_MODE, sizeof(ICA_AES_GCM_CTX),
-		ibmca_init_key, ibmca_aes_256_cipher, ibmca_cipher_cleanup,
-		EVP_CIPHER_set_asn1_iv, EVP_CIPHER_get_asn1_iv, NULL)
-DECLARE_AES_EVP(256, ofb, 1, sizeof(ica_aes_key_len_256_t),
-		sizeof(ica_aes_vector_t), EVP_CIPH_OFB_MODE,
-		sizeof(ICA_AES_GCM_CTX), ibmca_init_key,
-		ibmca_aes_256_cipher, ibmca_cipher_cleanup,
-		EVP_CIPHER_set_asn1_iv, EVP_CIPHER_get_asn1_iv, NULL)
-DECLARE_AES_EVP(256, cfb, 1, sizeof(ica_aes_key_len_256_t),
-		sizeof(ica_aes_vector_t), EVP_CIPH_CFB_MODE,
-		sizeof(ICA_AES_GCM_CTX), ibmca_init_key,
-		ibmca_aes_256_cipher, ibmca_cipher_cleanup,
-		EVP_CIPHER_set_asn1_iv, EVP_CIPHER_get_asn1_iv, NULL)
-#ifndef OPENSSL_NO_AES_GCM
-DECLARE_AES_EVP(256, gcm, 1, sizeof(ica_aes_key_len_256_t),
-		sizeof(ica_aes_vector_t) - sizeof(uint32_t),
-		EVP_CIPH_GCM_MODE | EVP_CIPH_FLAG_DEFAULT_ASN1
-		| EVP_CIPH_CUSTOM_IV | EVP_CIPH_FLAG_CUSTOM_CIPHER
-		| EVP_CIPH_ALWAYS_CALL_INIT | EVP_CIPH_CTRL_INIT
-		| EVP_CIPH_CUSTOM_COPY | EVP_CIPH_FLAG_AEAD_CIPHER,
-		sizeof(ICA_AES_GCM_CTX),
-		ibmca_aes_gcm_init_key, ibmca_aes_gcm_cipher, NULL, NULL,
-		NULL, ibmca_aes_gcm_ctrl)
-#endif
-
-#ifdef OLDER_OPENSSL
-#ifndef OPENSSL_NO_SHA1
-static const EVP_MD ibmca_sha1 = {
-	NID_sha1,
-	NID_sha1WithRSAEncryption,
-	SHA_HASH_LENGTH,
-	EVP_MD_FLAG_PKEY_METHOD_SIGNATURE|EVP_MD_FLAG_FIPS,
-	ibmca_sha1_init,
-	ibmca_sha1_update,
-	ibmca_sha1_final,
-	NULL,
-	ibmca_sha1_cleanup,
-	EVP_PKEY_RSA_method,
-	SHA_BLOCK_SIZE,
-	sizeof(EVP_MD *) + sizeof(struct ibmca_sha1_ctx)
-};
-#endif
-
-#ifndef OPENSSL_NO_SHA256
-static const EVP_MD ibmca_sha256 = {
-	NID_sha256,
-	NID_sha256WithRSAEncryption,
-	SHA256_HASH_LENGTH,
-	EVP_MD_FLAG_PKEY_METHOD_SIGNATURE|EVP_MD_FLAG_FIPS,
-	ibmca_sha256_init,
-	ibmca_sha256_update,
-	ibmca_sha256_final,
-	NULL,
-	ibmca_sha256_cleanup,
-	EVP_PKEY_RSA_method,
-	SHA256_BLOCK_SIZE,
-	sizeof(EVP_MD *) + sizeof(struct ibmca_sha256_ctx)
-};
-#endif
-
-#ifndef OPENSSL_NO_SHA512
-static const EVP_MD ibmca_sha512 = {
-	NID_sha512,
-	NID_sha512WithRSAEncryption,
-	SHA512_HASH_LENGTH,
-	EVP_MD_FLAG_PKEY_METHOD_SIGNATURE|EVP_MD_FLAG_FIPS,
-	ibmca_sha512_init,
-	ibmca_sha512_update,
-	ibmca_sha512_final,
-	NULL,
-	ibmca_sha512_cleanup,
-	EVP_PKEY_RSA_method,
-	SHA512_BLOCK_SIZE,
-	sizeof(EVP_MD *) + sizeof(struct ibmca_sha512_ctx)
-};
-#endif
-
-#else
-
-#define DECLARE_SHA_EVP(sha,len)								\
-static EVP_MD *sha##_md = NULL;									\
-static const EVP_MD *ibmca_##sha(void)								\
-{												\
-	if (sha##_md == NULL) {									\
-		EVP_MD *md;									\
-		if (( md = EVP_MD_meth_new(NID_##sha,						\
-					   NID_##sha##WithRSAEncryption)) == NULL	  	\
-		   || !EVP_MD_meth_set_result_size(md, len##_HASH_LENGTH)			\
-		   || !EVP_MD_meth_set_input_blocksize(md, len##_BLOCK_SIZE)			\
-		   || !EVP_MD_meth_set_app_datasize(md, sizeof(EVP_MD *) + 			\
-							   sizeof(struct ibmca_##sha##_ctx))	\
-		   || !EVP_MD_meth_set_flags(md, EVP_MD_FLAG_FIPS)					\
-		   || !EVP_MD_meth_set_init(md, ibmca_##sha##_init)				\
-		   || !EVP_MD_meth_set_update(md, ibmca_##sha##_update)				\
-		   || !EVP_MD_meth_set_final(md, ibmca_##sha##_final)			 	\
-		   || !EVP_MD_meth_set_cleanup(md, ibmca_##sha##_cleanup)) {			\
-			EVP_MD_meth_free(md);					        	\
-			md = NULL;                           					\
-		}										\
-		sha##_md = md;									\
-	}											\
-	return sha##_md;									\
-}												\
-												\
-static void ibmca_##sha##_destroy(void)								\
-{												\
-	EVP_MD_meth_free(sha##_md);								\
-	sha##_md = NULL;									\
-}
-
-DECLARE_SHA_EVP(sha1, SHA)
-DECLARE_SHA_EVP(sha256, SHA256)
-DECLARE_SHA_EVP(sha512, SHA512)
-#endif
-
-/* Constants used when creating the ENGINE */
-static const char *engine_ibmca_id = "ibmca";
-static const char *engine_ibmca_name = "Ibmca hardware engine support";
-
-
-inline static int set_RSA_prop(ENGINE *e)
-{
-	static int rsa_enabled = 0;
-#ifndef OPENSSL_NO_RSA
-	const RSA_METHOD *meth1;
-#ifndef OLDER_OPENSSL
-	ibmca_rsa = RSA_meth_new("Ibmca RSA method", 0);
-#endif
-#endif
-#ifndef OPENSSL_NO_DSA
-	const DSA_METHOD *meth2;
-#ifndef OLDER_OPENSSL
-	ibmca_dsa = DSA_meth_new("Ibmca DSA method", 0);
-#endif
-#endif
-#ifndef OPENSSL_NO_DH
-	const DH_METHOD *meth3;
-#ifndef OLDER_OPENSSL
-	ibmca_dh = DH_meth_new("Ibmca DH method", 0);
-#endif
-#endif
-
-	if(rsa_enabled){
-		return 1;
-	}
-        if(
-#ifndef OPENSSL_NO_RSA
-#ifdef OLDER_OPENSSL
-	   !ENGINE_set_RSA(e, &ibmca_rsa) ||
-#else
-	   !ENGINE_set_RSA(e, ibmca_rsa) ||
-#endif
-#endif
-#ifndef OPENSSL_NO_DSA
-#ifdef OLDER_OPENSSL
-	   !ENGINE_set_DSA(e, &ibmca_dsa) ||
-#else
-	   !ENGINE_set_DSA(e, ibmca_dsa) ||
-#endif
-#endif
-#ifndef OPENSSL_NO_DH
-#ifdef OLDER_OPENSSL
-	   !ENGINE_set_DH(e, &ibmca_dh)
-#else
-	   !ENGINE_set_DH(e, ibmca_dh)
-#endif
-	  )
-#endif
-		return 0;
-#ifndef OPENSSL_NO_RSA
-        /* We know that the "PKCS1_SSLeay()" functions hook properly
-         * to the ibmca-specific mod_exp and mod_exp_crt so we use
-         * those functions. NB: We don't use ENGINE_openssl() or
-         * anything "more generic" because something like the RSAref
-         * code may not hook properly, and if you own one of these here
-         * cards then you have the right to do RSA operations on it
-         * anyway! */
-#ifdef OLDER_OPENSSL
-        meth1 = RSA_PKCS1_SSLeay();
-        ibmca_rsa.rsa_pub_enc = meth1->rsa_pub_enc;
-        ibmca_rsa.rsa_pub_dec = meth1->rsa_pub_dec;
-        ibmca_rsa.rsa_priv_enc = meth1->rsa_priv_enc;
-        ibmca_rsa.rsa_priv_dec = meth1->rsa_priv_dec;
-#else
-	meth1 = RSA_PKCS1_OpenSSL();
-	if (   !RSA_meth_set_pub_enc(ibmca_rsa, RSA_meth_get_pub_enc(meth1))
-	    || !RSA_meth_set_pub_dec(ibmca_rsa, RSA_meth_get_pub_dec(meth1))
-	    || !RSA_meth_set_priv_enc(ibmca_rsa, RSA_meth_get_priv_enc(meth1))
-	    || !RSA_meth_set_priv_dec(ibmca_rsa, RSA_meth_get_priv_dec(meth1))
-	    || !RSA_meth_set_mod_exp(ibmca_rsa, ibmca_rsa_mod_exp)
-	    || !RSA_meth_set_bn_mod_exp(ibmca_rsa, ibmca_mod_exp_mont)
-	    || !RSA_meth_set_init(ibmca_rsa, ibmca_rsa_init) )
-		return 0;
-#endif
-#endif
-#ifndef OPENSSL_NO_DSA
-	meth2 = DSA_OpenSSL();
-#ifdef OLDER_OPENSSL
-        ibmca_dsa.dsa_do_sign = meth2->dsa_do_sign;
-        ibmca_dsa.dsa_sign_setup = meth2->dsa_sign_setup;
-        ibmca_dsa.dsa_do_verify = meth2->dsa_do_verify;
-#else
-	if (   !DSA_meth_set_sign(ibmca_dsa, DSA_meth_get_sign(meth2))
-	    || !DSA_meth_set_verify(ibmca_dsa, DSA_meth_get_verify(meth2))
-	    || !DSA_meth_set_mod_exp(ibmca_dsa, ibmca_dsa_mod_exp)
-	    || !DSA_meth_set_bn_mod_exp(ibmca_dsa, ibmca_mod_exp_dsa) )
-		return 0;
-#endif
-#endif
-#ifndef OPENSSL_NO_DH
-        /* Much the same for Diffie-Hellman */
-        meth3 = DH_OpenSSL();
-#ifdef OLDER_OPENSSL
-        ibmca_dh.generate_key = meth3->generate_key;
-        ibmca_dh.compute_key = meth3->compute_key;
-#else
-	if (   !DH_meth_set_generate_key(ibmca_dh, DH_meth_get_generate_key(meth3))
-	    || !DH_meth_set_compute_key(ibmca_dh, DH_meth_get_compute_key(meth3))
-	    || !DH_meth_set_bn_mod_exp(ibmca_dh, ibmca_mod_exp_dh) )
-		return 0;
-#endif
-#endif
-	rsa_enabled = 1;
-	return 1;
-}
-
-
-/*
- * dig_nid_cnt and ciph_nid_cnt count the number of enabled crypt mechanims.
- * dig_nid_cnt and ciph_nid_cnt needs to be pointer, because only set_engine_prop
- * knows about how much digest or cipher will be set per call. To count the number of
- * cipher and digest outside of the function is not feasible
- */
-inline static int set_engine_prop(ENGINE *e, int algo_id, int *dig_nid_cnt, int *ciph_nid_cnt)
-{
-        switch(algo_id) {
-                case P_RNG:
-                        if(!ENGINE_set_RAND(e, &ibmca_rand))
-                                return 0;
-                        break;
-		/*
-		 * RSA will be enabled if one of this is set. OpenSSL does not distinguish
-		 * between RSA_ME and RSA_CRT. It is not the task of ibmca to route one ME
-		 * call to CRT or vice versa.
-		 */
-                case RSA_ME:
-                case RSA_CRT:
-                        if(!set_RSA_prop(e))
-                                return 0;
-			break;
-#ifndef OPENSSL_NO_SHA1
-		case SHA1:
-			ibmca_digest_lists.nids[*dig_nid_cnt] = NID_sha1;
-#ifdef OLDER_OPENSSL
-			ibmca_digest_lists.crypto_meths[(*dig_nid_cnt)++]=  &ibmca_sha1;
-#else
-			ibmca_digest_lists.crypto_meths[(*dig_nid_cnt)++]=  ibmca_sha1();
-#endif
-			break;
-#endif
-#ifndef OPENSSL_NO_SHA256
-                case SHA256:
-                        ibmca_digest_lists.nids[*dig_nid_cnt] = NID_sha256;
-#ifdef OLDER_OPENSSL
-			ibmca_digest_lists.crypto_meths[(*dig_nid_cnt)++] =  &ibmca_sha256;
-#else
-			ibmca_digest_lists.crypto_meths[(*dig_nid_cnt)++] =  ibmca_sha256();
-#endif
-			break;
-#endif
-#ifndef OPENSSL_NO_SHA512
-                case SHA512:
-                        ibmca_digest_lists.nids[*dig_nid_cnt] = NID_sha512;
-#ifdef OLDER_OPENSSL
-			ibmca_digest_lists.crypto_meths[(*dig_nid_cnt)++] =  &ibmca_sha512;
-#else
-			ibmca_digest_lists.crypto_meths[(*dig_nid_cnt)++] =  ibmca_sha512();
-#endif
-			break;
-#endif
-                case DES_ECB:
-			ibmca_cipher_lists.nids[*ciph_nid_cnt]  = NID_des_ecb;
-#ifdef OLDER_OPENSSL
-			ibmca_cipher_lists.crypto_meths[(*ciph_nid_cnt)++] = &ibmca_des_ecb;
-#else
-			ibmca_cipher_lists.crypto_meths[(*ciph_nid_cnt)++] = ibmca_des_ecb();
-#endif
-			break;
-		case DES_CBC:
-			ibmca_cipher_lists.nids[*ciph_nid_cnt] = NID_des_cbc;
-#ifdef OLDER_OPENSSL
-			ibmca_cipher_lists.crypto_meths[(*ciph_nid_cnt)++] = &ibmca_des_cbc;
-#else
-			ibmca_cipher_lists.crypto_meths[(*ciph_nid_cnt)++] = ibmca_des_cbc();
-#endif
-			break;
-		case DES_OFB:
-			ibmca_cipher_lists.nids[*ciph_nid_cnt] = NID_des_ofb;
-#ifdef OLDER_OPENSSL
-			ibmca_cipher_lists.crypto_meths[(*ciph_nid_cnt)++] = &ibmca_des_ofb;
-#else
-			ibmca_cipher_lists.crypto_meths[(*ciph_nid_cnt)++] = ibmca_des_ofb();
-#endif
-			break;
-		case DES_CFB:
-			ibmca_cipher_lists.nids[*ciph_nid_cnt] = NID_des_cfb;
-#ifdef OLDER_OPENSSL
-			ibmca_cipher_lists.crypto_meths[(*ciph_nid_cnt)++] = &ibmca_des_cfb;
-#else
-			ibmca_cipher_lists.crypto_meths[(*ciph_nid_cnt)++] = ibmca_des_cfb();
-#endif
-			break;
-		case DES3_ECB:
-			ibmca_cipher_lists.nids[*ciph_nid_cnt] = NID_des_ede3_ecb;
-#ifdef OLDER_OPENSSL
-			ibmca_cipher_lists.crypto_meths[(*ciph_nid_cnt)++] = &ibmca_tdes_ecb;
-#else
-			ibmca_cipher_lists.crypto_meths[(*ciph_nid_cnt)++] = ibmca_tdes_ecb();
-#endif
-			break;
-		case DES3_CBC:
-			ibmca_cipher_lists.nids[*ciph_nid_cnt] = NID_des_ede3_cbc;
-#ifdef OLDER_OPENSSL
-			ibmca_cipher_lists.crypto_meths[(*ciph_nid_cnt)++] = &ibmca_tdes_cbc;
-#else
-			ibmca_cipher_lists.crypto_meths[(*ciph_nid_cnt)++] = ibmca_tdes_cbc();
-#endif
-			break;
-		case DES3_OFB:
-			ibmca_cipher_lists.nids[*ciph_nid_cnt] = NID_des_ede3_ofb;
-#ifdef OLDER_OPENSSL
-			ibmca_cipher_lists.crypto_meths[(*ciph_nid_cnt)++] = &ibmca_tdes_ofb;
-#else
-			ibmca_cipher_lists.crypto_meths[(*ciph_nid_cnt)++] = ibmca_tdes_ofb();
-#endif
-			break;
-		case DES3_CFB:
-			ibmca_cipher_lists.nids[*ciph_nid_cnt] = NID_des_ede3_cfb;
-#ifdef OLDER_OPENSSL
-			ibmca_cipher_lists.crypto_meths[(*ciph_nid_cnt)++] = &ibmca_tdes_cfb;
-#else
-			ibmca_cipher_lists.crypto_meths[(*ciph_nid_cnt)++] = ibmca_tdes_cfb();
-#endif
-			break;
-		case AES_ECB:
-			ibmca_cipher_lists.nids[*ciph_nid_cnt] = NID_aes_128_ecb;
-			ibmca_cipher_lists.crypto_meths[(*ciph_nid_cnt)++] = ibmca_aes_128_ecb();
-			ibmca_cipher_lists.nids[*ciph_nid_cnt] = NID_aes_192_ecb;
-			ibmca_cipher_lists.crypto_meths[(*ciph_nid_cnt)++] = ibmca_aes_192_ecb();
-			ibmca_cipher_lists.nids[*ciph_nid_cnt] = NID_aes_256_ecb;
-			ibmca_cipher_lists.crypto_meths[(*ciph_nid_cnt)++] = ibmca_aes_256_ecb();
-			break;
-		case AES_CBC:
-			ibmca_cipher_lists.nids[*ciph_nid_cnt] = NID_aes_128_cbc;
-			ibmca_cipher_lists.crypto_meths[(*ciph_nid_cnt)++] = ibmca_aes_128_cbc();
-			ibmca_cipher_lists.nids[*ciph_nid_cnt] = NID_aes_192_cbc;
-			ibmca_cipher_lists.crypto_meths[(*ciph_nid_cnt)++] = ibmca_aes_192_cbc();
-			ibmca_cipher_lists.nids[*ciph_nid_cnt] = NID_aes_256_cbc;
-			ibmca_cipher_lists.crypto_meths[(*ciph_nid_cnt)++] = ibmca_aes_256_cbc();
-			break;
-		case AES_OFB:
-			ibmca_cipher_lists.nids[*ciph_nid_cnt] = NID_aes_128_ofb;
-			ibmca_cipher_lists.crypto_meths[(*ciph_nid_cnt)++] = ibmca_aes_128_ofb();
-			ibmca_cipher_lists.nids[*ciph_nid_cnt] = NID_aes_192_ofb;
-			ibmca_cipher_lists.crypto_meths[(*ciph_nid_cnt)++] = ibmca_aes_192_ofb();
-			ibmca_cipher_lists.nids[*ciph_nid_cnt] = NID_aes_256_ofb;
-			ibmca_cipher_lists.crypto_meths[(*ciph_nid_cnt)++] = ibmca_aes_256_ofb();
-			break;
-		case AES_CFB:
-			ibmca_cipher_lists.nids[*ciph_nid_cnt] = NID_aes_128_cfb;
-			ibmca_cipher_lists.crypto_meths[(*ciph_nid_cnt)++] = ibmca_aes_128_cfb();
-			ibmca_cipher_lists.nids[*ciph_nid_cnt] = NID_aes_192_cfb;
-			ibmca_cipher_lists.crypto_meths[(*ciph_nid_cnt)++] = ibmca_aes_192_cfb();
-			ibmca_cipher_lists.nids[*ciph_nid_cnt] = NID_aes_256_cfb;
-			ibmca_cipher_lists.crypto_meths[(*ciph_nid_cnt)++] = ibmca_aes_256_cfb();
-			break;
-#ifndef OPENSSL_NO_AES_GCM
-		case AES_GCM_KMA:
-			ibmca_cipher_lists.nids[*ciph_nid_cnt] = NID_aes_128_gcm;
-			ibmca_cipher_lists.crypto_meths[(*ciph_nid_cnt)++] = ibmca_aes_128_gcm();
-			ibmca_cipher_lists.nids[*ciph_nid_cnt] = NID_aes_192_gcm;
-			ibmca_cipher_lists.crypto_meths[(*ciph_nid_cnt)++] = ibmca_aes_192_gcm();
-			ibmca_cipher_lists.nids[*ciph_nid_cnt] = NID_aes_256_gcm;
-			ibmca_cipher_lists.crypto_meths[(*ciph_nid_cnt)++] = ibmca_aes_256_gcm();
-			break;
-#endif
-		default:
-			break;	/* do nothing */
-	}
-
-	size_cipher_list = *ciph_nid_cnt;
-	size_digest_list = *dig_nid_cnt;
-	return 1;
-}
-
-int is_crypto_card_loaded()
-{
-	DIR* sysDir;
-	FILE *file;
-	char dev[PATH_MAX] = AP_PATH;
-	struct dirent *direntp;
-	char *type = NULL;
-	size_t size;
-	char c;
-
-	if ((sysDir = opendir(dev)) == NULL )
-		return 0;
-
-	while((direntp = readdir(sysDir)) != NULL){
-		if(strstr(direntp->d_name, "card") != 0){
-			snprintf(dev, PATH_MAX, "%s/%s/type", AP_PATH,
-				direntp->d_name);
-
-			if ((file = fopen(dev, "r")) == NULL){
-	                        closedir(sysDir);
-                                return 0;
-			}
-
-			if (getline(&type, &size, file) == -1){
-				fclose(file);
-				closedir(sysDir);
-				return 0;
-			}
-
-			/* ignore \n
-			 * looking for CEX??A and CEX??C
-			 * Skip type CEX??P cards
-			 */
-			if (type[strlen(type)-2] == 'P'){
-				free(type);
-				type = NULL;
-				fclose(file);
-				continue;
-			}
-			free(type);
-			type = NULL;
-			fclose(file);
-
-			snprintf(dev, PATH_MAX, "%s/%s/online", AP_PATH,
-				direntp->d_name);
-			if ((file = fopen(dev, "r")) == NULL){
-				closedir(sysDir);
-				return 0;
-			}
-			if((c = fgetc(file)) == '1'){
-				fclose(file);
-				return 1;
-			}
-			fclose(file);
-		}
-	}
-	closedir(sysDir);
-	return 0;
-}
-
-
-typedef unsigned int (*ica_get_functionlist_t)(libica_func_list_element *, unsigned int *);
-ica_get_functionlist_t          p_ica_get_functionlist;
-
-static int set_supported_meths(ENGINE *e)
-{
-        int i, j;
-        unsigned int mech_len;
-        libica_func_list_element *pmech_list = NULL;
-	int rc = 0;
-        int dig_nid_cnt = 0;
-        int ciph_nid_cnt = 0;
-	int card_loaded;
-
-        if (p_ica_get_functionlist(NULL, &mech_len))
-		return 0;
-
-	pmech_list = malloc(sizeof(libica_func_list_element)*mech_len);
-	if (pmech_list == NULL)
-		return 0;
-
-	if (p_ica_get_functionlist(pmech_list, &mech_len))
-		goto out;
-
-	card_loaded = is_crypto_card_loaded();
-
-	for (i = 0; i < mech_len; i++) {
-	        for (j = 0; ibmca_crypto_algos[j]; j++){
-			/* Disable crypto algorithm if it
-			 * is not supported in hardware
-			 */
-			if (!(pmech_list[i].flags &
-			      (ICA_FLAG_SHW | ICA_FLAG_DHW)))
-				break;
-
-			/* If no crypto card is available,
-			 * disable crypto algos that can
-			 * only operate on HW on card
-			 */
-			if ((pmech_list[i].flags & ICA_FLAG_DHW)
-			    && !card_loaded)
-				break;
-
-	                if(ibmca_crypto_algos[j]
-			   == pmech_list[i].mech_mode_id){
-	                        /* Set NID, ibmca struct and the
-				 * info for the ENGINE struct
-				 */
-	                        if(!set_engine_prop(e, ibmca_crypto_algos[j],
-	                                            &dig_nid_cnt,
-	                                            &ciph_nid_cnt)){
-					goto out;
-				}
-	               }
-	       }
-	}
-
-        if(dig_nid_cnt > 0) {
-                if(!ENGINE_set_digests(e, ibmca_engine_digests))
-			goto out;
-	}
-        if(ciph_nid_cnt > 0) {
-                if(!ENGINE_set_ciphers(e, ibmca_engine_ciphers))
-			goto out;
-	}
-	rc = 1;
-out:
-        free(pmech_list);
-	return rc;
-}
-
-
-/* This internal function is used by ENGINE_ibmca() and possibly by the
- * "dynamic" ENGINE support too */
-static int bind_helper(ENGINE * e)
-{
-	if (!ENGINE_set_id(e, engine_ibmca_id) ||
-	    !ENGINE_set_name(e, engine_ibmca_name) ||
-	    !ENGINE_set_destroy_function(e, ibmca_destroy) ||
-	    !ENGINE_set_init_function(e, ibmca_init) ||
-	    !ENGINE_set_finish_function(e, ibmca_finish) ||
-	    !ENGINE_set_ctrl_function(e, ibmca_ctrl) ||
-	    !ENGINE_set_cmd_defns(e, ibmca_cmd_defns))
-		return 0;
-
-	/* Ensure the ibmca error handling is set up */
-	ERR_load_IBMCA_strings();
-	/* initialize the engine implizit */
-	ibmca_init(e);
-	return 1;
-}
-
-static ENGINE *engine_ibmca(void)
-{
-	ENGINE *ret = ENGINE_new();
-	if (!ret)
-		return NULL;
-	if (!bind_helper(ret)) {
-		ENGINE_free(ret);
-		return NULL;
-	}
-	return ret;
-}
-
-void ENGINE_load_ibmca(void)
-{
-	/* Copied from eng_[openssl|dyn].c */
-	ENGINE *toadd = engine_ibmca();
-	if (!toadd)
-		return;
-	ENGINE_add(toadd);
-	ENGINE_free(toadd);
-	ERR_clear_error();
-}
-
-/* Destructor (complements the "ENGINE_ibmca()" constructor) */
-static int ibmca_destroy(ENGINE * e)
-{
-	/* Unload the ibmca error strings so any error state including our
-	 * functs or reasons won't lead to a segfault (they simply get displayed
-	 * without corresponding string data because none will be found). */
-#ifndef OLDER_OPENSSL
-	ibmca_des_ecb_destroy();
-	ibmca_des_cbc_destroy();
-	ibmca_des_ofb_destroy();
-	ibmca_des_cfb_destroy();
-	ibmca_tdes_ecb_destroy();
-	ibmca_tdes_cbc_destroy();
-	ibmca_tdes_ofb_destroy();
-	ibmca_tdes_cfb_destroy();
-
-	ibmca_aes_128_ecb_destroy();
-	ibmca_aes_128_cbc_destroy();
-	ibmca_aes_128_ofb_destroy();
-	ibmca_aes_128_cfb_destroy();
-	ibmca_aes_192_ecb_destroy();
-	ibmca_aes_192_cbc_destroy();
-	ibmca_aes_192_ofb_destroy();
-	ibmca_aes_192_cfb_destroy();
-	ibmca_aes_256_ecb_destroy();
-	ibmca_aes_256_cbc_destroy();
-	ibmca_aes_256_ofb_destroy();
-	ibmca_aes_256_cfb_destroy();
-
-# ifndef OPENSSL_NO_AES_GCM
-	ibmca_aes_128_gcm_destroy();
-	ibmca_aes_192_gcm_destroy();
-	ibmca_aes_256_gcm_destroy();
-# endif
-
-	ibmca_sha1_destroy();
-	ibmca_sha256_destroy();
-	ibmca_sha512_destroy();
-#endif
-	ERR_unload_IBMCA_strings();
-	return 1;
-}
-
-/* This is a process-global DSO handle used for loading and unloading
- * the Ibmca library. NB: This is only set (or unset) during an
- * init() or finish() call (reference counts permitting) and they're
- * operating with global locks, so this should be thread-safe
- * implicitly. */
-
-void *ibmca_dso = NULL;
-
-/* These are the function pointers that are (un)set when the library has
- * successfully (un)loaded. */
-
-typedef unsigned int (*ica_open_adapter_t)(ica_adapter_handle_t *);
-typedef unsigned int (*ica_close_adapter_t)(ica_adapter_handle_t);
-typedef unsigned int (*ica_rsa_mod_expo_t)(ica_adapter_handle_t, unsigned char *,
-			ica_rsa_key_mod_expo_t *, unsigned char *);
-typedef unsigned int (*ica_random_number_generate_t)(unsigned int, unsigned char *);
-typedef unsigned int (*ica_rsa_crt_t)(ica_adapter_handle_t, unsigned char *,
-			ica_rsa_key_crt_t *, unsigned char *);
-typedef unsigned int (*ica_sha1_t)(unsigned int, unsigned int, unsigned char *, sha_context_t *,
-			unsigned char *);
-typedef unsigned int (*ica_des_encrypt_t)(unsigned int, unsigned int, unsigned char *,
-			ica_des_vector_t *, ica_des_key_single_t *, unsigned char *);
-typedef unsigned int (*ica_des_decrypt_t)(unsigned int, unsigned int, unsigned char *,
-			ica_des_vector_t *, ica_des_key_single_t *, unsigned char *);
-typedef unsigned int (*ica_3des_encrypt_t)(unsigned int, unsigned int, unsigned char *,
-			ica_des_vector_t *, ica_des_key_triple_t *, unsigned char *);
-typedef unsigned int (*ica_3des_decrypt_t)(unsigned int, unsigned int, unsigned char *,
-			ica_des_vector_t *, ica_des_key_triple_t *, unsigned char *);
-typedef unsigned int (*ica_aes_encrypt_t)(unsigned int, unsigned int, unsigned char *,
-			ica_aes_vector_t *, unsigned int, unsigned char *, unsigned char *);
-typedef unsigned int (*ica_aes_decrypt_t)(unsigned int, unsigned int, unsigned char *,
-			ica_aes_vector_t *, unsigned int, unsigned char *, unsigned char *);
-typedef unsigned int (*ica_sha256_t)(unsigned int, unsigned int, unsigned char *,
-			sha256_context_t *, unsigned char *);
-typedef unsigned int (*ica_des_ofb_t)(const unsigned char *in_data, unsigned char *out_data,
-			 unsigned long data_length, const unsigned char *key,
-			 unsigned char *iv, unsigned int direction);
-typedef unsigned int (*ica_sha512_t)(unsigned int, unsigned int,
-				     unsigned char *, sha512_context_t *,
-				     unsigned char *);
-typedef unsigned int (*ica_des_cfb_t)(const unsigned char *in_data, unsigned char *out_data,
-			 unsigned long data_length, const unsigned char *key,
-			 unsigned char *iv, unsigned int lcfb,
-			 unsigned int direction);
-typedef unsigned int (*ica_3des_cfb_t)(const unsigned char *, unsigned char *,
-			unsigned long, const unsigned char *, unsigned char *,
-			unsigned int, unsigned int);
-typedef unsigned int (*ica_3des_ofb_t)(const unsigned char *in_data, unsigned char *out_data,
-			  unsigned long data_length, const unsigned char *key,
-			  unsigned char *iv, unsigned int direction);
-typedef unsigned int (*ica_aes_ofb_t)(const unsigned char *in_data, unsigned char *out_data,
-			 unsigned long data_length, const unsigned char *key,
-			 unsigned int key_length, unsigned char *iv,
-			 unsigned int direction);
-typedef unsigned int (*ica_aes_cfb_t)(const unsigned char *in_data, unsigned char *out_data,
-			 unsigned long data_length, const unsigned char *key,
-			 unsigned int key_length, unsigned char *iv, unsigned int lcfb,
-			 unsigned int direction);
-
-typedef unsigned int (*ica_aes_gcm_initialize_t)(const unsigned char *iv,
-						 unsigned int iv_length,
-						 unsigned char *key,
-						 unsigned int key_length,
-						 unsigned char *icb,
-						 unsigned char *ucb,
-						 unsigned char *subkey,
-						 unsigned int direction);
-typedef unsigned int (*ica_aes_gcm_intermediate_t)(unsigned char *plaintext,
-						   unsigned long
-						       plaintext_length,
-						   unsigned char *ciphertext,
-						   unsigned char *ucb,
-						   unsigned char *aad,
-						   unsigned long aad_length,
-						   unsigned char *tag,
-						   unsigned int tag_length,
-						   unsigned char *key,
-						   unsigned int key_length,
-						   unsigned char *subkey,
-						   unsigned int direction);
-typedef unsigned int (*ica_aes_gcm_last_t)(unsigned char *icb,
-					   unsigned long aad_length,
-					   unsigned long ciph_length,
-					   unsigned char *tag,
-					   unsigned char *final_tag,
-					   unsigned int final_tag_length,
-					   unsigned char *key,
-					   unsigned int key_length,
-					   unsigned char *subkey,
-					   unsigned int direction);
-
-/* entry points into libica, filled out at DSO load time */
-ica_open_adapter_t		p_ica_open_adapter;
-ica_close_adapter_t		p_ica_close_adapter;
-ica_rsa_mod_expo_t		p_ica_rsa_mod_expo;
-ica_random_number_generate_t	p_ica_random_number_generate;
-ica_rsa_crt_t			p_ica_rsa_crt;
-ica_sha1_t			p_ica_sha1;
-ica_des_encrypt_t		p_ica_des_encrypt;
-ica_des_decrypt_t		p_ica_des_decrypt;
-ica_3des_encrypt_t		p_ica_3des_encrypt;
-ica_3des_decrypt_t		p_ica_3des_decrypt;
-ica_aes_encrypt_t		p_ica_aes_encrypt;
-ica_aes_decrypt_t		p_ica_aes_decrypt;
-ica_sha256_t			p_ica_sha256;
-ica_sha512_t			p_ica_sha512;
-ica_des_ofb_t			p_ica_des_ofb;
-ica_des_cfb_t			p_ica_des_cfb;
-ica_3des_cfb_t			p_ica_3des_cfb;
-ica_3des_ofb_t			p_ica_3des_ofb;
-ica_aes_ofb_t			p_ica_aes_ofb;
-ica_aes_cfb_t			p_ica_aes_cfb;
-#ifndef OPENSSL_NO_AES_GCM
-ica_aes_gcm_initialize_t	p_ica_aes_gcm_initialize;
-ica_aes_gcm_intermediate_t	p_ica_aes_gcm_intermediate;
-ica_aes_gcm_last_t		p_ica_aes_gcm_last;
-#endif
-
-/* utility function to obtain a context */
-static int get_context(ica_adapter_handle_t * p_handle)
-{
-	unsigned int status = 0;
-
-	status = p_ica_open_adapter(p_handle);
-	if (status != 0)
-		return 0;
-	return 1;
-}
-
-/* similarly to release one. */
-static void release_context(ica_adapter_handle_t i_handle)
-{
-	p_ica_close_adapter(i_handle);
-}
-
-/* initialisation functions. */
-#define BIND(dso, sym)	(p_##sym = (sym##_t)dlsym(dso, #sym))
-static int ibmca_init(ENGINE * e)
-{
-	static int init = 0;
-
-	if (init)	/* Engine already loaded. */
-		return 1;
-
-	/* Attempt to load libica.so. Needs to be
-	 * changed unfortunately because the Ibmca drivers don't have
-	 * standard library names that can be platform-translated well. */
-	/* TODO: Work out how to actually map to the names the Ibmca
-	 * drivers really use - for now a symbollic link needs to be
-	 * created on the host system from libica.so to ica.so on
-	 * unix variants. */
-
-	/* WJH XXX check name translation */
-
-	ibmca_dso = dlopen(LIBICA_SHARED_LIB, RTLD_NOW);
-	if (ibmca_dso == NULL) {
-		IBMCAerr(IBMCA_F_IBMCA_INIT, IBMCA_R_DSO_FAILURE);
-		goto err;
-	}
-
-	if (!BIND(ibmca_dso, ica_open_adapter)
-	    || !BIND(ibmca_dso, ica_close_adapter)
-	    || !BIND(ibmca_dso, ica_rsa_mod_expo)
-	    || !BIND(ibmca_dso, ica_random_number_generate)
-	    || !BIND(ibmca_dso, ica_rsa_crt)
-	    || !BIND(ibmca_dso, ica_sha1)
-	    || !BIND(ibmca_dso, ica_des_encrypt)
-	    || !BIND(ibmca_dso, ica_des_decrypt)
-	    || !BIND(ibmca_dso, ica_3des_encrypt)
-	    || !BIND(ibmca_dso, ica_3des_decrypt)
-	    || !BIND(ibmca_dso, ica_aes_encrypt)
-	    || !BIND(ibmca_dso, ica_aes_decrypt)
-	    || !BIND(ibmca_dso, ica_sha256)
-	    || !BIND(ibmca_dso, ica_sha512)
-	    || !BIND(ibmca_dso, ica_aes_ofb)
-	    || !BIND(ibmca_dso, ica_des_ofb)
-	    || !BIND(ibmca_dso, ica_3des_ofb)
-	    || !BIND(ibmca_dso, ica_aes_cfb)
-	    || !BIND(ibmca_dso, ica_des_cfb)
-	    || !BIND(ibmca_dso, ica_get_functionlist)
-	    || !BIND(ibmca_dso, ica_3des_cfb)
-#ifndef OPENSSL_NO_AES_GCM
-	    || !BIND(ibmca_dso, ica_aes_gcm_initialize)
-	    || !BIND(ibmca_dso, ica_aes_gcm_intermediate)
-	    || !BIND(ibmca_dso, ica_aes_gcm_last)
-#endif
-	   ) {
-		IBMCAerr(IBMCA_F_IBMCA_INIT, IBMCA_R_DSO_FAILURE);
-		goto err;
-	}
-
-        if(!set_supported_meths(e))
-                goto err;
-
-	if (!get_context(&ibmca_handle)) {
-		IBMCAerr(IBMCA_F_IBMCA_INIT, IBMCA_R_UNIT_FAILURE);
-		goto err;
-	}
-
-	init = 1;
-	return 1;
-err:
-	if (ibmca_dso) {
-		dlclose(ibmca_dso);
-		ibmca_dso = NULL;
-	}
-	p_ica_open_adapter = NULL;
-	p_ica_close_adapter = NULL;
-	p_ica_rsa_mod_expo = NULL;
-	p_ica_random_number_generate = NULL;
-	p_ica_rsa_crt = NULL;
-	p_ica_sha1 = NULL;
-	p_ica_des_encrypt = NULL;
-	p_ica_des_decrypt = NULL;
-	p_ica_3des_encrypt = NULL;
-	p_ica_3des_decrypt = NULL;
-	p_ica_aes_encrypt = NULL;
-	p_ica_aes_decrypt = NULL;
-	p_ica_sha256 = NULL;
-	p_ica_sha512 = NULL;
-	p_ica_aes_ofb = NULL;
-	p_ica_des_ofb = NULL;
-	p_ica_3des_ofb = NULL;
-	p_ica_aes_cfb = NULL;
-	p_ica_des_cfb = NULL;
-	p_ica_3des_cfb = NULL;
-#ifndef OPENSSL_NO_AES_GCM
-	p_ica_aes_gcm_initialize = NULL;
-	p_ica_aes_gcm_intermediate = NULL;
-	p_ica_aes_gcm_last = NULL;
-#endif
-	return 0;
-}
-
-static int ibmca_finish(ENGINE * e)
-{
-	if (ibmca_dso == NULL) {
-		IBMCAerr(IBMCA_F_IBMCA_FINISH, IBMCA_R_NOT_LOADED);
-		return 0;
-	}
-	release_context(ibmca_handle);
-	if (!dlclose(ibmca_dso)) {
-		IBMCAerr(IBMCA_F_IBMCA_FINISH, IBMCA_R_DSO_FAILURE);
-		return 0;
-	}
-	ibmca_dso = NULL;
-
-	return 1;
-}
-
-static int ibmca_ctrl(ENGINE * e, int cmd, long i, void *p, void (*f) ())
-{
-	int initialised = ((ibmca_dso == NULL) ? 0 : 1);
-	switch (cmd) {
-	case IBMCA_CMD_SO_PATH:
-		if (p == NULL) {
-			IBMCAerr(IBMCA_F_IBMCA_CTRL,
-				 ERR_R_PASSED_NULL_PARAMETER);
-			return 0;
-		}
-		if (initialised) {
-			IBMCAerr(IBMCA_F_IBMCA_CTRL,
-				 IBMCA_R_ALREADY_LOADED);
-			return 0;
-		}
-		LIBICA_NAME = (const char *) p;
-		return 1;
-	default:
-		break;
-	}
-	IBMCAerr(IBMCA_F_IBMCA_CTRL, IBMCA_R_CTRL_COMMAND_NOT_IMPLEMENTED);
-	return 0;
-}
-
-/*
- * ENGINE calls this to find out how to deal with
- * a particular NID in the ENGINE.
- */
-static int ibmca_engine_ciphers(ENGINE * e, const EVP_CIPHER ** cipher,
-				const int **nids, int nid)
-{
-        int i;
-	if (!cipher)
-		return (ibmca_usable_ciphers(nids));
-
-        *cipher = NULL;
-        for(i = 0; i < size_cipher_list;i++)
-                if(nid == ibmca_cipher_lists.nids[i]){
-                        *cipher = (EVP_CIPHER*) ibmca_cipher_lists.crypto_meths [i];
-                        break;
-		}
-        /* Check: how can *cipher be NULL? */
-	return (*cipher != NULL);
-}
-
-static int ibmca_usable_ciphers(const int **nids)
-{
-
-        if(nids)
-	        *nids = ibmca_cipher_lists.nids;
-	return size_cipher_list;
-}
-
-static int ibmca_init_key(EVP_CIPHER_CTX * ctx, const unsigned char *key,
-			  const unsigned char *iv, int enc)
-{
-#ifdef OLDER_OPENSSL
-	ICA_DES_CTX *pCtx = ctx->cipher_data;
-
-	memcpy(pCtx->key, key, ctx->cipher->key_len);
-#else
-	ICA_DES_CTX *pCtx = (ICA_DES_CTX *) EVP_CIPHER_CTX_get_cipher_data(ctx);
-
-	memcpy(pCtx->key, key, EVP_CIPHER_CTX_key_length(ctx));
-#endif
-
-	return 1;
-}				// end ibmca_init_key
-
-static int ibmca_des_cipher(EVP_CIPHER_CTX * ctx, unsigned char *out,
-			    const unsigned char *in, size_t inlen)
-{
-	int mode = 0;
-	int rv;
-	unsigned int len;
-#ifdef OLDER_OPENSSL
-	ICA_DES_CTX *pCtx = ctx->cipher_data;
-#else
-	ICA_DES_CTX *pCtx = (ICA_DES_CTX *) EVP_CIPHER_CTX_get_cipher_data(ctx);
-#endif
-
-	ica_des_vector_t pre_iv;
-
-	if (inlen > UINT32_MAX) {
-		IBMCAerr(IBMCA_F_IBMCA_DES_CIPHER, IBMCA_R_OUTLEN_TO_LARGE);
-		return 0;
-	}
-	len = inlen;
-
-	if (EVP_CIPHER_CTX_mode(ctx) == EVP_CIPH_ECB_MODE) {
-		mode = MODE_ECB;
-	} else if (EVP_CIPHER_CTX_mode(ctx) == EVP_CIPH_CBC_MODE) {
-		mode = MODE_CBC;
-	} else if ((EVP_CIPHER_CTX_mode(ctx) != EVP_CIPH_CFB_MODE) &&
-		   (EVP_CIPHER_CTX_mode(ctx) != EVP_CIPH_OFB_MODE)) {
-		IBMCAerr(IBMCA_F_IBMCA_DES_CIPHER,
-				IBMCA_R_CIPHER_MODE_NOT_SUPPORTED);
-		return 0;
-	}
-
-#ifdef OLDER_OPENSSL
-	if (ctx->encrypt) {
-#else
-	if (EVP_CIPHER_CTX_encrypting(ctx)) {
-#endif
-		if (EVP_CIPHER_CTX_mode(ctx) == EVP_CIPH_CFB_MODE) {
-			rv = p_ica_des_cfb(in, out, len, pCtx->key,
-#ifdef OLDER_OPENSSL
-					   ctx->iv,
-#else
-					   EVP_CIPHER_CTX_iv_noconst(ctx),
-#endif
-					   8, ICA_ENCRYPT);
-		} else if (EVP_CIPHER_CTX_mode(ctx) == EVP_CIPH_OFB_MODE) {
-			rv = p_ica_des_ofb(in, out, len, pCtx->key,
-#ifdef OLDER_OPENSSL
-					   ctx->iv,
-#else
-					   EVP_CIPHER_CTX_iv_noconst(ctx),
-#endif
-					   ICA_ENCRYPT);
-		} else {
-			rv = p_ica_des_encrypt(mode, len, (unsigned char *)in,
-#ifdef OLDER_OPENSSL
-				(ica_des_vector_t *) ctx->iv,
-#else
-				(ica_des_vector_t *) EVP_CIPHER_CTX_iv_noconst(ctx),
-#endif
-				(ica_des_key_single_t *) pCtx->key, out);
-		}
-
-		if (rv) {
-			IBMCAerr(IBMCA_F_IBMCA_DES_CIPHER,
-					IBMCA_R_REQUEST_FAILED);
-			return 0;
-		} else if (EVP_CIPHER_CTX_mode(ctx) != EVP_CIPH_OFB_MODE) {
-#ifdef OLDER_OPENSSL
-			memcpy(ctx->iv,
-#else
-			memcpy(EVP_CIPHER_CTX_iv_noconst(ctx),
-#endif
-			       out + len - EVP_CIPHER_CTX_iv_length(ctx),
-			       EVP_CIPHER_CTX_iv_length(ctx));
-		}
-	} else {
-		if (EVP_CIPHER_CTX_mode(ctx) == EVP_CIPH_CFB_MODE) {
-			/* Protect against decrypt in place */
-			/* FIXME: Shouldn't we use EVP_CIPHER_CTX_iv_length() instead? */
-			memcpy(pre_iv, in + len - sizeof(pre_iv), sizeof(pre_iv));
-
-			rv = p_ica_des_cfb(in, out, len, pCtx->key,
-#ifdef OLDER_OPENSSL
-					   ctx->iv,
-#else
-					   EVP_CIPHER_CTX_iv_noconst(ctx),
-#endif
-					   8, ICA_DECRYPT);
-		} else if (EVP_CIPHER_CTX_mode(ctx) == EVP_CIPH_OFB_MODE) {
-			rv = p_ica_des_ofb(in, out, len, pCtx->key,
-#ifdef OLDER_OPENSSL
-					   ctx->iv,
-#else
-					   EVP_CIPHER_CTX_iv_noconst(ctx),
-#endif
-					   ICA_DECRYPT);
-		} else {
-			/* Protect against decrypt in place */
-			/* FIXME: Shouldn't we use EVP_CIPHER_CTX_iv_length() instead? */
-			memcpy(pre_iv, in + len - sizeof(pre_iv), sizeof(pre_iv));
-
-			rv = p_ica_des_decrypt(mode, len, (unsigned char *)in,
-#ifdef OLDER_OPENSSL
-				(ica_des_vector_t *) ctx->iv,
-#else
-				(ica_des_vector_t *) EVP_CIPHER_CTX_iv_noconst(ctx),
-#endif
-				(ica_des_key_single_t *) pCtx->key, out);
-		}
-
-		if (rv) {
-			IBMCAerr(IBMCA_F_IBMCA_DES_CIPHER,
-					IBMCA_R_REQUEST_FAILED);
-			return 0;
-		} else if (EVP_CIPHER_CTX_mode(ctx) != EVP_CIPH_OFB_MODE) {
-#ifdef OLDER_OPENSSL
-			memcpy(ctx->iv,
-#else
-			memcpy(EVP_CIPHER_CTX_iv_noconst(ctx),
-#endif
-				pre_iv, EVP_CIPHER_CTX_iv_length(ctx));
-		}
-	}
-
-	return 1;
-}				// end ibmca_des_cipher
-
-static int ibmca_tdes_cipher(EVP_CIPHER_CTX * ctx, unsigned char *out,
-			     const unsigned char *in, size_t inlen)
-{
-	int mode = 0;
-	int rv;
-	unsigned int len;
-#ifdef OLDER_OPENSSL
-	ICA_DES_CTX *pCtx = ctx->cipher_data;
-#else
-	ICA_DES_CTX *pCtx = (ICA_DES_CTX *) EVP_CIPHER_CTX_get_cipher_data(ctx);
-#endif
-	ica_des_vector_t pre_iv;
-
-	if (inlen > UINT32_MAX) {
-		IBMCAerr(IBMCA_F_IBMCA_TDES_CIPHER, IBMCA_R_OUTLEN_TO_LARGE);
-		return 0;
-	}
-	len = inlen;
-
-	if (EVP_CIPHER_CTX_mode(ctx) == EVP_CIPH_ECB_MODE) {
-		mode = MODE_ECB;
-	} else if (EVP_CIPHER_CTX_mode(ctx) == EVP_CIPH_CBC_MODE) {
-		mode = MODE_CBC;
-	} else if ((EVP_CIPHER_CTX_mode(ctx) != EVP_CIPH_CFB_MODE) &&
-		   (EVP_CIPHER_CTX_mode(ctx) != EVP_CIPH_OFB_MODE)) {
-		IBMCAerr(IBMCA_F_IBMCA_TDES_CIPHER,
-				IBMCA_R_CIPHER_MODE_NOT_SUPPORTED);
-		return 0;
-	}
-
-#ifdef OLDER_OPENSSL
-	if (ctx->encrypt) {
-#else
-	if (EVP_CIPHER_CTX_encrypting(ctx)) {
-#endif
-		if (EVP_CIPHER_CTX_mode(ctx) == EVP_CIPH_CFB_MODE) {
-			rv = p_ica_3des_cfb(in, out, len, pCtx->key,
-#ifdef OLDER_OPENSSL
-					ctx->iv,
-#else
-					EVP_CIPHER_CTX_iv_noconst(ctx),
-#endif
-					8, ICA_ENCRYPT);
-		} else if (EVP_CIPHER_CTX_mode(ctx) == EVP_CIPH_OFB_MODE) {
-			rv = p_ica_3des_ofb(in, out, len, pCtx->key,
-#ifdef OLDER_OPENSSL
-					ctx->iv,
-#else
-					EVP_CIPHER_CTX_iv_noconst(ctx),
-#endif
-					ICA_ENCRYPT);
-		} else {
-			rv = p_ica_3des_encrypt(mode, len, (unsigned char *)in,
-#ifdef OLDER_OPENSSL
-				(ica_des_vector_t *) ctx->iv,
-#else
-				(ica_des_vector_t *) EVP_CIPHER_CTX_iv_noconst(ctx),
-#endif
-				(ica_des_key_triple_t *) pCtx->key, out);
-		}
-
-		if (rv) {
-			IBMCAerr(IBMCA_F_IBMCA_TDES_CIPHER,
-					IBMCA_R_REQUEST_FAILED);
-			return 0;
-		} else if (EVP_CIPHER_CTX_mode(ctx) != EVP_CIPH_OFB_MODE) {
-#ifdef OLDER_OPENSSL
-			memcpy(ctx->iv,
-#else
-			memcpy(EVP_CIPHER_CTX_iv_noconst(ctx),
-#endif
-			       out + len - EVP_CIPHER_CTX_iv_length(ctx),
-			       EVP_CIPHER_CTX_iv_length(ctx));
-		}
-	} else {
-		if (EVP_CIPHER_CTX_mode(ctx) == EVP_CIPH_CFB_MODE) {
-			/* Protect against decrypt in place */
-			/* FIXME: Again, check if EVP_CIPHER_CTX_iv_length() should be used */
-			memcpy(pre_iv, in + len - sizeof(pre_iv), sizeof(pre_iv));
-
-			rv = p_ica_3des_cfb(in, out, len, pCtx->key,
-#ifdef OLDER_OPENSSL
-					ctx->iv,
-#else
-					EVP_CIPHER_CTX_iv_noconst(ctx),
-#endif
-					8, ICA_DECRYPT);
-		} else if (EVP_CIPHER_CTX_mode(ctx) == EVP_CIPH_OFB_MODE) {
-			rv = p_ica_3des_ofb(in, out, len, pCtx->key,
-#ifdef OLDER_OPENSSL
-					ctx->iv,
-#else
-					EVP_CIPHER_CTX_iv_noconst(ctx),
-#endif
-					ICA_DECRYPT);
-		} else {
-			/* Protect against decrypt in place */
-			/* FIXME: Again, check if EVP_CIPHER_CTX_iv_length() should be used */
-			memcpy(pre_iv, in + len - sizeof(pre_iv), sizeof(pre_iv));
-
-			rv = p_ica_3des_decrypt(mode, len, (unsigned char *)in,
-#ifdef OLDER_OPENSSL
-				(ica_des_vector_t *) ctx->iv,
-#else
-				(ica_des_vector_t *) EVP_CIPHER_CTX_iv_noconst(ctx),
-#endif
-				(ica_des_key_triple_t *) pCtx->key, out);
-		}
-
-		if (rv) {
-			IBMCAerr(IBMCA_F_IBMCA_TDES_CIPHER,
-					IBMCA_R_REQUEST_FAILED);
-			return 0;
-		} else if (EVP_CIPHER_CTX_mode(ctx) != EVP_CIPH_OFB_MODE) {
-#ifdef OLDER_OPENSSL
-			memcpy(ctx->iv,
-#else
-			memcpy(EVP_CIPHER_CTX_iv_noconst(ctx),
-#endif
-				pre_iv, EVP_CIPHER_CTX_iv_length(ctx));
-		}
-	}
-
-	return 1;
-}				// end ibmca_tdes_cipher
-
-/* FIXME: a lot of common code between ica_aes_[128|192|256]_cipher() fncs */
-static int ibmca_aes_128_cipher(EVP_CIPHER_CTX * ctx, unsigned char *out,
-				const unsigned char *in, size_t inlen)
-{
-	int mode = 0;
-	int rv;
-	unsigned int len;
-#ifdef OLDER_OPENSSL
-	ICA_AES_128_CTX *pCtx = ctx->cipher_data;
-#else
-	ICA_AES_128_CTX *pCtx = (ICA_AES_128_CTX *) EVP_CIPHER_CTX_get_cipher_data(ctx);
-#endif
-	ica_aes_vector_t pre_iv;
-
-	if (inlen > UINT32_MAX) {
-		IBMCAerr(IBMCA_F_IBMCA_AES_128_CIPHER, IBMCA_R_OUTLEN_TO_LARGE);
-		return 0;
-	}
-	len = inlen;
-
-	if (EVP_CIPHER_CTX_mode(ctx) == EVP_CIPH_ECB_MODE) {
-		mode = MODE_ECB;
-	} else if (EVP_CIPHER_CTX_mode(ctx) == EVP_CIPH_CBC_MODE) {
-		mode = MODE_CBC;
-	} else if ((EVP_CIPHER_CTX_mode(ctx) != EVP_CIPH_CFB_MODE) &&
-		   (EVP_CIPHER_CTX_mode(ctx) != EVP_CIPH_OFB_MODE)) {
-		IBMCAerr(IBMCA_F_IBMCA_AES_128_CIPHER,
-			 IBMCA_R_CIPHER_MODE_NOT_SUPPORTED);
-		return 0;
-	}
-
-#ifdef OLDER_OPENSSL
-	if (ctx->encrypt) {
-#else
-	if (EVP_CIPHER_CTX_encrypting(ctx)) {
-#endif
-		if (EVP_CIPHER_CTX_mode(ctx) == EVP_CIPH_CFB_MODE) {
-			rv = p_ica_aes_cfb(in, out, len, pCtx->key,
-					AES_KEY_LEN128,
-#ifdef OLDER_OPENSSL
-					ctx->iv,
-#else
-					EVP_CIPHER_CTX_iv_noconst(ctx),
-#endif
-					AES_BLOCK_SIZE, ICA_ENCRYPT);
-		} else if (EVP_CIPHER_CTX_mode(ctx) == EVP_CIPH_OFB_MODE) {
-			rv = p_ica_aes_ofb(in, out, len, pCtx->key,
-					AES_KEY_LEN128,
-#ifdef OLDER_OPENSSL
-					ctx->iv,
-#else
-					EVP_CIPHER_CTX_iv_noconst(ctx),
-#endif
-					ICA_ENCRYPT);
-		} else {
-			rv = p_ica_aes_encrypt(mode, len, (unsigned char *)in,
-#ifdef OLDER_OPENSSL
-				(ica_aes_vector_t *)ctx->iv,
-#else
-				(ica_aes_vector_t *)EVP_CIPHER_CTX_iv_noconst(ctx),
-#endif
-				AES_KEY_LEN128,
-				(unsigned char *)pCtx->key,
-				out);
-		}
-
-		if (rv) {
-			IBMCAerr(IBMCA_F_IBMCA_AES_128_CIPHER,
-				 IBMCA_R_REQUEST_FAILED);
-			return 0;
-		} else if (EVP_CIPHER_CTX_mode(ctx) != EVP_CIPH_OFB_MODE) {
-#ifdef OLDER_OPENSSL
-			memcpy(ctx->iv,
-#else
-			memcpy(EVP_CIPHER_CTX_iv_noconst(ctx),
-#endif
-			       out + len - EVP_CIPHER_CTX_iv_length(ctx),
-			       EVP_CIPHER_CTX_iv_length(ctx));
-		}
-	} else {
-		if (EVP_CIPHER_CTX_mode(ctx) == EVP_CIPH_CFB_MODE) {
-			/* Protect against decrypt in place */
-			/* FIXME: Again, check if EVP_CIPHER_CTX_iv_length() should be used */
-			memcpy(pre_iv, in + len - sizeof(pre_iv), sizeof(pre_iv));
-
-			rv = p_ica_aes_cfb(in, out, len, pCtx->key,
-					AES_KEY_LEN128,
-#ifdef OLDER_OPENSSL
-					ctx->iv,
-#else
-					EVP_CIPHER_CTX_iv_noconst(ctx),
-#endif
-					AES_BLOCK_SIZE, ICA_DECRYPT);
-		} else if (EVP_CIPHER_CTX_mode(ctx) == EVP_CIPH_OFB_MODE) {
-			rv = p_ica_aes_ofb(in, out, len, pCtx->key,
-					AES_KEY_LEN128,
-#ifdef OLDER_OPENSSL
-					ctx->iv,
-#else
-					EVP_CIPHER_CTX_iv_noconst(ctx),
-#endif
-					ICA_DECRYPT);
-		} else {
-			/* Protect against decrypt in place */
-			/* FIXME: Again, check if EVP_CIPHER_CTX_iv_length() should be used */
-			memcpy(pre_iv, in + len - sizeof(pre_iv), sizeof(pre_iv));
-
-			rv = p_ica_aes_decrypt(mode, len, (unsigned char *)in,
-#ifdef OLDER_OPENSSL
-				(ica_aes_vector_t *)ctx->iv,
-#else
-				(ica_aes_vector_t *)EVP_CIPHER_CTX_iv_noconst(ctx),
-#endif
-				AES_KEY_LEN128,
-				(unsigned char *)pCtx->key,
-				out);
-		}
-
-		if (rv) {
-			IBMCAerr(IBMCA_F_IBMCA_AES_128_CIPHER,
-				 IBMCA_R_REQUEST_FAILED);
-			return 0;
-		} else if (EVP_CIPHER_CTX_mode(ctx) != EVP_CIPH_OFB_MODE) {
-#ifdef OLDER_OPENSSL
-			memcpy(ctx->iv,
-#else
-			memcpy(EVP_CIPHER_CTX_iv_noconst(ctx),
-#endif
-				pre_iv, EVP_CIPHER_CTX_iv_length(ctx));
-		}
-	}
-
-	return 1;
-}
-
-static int ibmca_aes_192_cipher(EVP_CIPHER_CTX * ctx, unsigned char *out,
-				const unsigned char *in, size_t inlen)
-{
-	int mode = 0;
-	int rv;
-
-	unsigned int len;
-#ifdef OLDER_OPENSSL
-	ICA_AES_192_CTX *pCtx = ctx->cipher_data;
-#else
-	ICA_AES_192_CTX *pCtx = (ICA_AES_192_CTX *) EVP_CIPHER_CTX_get_cipher_data(ctx);
-#endif
-	ica_aes_vector_t pre_iv;
-
-	if (inlen > UINT32_MAX) {
-		IBMCAerr(IBMCA_F_IBMCA_AES_192_CIPHER, IBMCA_R_OUTLEN_TO_LARGE);
-		return 0;
-	}
-	len = inlen;
-
-	if (EVP_CIPHER_CTX_mode(ctx) == EVP_CIPH_ECB_MODE) {
-		mode = MODE_ECB;
-	} else if (EVP_CIPHER_CTX_mode(ctx) == EVP_CIPH_CBC_MODE) {
-		mode = MODE_CBC;
-	} else if ((EVP_CIPHER_CTX_mode(ctx) != EVP_CIPH_CFB_MODE) &&
-		   (EVP_CIPHER_CTX_mode(ctx) != EVP_CIPH_OFB_MODE)) {
-		IBMCAerr(IBMCA_F_IBMCA_AES_192_CIPHER,
-			 IBMCA_R_CIPHER_MODE_NOT_SUPPORTED);
-		return 0;
-	}
-
-#ifdef OLDER_OPENSSL
-	if (ctx->encrypt) {
-#else
-	if (EVP_CIPHER_CTX_encrypting(ctx)) {
-#endif
-		if (EVP_CIPHER_CTX_mode(ctx) == EVP_CIPH_CFB_MODE) {
-			rv = p_ica_aes_cfb(in, out, len, pCtx->key,
-					AES_KEY_LEN192,
-#ifdef OLDER_OPENSSL
-					ctx->iv,
-#else
-					EVP_CIPHER_CTX_iv_noconst(ctx),
-#endif
-					AES_BLOCK_SIZE, ICA_ENCRYPT);
-		} else if (EVP_CIPHER_CTX_mode(ctx) == EVP_CIPH_OFB_MODE) {
-			rv = p_ica_aes_ofb(in, out, len, pCtx->key,
-					AES_KEY_LEN192,
-#ifdef OLDER_OPENSSL
-					ctx->iv,
-#else
-					EVP_CIPHER_CTX_iv_noconst(ctx),
-#endif
-					ICA_ENCRYPT);
-		} else {
-			rv = p_ica_aes_encrypt(mode, len, (unsigned char *)in,
-#ifdef OLDER_OPENSSL
-				(ica_aes_vector_t *)ctx->iv,
-#else
-				(ica_aes_vector_t *)EVP_CIPHER_CTX_iv_noconst(ctx),
-#endif
-				AES_KEY_LEN192,
-				(unsigned char *)pCtx->key, out);
-		}
-
-		if (rv) {
-			IBMCAerr(IBMCA_F_IBMCA_AES_192_CIPHER,
-				 IBMCA_R_REQUEST_FAILED);
-			return 0;
-		} else if (EVP_CIPHER_CTX_mode(ctx) != EVP_CIPH_OFB_MODE) {
-#ifdef OLDER_OPENSSL
-			memcpy(ctx->iv,
-#else
-			memcpy(EVP_CIPHER_CTX_iv_noconst(ctx),
-#endif
-			       out + len - EVP_CIPHER_CTX_iv_length(ctx),
-			       EVP_CIPHER_CTX_iv_length(ctx));
-		}
-	} else {
-		if (EVP_CIPHER_CTX_mode(ctx) == EVP_CIPH_CFB_MODE) {
-			/* Protect against decrypt in place */
-			memcpy(pre_iv, in + len - sizeof(pre_iv), sizeof(pre_iv));
-
-			rv = p_ica_aes_cfb(in, out, len, pCtx->key,
-					AES_KEY_LEN192,
-#ifdef OLDER_OPENSSL
-					ctx->iv,
-#else
-					EVP_CIPHER_CTX_iv_noconst(ctx),
-#endif
-					AES_BLOCK_SIZE, ICA_DECRYPT);
-		} else if (EVP_CIPHER_CTX_mode(ctx) == EVP_CIPH_OFB_MODE) {
-			rv = p_ica_aes_ofb(in, out, len, pCtx->key,
-					AES_KEY_LEN192,
-#ifdef OLDER_OPENSSL
-					ctx->iv,
-#else
-					EVP_CIPHER_CTX_iv_noconst(ctx),
-#endif
-					ICA_DECRYPT);
-		} else {
-			/* Protect against decrypt in place */
-			memcpy(pre_iv, in + len - sizeof(pre_iv), sizeof(pre_iv));
-
-			rv = p_ica_aes_decrypt(mode, len, (unsigned char *)in,
-#ifdef OLDER_OPENSSL
-				(ica_aes_vector_t *)ctx->iv,
-#else
-				(ica_aes_vector_t *)EVP_CIPHER_CTX_iv_noconst(ctx),
-#endif
-				AES_KEY_LEN192,
-				(unsigned char *)pCtx->key, out);
-		}
-
-		if (rv) {
-			IBMCAerr(IBMCA_F_IBMCA_AES_192_CIPHER,
-				 IBMCA_R_REQUEST_FAILED);
-			return 0;
-		} else if (EVP_CIPHER_CTX_mode(ctx) != EVP_CIPH_OFB_MODE) {
-#ifdef OLDER_OPENSSL
-			memcpy(ctx->iv,
-#else
-			memcpy(EVP_CIPHER_CTX_iv_noconst(ctx),
-#endif
-			pre_iv, EVP_CIPHER_CTX_iv_length(ctx));
-		}
-	}
-
-	return 1;
-}
-
-static int ibmca_aes_256_cipher(EVP_CIPHER_CTX * ctx, unsigned char *out,
-				const unsigned char *in, size_t inlen)
-{
-	int mode = 0;
-	int rv;
-	unsigned int len;
-#ifdef OLDER_OPENSSL
-	ICA_AES_256_CTX *pCtx = ctx->cipher_data;
-#else
-	ICA_AES_256_CTX *pCtx = (ICA_AES_256_CTX *) EVP_CIPHER_CTX_get_cipher_data(ctx);
-#endif
-	ica_aes_vector_t pre_iv;
-
-	if (inlen > UINT32_MAX) {
-		IBMCAerr(IBMCA_F_IBMCA_AES_256_CIPHER, IBMCA_R_OUTLEN_TO_LARGE);
-		return 0;
-	}
-	len = inlen;
-
-	if (EVP_CIPHER_CTX_mode(ctx) == EVP_CIPH_ECB_MODE) {
-		mode = MODE_ECB;
-	} else if (EVP_CIPHER_CTX_mode(ctx) == EVP_CIPH_CBC_MODE) {
-		mode = MODE_CBC;
-	} else if ((EVP_CIPHER_CTX_mode(ctx) != EVP_CIPH_CFB_MODE) &&
-		   (EVP_CIPHER_CTX_mode(ctx) != EVP_CIPH_OFB_MODE)) {
-		IBMCAerr(IBMCA_F_IBMCA_AES_256_CIPHER,
-			 IBMCA_R_CIPHER_MODE_NOT_SUPPORTED);
-		return 0;
-	}
-
-#ifdef OLDER_OPENSSL
-	if (ctx->encrypt) {
-#else
-	if (EVP_CIPHER_CTX_encrypting(ctx)) {
-#endif
-		if (EVP_CIPHER_CTX_mode(ctx) == EVP_CIPH_CFB_MODE) {
-			rv = p_ica_aes_cfb(in, out, len, pCtx->key,
-					AES_KEY_LEN256,
-#ifdef OLDER_OPENSSL
-					ctx->iv,
-#else
-					EVP_CIPHER_CTX_iv_noconst(ctx),
-#endif
-					AES_BLOCK_SIZE, ICA_ENCRYPT);
-		} else if (EVP_CIPHER_CTX_mode(ctx) == EVP_CIPH_OFB_MODE) {
-			rv = p_ica_aes_ofb(in, out, len, pCtx->key,
-					AES_KEY_LEN256,
-#ifdef OLDER_OPENSSL
-					ctx->iv,
-#else
-					EVP_CIPHER_CTX_iv_noconst(ctx),
-#endif
-					ICA_ENCRYPT);
-		} else {
-			rv = p_ica_aes_encrypt(mode, len, (unsigned char *)in,
-#ifdef OLDER_OPENSSL
-				(ica_aes_vector_t *)ctx->iv,
-#else
-				(ica_aes_vector_t *)EVP_CIPHER_CTX_iv_noconst(ctx),
-#endif
-				AES_KEY_LEN256,
-				(unsigned char *)pCtx->key, out);
-		}
-
-		if (rv) {
-			IBMCAerr(IBMCA_F_IBMCA_AES_256_CIPHER,
-				 IBMCA_R_REQUEST_FAILED);
-			return 0;
-		} else if (EVP_CIPHER_CTX_mode(ctx) != EVP_CIPH_OFB_MODE) {
-#ifdef OLDER_OPENSSL
-			memcpy(ctx->iv,
-#else
-			memcpy(EVP_CIPHER_CTX_iv_noconst(ctx),
-#endif
-			       out + len - EVP_CIPHER_CTX_iv_length(ctx),
-			       EVP_CIPHER_CTX_iv_length(ctx));
-		}
-	} else {
-		if (EVP_CIPHER_CTX_mode(ctx) == EVP_CIPH_CFB_MODE) {
-			/* Protect against decrypt in place */
-			memcpy(pre_iv, in + len - sizeof(pre_iv), sizeof(pre_iv));
-
-			rv = p_ica_aes_cfb(in, out, len, pCtx->key,
-					AES_KEY_LEN256,
-#ifdef OLDER_OPENSSL
-					ctx->iv,
-#else
-					EVP_CIPHER_CTX_iv_noconst(ctx),
-#endif
-					AES_BLOCK_SIZE, ICA_DECRYPT);
-		} else if (EVP_CIPHER_CTX_mode(ctx) == EVP_CIPH_OFB_MODE) {
-			rv = p_ica_aes_ofb(in, out, len, pCtx->key,
-					AES_KEY_LEN256,
-#ifdef OLDER_OPENSSL
-					ctx->iv,
-#else
-					EVP_CIPHER_CTX_iv_noconst(ctx),
-#endif
-					ICA_DECRYPT);
-		} else {
-			/* Protect against decrypt in place */
-			memcpy(pre_iv, in + len - sizeof(pre_iv), sizeof(pre_iv));
-
-			rv = p_ica_aes_decrypt(mode, len, (unsigned char *)in,
-#ifdef OLDER_OPENSSL
-				(ica_aes_vector_t *)ctx->iv,
-#else
-				(ica_aes_vector_t *)EVP_CIPHER_CTX_iv_noconst(ctx),
-#endif
-				AES_KEY_LEN256,
-				(unsigned char *)pCtx->key, out);
-		}
-
-		if (rv) {
-			IBMCAerr(IBMCA_F_IBMCA_AES_256_CIPHER,
-				 IBMCA_R_REQUEST_FAILED);
-			return 0;
-		} else if (EVP_CIPHER_CTX_mode(ctx) != EVP_CIPH_OFB_MODE) {
-#ifdef OLDER_OPENSSL
-			memcpy(ctx->iv,
-#else
-			memcpy(EVP_CIPHER_CTX_iv_noconst(ctx),
-#endif
-				pre_iv, EVP_CIPHER_CTX_iv_length(ctx));
-		}
-	}
-
-	return 1;
-}
-
-static int ibmca_cipher_cleanup(EVP_CIPHER_CTX * ctx)
-{
-	return 1;
-}
-
-#ifndef OPENSSL_NO_AES_GCM
-static int ibmca_gcm_aad(ICA_AES_GCM_CTX *ctx, const unsigned char *aad,
-			 size_t len, int enc, int keylen)
-{
-	uint64_t alen = ctx->aadlen;
-
-	if (ctx->ptlen)
-		return -2;
-
-	alen += len;
-	if (alen > (1ULL << 61) || (sizeof(len) == 8 && alen < len))
-		return -1;
-
-	ctx->aadlen = alen;
-
-	/* ctx->taglen is not set at this time... and is not needed. The
-	 * function only checks, if it's a valid gcm tag length. So we chose
-	 * 16. */
-	return !(p_ica_aes_gcm_intermediate(NULL, 0, NULL, ctx->ucb,
-					    (unsigned char *)aad, len,
-					    ctx->tag, 16, ctx->key, keylen,
-					    ctx->subkey, enc));
-}
-
-static int ibmca_aes_gcm(ICA_AES_GCM_CTX *ctx, const unsigned char *in,
-                         unsigned char *out, size_t len, int enc, int keylen)
-{
-	uint64_t mlen = ctx->ptlen;
-	unsigned char *pt, *ct;
-	int rv;
-
-	mlen += len;
-	if (mlen > ((1ULL << 36) - 32) || (sizeof(len) == 8 && mlen < len))
-		return -1;
-
-	ctx->ptlen = mlen;
-
-	if (enc) {
-		pt = (unsigned char *)in;
-		ct = out;
-	} else {
-		ct = (unsigned char *)in;
-		pt = out;
-	}
-
-	/* ctx->taglen is not set at this time... and is not needed. The
-	 * function only checks, if it's a valid gcm tag length. So we chose
-	 * 16. */
-	rv = !(p_ica_aes_gcm_intermediate(pt, len, ct, ctx->ucb, NULL, 0,
-					  ctx->tag, 16, ctx->key, keylen,
-					  ctx->subkey, enc));
-	return rv;
-}
-
-static int ibmca_aes_gcm_init_key(EVP_CIPHER_CTX *ctx,
-                                  const unsigned char *key,
-                                  const unsigned char *iv, int enc)
-{
-	ICA_AES_GCM_CTX *gctx =
-	    (ICA_AES_GCM_CTX *)EVP_CIPHER_CTX_get_cipher_data(ctx);
-	const int gkeylen = EVP_CIPHER_CTX_key_length(ctx);
-
-	if (!iv && !key)
-	    return 1;
-
-	if (key) {
-		memcpy(gctx->key, key, gkeylen);
-
-		if (iv == NULL && gctx->iv_set)
-			iv = gctx->iv;
-
-		if (iv) {
-			memset(gctx->icb, 0, sizeof(gctx->icb));
-			memset(gctx->tag, 0, sizeof(gctx->tag));
-			gctx->aadlen = 0;
-			gctx->ptlen = 0;
-			if (p_ica_aes_gcm_initialize(iv, gctx->ivlen,
-						     gctx->key, gkeylen,
-						     gctx->icb, gctx->ucb,
-						     gctx->subkey, enc))
-				return 0;
-
-			gctx->iv_set = 1;
-		}
-		gctx->key_set = 1;
-	} else {
-		if (gctx->key_set) {
-			memset(gctx->icb, 0, sizeof(gctx->icb));
-			memset(gctx->tag, 0, sizeof(gctx->tag));
-			gctx->aadlen = 0;
-			gctx->ptlen = 0;
-			if (p_ica_aes_gcm_initialize(iv, gctx->ivlen,
-						     gctx->key, gkeylen,
-						     gctx->icb, gctx->ucb,
-						     gctx->subkey, enc))
-				return 0;
-		} else {
-			memcpy(gctx->iv, iv, gctx->ivlen);
-		}
-		gctx->iv_set = 1;
-		gctx->iv_gen = 0;
-	}
-	return 1;
-}
-
-static int ibmca_aes_gcm_setiv(EVP_CIPHER_CTX *c)
-{
-	ICA_AES_GCM_CTX *gctx =
-	    (ICA_AES_GCM_CTX *)EVP_CIPHER_CTX_get_cipher_data(c);
-	const int gkeylen = EVP_CIPHER_CTX_key_length(c);
-	int enc = EVP_CIPHER_CTX_encrypting(c);
-
-	if (gctx->key == NULL || !gctx->key_set)
-		return 0;
-
-	memset(gctx->icb, 0, sizeof(gctx->icb));
-	memset(gctx->tag, 0, sizeof(gctx->tag));
-	gctx->aadlen = 0;
-	gctx->ptlen = 0;
-	return !(p_ica_aes_gcm_initialize(gctx->iv, gctx->ivlen, gctx->key,
-					  gkeylen, gctx->icb, gctx->ucb,
-					  gctx->subkey, enc));
-}
-
-static int ibmca_aes_gcm_ctrl(EVP_CIPHER_CTX *c, int type, int arg,
-			      void *ptr)
-{
-	ICA_AES_GCM_CTX *gctx =
-	    (ICA_AES_GCM_CTX *)EVP_CIPHER_CTX_get_cipher_data(c);
-	unsigned char *iv_noconst = EVP_CIPHER_CTX_iv_noconst(c);
-	unsigned char *buf_noconst = EVP_CIPHER_CTX_buf_noconst(c);
-	int enc = EVP_CIPHER_CTX_encrypting(c);
-	EVP_CIPHER_CTX *out;
-	ICA_AES_GCM_CTX *gctx_out;
-	unsigned char *iv_noconst_out;
-	unsigned int len;
-
-	switch (type) {
-	case EVP_CTRL_INIT:
-		gctx->key_set = 0;
-		gctx->iv_set = 0;
-		gctx->ivlen = EVP_CIPHER_CTX_iv_length(c);
-		gctx->iv = iv_noconst;
-		gctx->taglen = -1;
-		gctx->iv_gen = 0;
-		gctx->tls_aadlen = -1;
-		return 1;
-
-	case EVP_CTRL_GCM_SET_IVLEN:
-		if (arg <= 0)
-			return 0;
-		if ((arg > EVP_MAX_IV_LENGTH) && (arg > gctx->ivlen)) {
-			if (gctx->iv != iv_noconst)
-				OPENSSL_free(gctx->iv);
-			gctx->iv = OPENSSL_malloc(arg);
-			if (gctx->iv == NULL)
-				return 0;
-		}
-		gctx->ivlen = arg;
-		return 1;
-
-	case EVP_CTRL_GCM_SET_TAG:
-		if (arg <= 0 || arg > 16 || enc)
-			return 0;
-		memcpy(buf_noconst, ptr, arg);
-		gctx->taglen = arg;
-		return 1;
-
-	case EVP_CTRL_GCM_GET_TAG:
-		if (arg <= 0 || arg > 16 || !enc || gctx->taglen < 0)
-			return 0;
-		memcpy(ptr, buf_noconst, arg);
-		return 1;
-
-	case EVP_CTRL_GCM_SET_IV_FIXED:
-		if (arg == -1) {
-			memcpy(gctx->iv, ptr, gctx->ivlen);
-			gctx->iv_gen = 1;
-			return 1;
-		}
-		if ((arg < 4) || (gctx->ivlen - arg) < 8)
-			return 0;
-		if (arg)
-			memcpy(gctx->iv, ptr, arg);
-		if (enc && RAND_bytes(gctx->iv + arg, gctx->ivlen - arg) <= 0)
-			return 0;
-		gctx->iv_gen = 1;
-		return 1;
-
-	case EVP_CTRL_GCM_IV_GEN:
-		if (gctx->iv_gen == 0 || gctx->key_set == 0)
-			return 0;
-		if (!ibmca_aes_gcm_setiv(c))
-			return 0;
-		if (arg <= 0 || arg > gctx->ivlen)
-			arg = gctx->ivlen;
-		memcpy(ptr, gctx->iv + gctx->ivlen - arg, arg);
-		++*(uint64_t *)(gctx->iv + gctx->ivlen - 8);
-		gctx->iv_set = 1;
-		return 1;
-
-	case EVP_CTRL_GCM_SET_IV_INV:
-		if (gctx->iv_gen == 0 || gctx->key_set == 0 || enc)
-			return 0;
-		memcpy(gctx->iv + gctx->ivlen - arg, ptr, arg);
-		if (!ibmca_aes_gcm_setiv(c))
-			return 0;
-		gctx->iv_set = 1;
-		return 1;
-
-	case EVP_CTRL_AEAD_TLS1_AAD:
-		if (arg != EVP_AEAD_TLS1_AAD_LEN)
-			return 0;
-		memcpy(buf_noconst, ptr, arg);
-		gctx->tls_aadlen = arg;
-		len = buf_noconst[arg - 2] << 8 | buf_noconst[arg - 1];
-		if (len < EVP_GCM_TLS_EXPLICIT_IV_LEN)
-			return 0;
-		len -= EVP_GCM_TLS_EXPLICIT_IV_LEN;
-		if (!enc) {
-			if (len < EVP_GCM_TLS_TAG_LEN)
-				return 0;
-			len -= EVP_GCM_TLS_TAG_LEN;
-		}
-		buf_noconst[arg - 2] = len >> 8;
-		buf_noconst[arg - 1] = len & 0xff;
-		return EVP_GCM_TLS_TAG_LEN;
-
-	case EVP_CTRL_COPY: {
-		out = ptr;
-		gctx_out = (ICA_AES_GCM_CTX *)
-			   EVP_CIPHER_CTX_get_cipher_data(out);
-		iv_noconst_out = EVP_CIPHER_CTX_iv_noconst(out);
-		if (gctx->iv == iv_noconst) {
-			gctx_out->iv = iv_noconst_out;
-		} else {
-			gctx_out->iv = OPENSSL_malloc(gctx->ivlen);
-			if (gctx_out->iv == NULL)
-				return 0;
-			memcpy(gctx_out->iv, gctx->iv, gctx->ivlen);
-		}
-		return 1;
-	}
-	default:
-		return -1;
-    }
-}
-
-static int ibmca_gcm_tag(EVP_CIPHER_CTX *ctx, unsigned char *out,
-			 const unsigned char *in, int taglen)
-{
-	ICA_AES_GCM_CTX *gctx =
-	    (ICA_AES_GCM_CTX *)EVP_CIPHER_CTX_get_cipher_data(ctx);
-	int enc = EVP_CIPHER_CTX_encrypting(ctx);
-	const int gkeylen = EVP_CIPHER_CTX_key_length(ctx);
-
-	if (p_ica_aes_gcm_last(gctx->icb, gctx->aadlen, gctx->ptlen,
-			       gctx->tag, (unsigned char *)in, taglen,
-			       gctx->key, gkeylen, gctx->subkey, enc))
-		return 0;
-
-	if (out)
-		memcpy(out, gctx->tag, taglen);
-
-	return 1;
-}
-
-static int ibmca_aes_gcm_tls_cipher(EVP_CIPHER_CTX *ctx, unsigned char *out,
-                                    const unsigned char *in, size_t len)
-{
-	ICA_AES_GCM_CTX *gctx =
-	    (ICA_AES_GCM_CTX *)EVP_CIPHER_CTX_get_cipher_data(ctx);
-	unsigned char *buf = EVP_CIPHER_CTX_buf_noconst(ctx);
-	int enc = EVP_CIPHER_CTX_encrypting(ctx);
-	const int keylen = EVP_CIPHER_CTX_key_length(ctx);
-	int rv = -1;
-
-	if (out != in
-	    || len < (EVP_GCM_TLS_EXPLICIT_IV_LEN + EVP_GCM_TLS_TAG_LEN))
-		return -1;
-	if (EVP_CIPHER_CTX_ctrl(ctx, enc ? EVP_CTRL_GCM_IV_GEN :
-	    EVP_CTRL_GCM_SET_IV_INV, EVP_GCM_TLS_EXPLICIT_IV_LEN, out) <= 0)
-		goto err;
-
-	if (!ibmca_gcm_aad(gctx, buf, gctx->tls_aadlen, enc, keylen))
-		goto err;
-
-	in += EVP_GCM_TLS_EXPLICIT_IV_LEN;
-	out += EVP_GCM_TLS_EXPLICIT_IV_LEN;
-	len -= EVP_GCM_TLS_EXPLICIT_IV_LEN + EVP_GCM_TLS_TAG_LEN;
-
-	if (!ibmca_aes_gcm(gctx, in, out, len, enc, keylen))
-		goto err;
-
-	if (enc) {
-		out += len;
-		if (!ibmca_gcm_tag(ctx, out, NULL, EVP_GCM_TLS_TAG_LEN)) {
-			goto err;
-		}
-		rv = len + EVP_GCM_TLS_EXPLICIT_IV_LEN + EVP_GCM_TLS_TAG_LEN;
-	} else {
-		if (!ibmca_gcm_tag(ctx, buf, in + len, EVP_GCM_TLS_TAG_LEN)) {
-			OPENSSL_cleanse(out, len);
-			goto err;
-		}
-		rv = len;
-	}
-err:
-	gctx->iv_set = 0;
-	gctx->tls_aadlen = -1;
-	return rv;
-}
-
-static int ibmca_aes_gcm_cipher(EVP_CIPHER_CTX *ctx, unsigned char *out,
-                                const unsigned char *in, size_t len)
-{
-	ICA_AES_GCM_CTX *gctx =
-	    (ICA_AES_GCM_CTX *)EVP_CIPHER_CTX_get_cipher_data(ctx);
-	unsigned char *buf = EVP_CIPHER_CTX_buf_noconst(ctx);
-	int enc = EVP_CIPHER_CTX_encrypting(ctx);
-	const int keylen = EVP_CIPHER_CTX_key_length(ctx);
-
-	if (!gctx->key_set)
-		return -1;
-
-	if (gctx->tls_aadlen >= 0)
-		return ibmca_aes_gcm_tls_cipher(ctx, out, in, len);
-
-	if (!gctx->iv_set)
-		return -1;
-
-	if (in) {
-		if (out == NULL) {
-			if (!ibmca_gcm_aad(gctx, in, len, enc, keylen))
-				return -1;
-		} else {
-			if (!ibmca_aes_gcm(gctx, in, out, len, enc, keylen))
-				return -1;
-		}
-		return len;
-	} else {
-		if (enc) {
-			gctx->taglen = 16;
-			if (!ibmca_gcm_tag(ctx, buf, NULL, gctx->taglen))
-				return -1;
-		} else {
-			if (gctx->taglen < 0)
-				return -1;
-			if (!ibmca_gcm_tag(ctx, NULL, buf, gctx->taglen))
-				return -1;
-		}
-		gctx->iv_set = 0;
-		return 0;
-	}
-}
-#endif
-
-static int ibmca_engine_digests(ENGINE * e, const EVP_MD ** digest,
-				const int **nids, int nid)
-{
-	int i;
-	if (!digest)
-		return (ibmca_usable_digests(nids));
-
-        *digest = NULL;
-        for(i = 0; i < size_digest_list;i++)
-                if(nid == ibmca_digest_lists.nids[i]){
-                        *digest = (EVP_MD*) ibmca_digest_lists.crypto_meths[i];
-                        break;
-		}
-
-
-	return (*digest != NULL);
-}
-
-static int ibmca_usable_digests(const int **nids)
-{
-        if(nids)
-		*nids = ibmca_digest_lists.nids;
-	return size_digest_list;
-}
-
-#ifndef OPENSSL_NO_SHA1
-static int ibmca_sha1_init(EVP_MD_CTX * ctx)
-{
-#ifdef OLDER_OPENSSL
-	IBMCA_SHA_CTX *ibmca_sha_ctx = ctx->md_data;
-#else
-	IBMCA_SHA_CTX *ibmca_sha_ctx = (IBMCA_SHA_CTX *) EVP_MD_CTX_md_data(ctx);
-#endif
-	memset((unsigned char *)ibmca_sha_ctx, 0, sizeof(*ibmca_sha_ctx));
-	return 1;
-}
-
-static int ibmca_sha1_update(EVP_MD_CTX * ctx, const void *in_data,
-			     unsigned long inlen)
-{
-#ifdef OLDER_OPENSSL
-	IBMCA_SHA_CTX *ibmca_sha_ctx = ctx->md_data;
-#else
-	IBMCA_SHA_CTX *ibmca_sha_ctx = (IBMCA_SHA_CTX *) EVP_MD_CTX_md_data(ctx);
-#endif
-	unsigned int message_part=SHA_MSG_PART_MIDDLE,
-		fill_size=0;
-	unsigned long in_data_len=inlen;
-	unsigned char tmp_hash[SHA_HASH_LENGTH];
-
-	if (in_data_len == 0)
-		return 1;
-
-	if( ibmca_sha_ctx->c.runningLength == 0 && ibmca_sha_ctx->tail_len == 0) {
-		message_part = SHA_MSG_PART_FIRST;
-
-		ibmca_sha_ctx->tail_len = in_data_len & 0x3f;
-		if(ibmca_sha_ctx->tail_len) {
-			in_data_len &= ~0x3f;
-			memcpy(ibmca_sha_ctx->tail, in_data + in_data_len, ibmca_sha_ctx->tail_len);
-		}
-	}
-	else if( ibmca_sha_ctx->c.runningLength == 0 && ibmca_sha_ctx->tail_len > 0 ) {
-
-		/* Here we need to fill out the temporary tail buffer until
-		 * it has 64 bytes in it, then call ica_sha1 on that buffer.
-		 * If there weren't enough bytes passed in to fill it out,
-		 * just copy in what we can and return success without calling
-		 * ica_sha1. - KEY
-		 */
-
-		fill_size = SHA_BLOCK_SIZE - ibmca_sha_ctx->tail_len;
-		if(fill_size < in_data_len) {
-			memcpy(ibmca_sha_ctx->tail + ibmca_sha_ctx->tail_len, in_data, fill_size);
-
-			/* Submit the filled out tail buffer */
-			if( p_ica_sha1( (unsigned int)SHA_MSG_PART_FIRST,
-					(unsigned int)SHA_BLOCK_SIZE, ibmca_sha_ctx->tail,
-					&ibmca_sha_ctx->c,
-					tmp_hash)) {
-
-				IBMCAerr(IBMCA_F_IBMCA_SHA1_UPDATE,
-						IBMCA_R_REQUEST_FAILED);
-				return 0;
-			}
-		} else {
-			memcpy(ibmca_sha_ctx->tail + ibmca_sha_ctx->tail_len,
-					in_data, in_data_len);
-			ibmca_sha_ctx->tail_len += in_data_len;
-
-			return 1;
-		}
-
-		/* We had to use 'fill_size' bytes from in_data to fill out the
-		 * empty part of save data, so adjust in_data_len
-		 */
-		in_data_len -= fill_size;
-
-		ibmca_sha_ctx->tail_len = in_data_len & 0x3f;
-		if(ibmca_sha_ctx->tail_len) {
-			in_data_len &= ~0x3f;
-			memcpy(ibmca_sha_ctx->tail,
-				in_data + fill_size + in_data_len,
-				ibmca_sha_ctx->tail_len);
-			// fill_size is added to in_data down below
-		}
-	}
-	else if( ibmca_sha_ctx->c.runningLength > 0 ) {
-		if(ibmca_sha_ctx->tail_len) {
-			fill_size = SHA_BLOCK_SIZE - ibmca_sha_ctx->tail_len;
-			if(fill_size < in_data_len) {
-				memcpy(ibmca_sha_ctx->tail + ibmca_sha_ctx->tail_len,
-						in_data, fill_size);
-
-				/* Submit the filled out save buffer */
-				if( p_ica_sha1( message_part,
-						(unsigned int)SHA_BLOCK_SIZE, ibmca_sha_ctx->tail,
-						&ibmca_sha_ctx->c,
-						tmp_hash)) {
-
-					IBMCAerr(IBMCA_F_IBMCA_SHA1_UPDATE,
-							IBMCA_R_REQUEST_FAILED);
-					return 0;
-				}
-			} else {
-				memcpy(ibmca_sha_ctx->tail + ibmca_sha_ctx->tail_len,
-						in_data, in_data_len);
-				ibmca_sha_ctx->tail_len += in_data_len;
-
-				return 1;
-			}
-
-			/*
-			 * We had to use some of the data from in_data to
-			 * fill out the empty part of save data, so adjust
-			 * in_data_len
-			 */
-			in_data_len -= fill_size;
-
-			ibmca_sha_ctx->tail_len = in_data_len & 0x3f;
-			if(ibmca_sha_ctx->tail_len) {
-				in_data_len &= ~0x3f;
-				memcpy(ibmca_sha_ctx->tail,
-					in_data + fill_size +in_data_len,
-					ibmca_sha_ctx->tail_len);
-			}
-		} else {
-			/* This is the odd case, where we need to go ahead and
-			 * send the first X * 64 byte chunks in to be processed
-			 * and copy the last <64 byte area into the tail. -KEY
-			 */
-			ibmca_sha_ctx->tail_len = in_data_len & 0x3f;
-			if( ibmca_sha_ctx->tail_len) {
-				in_data_len &= ~0x3f;
-				memcpy(ibmca_sha_ctx->tail, in_data + in_data_len,
-						ibmca_sha_ctx->tail_len);
-			}
-		}
-	}
-
-	/* If the data passed in was <64 bytes, in_data_len will be 0 */
-        if( in_data_len &&
-		p_ica_sha1(message_part,
-			(unsigned int)in_data_len, (unsigned char *)(in_data + fill_size),
-			&ibmca_sha_ctx->c,
-			tmp_hash)) {
-
-		IBMCAerr(IBMCA_F_IBMCA_SHA1_UPDATE, IBMCA_R_REQUEST_FAILED);
-		return 0;
-	}
-
-	return 1;
-}
-
-static int ibmca_sha1_final(EVP_MD_CTX * ctx, unsigned char *md)
-{
-#ifdef OLDER_OPENSSL
-	IBMCA_SHA_CTX *ibmca_sha_ctx = ctx->md_data;
-#else
-	IBMCA_SHA_CTX *ibmca_sha_ctx = (IBMCA_SHA_CTX *) EVP_MD_CTX_md_data(ctx);
-#endif
-	unsigned int message_part = 0;
-
-	if (ibmca_sha_ctx->c.runningLength)
-		message_part = SHA_MSG_PART_FINAL;
-	else
-		message_part = SHA_MSG_PART_ONLY;
-
-	if( p_ica_sha1(message_part,
-		       ibmca_sha_ctx->tail_len,
-		       (unsigned char *)ibmca_sha_ctx->tail,
-		       &ibmca_sha_ctx->c, md)) {
-
-		IBMCAerr(IBMCA_F_IBMCA_SHA1_FINAL, IBMCA_R_REQUEST_FAILED);
-		return 0;
-	}
-
-	return 1;
-}				// end ibmca_sha1_final
-
-static int ibmca_sha1_cleanup(EVP_MD_CTX * ctx)
-{
-	return 1;
-}				// end ibmca_sha1_cleanup
-
-#endif // OPENSSL_NO_SHA1
-
-#ifndef OPENSSL_NO_SHA256
-static int ibmca_sha256_init(EVP_MD_CTX *ctx)
-{
-#ifdef OLDER_OPENSSL
-	IBMCA_SHA256_CTX *ibmca_sha256_ctx = ctx->md_data;
-#else
-	IBMCA_SHA256_CTX *ibmca_sha256_ctx = (IBMCA_SHA256_CTX *) EVP_MD_CTX_md_data(ctx);
-#endif
-	memset((unsigned char *)ibmca_sha256_ctx, 0, sizeof(*ibmca_sha256_ctx));
-	return 1;
-}				// end ibmca_sha256_init
-
-static int
-ibmca_sha256_update(EVP_MD_CTX *ctx, const void *in_data, unsigned long inlen)
-{
-#ifdef OLDER_OPENSSL
-	IBMCA_SHA256_CTX *ibmca_sha256_ctx = ctx->md_data;
-#else
-	IBMCA_SHA256_CTX *ibmca_sha256_ctx = (IBMCA_SHA256_CTX *) EVP_MD_CTX_md_data(ctx);
-#endif
-	unsigned int message_part = SHA_MSG_PART_MIDDLE, fill_size = 0;
-	unsigned long in_data_len = inlen;
-	unsigned char tmp_hash[SHA256_HASH_LENGTH];
-
-	if (in_data_len == 0)
-		return 1;
-
-	if (ibmca_sha256_ctx->c.runningLength == 0
-	    && ibmca_sha256_ctx->tail_len == 0) {
-		message_part = SHA_MSG_PART_FIRST;
-
-		ibmca_sha256_ctx->tail_len = in_data_len & 0x3f;
-		if(ibmca_sha256_ctx->tail_len) {
-			in_data_len &= ~0x3f;
-			memcpy(ibmca_sha256_ctx->tail, in_data + in_data_len,
-			       ibmca_sha256_ctx->tail_len);
-		}
-	} else if (ibmca_sha256_ctx->c.runningLength == 0
-		   && ibmca_sha256_ctx->tail_len > 0 ) {
-		/* Here we need to fill out the temporary tail buffer
-		 * until it has 64 bytes in it, then call ica_sha256 on
-		 * that buffer.  If there weren't enough bytes passed
-		 * in to fill it out, just copy in what we can and
-		 * return success without calling ica_sha256. - KEY */
-
-		fill_size = SHA256_BLOCK_SIZE - ibmca_sha256_ctx->tail_len;
-		if (fill_size < in_data_len) {
-			memcpy(ibmca_sha256_ctx->tail
-			       + ibmca_sha256_ctx->tail_len, in_data,
-			       fill_size);
-
-			/* Submit the filled out tail buffer */
-			if (p_ica_sha256((unsigned int)SHA_MSG_PART_FIRST,
-					(unsigned int)SHA256_BLOCK_SIZE,
-					ibmca_sha256_ctx->tail,
-					&ibmca_sha256_ctx->c,
-					tmp_hash)) {
-				IBMCAerr(IBMCA_F_IBMCA_SHA256_UPDATE,
-					 IBMCA_R_REQUEST_FAILED);
-				return 0;
-			}
-		} else {
-			memcpy(ibmca_sha256_ctx->tail
-			       + ibmca_sha256_ctx->tail_len, in_data,
-			       in_data_len);
-			ibmca_sha256_ctx->tail_len += in_data_len;
-			return 1;
-		}
-
-		/* We had to use 'fill_size' bytes from in_data to fill out the
-		 * empty part of save data, so adjust in_data_len */
-		in_data_len -= fill_size;
-
-		ibmca_sha256_ctx->tail_len = in_data_len & 0x3f;
-		if(ibmca_sha256_ctx->tail_len) {
-			in_data_len &= ~0x3f;
-			memcpy(ibmca_sha256_ctx->tail,
-			       in_data + fill_size + in_data_len,
-			       ibmca_sha256_ctx->tail_len);
-			/* fill_size is added to in_data down below */
-		}
-	} else if (ibmca_sha256_ctx->c.runningLength > 0) {
-		if (ibmca_sha256_ctx->tail_len) {
-			fill_size = SHA256_BLOCK_SIZE - ibmca_sha256_ctx->tail_len;
-			if (fill_size < in_data_len) {
-				memcpy(ibmca_sha256_ctx->tail
-				       + ibmca_sha256_ctx->tail_len, in_data,
-				       fill_size);
-
-				/* Submit the filled out save buffer */
-				if (p_ica_sha256(message_part,
-						(unsigned int)SHA256_BLOCK_SIZE,
-						ibmca_sha256_ctx->tail,
-						&ibmca_sha256_ctx->c,
-						tmp_hash)) {
-					IBMCAerr(IBMCA_F_IBMCA_SHA256_UPDATE,
-						 IBMCA_R_REQUEST_FAILED);
-					return 0;
-				}
-			} else {
-				memcpy(ibmca_sha256_ctx->tail
-				       + ibmca_sha256_ctx->tail_len, in_data,
-				       in_data_len);
-				ibmca_sha256_ctx->tail_len += in_data_len;
-				return 1;
-			}
-
-			/*
-			 * We had to use some of the data from in_data to
-			 * fill out the empty part of save data, so adjust
-			 * in_data_len
-			 */
-			in_data_len -= fill_size;
-
-			ibmca_sha256_ctx->tail_len = in_data_len & 0x3f;
-			if (ibmca_sha256_ctx->tail_len) {
-				in_data_len &= ~0x3f;
-				memcpy(ibmca_sha256_ctx->tail,
-				       in_data + fill_size + in_data_len,
-					ibmca_sha256_ctx->tail_len);
-			}
-		} else {
-			/* This is the odd case, where we need to go
-			 * ahead and send the first X * 64 byte chunks
-			 * in to be processed and copy the last <64
-			 * byte area into the tail. -KEY */
-			ibmca_sha256_ctx->tail_len = in_data_len & 0x3f;
-			if (ibmca_sha256_ctx->tail_len) {
-				in_data_len &= ~0x3f;
-				memcpy(ibmca_sha256_ctx->tail,
-				       in_data + in_data_len,
-				       ibmca_sha256_ctx->tail_len);
-			}
-		}
-	}
-
-	/* If the data passed in was <64 bytes, in_data_len will be 0 */
-        if (in_data_len &&
-	    p_ica_sha256(message_part,
-			(unsigned int)in_data_len, (unsigned char *)(in_data + fill_size),
-			&ibmca_sha256_ctx->c,
-			tmp_hash)) {
-		IBMCAerr(IBMCA_F_IBMCA_SHA256_UPDATE, IBMCA_R_REQUEST_FAILED);
-		return 0;
-	}
-
-	return 1;
-}				// end ibmca_sha256_update
-
-static int ibmca_sha256_final(EVP_MD_CTX *ctx, unsigned char *md)
-{
-#ifdef OLDER_OPENSSL
-	IBMCA_SHA256_CTX *ibmca_sha256_ctx = ctx->md_data;
-#else
-	IBMCA_SHA256_CTX *ibmca_sha256_ctx = (IBMCA_SHA256_CTX *) EVP_MD_CTX_md_data(ctx);
-#endif
-	unsigned int message_part = 0;
-
-	if (ibmca_sha256_ctx->c.runningLength)
-		message_part = SHA_MSG_PART_FINAL;
-	else
-		message_part = SHA_MSG_PART_ONLY;
-
-	if (p_ica_sha256(message_part,
-			ibmca_sha256_ctx->tail_len,
-			(unsigned char *)ibmca_sha256_ctx->tail,
-			&ibmca_sha256_ctx->c,
-			md)) {
-		IBMCAerr(IBMCA_F_IBMCA_SHA256_FINAL, IBMCA_R_REQUEST_FAILED);
-		return 0;
-	}
-
-	return 1;
-}				// end ibmca_sha256_final
-
-static int ibmca_sha256_cleanup(EVP_MD_CTX *ctx)
-{
-	return 1;
-}				// end ibmca_sha256_cleanup
-#endif // OPENSSL_NO_SHA256
-
-#ifndef OPENSSL_NO_SHA512
-static int ibmca_sha512_init(EVP_MD_CTX *ctx)
-{
-#ifdef OLDER_OPENSSL
-	IBMCA_SHA512_CTX *ibmca_sha512_ctx = ctx->md_data;
-#else
-	IBMCA_SHA512_CTX *ibmca_sha512_ctx = (IBMCA_SHA512_CTX *) EVP_MD_CTX_md_data(ctx);
-#endif
-	memset((unsigned char *)ibmca_sha512_ctx, 0, sizeof(*ibmca_sha512_ctx));
-	return 1;
-}
-
-static int
-ibmca_sha512_update(EVP_MD_CTX *ctx, const void *in_data, unsigned long inlen)
-{
-#ifdef OLDER_OPENSSL
-	IBMCA_SHA512_CTX *ibmca_sha512_ctx = ctx->md_data;
-#else
-	IBMCA_SHA512_CTX *ibmca_sha512_ctx = (IBMCA_SHA512_CTX *) EVP_MD_CTX_md_data(ctx);
-#endif
-	unsigned int message_part = SHA_MSG_PART_MIDDLE, fill_size = 0;
-	unsigned long in_data_len = inlen;
-	unsigned char tmp_hash[SHA512_HASH_LENGTH];
-
-	if (in_data_len == 0)
-		return 1;
-
-	if (ibmca_sha512_ctx->c.runningLengthLow == 0
-	    && ibmca_sha512_ctx->tail_len == 0) {
-		message_part = SHA_MSG_PART_FIRST;
-
-		ibmca_sha512_ctx->tail_len = in_data_len & 0x7f;
-		if (ibmca_sha512_ctx->tail_len) {
-			in_data_len &= ~0x7f;
-			memcpy(ibmca_sha512_ctx->tail, in_data + in_data_len,
-			       ibmca_sha512_ctx->tail_len);
-		}
-	} else if (ibmca_sha512_ctx->c.runningLengthLow == 0
-		   && ibmca_sha512_ctx->tail_len > 0 ) {
-		/* Here we need to fill out the temporary tail buffer
-		 * until it has 128 bytes in it, then call ica_sha512 on
-		 * that buffer.  If there weren't enough bytes passed
-		 * in to fill it out, just copy in what we can and
-		 * return success without calling ica_sha512.
-		 */
-
-		fill_size = SHA512_BLOCK_SIZE - ibmca_sha512_ctx->tail_len;
-		if (fill_size < in_data_len) {
-			memcpy(ibmca_sha512_ctx->tail
-			       + ibmca_sha512_ctx->tail_len, in_data,
-			       fill_size);
-
-			/* Submit the filled out tail buffer */
-			if (p_ica_sha512((unsigned int)SHA_MSG_PART_FIRST,
-					 (unsigned int)SHA512_BLOCK_SIZE,
-					 ibmca_sha512_ctx->tail,
-					 &ibmca_sha512_ctx->c, tmp_hash)) {
-				IBMCAerr(IBMCA_F_IBMCA_SHA512_UPDATE,
-					 IBMCA_R_REQUEST_FAILED);
-				return 0;
-			}
-		} else {
-			memcpy(ibmca_sha512_ctx->tail
-			       + ibmca_sha512_ctx->tail_len, in_data,
-			       in_data_len);
-			ibmca_sha512_ctx->tail_len += in_data_len;
-			return 1;
-		}
-
-		/* We had to use 'fill_size' bytes from in_data to fill out the
-		 * empty part of save data, so adjust in_data_len
-		 */
-		in_data_len -= fill_size;
-
-		ibmca_sha512_ctx->tail_len = in_data_len & 0x7f;
-		if (ibmca_sha512_ctx->tail_len) {
-			in_data_len &= ~0x7f;
-			memcpy(ibmca_sha512_ctx->tail,
-			       in_data + fill_size + in_data_len,
-			       ibmca_sha512_ctx->tail_len);
-			/* fill_size is added to in_data down below */
-		}
-	} else if (ibmca_sha512_ctx->c.runningLengthLow > 0) {
-		if (ibmca_sha512_ctx->tail_len) {
-			fill_size = SHA512_BLOCK_SIZE - ibmca_sha512_ctx->tail_len;
-			if (fill_size < in_data_len) {
-				memcpy(ibmca_sha512_ctx->tail
-				       + ibmca_sha512_ctx->tail_len, in_data,
-					fill_size);
-
-				/* Submit the filled out save buffer */
-				if (p_ica_sha512(message_part,
-						(unsigned int)SHA512_BLOCK_SIZE,
-						ibmca_sha512_ctx->tail,
-						&ibmca_sha512_ctx->c,
-						tmp_hash)) {
-					IBMCAerr(IBMCA_F_IBMCA_SHA512_UPDATE,
-						 IBMCA_R_REQUEST_FAILED);
-					return 0;
-				}
-			} else {
-				memcpy(ibmca_sha512_ctx->tail
-				       + ibmca_sha512_ctx->tail_len, in_data,
-				       in_data_len);
-				ibmca_sha512_ctx->tail_len += in_data_len;
-				return 1;
-			}
-
-			/*
-			 * We had to use some of the data from in_data to
-			 * fill out the empty part of save data, so adjust
-			 * in_data_len
-			 */
-			in_data_len -= fill_size;
-
-			ibmca_sha512_ctx->tail_len = in_data_len & 0x7f;
-			if (ibmca_sha512_ctx->tail_len) {
-				in_data_len &= ~0x7f;
-				memcpy(ibmca_sha512_ctx->tail,
-				       in_data + fill_size + in_data_len,
-				       ibmca_sha512_ctx->tail_len);
-			}
-		} else {
-			/* This is the odd case, where we need to go
-			 * ahead and send the first X * 128 byte chunks
-			 * in to be processed and copy the last <128
-			 * byte area into the tail.
-			 */
-			ibmca_sha512_ctx->tail_len = in_data_len & 0x7f;
-			if (ibmca_sha512_ctx->tail_len) {
-				in_data_len &= ~0x7f;
-				memcpy(ibmca_sha512_ctx->tail,
-				       in_data + in_data_len,
-				       ibmca_sha512_ctx->tail_len);
-			}
-		}
-	}
-
-	/* If the data passed in was <128 bytes, in_data_len will be 0 */
-	if (in_data_len &&
-	    p_ica_sha512(message_part, (unsigned int)in_data_len,
-			 (unsigned char *)(in_data + fill_size),
-			 &ibmca_sha512_ctx->c, tmp_hash)) {
-		IBMCAerr(IBMCA_F_IBMCA_SHA512_UPDATE, IBMCA_R_REQUEST_FAILED);
-		return 0;
-	}
-
-	return 1;
-}
-
-static int ibmca_sha512_final(EVP_MD_CTX *ctx, unsigned char *md)
-{
-#ifdef OLDER_OPENSSL
-	IBMCA_SHA512_CTX *ibmca_sha512_ctx = ctx->md_data;
-#else
-	IBMCA_SHA512_CTX *ibmca_sha512_ctx = (IBMCA_SHA512_CTX *) EVP_MD_CTX_md_data(ctx);
-#endif
-	unsigned int message_part = 0;
-
-	if (ibmca_sha512_ctx->c.runningLengthLow)
-		message_part = SHA_MSG_PART_FINAL;
-	else
-		message_part = SHA_MSG_PART_ONLY;
-
-	if (p_ica_sha512(message_part, ibmca_sha512_ctx->tail_len,
-			 (unsigned char *)ibmca_sha512_ctx->tail,
-			 &ibmca_sha512_ctx->c, md)) {
-		IBMCAerr(IBMCA_F_IBMCA_SHA512_FINAL, IBMCA_R_REQUEST_FAILED);
-		return 0;
-	}
-
-	return 1;
-}
-
-static int ibmca_sha512_cleanup(EVP_MD_CTX *ctx)
-{
-	return 1;
-}
-#endif // OPENSSL_NO_SHA512
-
-static int ibmca_mod_exp(BIGNUM *r, const BIGNUM *a, const BIGNUM *p,
-			 const BIGNUM *m, BN_CTX *ctx)
-{
-	/* r = (a^p) mod m
-	                        r = output
-	                        a = input
-	                        p = exponent
-	                        m = modulus
-	*/
-
-	unsigned char *input = NULL, *output =  NULL;
-	ica_rsa_key_mod_expo_t *key = NULL;
-	unsigned int rc;
-	int plen, mlen, inputlen;
-
-	if (!ibmca_dso) {
-		IBMCAerr(IBMCA_F_IBMCA_MOD_EXP, IBMCA_R_NOT_LOADED);
-		goto err;
-	}
-
-	/*
-	 make necessary memory allocations
-	 FIXME: Would it be possible to minimize memory allocation overhead by either
-                allocating it all at once or having a static storage?
-	*/
-	key = (ica_rsa_key_mod_expo_t *) calloc(1, sizeof(ica_rsa_key_mod_expo_t));
-	if (key == NULL) {
-		IBMCAerr(IBMCA_F_IBMCA_MOD_EXP, IBMCA_R_REQUEST_FAILED);
-		goto err;
-	}
-
-	key->key_length = mlen = BN_num_bytes(m);
-
-	key->modulus = (unsigned char *) calloc(1, key->key_length);
-	if (key->modulus == NULL) {
-		IBMCAerr(IBMCA_F_IBMCA_MOD_EXP, IBMCA_R_REQUEST_FAILED);
-		goto err;
-	}
-
-	plen = BN_num_bytes(p);
-
-	/* despite plen, key->exponent must be key->key_length in size */
-	key->exponent = (unsigned char *) calloc(1, key->key_length);
-	if (key->exponent == NULL) {
-		IBMCAerr(IBMCA_F_IBMCA_MOD_EXP, IBMCA_R_REQUEST_FAILED);
-		goto err;
-	}
-
-	inputlen = BN_num_bytes(a);
-
-	/* despite inputlen, input and output must be key->key_length in size */
-	input = (unsigned char *) calloc(1, key->key_length);
-	if (input == NULL) {
-		IBMCAerr(IBMCA_F_IBMCA_MOD_EXP, IBMCA_R_REQUEST_FAILED);
-		goto err;
-	}
-
-	output = (unsigned char *) calloc(1, key->key_length);
-	if (output == NULL) {
-		IBMCAerr(IBMCA_F_IBMCA_MOD_EXP, IBMCA_R_REQUEST_FAILED);
-		goto err;
-	}
-
-	/* Now convert from BIGNUM representation.
-	 * Everything must be right-justified
-	 */
-	BN_bn2bin(m, key->modulus);
-
-	BN_bn2bin(p, key->exponent + key->key_length - plen);
-
-	BN_bn2bin(a, input + key->key_length - inputlen);
-
-	/* execute the ica mod_exp call */
-	rc = p_ica_rsa_mod_expo(ibmca_handle, input, key, output);
-	if (rc != 0) {
-		goto err;
-	}
-	else {
-		rc = 1;
-	}
-
-        /* Convert output to BIGNUM representation.
-	 * right-justified output applies
-	 */
-	/* BN_bin2bn((unsigned char *) (output + key->key_length - inputlen), inputlen, r); */
-	BN_bin2bn((unsigned char *) output, key->key_length, r);
-
-	goto end;
-
-err:
-	rc = 0;    /* error condition */
-
-end:
-	free(key->exponent);
-	free(key->modulus);
-	free(key);
-	free(input);
-	free(output);
-
-	return rc;
-}
-
-#ifndef OPENSSL_NO_RSA
-static int ibmca_rsa_init(RSA *rsa)
-{
-	RSA_blinding_off(rsa);
-
-	return 1;
-}
-
-#ifdef OLDER_OPENSSL
-static int ibmca_rsa_mod_exp(BIGNUM * r0, const BIGNUM * I, RSA * rsa,
-                             BN_CTX *ctx)
-{
-	int to_return = 0;
-
-	if (!rsa->p || !rsa->q || !rsa->dmp1 || !rsa->dmq1 || !rsa->iqmp) {
-		if (!rsa->d || !rsa->n) {
-			IBMCAerr(IBMCA_F_IBMCA_RSA_MOD_EXP,
-				 IBMCA_R_MISSING_KEY_COMPONENTS);
-			goto err;
-		}
-		to_return = ibmca_mod_exp(r0, I, rsa->d, rsa->n, ctx);
-	} else {
-		to_return =
-		    ibmca_mod_exp_crt(r0, I, rsa->p, rsa->q, rsa->dmp1,
-				      rsa->dmq1, rsa->iqmp, ctx);
-	}
-err:
-	return to_return;
-}
-#else
-static int ibmca_rsa_mod_exp(BIGNUM * r0, const BIGNUM * I, RSA * rsa,
-                             BN_CTX *ctx)
-{
-	int to_return = 0;
-	const BIGNUM *d, *n, *p, *q, *dmp1, *dmq1, *iqmp;
-
-	RSA_get0_key(rsa, &n, NULL, &d);
-	RSA_get0_factors(rsa, &p, &q);
-	RSA_get0_crt_params(rsa, &dmp1, &dmq1, &iqmp);
-	if (!p || !q || !dmp1 || !dmq1 || iqmp) {
-		if (!d || !n) {
-			IBMCAerr(IBMCA_F_IBMCA_RSA_MOD_EXP,
-				 IBMCA_R_MISSING_KEY_COMPONENTS);
-			goto err;
-		}
-		to_return = ibmca_mod_exp(r0, I, d, n, ctx);
-	} else {
-		to_return = ibmca_mod_exp_crt(r0, I, p, q, dmp1, dmq1, iqmp, ctx);
-	}
-err:
-	return to_return;
-}
-#endif
-#endif
-
-/* Ein kleines chinesisches "Restessen"  */
-static int ibmca_mod_exp_crt(BIGNUM * r, const BIGNUM * a,
-			     const BIGNUM * p, const BIGNUM * q,
-			     const BIGNUM * dmp1, const BIGNUM * dmq1,
-			     const BIGNUM * iqmp, BN_CTX * ctx)
-{
-	/*
-	r = output
-	a = input
-	p and q are themselves
-	dmp1, dmq1 are dp and dq respectively
-	iqmp is qInverse
-	*/
-
-	ica_rsa_key_crt_t *key = NULL;
-	unsigned char *output = NULL, *input = NULL;
-	int rc;
-	int plen, qlen, dplen, dqlen, qInvlen;
-	int inputlen;
-
-	/*
-	 make necessary memory allocations
-	 FIXME: Would it be possible to minimize memory allocation overhead by either
-                allocating it all at once or having a static storage?
-	*/
-	key = (ica_rsa_key_crt_t *) calloc(1, sizeof(ica_rsa_key_crt_t));
-	if (key == NULL) {
-		IBMCAerr(IBMCA_F_IBMCA_MOD_EXP, IBMCA_R_REQUEST_FAILED);
-		goto err;
-	}
-
-	/* buffers pointed by p, q, dp, dq and qInverse in struct
-	 * ica_rsa_key_crt_t must be of size key_legth/2 or larger.
-	 * p, dp and qInverse have an additional 8-byte padding. */
-
-	plen = BN_num_bytes(p);
-	qlen = BN_num_bytes(q);
-	key->key_length = 2 * (plen > qlen ? plen : qlen);
-
-	key->p = (unsigned char *) calloc(1, (key->key_length/2) + 8);
-	if (key->p == NULL) {
-		IBMCAerr(IBMCA_F_IBMCA_MOD_EXP, IBMCA_R_REQUEST_FAILED);
-		goto err;
-	}
-
-	dplen = BN_num_bytes(dmp1);
-	key->dp = (unsigned char *) calloc(1, (key->key_length/2) + 8);
-	if (key->dp == NULL) {
-		IBMCAerr(IBMCA_F_IBMCA_MOD_EXP, IBMCA_R_REQUEST_FAILED);
-		goto err;
-	}
-
-	key->q = (unsigned char *) calloc(1, key->key_length/2);
-	if (key->q == NULL) {
-		IBMCAerr(IBMCA_F_IBMCA_MOD_EXP, IBMCA_R_REQUEST_FAILED);
-		goto err;
-	}
-
-	dqlen = BN_num_bytes(dmq1);
-	key->dq = (unsigned char *) calloc(1, key->key_length/2);
-	if (key->dq == NULL) {
-		IBMCAerr(IBMCA_F_IBMCA_MOD_EXP, IBMCA_R_REQUEST_FAILED);
-		goto err;
-	}
-
-	qInvlen = BN_num_bytes(iqmp);
-	key->qInverse = (unsigned char *) calloc(1, (key->key_length/2) + 8);
-	if (key->qInverse == NULL) {
-		IBMCAerr(IBMCA_F_IBMCA_MOD_EXP, IBMCA_R_REQUEST_FAILED);
-		goto err;
-	}
-
-	inputlen = BN_num_bytes(a);
-	if (inputlen > key->key_length) {     /* input can't be larger than key */
-		IBMCAerr(IBMCA_F_IBMCA_MOD_EXP, IBMCA_R_REQUEST_FAILED);
-		goto err;
-	}
-
-	/* allocate input to the size of key_length in bytes, and
-	 * pad front with zero if inputlen < key->key_length */
-	input = (unsigned char *) calloc(1, key->key_length);
-	if (input == NULL) {
-		IBMCAerr(IBMCA_F_IBMCA_MOD_EXP, IBMCA_R_REQUEST_FAILED);
-		goto err;
-	}
-
-	/* output must also be key_length in size */
-	output = (unsigned char *) calloc(1, key->key_length);
-	if (output == NULL) {
-		IBMCAerr(IBMCA_F_IBMCA_MOD_EXP, IBMCA_R_REQUEST_FAILED);
-		goto err;
-	}
-
-
-	/* Now convert from BIGNUM representation.
-	 * p, dp and qInverse have an additional 8-byte padding,
-	 * and everything must be right-justified */
-	BN_bn2bin(p, key->p + 8 + (key->key_length/2) - plen);
-
-	BN_bn2bin(dmp1, key->dp + 8 + (key->key_length/2) - dplen);
-
-	BN_bn2bin(q, key->q + (key->key_length/2) - qlen);
-
-	BN_bn2bin(dmq1, key->dq + (key->key_length/2) - dqlen);
-
-	BN_bn2bin(iqmp, key->qInverse + 8 + (key->key_length/2) - qInvlen);
-
-	BN_bn2bin(a, input + key->key_length - inputlen);
-
-	/* execute the ica crt call */
-
-	rc = p_ica_rsa_crt(ibmca_handle, input, key, output);
-	if (rc != 0) {
-		IBMCAerr(IBMCA_F_IBMCA_MOD_EXP, IBMCA_R_REQUEST_FAILED);
-		goto err;
-	}
-	else {
-		rc = 1;
-	}
-
-	/* Convert output to BIGNUM representation */
-	/* BN_bin2bn((unsigned char *) (output + key->key_length - inputlen), inputlen, r); */
-	BN_bin2bn((unsigned char *) output, key->key_length, r);
-
-
-	goto end;
-
-err:
-	rc = 0;    /* error condition */
-
-end:
-	free(key->p);
-	free(key->q);
-	free(key->dp);
-	free(key->dq);
-	free(key->qInverse);
-	free(key);
-	free(input);
-	free(output);
-
-	return rc;
-}
-
-#ifndef OPENSSL_NO_DSA
-/* This code was liberated and adapted from the commented-out code in
- * dsa_ossl.c. Because of the unoptimised form of the Ibmca acceleration
- * (it doesn't have a CRT form for RSA), this function means that an
- * Ibmca system running with a DSA server certificate can handshake
- * around 5 or 6 times faster/more than an equivalent system running with
- * RSA. Just check out the "signs" statistics from the RSA and DSA parts
- * of "openssl speed -engine ibmca dsa1024 rsa1024". */
-#ifdef OLDER_OPENSSL
-static int ibmca_dsa_mod_exp(DSA * dsa, BIGNUM * rr, BIGNUM * a1,
-			     BIGNUM * p1, BIGNUM * a2, BIGNUM * p2,
-			     BIGNUM * m, BN_CTX * ctx,
-			     BN_MONT_CTX * in_mont)
-#else
-static int ibmca_dsa_mod_exp(DSA * dsa, BIGNUM * rr, const BIGNUM * a1,
-			     const BIGNUM * p1, const BIGNUM * a2,
-			     const BIGNUM * p2, const BIGNUM * m,
-			     BN_CTX * ctx, BN_MONT_CTX * in_mont)
-#endif
-{
-	BIGNUM *t;
-	int to_return = 0;
-
-	t = BN_new();
-	/* let rr = a1 ^ p1 mod m */
-	if (!ibmca_mod_exp(rr, a1, p1, m, ctx))
-		goto end;
-	/* let t = a2 ^ p2 mod m */
-	if (!ibmca_mod_exp(t, a2, p2, m, ctx))
-		goto end;
-	/* let rr = rr * t mod m */
-	if (!BN_mod_mul(rr, rr, t, m, ctx))
-		goto end;
-	to_return = 1;
-end:
-	BN_free(t);
-	return to_return;
-}
-
-#ifdef OLDER_OPENSSL
-static int ibmca_mod_exp_dsa(DSA * dsa, BIGNUM * r, BIGNUM * a,
-			     const BIGNUM * p, const BIGNUM * m,
-			     BN_CTX * ctx, BN_MONT_CTX * m_ctx)
-#else
-static int ibmca_mod_exp_dsa(DSA * dsa, BIGNUM * r, const BIGNUM * a,
-			     const BIGNUM * p, const BIGNUM * m,
-			     BN_CTX * ctx, BN_MONT_CTX * m_ctx)
-#endif
-{
-	return ibmca_mod_exp(r, a, p, m, ctx);
-}
-#endif
-
-/* This function is aliased to mod_exp (with the mont stuff dropped). */
-static int ibmca_mod_exp_mont(BIGNUM * r, const BIGNUM * a,
-			      const BIGNUM * p, const BIGNUM * m,
-			      BN_CTX * ctx, BN_MONT_CTX * m_ctx)
-{
-	return ibmca_mod_exp(r, a, p, m, ctx);
-}
-
-#ifndef OPENSSL_NO_DH
-/* This function is aliased to mod_exp (with the dh and mont dropped). */
-static int ibmca_mod_exp_dh(DH const *dh, BIGNUM * r,
-			    const BIGNUM * a, const BIGNUM * p,
-			    const BIGNUM * m, BN_CTX * ctx,
-			    BN_MONT_CTX * m_ctx)
-{
-	return ibmca_mod_exp(r, a, p, m, ctx);
-}
-#endif
-
-/* Random bytes are good */
-static int ibmca_rand_bytes(unsigned char *buf, int num)
-{
-	unsigned int rc;
-
-	rc = p_ica_random_number_generate(num, buf);
-	if (rc < 0) {
-		IBMCAerr(IBMCA_F_IBMCA_RAND_BYTES, IBMCA_R_REQUEST_FAILED);
-		return 0;
-	}
-	return 1;
-}
-
-static int ibmca_rand_status(void)
-{
-	return 1;
-}
-
-/* This stuff is needed if this ENGINE is being compiled into a self-contained
- * shared-library. */
-static int bind_fn(ENGINE * e, const char *id)
-{
-	if (id && (strcmp(id, engine_ibmca_id) != 0))	/* WJH XXX */
-		return 0;
-	if (!bind_helper(e))
-		return 0;
-	return 1;
-}
-
-IMPLEMENT_DYNAMIC_CHECK_FN()
-IMPLEMENT_DYNAMIC_BIND_FN(bind_fn)
-
-#endif				/* !OPENSSL_NO_HW_IBMCA */
-#endif				/* !OPENSSL_NO_HW */
diff -pruN 1.4.0-1/src/e_ibmca_err.c 2.5.0-0ubuntu1/src/e_ibmca_err.c
--- 1.4.0-1/src/e_ibmca_err.c	2017-09-08 17:54:06.000000000 +0000
+++ 2.5.0-0ubuntu1/src/e_ibmca_err.c	1970-01-01 00:00:00.000000000 +0000
@@ -1,126 +0,0 @@
-/*
- * Copyright [2005-2017] International Business Machines Corp.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
-
-/* NOTE: this file was auto generated by the mkerr.pl script: any changes
- * made to it will be overwritten when the script next updates this file,
- * only reason strings will be preserved.
- */
-
-#include <stdio.h>
-#include <openssl/err.h>
-#include "e_ibmca_err.h"
-
-/* BEGIN ERROR CODES */
-#ifndef OPENSSL_NO_ERR
-static ERR_STRING_DATA IBMCA_str_functs[] = {
-	{ERR_PACK(0, IBMCA_F_IBMCA_CTRL, 0), "IBMCA_CTRL"},
-	{ERR_PACK(0, IBMCA_F_IBMCA_FINISH, 0), "IBMCA_FINISH"},
-	{ERR_PACK(0, IBMCA_F_IBMCA_INIT, 0), "IBMCA_INIT"},
-	{ERR_PACK(0, IBMCA_F_IBMCA_MOD_EXP, 0), "IBMCA_MOD_EXP"},
-	{ERR_PACK(0, IBMCA_F_IBMCA_MOD_EXP_CRT, 0), "IBMCA_MOD_EXP_CRT"},
-	{ERR_PACK(0, IBMCA_F_IBMCA_RAND_BYTES, 0), "IBMCA_RAND_BYTES"},
-	{ERR_PACK(0, IBMCA_F_IBMCA_RSA_MOD_EXP, 0), "IBMCA_RSA_MOD_EXP"},
-	{ERR_PACK(0, IBMCA_F_IBMCA_DES_CIPHER, 0), "IBMCA_DES_CIPHER"},
-	{ERR_PACK(0, IBMCA_F_IBMCA_TDES_CIPHER, 0), "IBMCA_TDES_CIPHER"},
-	{ERR_PACK(0, IBMCA_F_IBMCA_SHA1_UPDATE, 0), "IBMCA_SHA1_UPDATE"},
-	{ERR_PACK(0, IBMCA_F_IBMCA_SHA1_FINAL, 0), "IBMCA_SHA1_FINAL"},
-	{ERR_PACK(0, IBMCA_F_IBMCA_AES_128_CIPHER, 0), "IBMCA_AES_128_CIPHER"},
-	{ERR_PACK(0, IBMCA_F_IBMCA_AES_192_CIPHER, 0), "IBMCA_AES_192_CIPHER"},
-	{ERR_PACK(0, IBMCA_F_IBMCA_AES_256_CIPHER, 0), "IBMCA_AES_256_CIPHER"},
-	{ERR_PACK(0, IBMCA_F_IBMCA_SHA256_UPDATE, 0), "IBMCA_SHA256_UPDATE"},
-	{ERR_PACK(0, IBMCA_F_IBMCA_SHA256_FINAL, 0), "IBMCA_SHA256_FINAL"},
-	{ERR_PACK(0, IBMCA_F_IBMCA_SHA512_UPDATE, 0), "IBMCA_SHA512_UPDATE"},
-	{ERR_PACK(0, IBMCA_F_IBMCA_SHA512_FINAL, 0), "IBMCA_SHA512_FINAL"},
-	{0, NULL}
-};
-
-static ERR_STRING_DATA IBMCA_str_reasons[] = {
-	{IBMCA_R_ALREADY_LOADED, "already loaded"},
-	{IBMCA_R_BN_CTX_FULL, "bn ctx full"},
-	{IBMCA_R_BN_EXPAND_FAIL, "bn expand fail"},
-	{IBMCA_R_CTRL_COMMAND_NOT_IMPLEMENTED,
-	 "ctrl command not implemented"},
-	{IBMCA_R_DSO_FAILURE, "dso failure"},
-	{IBMCA_R_MEXP_LENGTH_TO_LARGE, "mexp length to large"},
-	{IBMCA_R_MISSING_KEY_COMPONENTS, "missing key components"},
-	{IBMCA_R_NOT_INITIALISED, "not initialised"},
-	{IBMCA_R_NOT_LOADED, "not loaded"},
-	{IBMCA_R_OPERANDS_TO_LARGE, "operands to large"},
-	{IBMCA_R_OUTLEN_TO_LARGE, "outlen to large"},
-	{IBMCA_R_REQUEST_FAILED, "request failed"},
-	{IBMCA_R_UNDERFLOW_CONDITION, "underflow condition"},
-	{IBMCA_R_UNDERFLOW_KEYRECORD, "underflow keyrecord"},
-	{IBMCA_R_UNIT_FAILURE, "unit failure"},
-	{IBMCA_R_CIPHER_MODE_NOT_SUPPORTED, "cipher mode not supported"},
-	{0, NULL}
-};
-
-#endif
-
-#ifdef IBMCA_LIB_NAME
-static ERR_STRING_DATA IBMCA_lib_name[] = {
-	{0, IBMCA_LIB_NAME},
-	{0, NULL}
-};
-#endif
-
-
-static int IBMCA_lib_error_code = 0;
-static int IBMCA_error_init = 1;
-
-void ERR_load_IBMCA_strings(void)
-{
-	if (IBMCA_lib_error_code == 0)
-		IBMCA_lib_error_code = ERR_get_next_error_library();
-
-	if (IBMCA_error_init) {
-		IBMCA_error_init = 0;
-#ifndef OPENSSL_NO_ERR
-		ERR_load_strings(IBMCA_lib_error_code, IBMCA_str_functs);
-		ERR_load_strings(IBMCA_lib_error_code, IBMCA_str_reasons);
-#endif
-
-#ifdef IBMCA_LIB_NAME
-		IBMCA_lib_name->error =
-		    ERR_PACK(IBMCA_lib_error_code, 0, 0);
-		ERR_load_strings(0, IBMCA_lib_name);
-#endif
-	}
-}
-
-void ERR_unload_IBMCA_strings(void)
-{
-	if (IBMCA_error_init == 0) {
-#ifndef OPENSSL_NO_ERR
-		ERR_unload_strings(IBMCA_lib_error_code, IBMCA_str_functs);
-		ERR_unload_strings(IBMCA_lib_error_code,
-				   IBMCA_str_reasons);
-#endif
-
-#ifdef IBMCA_LIB_NAME
-		ERR_unload_strings(0, IBMCA_lib_name);
-#endif
-		IBMCA_error_init = 1;
-	}
-}
-
-void ERR_IBMCA_error(int function, int reason, char *file, int line)
-{
-	if (IBMCA_lib_error_code == 0)
-		IBMCA_lib_error_code = ERR_get_next_error_library();
-	ERR_PUT_error(IBMCA_lib_error_code, function, reason, file, line);
-}
diff -pruN 1.4.0-1/src/e_ibmca_err.h 2.5.0-0ubuntu1/src/e_ibmca_err.h
--- 1.4.0-1/src/e_ibmca_err.h	2017-09-08 17:54:06.000000000 +0000
+++ 2.5.0-0ubuntu1/src/e_ibmca_err.h	1970-01-01 00:00:00.000000000 +0000
@@ -1,70 +0,0 @@
-/*
- * Copyright [2005-2017] International Business Machines Corp.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
-
-#ifndef HEADER_IBMCA_ERR_H
-#define HEADER_IBMCA_ERR_H
-
-/* BEGIN ERROR CODES */
-/* The following lines are auto generated by the script mkerr.pl. Any changes
- * made after this point may be overwritten when the script is next run.
- */
-void ERR_load_IBMCA_strings(void);
-void ERR_unload_IBMCA_strings(void);
-void ERR_IBMCA_error(int function, int reason, char *file, int line);
-#define IBMCAerr(f,r) ERR_IBMCA_error((f),(r),__FILE__,__LINE__)
-
-/* Error codes for the IBMCA functions. */
-
-/* Function codes. */
-#define IBMCA_F_IBMCA_CTRL				 100
-#define IBMCA_F_IBMCA_FINISH				 101
-#define IBMCA_F_IBMCA_INIT				 102
-#define IBMCA_F_IBMCA_MOD_EXP				 103
-#define IBMCA_F_IBMCA_MOD_EXP_CRT			 104
-#define IBMCA_F_IBMCA_RAND_BYTES			 105
-#define IBMCA_F_IBMCA_RSA_MOD_EXP			 106
-#define IBMCA_F_IBMCA_DES_CIPHER			 107
-#define IBMCA_F_IBMCA_TDES_CIPHER			 108
-#define IBMCA_F_IBMCA_SHA1_UPDATE			 109
-#define IBMCA_F_IBMCA_SHA1_FINAL			 110
-#define IBMCA_F_IBMCA_AES_128_CIPHER			 111
-#define IBMCA_F_IBMCA_AES_192_CIPHER			 112
-#define IBMCA_F_IBMCA_AES_256_CIPHER			 113
-#define IBMCA_F_IBMCA_SHA256_UPDATE			 114
-#define IBMCA_F_IBMCA_SHA256_FINAL			 115
-#define IBMCA_F_IBMCA_SHA512_UPDATE			 116
-#define IBMCA_F_IBMCA_SHA512_FINAL			 117
-
-/* Reason codes. */
-#define IBMCA_R_ALREADY_LOADED				 100
-#define IBMCA_R_BN_CTX_FULL				 101
-#define IBMCA_R_BN_EXPAND_FAIL				 102
-#define IBMCA_R_CTRL_COMMAND_NOT_IMPLEMENTED		 103
-#define IBMCA_R_DSO_FAILURE				 104
-#define IBMCA_R_MEXP_LENGTH_TO_LARGE			 110
-#define IBMCA_R_MISSING_KEY_COMPONENTS			 105
-#define IBMCA_R_NOT_INITIALISED				 106
-#define IBMCA_R_NOT_LOADED				 107
-#define IBMCA_R_OPERANDS_TO_LARGE			 111
-#define IBMCA_R_OUTLEN_TO_LARGE				 112
-#define IBMCA_R_REQUEST_FAILED				 108
-#define IBMCA_R_UNDERFLOW_CONDITION			 113
-#define IBMCA_R_UNDERFLOW_KEYRECORD			 114
-#define IBMCA_R_UNIT_FAILURE				 109
-#define IBMCA_R_CIPHER_MODE_NOT_SUPPORTED		 115
-
-#endif
diff -pruN 1.4.0-1/src/engine/Makefile.am 2.5.0-0ubuntu1/src/engine/Makefile.am
--- 1.4.0-1/src/engine/Makefile.am	1970-01-01 00:00:00.000000000 +0000
+++ 2.5.0-0ubuntu1/src/engine/Makefile.am	2025-04-16 08:09:24.000000000 +0000
@@ -0,0 +1,33 @@
+VERSION = 2:5:0
+
+lib_LTLIBRARIES=ibmca.la
+
+ibmca_la_SOURCES=e_ibmca.c \
+				 e_ibmca_err.c \
+				 ibmca_cipher.c \
+				 ibmca_digest.c \
+				 ibmca_rsa.c \
+				 ibmca_dsa.c \
+				 ibmca_dh.c \
+				 ibmca_ec.c \
+				 ibmca_pkey.c
+
+ibmca_la_LIBADD=-ldl
+ibmca_la_LDFLAGS=-module -version-number ${VERSION} -shared -no-undefined \
+		  -avoid-version -Wl,--version-script=${srcdir}/ibmca.map
+
+dist_ibmca_la_SOURCES=ibmca.h e_ibmca_err.h openssl-compat.h
+EXTRA_DIST = openssl.cnf.sample ibmca.map test/ibmca_mechaList_test.c
+
+ACLOCAL_AMFLAGS = -I m4
+SUBDIRS = doc
+
+ibmca-engine-opensslconfig: ibmca-engine-opensslconfig.in
+	$(AM_V_GEN)@SED@ -e s!\@libdir\@!@libdir@!g < $< > $@-t && 	\
+	chmod a+x $@-t &&						\
+	$(am__mv) $@-t $@
+
+noinst_SCRIPTS = ibmca-engine-opensslconfig
+EXTRA_DIST += ibmca-engine-opensslconfig.in
+
+CLEANFILES = ibmca-engine-opensslconfig
diff -pruN 1.4.0-1/src/engine/doc/Makefile.am 2.5.0-0ubuntu1/src/engine/doc/Makefile.am
--- 1.4.0-1/src/engine/doc/Makefile.am	1970-01-01 00:00:00.000000000 +0000
+++ 2.5.0-0ubuntu1/src/engine/doc/Makefile.am	2025-04-16 08:09:24.000000000 +0000
@@ -0,0 +1,2 @@
+man5_MANS = ibmca.man
+dist_man5_MANS = $(man5_MANS)
diff -pruN 1.4.0-1/src/engine/doc/ibmca.man 2.5.0-0ubuntu1/src/engine/doc/ibmca.man
--- 1.4.0-1/src/engine/doc/ibmca.man	1970-01-01 00:00:00.000000000 +0000
+++ 2.5.0-0ubuntu1/src/engine/doc/ibmca.man	2025-04-16 08:09:24.000000000 +0000
@@ -0,0 +1,70 @@
+.\" Process this file with
+.\" groff -man -Tascii ibmca.5
+.TH IBMCA 5 2017-08-24 IBM "IBMCA user manual"
+.SH NAME
+IBMCA \- IBMCA is an OpenSSL engine that uses the libica library under s390x to
+accelerate cryptographic operations.
+
+.SH DESCRIPTION
+IBMCA accelerates cryptographic operations of applications that use OpenSSL.
+The engine can be configured by the OpenSSL configuration file.
+
+.SS openssl.cnf
+The OpenSSL configuration file can have an IBMCA section. This section includes
+only OpenSSL configuration options for the IBMCA engine.
+
+.SS Control Commands
+Applications that load an OpenSSL engine can optionally send control commands
+to the engine. Control Commands are key value pairs. The value can be a string,
+a numeric integer or be null. See the engine(3) manpage for a mechanism to
+discover control commands.
+
+.SH OPTIONS
+.SS openssl.cnf
+Options for the IBMCA section in openssl.cnf:
+.PP
+dynamic_path =
+.I /path/to/ibmca.so
+.RS
+Set the path to the IBMCA shared object file allowing OpenSSL to find the file.
+.RE
+.PP
+engine_id =
+.I name
+.RS
+Set the name of the engine. The default name is "ibmca".
+.RE
+.IP "init = 0 | 1"
+OpenSSL will try to initialize the engine if this option is set to 1.
+If set to 0, OpenSSL will not try to initialize the engine.
+.PP
+default_algorithms = ALL |
+.I mechanisms
+.RS
+Redirect all cryptographic operations through the engine or disable types of
+mechanisms that the engine supports.
+If ALL is not used, the default_algorithms consists of a comma separated list
+of
+.I mechanisms
+:
+.B CIPHERS | DIGESTS | RSA | DH | DSA | EC | PKEY_CRYPTO | RAND
+.PP
+Only all CIPHERS and/or DIGESTS can be
+de/activated. Algorithms like AES can not be de/activated independently.
+.PP
+.B Note: 
+Algorithms denoted by CIPHERS, DIGESTS, EC (since IBM z15 for certain curves),
+and PKEY are already accelerated by OpenSSL itself using CPACF.
+Therefore, do not accelerate them using the IBMCA engine. This would actually
+make them slower.
+.SS Control Command
+IBMCA does support one optional control command:
+.PP
+SO_PATH:
+.I /path/to/libica.so
+.RS
+Replaces the current libica library by an libica library located at SO_PATH.
+.RE
+
+.SH SEE ALSO
+.B engine(3)
diff -pruN 1.4.0-1/src/engine/e_ibmca.c 2.5.0-0ubuntu1/src/engine/e_ibmca.c
--- 1.4.0-1/src/engine/e_ibmca.c	1970-01-01 00:00:00.000000000 +0000
+++ 2.5.0-0ubuntu1/src/engine/e_ibmca.c	2025-04-16 08:09:24.000000000 +0000
@@ -0,0 +1,1141 @@
+/*
+ * Copyright [2005-2021] International Business Machines Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+/*
+ * Digest and Cipher support added by Robert H Burroughs (burrough@us.ibm.com).
+ *
+ * DES/3DES/AES-CFB/OFB support added by Kent Yoder (yoder1@us.ibm.com)
+ */
+
+#include <stdint.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <dirent.h>
+#include <dlfcn.h>
+#include <string.h>
+#include <openssl/crypto.h>
+#include <openssl/engine.h>
+#include <openssl/evp.h>
+#include <openssl/objects.h>
+#include <openssl/sha.h>
+#include <openssl/obj_mac.h>
+#include <openssl/aes.h>
+
+#include <ica_api.h>
+#include "ibmca.h"
+#include "e_ibmca_err.h"
+
+#ifndef OPENSSL_NO_HW
+#ifndef OPENSSL_NO_HW_IBMCA
+
+#define IBMCA_LIB_NAME "ibmca engine"
+
+#define AP_PATH "/sys/devices/ap"
+
+/*
+ * The default library name.  The macro LIBICA_SHARED_LIB is provided
+ * via configure at the command line.
+ */
+static const char *LIBICA_NAME = LIBICA_SHARED_LIB;
+/*
+ * If a ctrl command is used to set libica name, we have to strdup the
+ * argument since the config parser will free and clear the element at
+ * the end of parsing.  If the engine is not loaded during
+ * configuration, we will not be able to use the string provided by
+ * the ctrl command since it is cleared.  Remember if we strdup'ed the
+ * string such that we free it at the end.
+ */
+static int LIBICA_NAME_allocated;
+
+/* Constants used when creating the ENGINE */
+static const char *engine_ibmca_id = "ibmca";
+static const char *engine_ibmca_name = "Ibmca hardware engine support";
+
+/* This is a process-global DSO handle used for loading and unloading
+ * the Ibmca library. NB: This is only set (or unset) during an
+ * init() or finish() call (reference counts permitting) and they're
+ * operating with global locks, so this should be thread-safe
+ * implicitly. */
+void *ibmca_dso = NULL;
+
+ica_adapter_handle_t ibmca_handle = DRIVER_NOT_LOADED;
+
+/* entry points into libica, filled out at DSO load time */
+ica_get_functionlist_t          p_ica_get_functionlist;
+ica_set_fallback_mode_t         p_ica_set_fallback_mode;
+ica_open_adapter_t              p_ica_open_adapter;
+ica_close_adapter_t             p_ica_close_adapter;
+ica_rsa_mod_expo_t              p_ica_rsa_mod_expo;
+ica_random_number_generate_t    p_ica_random_number_generate;
+ica_rsa_crt_t                   p_ica_rsa_crt;
+ica_sha1_t                      p_ica_sha1;
+ica_sha256_t                    p_ica_sha256;
+ica_sha512_t                    p_ica_sha512;
+ica_des_ecb_t                   p_ica_des_ecb;
+ica_des_cbc_t                   p_ica_des_cbc;
+ica_des_ofb_t                   p_ica_des_ofb;
+ica_des_cfb_t                   p_ica_des_cfb;
+ica_3des_ecb_t                  p_ica_3des_ecb;
+ica_3des_cbc_t                  p_ica_3des_cbc;
+ica_3des_cfb_t                  p_ica_3des_cfb;
+ica_3des_ofb_t                  p_ica_3des_ofb;
+ica_aes_ecb_t                   p_ica_aes_ecb;
+ica_aes_cbc_t                   p_ica_aes_cbc;
+ica_aes_ofb_t                   p_ica_aes_ofb;
+ica_aes_cfb_t                   p_ica_aes_cfb;
+#ifndef OPENSSL_NO_AES_GCM
+ica_aes_gcm_initialize_t        p_ica_aes_gcm_initialize;
+ica_aes_gcm_intermediate_t      p_ica_aes_gcm_intermediate;
+ica_aes_gcm_last_t              p_ica_aes_gcm_last;
+#endif
+ica_cleanup_t                   p_ica_cleanup;
+ica_allow_external_gcm_iv_in_fips_mode_t
+                                p_ica_allow_external_gcm_iv_in_fips_mode;
+
+/* save libcrypto's default ec methods */
+#ifndef NO_EC
+ #ifdef OLDER_OPENSSL
+    const ECDSA_METHOD *ossl_ecdsa;
+    const ECDH_METHOD *ossl_ecdh;
+ #else
+    const EC_KEY_METHOD *ossl_ec;
+ #endif
+#endif
+
+/*
+ * ibmca_crypto_algos lists the supported crypto algos by ibmca.
+ * This list is matched against all algo support by libica. Only if
+ * the algo is in this list it is activated in ibmca.
+ * The defines can be found in the libica header file.
+ */
+static int ibmca_crypto_algos[] = {
+    SHA1,
+    SHA256,
+    SHA512,
+    P_RNG,
+    RSA_ME,
+    RSA_CRT,
+    DES_ECB,
+    DES_CBC,
+    DES_OFB,
+    DES_CFB,
+    DES3_ECB,
+    DES3_CBC,
+    DES3_OFB,
+    DES3_CFB,
+    DES3_CTR,
+    AES_ECB,
+    AES_CBC,
+    AES_OFB,
+    AES_CFB,
+    AES_GCM_KMA,
+    EC_KGEN,
+    EC_DSA_SIGN,
+    EC_DSA_VERIFY,
+    EC_DH,
+    ED25519_KEYGEN,
+    ED25519_SIGN,
+    ED25519_VERIFY,
+    ED448_KEYGEN,
+    ED448_SIGN,
+    ED448_VERIFY,
+    X25519_KEYGEN,
+    X25519_DERIVE,
+    X448_KEYGEN,
+    X448_DERIVE,
+    0
+};
+
+#define MAX_CIPHER_NIDS sizeof(ibmca_crypto_algos)
+
+/*
+ * This struct maps one NID to one crypto algo.
+ * So we can tell OpenSSL this NID maps to this function.
+ */
+struct crypto_pair {
+    int nids[MAX_CIPHER_NIDS];
+    const void *crypto_meths[MAX_CIPHER_NIDS];
+};
+
+/* We can not say how much crypto algos are
+ * supported by libica. We can only say the
+ * size is not greater as the supported
+ * crypto algos by ibmca.
+ * The actual number of supported crypto algos
+ * is saved to the size_****_nid variabes
+ */
+static size_t size_cipher_list = 0;
+static size_t size_digest_list = 0;
+static size_t size_pkey_meths_list = 0;
+
+static struct registration_helper {
+    int rsa_enabled;
+    int ec_enabled;
+    int ec_kgen_switch;
+    int ec_dh_switch;
+    int ec_dsa_sign_switch;
+    int ec_dsa_verify_switch;
+    int x25519_keygen_switch;
+    int x25519_derive_switch;
+    int x448_keygen_switch;
+    int x448_derive_switch;
+    int ed25519_keygen_switch;
+    int ed25519_sign_switch;
+    int ed25519_verify_switch;
+    int ed448_keygen_switch;
+    int ed448_sign_switch;
+    int ed448_verify_switch;
+    int x25519_switch;
+    int x448_switch;
+    int ed25519_switch;
+    int ed448_switch;
+} ibmca_registration;
+
+static CRYPTO_ONCE bindcountlockinitonce = CRYPTO_ONCE_STATIC_INIT;
+static CRYPTO_RWLOCK *bindcountlock = NULL;
+static int bindcount = 0;
+
+static struct crypto_pair ibmca_cipher_lists;
+static struct crypto_pair ibmca_digest_lists;
+static struct crypto_pair ibmca_pkey_meths_lists;
+
+static int ibmca_usable_ciphers(const int **nids);
+static int ibmca_engine_ciphers(ENGINE * e, const EVP_CIPHER ** cipher,
+                                const int **nids, int nid);
+static int ibmca_usable_digests(const int **nids);
+static int ibmca_engine_digests(ENGINE * e, const EVP_MD ** digest,
+                                const int **nids, int nid);
+static int ibmca_engine_pkey_meths(ENGINE *e, EVP_PKEY_METHOD **pmeth,
+                                   const int **nids, int nid);
+static int ibmca_usable_pkey_meths(const int **nids);
+
+static void bindcountlockinit(void)
+{
+    bindcountlock = CRYPTO_THREAD_lock_new();
+}
+
+/* RAND stuff */
+static int ibmca_rand_bytes(unsigned char *buf, int num);
+static int ibmca_rand_status(void);
+
+static RAND_METHOD ibmca_rand = {
+    /* "IBMCA RAND method", */
+    NULL,                       /* seed */
+    ibmca_rand_bytes,           /* bytes */
+    NULL,                       /* cleanup */
+    NULL,                       /* add */
+    ibmca_rand_bytes,           /* pseudorand */
+    ibmca_rand_status,          /* status */
+};
+
+
+/* The definitions for control commands specific to this engine */
+#define IBMCA_CMD_SO_PATH		ENGINE_CMD_BASE
+#define IBMCA_CMD_LIBICA        (ENGINE_CMD_BASE + 1)
+static const ENGINE_CMD_DEFN ibmca_cmd_defns[] = {
+    {IBMCA_CMD_SO_PATH,
+     "SO_PATH",
+     "Specifies the path to the 'ibmca' shared library",
+     ENGINE_CMD_FLAG_STRING},
+    {IBMCA_CMD_LIBICA,
+     "libica",
+     "Specifies the path to the 'libica' shared library",
+     ENGINE_CMD_FLAG_STRING},
+    {0, NULL, NULL, 0}
+};
+
+/* Destructor (complements the "ENGINE_ibmca()" constructor) */
+static int ibmca_destroy(ENGINE *e)
+{
+    int newbindcount;
+    CRYPTO_atomic_add(&bindcount, -1, &newbindcount, bindcountlock);
+    if (newbindcount)
+        return 1;
+    /* Unload the ibmca error strings so any error state including our
+     * functs or reasons won't lead to a segfault (they simply get displayed
+     * without corresponding string data because none will be found).
+     */
+    ERR_unload_IBMCA_strings();
+    return 1;
+}
+
+inline static int set_RSA_prop(ENGINE *e)
+{
+    if (ibmca_registration.rsa_enabled) {
+        return 1;
+    }
+    if (
+#ifndef OPENSSL_NO_RSA
+        !ENGINE_set_RSA(e, ibmca_rsa()) ||
+#endif
+#ifndef OPENSSL_NO_DSA
+        !ENGINE_set_DSA(e, ibmca_dsa()) ||
+#endif
+#ifndef OPENSSL_NO_DH
+        !ENGINE_set_DH(e, ibmca_dh())
+#endif
+        )
+        return 0;
+
+    ibmca_registration.rsa_enabled = 1;
+
+    return 1;
+}
+
+#ifndef OPENSSL_NO_EC
+static int set_EC_prop(ENGINE *e)
+{
+    int (*keygen_sw)(EC_KEY *key) = NULL;
+
+    if (ibmca_registration.ec_enabled) {
+        return 1;
+    }
+
+#  ifdef OLDER_OPENSSL
+    ossl_ecdh = ECDH_get_default_method();
+    ossl_ecdsa = ECDSA_get_default_method();
+
+    ibmca_ecdh = ECDH_METHOD_new(NULL);
+    ibmca_ecdsa = ECDSA_METHOD_new(NULL);
+
+    ECDSA_METHOD_set_name(ibmca_ecdsa, "Ibmca ECDSA method");
+    ECDSA_METHOD_set_sign(ibmca_ecdsa, ibmca_older_ecdsa_do_sign);
+    ECDSA_METHOD_set_verify(ibmca_ecdsa, ibmca_older_ecdsa_do_verify);
+#    ifdef ECDSA_FLAG_FIPS_METHOD
+    ECDSA_METHOD_set_flags(ibmca_ecdsa, ECDSA_FLAG_FIPS_METHOD);
+#    endif
+
+    ECDH_METHOD_set_name(ibmca_ecdh, "Ibmca ECDH method");
+    ECDH_METHOD_set_compute_key(ibmca_ecdh, ibmca_older_ecdh_compute_key);
+#    ifdef ECDH_FLAG_FIPS_METHOD
+    ECDH_METHOD_set_flags(ibmca_ecdh, ECDH_FLAG_FIPS_METHOD);
+#    endif
+
+    if (!ENGINE_set_ECDH(e, ibmca_ecdh))
+        return 0;
+    if (!ENGINE_set_ECDSA(e, ibmca_ecdsa))
+        return 0;
+#  else
+    ossl_ec = EC_KEY_get_default_method();
+    /*
+         * EC_KEY_METHOD_get_keygen misses the const-qualifier of the
+         * parameter in some openssl versions.
+         */
+    EC_KEY_METHOD_get_keygen((EC_KEY_METHOD *)ossl_ec, &keygen_sw);
+    if (keygen_sw == NULL) {
+        IBMCAerr(IBMCA_F_IBMCA_EC_KEY_GEN, IBMCA_R_EC_INTERNAL_ERROR);
+        return 0;
+    }
+
+    ibmca_ec = EC_KEY_METHOD_new(ibmca_ec);
+    EC_KEY_METHOD_set_keygen(ibmca_ec, keygen_sw);
+    EC_KEY_METHOD_set_compute_key(ibmca_ec, ibmca_ecdh_compute_key);
+    EC_KEY_METHOD_set_sign(ibmca_ec, ibmca_ecdsa_sign, ECDSA_sign_setup,
+                           ibmca_ecdsa_sign_sig);
+    EC_KEY_METHOD_set_verify(ibmca_ec, ibmca_ecdsa_verify,
+                             ibmca_ecdsa_verify_sig);
+
+    if (!ENGINE_set_EC(e, ibmca_ec))
+        return 0;
+#  endif
+
+    ibmca_registration.ec_enabled = 1;
+
+    return 1;
+}
+#endif
+
+/*
+ * dig_nid_cnt and ciph_nid_cnt count the number of enabled crypt mechanims.
+ * dig_nid_cnt and ciph_nid_cnt needs to be pointer, because only
+ * set_engine_prop knows about how much digest or cipher will be set per call.
+ * To count the number of cipher and digest outside of the function is not
+ * feasible
+ */
+inline static int set_engine_prop(ENGINE *e, int algo_id, int *dig_nid_cnt,
+                                  int *ciph_nid_cnt, int *pkey_nid_cnt)
+{
+    switch (algo_id) {
+    case P_RNG:
+        if (!ENGINE_set_RAND(e, &ibmca_rand))
+            return 0;
+        break;
+    /*
+     * RSA will be enabled if one of this is set. OpenSSL does not distinguish
+     * between RSA_ME and RSA_CRT. It is not the task of ibmca to route one ME
+     * call to CRT or vice versa.
+     */
+    case RSA_ME:
+    case RSA_CRT:
+        if (!set_RSA_prop(e))
+            return 0;
+        break;
+#ifndef OPENSSL_NO_SHA1
+    case SHA1:
+        ibmca_digest_lists.nids[*dig_nid_cnt] = NID_sha1;
+        ibmca_digest_lists.crypto_meths[(*dig_nid_cnt)++] = ibmca_sha1();
+        break;
+#endif
+#ifndef OPENSSL_NO_SHA256
+    case SHA256:
+        ibmca_digest_lists.nids[*dig_nid_cnt] = NID_sha256;
+        ibmca_digest_lists.crypto_meths[(*dig_nid_cnt)++] = ibmca_sha256();
+        break;
+#endif
+#ifndef OPENSSL_NO_SHA512
+    case SHA512:
+        ibmca_digest_lists.nids[*dig_nid_cnt] = NID_sha512;
+        ibmca_digest_lists.crypto_meths[(*dig_nid_cnt)++] = ibmca_sha512();
+        break;
+#endif
+    case DES_ECB:
+        ibmca_cipher_lists.nids[*ciph_nid_cnt] = NID_des_ecb;
+        ibmca_cipher_lists.crypto_meths[(*ciph_nid_cnt)++] = ibmca_des_ecb();
+        break;
+    case DES_CBC:
+        ibmca_cipher_lists.nids[*ciph_nid_cnt] = NID_des_cbc;
+        ibmca_cipher_lists.crypto_meths[(*ciph_nid_cnt)++] = ibmca_des_cbc();
+        break;
+    case DES_OFB:
+        ibmca_cipher_lists.nids[*ciph_nid_cnt] = NID_des_ofb;
+        ibmca_cipher_lists.crypto_meths[(*ciph_nid_cnt)++] = ibmca_des_ofb();
+        break;
+    case DES_CFB:
+        ibmca_cipher_lists.nids[*ciph_nid_cnt] = NID_des_cfb;
+        ibmca_cipher_lists.crypto_meths[(*ciph_nid_cnt)++] = ibmca_des_cfb();
+        break;
+    case DES3_ECB:
+        ibmca_cipher_lists.nids[*ciph_nid_cnt] = NID_des_ede3_ecb;
+        ibmca_cipher_lists.crypto_meths[(*ciph_nid_cnt)++] = ibmca_tdes_ecb();
+        break;
+    case DES3_CBC:
+        ibmca_cipher_lists.nids[*ciph_nid_cnt] = NID_des_ede3_cbc;
+        ibmca_cipher_lists.crypto_meths[(*ciph_nid_cnt)++] = ibmca_tdes_cbc();
+        break;
+    case DES3_OFB:
+        ibmca_cipher_lists.nids[*ciph_nid_cnt] = NID_des_ede3_ofb;
+        ibmca_cipher_lists.crypto_meths[(*ciph_nid_cnt)++] = ibmca_tdes_ofb();
+        break;
+    case DES3_CFB:
+        ibmca_cipher_lists.nids[*ciph_nid_cnt] = NID_des_ede3_cfb;
+        ibmca_cipher_lists.crypto_meths[(*ciph_nid_cnt)++] = ibmca_tdes_cfb();
+        break;
+    case AES_ECB:
+        ibmca_cipher_lists.nids[*ciph_nid_cnt] = NID_aes_128_ecb;
+        ibmca_cipher_lists.crypto_meths[(*ciph_nid_cnt)++] =
+            ibmca_aes_128_ecb();
+        ibmca_cipher_lists.nids[*ciph_nid_cnt] = NID_aes_192_ecb;
+        ibmca_cipher_lists.crypto_meths[(*ciph_nid_cnt)++] =
+            ibmca_aes_192_ecb();
+        ibmca_cipher_lists.nids[*ciph_nid_cnt] = NID_aes_256_ecb;
+        ibmca_cipher_lists.crypto_meths[(*ciph_nid_cnt)++] =
+            ibmca_aes_256_ecb();
+        break;
+    case AES_CBC:
+        ibmca_cipher_lists.nids[*ciph_nid_cnt] = NID_aes_128_cbc;
+        ibmca_cipher_lists.crypto_meths[(*ciph_nid_cnt)++] =
+            ibmca_aes_128_cbc();
+        ibmca_cipher_lists.nids[*ciph_nid_cnt] = NID_aes_192_cbc;
+        ibmca_cipher_lists.crypto_meths[(*ciph_nid_cnt)++] =
+            ibmca_aes_192_cbc();
+        ibmca_cipher_lists.nids[*ciph_nid_cnt] = NID_aes_256_cbc;
+        ibmca_cipher_lists.crypto_meths[(*ciph_nid_cnt)++] =
+            ibmca_aes_256_cbc();
+        break;
+    case AES_OFB:
+        ibmca_cipher_lists.nids[*ciph_nid_cnt] = NID_aes_128_ofb;
+        ibmca_cipher_lists.crypto_meths[(*ciph_nid_cnt)++] =
+            ibmca_aes_128_ofb();
+        ibmca_cipher_lists.nids[*ciph_nid_cnt] = NID_aes_192_ofb;
+        ibmca_cipher_lists.crypto_meths[(*ciph_nid_cnt)++] =
+            ibmca_aes_192_ofb();
+        ibmca_cipher_lists.nids[*ciph_nid_cnt] = NID_aes_256_ofb;
+        ibmca_cipher_lists.crypto_meths[(*ciph_nid_cnt)++] =
+            ibmca_aes_256_ofb();
+        break;
+    case AES_CFB:
+        ibmca_cipher_lists.nids[*ciph_nid_cnt] = NID_aes_128_cfb;
+        ibmca_cipher_lists.crypto_meths[(*ciph_nid_cnt)++] =
+            ibmca_aes_128_cfb();
+        ibmca_cipher_lists.nids[*ciph_nid_cnt] = NID_aes_192_cfb;
+        ibmca_cipher_lists.crypto_meths[(*ciph_nid_cnt)++] =
+            ibmca_aes_192_cfb();
+        ibmca_cipher_lists.nids[*ciph_nid_cnt] = NID_aes_256_cfb;
+        ibmca_cipher_lists.crypto_meths[(*ciph_nid_cnt)++] =
+            ibmca_aes_256_cfb();
+        break;
+#ifndef OPENSSL_NO_AES_GCM
+    case AES_GCM_KMA:
+        ibmca_cipher_lists.nids[*ciph_nid_cnt] = NID_aes_128_gcm;
+        ibmca_cipher_lists.crypto_meths[(*ciph_nid_cnt)++] =
+            ibmca_aes_128_gcm();
+        ibmca_cipher_lists.nids[*ciph_nid_cnt] = NID_aes_192_gcm;
+        ibmca_cipher_lists.crypto_meths[(*ciph_nid_cnt)++] =
+            ibmca_aes_192_gcm();
+        ibmca_cipher_lists.nids[*ciph_nid_cnt] = NID_aes_256_gcm;
+        ibmca_cipher_lists.crypto_meths[(*ciph_nid_cnt)++] =
+            ibmca_aes_256_gcm();
+        break;
+#endif
+#ifndef OPENSSL_NO_EC
+    case EC_KGEN:
+        ibmca_registration.ec_kgen_switch = 1;
+        break;
+    case EC_DH:
+        ibmca_registration.ec_dh_switch = 1;
+        break;
+    case EC_DSA_SIGN:
+        ibmca_registration.ec_dsa_sign_switch = 1;
+        break;
+    case EC_DSA_VERIFY:
+        ibmca_registration.ec_dsa_verify_switch = 1;
+        break;
+#endif
+    case ED25519_KEYGEN:
+        ibmca_registration.ed25519_keygen_switch = 1;
+        break;
+    case ED25519_SIGN:
+        ibmca_registration.ed25519_sign_switch = 1;
+        break;
+    case ED25519_VERIFY:
+        ibmca_registration.ed25519_verify_switch = 1;
+        break;
+    case ED448_KEYGEN:
+        ibmca_registration.ed448_keygen_switch = 1;
+        break;
+    case ED448_SIGN:
+        ibmca_registration.ed448_sign_switch = 1;
+        break;
+    case ED448_VERIFY:
+        ibmca_registration.ed448_verify_switch = 1;
+        break;
+    case X25519_KEYGEN:
+        ibmca_registration.x25519_keygen_switch = 1;
+        break;
+    case X25519_DERIVE:
+        ibmca_registration.x25519_derive_switch = 1;
+        break;
+    case X448_KEYGEN:
+        ibmca_registration.x448_keygen_switch = 1;
+        break;
+    case X448_DERIVE:
+        ibmca_registration.x448_derive_switch = 1;
+        break;
+    default:
+        break;                  /* do nothing */
+    }
+
+#ifndef OPENSSL_NO_EC
+    if (ibmca_registration.ec_kgen_switch && ibmca_registration.ec_dh_switch
+        && ibmca_registration.ec_dsa_sign_switch
+        && ibmca_registration.ec_dsa_verify_switch) {
+        if (!set_EC_prop(e))
+            return 0;
+    }
+#endif
+
+    if (ibmca_registration.x25519_keygen_switch
+        && ibmca_registration.x25519_derive_switch
+        && !ibmca_registration.x25519_switch) {
+        ibmca_registration.x25519_switch = 1;
+        ibmca_pkey_meths_lists.nids[*pkey_nid_cnt] = NID_X25519;
+        ibmca_pkey_meths_lists.crypto_meths[(*pkey_nid_cnt)++]
+          = ibmca_x25519();
+    }
+    if (ibmca_registration.x448_keygen_switch
+        && ibmca_registration.x448_derive_switch
+        && !ibmca_registration.x448_switch) {
+        ibmca_registration.x448_switch = 1;
+        ibmca_pkey_meths_lists.nids[*pkey_nid_cnt] = NID_X448;
+        ibmca_pkey_meths_lists.crypto_meths[(*pkey_nid_cnt)++]
+          = ibmca_x448();
+    }
+    if (ibmca_registration.ed25519_keygen_switch
+        && ibmca_registration.ed25519_sign_switch
+        && ibmca_registration.ed25519_verify_switch
+        && !ibmca_registration.ed25519_switch) {
+        ibmca_registration.ed25519_switch = 1;
+        ibmca_pkey_meths_lists.nids[*pkey_nid_cnt] = NID_ED25519;
+        ibmca_pkey_meths_lists.crypto_meths[(*pkey_nid_cnt)++]
+          = ibmca_ed25519();
+    }
+    if (ibmca_registration.ed448_keygen_switch
+        && ibmca_registration.ed448_sign_switch
+        && ibmca_registration.ed448_verify_switch
+        && !ibmca_registration.ed448_switch) {
+        ibmca_registration.ed448_switch = 1;
+        ibmca_pkey_meths_lists.nids[*pkey_nid_cnt] = NID_ED448;
+        ibmca_pkey_meths_lists.crypto_meths[(*pkey_nid_cnt)++]
+          = ibmca_ed448();
+    }
+
+    size_cipher_list = *ciph_nid_cnt;
+    size_digest_list = *dig_nid_cnt;
+    size_pkey_meths_list = *pkey_nid_cnt;
+    return 1;
+}
+
+static int set_supported_meths(ENGINE *e)
+{
+    int i, j;
+    unsigned int mech_len;
+    libica_func_list_element *pmech_list;
+    int rc = 0;
+    int dig_nid_cnt = 0;
+    int ciph_nid_cnt = 0;
+    int pkey_nid_cnt = 0;
+
+    if (p_ica_get_functionlist(NULL, &mech_len))
+        return 0;
+
+    pmech_list = malloc(sizeof(libica_func_list_element) * mech_len);
+    if (!pmech_list)
+        return 0;
+
+    if (p_ica_get_functionlist(pmech_list, &mech_len))
+        goto out;
+
+    for (i = 0; i < mech_len; i++) {
+
+        libica_func_list_element *f = &pmech_list[i];
+
+        /* Disable crypto algorithm if not supported in hardware */
+        if (!(f->flags & (ICA_FLAG_SHW | ICA_FLAG_DHW)))
+            continue;
+
+        /* Check if this crypto algorithm is supported by ibmca */
+        for (j = 0; ibmca_crypto_algos[j]; j++)
+            if (ibmca_crypto_algos[j] == f->mech_mode_id)
+                break;
+        if (!ibmca_crypto_algos[j])
+            continue;
+        /*
+         * This algorith is supported by ibmca and libica
+         * Set NID, ibmca struct and the info for the ENGINE struct
+         */
+        if (!set_engine_prop(e, ibmca_crypto_algos[j],
+                             &dig_nid_cnt, &ciph_nid_cnt, &pkey_nid_cnt))
+            goto out;
+    }
+
+    if (dig_nid_cnt > 0)
+        if (!ENGINE_set_digests(e, ibmca_engine_digests))
+            goto out;
+
+    if (ciph_nid_cnt > 0)
+        if (!ENGINE_set_ciphers(e, ibmca_engine_ciphers))
+            goto out;
+
+    if (pkey_nid_cnt > 0)
+        if (!ENGINE_set_pkey_meths(e, ibmca_engine_pkey_meths))
+            goto out;
+
+    if (!ENGINE_set_flags(e, ENGINE_FLAGS_NO_REGISTER_ALL))
+        goto out;
+
+    rc = 1;
+out:
+    free(pmech_list);
+
+    return rc;
+}
+
+__attribute__((constructor))
+static void ibmca_constructor(void)
+{
+    DEBUG_PRINTF(">%s\n", __func__);
+}
+
+__attribute__((destructor))
+static void ibmca_destructor(void)
+{
+    if (bindcountlock)
+        CRYPTO_THREAD_lock_free(bindcountlock);
+    if (LIBICA_NAME_allocated)
+        free((void *)LIBICA_NAME);
+}
+
+static void do_ica_cleanup(void)
+{
+    if (p_ica_cleanup)
+        p_ica_cleanup();
+    if (ibmca_dso && dlclose(ibmca_dso)) {
+        IBMCAerr(IBMCA_F_IBMCA_FINISH, IBMCA_R_DSO_FAILURE);
+        return;
+    }
+
+    ibmca_dso = NULL;
+
+    p_ica_open_adapter = NULL;
+    p_ica_close_adapter = NULL;
+
+    p_ica_rsa_mod_expo = NULL;
+    p_ica_rsa_crt = NULL;
+#ifndef OPENSSL_NO_EC
+    p_ica_ec_key_new = NULL;
+    p_ica_ec_key_init = NULL;
+    p_ica_ec_key_generate = NULL;
+    p_ica_ecdh_derive_secret = NULL;
+    p_ica_ecdsa_sign = NULL;
+    p_ica_ecdsa_verify = NULL;
+    p_ica_ec_key_get_public_key = NULL;
+    p_ica_ec_key_get_private_key = NULL;
+    p_ica_ec_key_free = NULL;
+#endif
+
+    p_ica_random_number_generate = NULL;
+    p_ica_sha1 = NULL;
+    p_ica_sha256 = NULL;
+    p_ica_sha512 = NULL;
+    p_ica_aes_ecb = NULL;
+    p_ica_des_ecb = NULL;
+    p_ica_3des_ecb = NULL;
+    p_ica_aes_cbc = NULL;
+    p_ica_des_cbc = NULL;
+    p_ica_3des_cbc = NULL;
+    p_ica_aes_ofb = NULL;
+    p_ica_des_ofb = NULL;
+    p_ica_3des_ofb = NULL;
+    p_ica_aes_cfb = NULL;
+    p_ica_des_cfb = NULL;
+    p_ica_3des_cfb = NULL;
+#ifndef OPENSSL_NO_AES_GCM
+    p_ica_aes_gcm_initialize = NULL;
+    p_ica_aes_gcm_intermediate = NULL;
+    p_ica_aes_gcm_last = NULL;
+#endif
+    p_ica_x25519_ctx_new = NULL;
+    p_ica_x448_ctx_new = NULL;
+    p_ica_ed25519_ctx_new = NULL;
+    p_ica_ed448_ctx_new = NULL;
+    p_ica_x25519_key_set = NULL;
+    p_ica_x448_key_set = NULL;
+    p_ica_ed25519_key_set = NULL;
+    p_ica_ed448_key_set = NULL;
+    p_ica_x25519_key_get = NULL;
+    p_ica_x448_key_get = NULL;
+    p_ica_ed25519_key_get = NULL;
+    p_ica_ed448_key_get = NULL;
+    p_ica_x25519_key_gen = NULL;
+    p_ica_x448_key_gen = NULL;
+    p_ica_ed25519_key_gen = NULL;
+    p_ica_ed448_key_gen = NULL;
+    p_ica_x25519_derive = NULL;
+    p_ica_x448_derive = NULL;
+    p_ica_ed25519_sign = NULL;
+    p_ica_ed448_sign = NULL;
+    p_ica_ed25519_verify = NULL;
+    p_ica_ed448_verify = NULL;
+    p_ica_x25519_ctx_del = NULL;
+    p_ica_x448_ctx_del = NULL;
+    p_ica_ed25519_ctx_del = NULL;
+    p_ica_ed448_ctx_del = NULL;
+    p_ica_cleanup = NULL;
+}
+
+static int ibmca_init(ENGINE *e)
+{
+    ibmca_dso = dlopen(LIBICA_NAME, RTLD_NOW);
+    if (ibmca_dso == NULL) {
+        DEBUG_PRINTF("%s: dlopen(%s) failed\n", __func__, LIBICA_NAME);
+        IBMCAerr(IBMCA_F_IBMCA_INIT, IBMCA_R_DSO_FAILURE);
+        goto err;
+    }
+
+#define BIND(dso, sym)	(p_##sym = (sym##_t)dlsym(dso, #sym))
+
+    if (!BIND(ibmca_dso, ica_open_adapter)
+        || !BIND(ibmca_dso, ica_close_adapter)
+        || !BIND(ibmca_dso, ica_get_functionlist)) {
+        IBMCAerr(IBMCA_F_IBMCA_INIT, IBMCA_R_DSO_FAILURE);
+        DEBUG_PRINTF("%s: function bind failed\n", __func__);
+        goto err;
+    }
+    BIND(ibmca_dso, ica_rsa_mod_expo);
+    BIND(ibmca_dso, ica_rsa_crt);
+    BIND(ibmca_dso, ica_random_number_generate);
+    BIND(ibmca_dso, ica_sha1);
+    BIND(ibmca_dso, ica_sha256);
+    BIND(ibmca_dso, ica_sha512);
+    BIND(ibmca_dso, ica_aes_ecb);
+    BIND(ibmca_dso, ica_des_ecb);
+    BIND(ibmca_dso, ica_3des_ecb);
+    BIND(ibmca_dso, ica_aes_cbc);
+    BIND(ibmca_dso, ica_des_cbc);
+    BIND(ibmca_dso, ica_3des_cbc);
+    BIND(ibmca_dso, ica_aes_ofb);
+    BIND(ibmca_dso, ica_des_ofb);
+    BIND(ibmca_dso, ica_3des_ofb);
+    BIND(ibmca_dso, ica_aes_cfb);
+    BIND(ibmca_dso, ica_des_cfb);
+    BIND(ibmca_dso, ica_3des_cfb);
+#ifndef OPENSSL_NO_AES_GCM
+    BIND(ibmca_dso, ica_aes_gcm_initialize);
+    BIND(ibmca_dso, ica_aes_gcm_intermediate);
+    BIND(ibmca_dso, ica_aes_gcm_last);
+#endif
+#ifndef OPENSSL_NO_EC
+    BIND(ibmca_dso, ica_ec_key_new);
+    BIND(ibmca_dso, ica_ec_key_init);
+    BIND(ibmca_dso, ica_ec_key_generate);
+    BIND(ibmca_dso, ica_ecdh_derive_secret);
+    BIND(ibmca_dso, ica_ecdsa_sign);
+    BIND(ibmca_dso, ica_ecdsa_verify);
+    BIND(ibmca_dso, ica_ec_key_get_public_key);
+    BIND(ibmca_dso, ica_ec_key_get_private_key);
+    BIND(ibmca_dso, ica_ec_key_free);
+#endif
+    BIND(ibmca_dso, ica_x25519_ctx_new);
+    BIND(ibmca_dso, ica_x448_ctx_new);
+    BIND(ibmca_dso, ica_ed25519_ctx_new);
+    BIND(ibmca_dso, ica_ed448_ctx_new);
+    BIND(ibmca_dso, ica_x25519_key_set);
+    BIND(ibmca_dso, ica_x448_key_set);
+    BIND(ibmca_dso, ica_ed25519_key_set);
+    BIND(ibmca_dso, ica_ed448_key_set);
+    BIND(ibmca_dso, ica_x25519_key_get);
+    BIND(ibmca_dso, ica_x448_key_get);
+    BIND(ibmca_dso, ica_ed25519_key_get);
+    BIND(ibmca_dso, ica_ed448_key_get);
+    BIND(ibmca_dso, ica_x25519_key_gen);
+    BIND(ibmca_dso, ica_x448_key_gen);
+    BIND(ibmca_dso, ica_ed25519_key_gen);
+    BIND(ibmca_dso, ica_ed448_key_gen);
+    BIND(ibmca_dso, ica_x25519_derive);
+    BIND(ibmca_dso, ica_x448_derive);
+    BIND(ibmca_dso, ica_ed25519_sign);
+    BIND(ibmca_dso, ica_ed448_sign);
+    BIND(ibmca_dso, ica_ed25519_verify);
+    BIND(ibmca_dso, ica_ed448_verify);
+    BIND(ibmca_dso, ica_x25519_ctx_del);
+    BIND(ibmca_dso, ica_x448_ctx_del);
+    BIND(ibmca_dso, ica_ed25519_ctx_del);
+    BIND(ibmca_dso, ica_ed448_ctx_del);
+
+    /* ica_cleanup is not always present and only needed for newer libraries */
+    BIND(ibmca_dso, ica_cleanup);
+
+    /*
+     * Allow external AES-GCM IV when libica runs in FIPS mode.
+     * ica_allow_external_gcm_iv_in_fips_mode() is not always present and only
+     * available with newer libraries.
+     */
+    if (BIND(ibmca_dso, ica_allow_external_gcm_iv_in_fips_mode))
+        p_ica_allow_external_gcm_iv_in_fips_mode(1);
+
+    /* disable fallbacks on Libica */
+    if (BIND(ibmca_dso, ica_set_fallback_mode))
+        p_ica_set_fallback_mode(0);
+
+    if (p_ica_open_adapter(&ibmca_handle)) {
+        IBMCAerr(IBMCA_F_IBMCA_INIT, IBMCA_R_UNIT_FAILURE);
+        goto err;
+    }
+
+#ifndef NO_EC
+    if (!ibmca_ec_init())
+        goto err;
+#endif
+
+    if (!set_supported_meths(e))
+        goto err;
+
+    return 1;
+
+err:
+    do_ica_cleanup();
+    return 0;
+}
+
+static int ibmca_finish(ENGINE *e)
+{
+#ifndef OLDER_OPENSSL
+    ibmca_des_ecb_destroy();
+    ibmca_des_cbc_destroy();
+    ibmca_des_ofb_destroy();
+    ibmca_des_cfb_destroy();
+    ibmca_tdes_ecb_destroy();
+    ibmca_tdes_cbc_destroy();
+    ibmca_tdes_ofb_destroy();
+    ibmca_tdes_cfb_destroy();
+
+    ibmca_aes_128_ecb_destroy();
+    ibmca_aes_128_cbc_destroy();
+    ibmca_aes_128_ofb_destroy();
+    ibmca_aes_128_cfb_destroy();
+    ibmca_aes_192_ecb_destroy();
+    ibmca_aes_192_cbc_destroy();
+    ibmca_aes_192_ofb_destroy();
+    ibmca_aes_192_cfb_destroy();
+    ibmca_aes_256_ecb_destroy();
+    ibmca_aes_256_cbc_destroy();
+    ibmca_aes_256_ofb_destroy();
+    ibmca_aes_256_cfb_destroy();
+
+#ifndef OPENSSL_NO_AES_GCM
+    ibmca_aes_128_gcm_destroy();
+    ibmca_aes_192_gcm_destroy();
+    ibmca_aes_256_gcm_destroy();
+#endif
+
+#ifndef OPENSSL_NO_SHA1
+    ibmca_sha1_destroy();
+#endif
+#ifndef OPENSSL_NO_SHA256
+    ibmca_sha256_destroy();
+#endif
+#ifndef OPENSSL_NO_SHA512
+    ibmca_sha512_destroy();
+#endif
+#ifndef OPENSSL_NO_RSA
+    ibmca_rsa_destroy();
+#endif
+#ifndef OPENSSL_NO_DSA
+    ibmca_dsa_destroy();
+#endif
+#ifndef OPENSSL_NO_DH
+    ibmca_dh_destroy();
+#endif
+
+#endif /* !OLDER_OPENSSL */
+
+#ifndef NO_EC
+    ibmca_ec_destroy();
+#endif
+
+    if (p_ica_close_adapter)
+        p_ica_close_adapter(ibmca_handle);
+
+    do_ica_cleanup();
+    memset(&ibmca_registration, 0, sizeof(ibmca_registration));
+    return 1;
+}
+
+static int ibmca_ctrl(ENGINE *e, int cmd, long i, void *p, void (*f) ())
+{
+    char *tmp;
+    int initialised = ((ibmca_dso == NULL) ? 0 : 1);
+    switch (cmd) {
+    case IBMCA_CMD_SO_PATH:
+        if (p == NULL) {
+            IBMCAerr(IBMCA_F_IBMCA_CTRL, ERR_R_PASSED_NULL_PARAMETER);
+            return 0;
+        }
+        if (initialised) {
+            IBMCAerr(IBMCA_F_IBMCA_CTRL, IBMCA_R_ALREADY_LOADED);
+            return 0;
+        }
+        return 1;
+    case IBMCA_CMD_LIBICA:
+        if (p == NULL) {
+            IBMCAerr(IBMCA_F_IBMCA_CTRL, ERR_R_PASSED_NULL_PARAMETER);
+            return 0;
+        }
+        if (initialised) {
+            IBMCAerr(IBMCA_F_IBMCA_CTRL, IBMCA_R_ALREADY_LOADED);
+            return 0;
+        }
+        tmp = strdup((const char *) p);
+        if (!tmp) {
+            IBMCAerr(IBMCA_F_IBMCA_CTRL, ERR_R_MALLOC_FAILURE);
+            return 0;
+        }
+        if (LIBICA_NAME_allocated)
+            free((void *)LIBICA_NAME);
+        LIBICA_NAME = tmp;
+        LIBICA_NAME_allocated = 1;
+        return 1;
+    default:
+        break;
+    }
+    IBMCAerr(IBMCA_F_IBMCA_CTRL, IBMCA_R_CTRL_COMMAND_NOT_IMPLEMENTED);
+
+    return 0;
+}
+
+/*
+ * This internal function is used by ENGINE_ibmca()
+ * and possibly by the "dynamic" ENGINE support too
+ */
+static int bind_helper(ENGINE *e)
+{
+    int ignored;
+
+    CRYPTO_THREAD_run_once(&bindcountlockinitonce, bindcountlockinit);
+    
+    CRYPTO_atomic_add(&bindcount, 1, &ignored, bindcountlock);
+    ERR_load_IBMCA_strings();
+
+    if (!ENGINE_set_id(e, engine_ibmca_id) ||
+        !ENGINE_set_name(e, engine_ibmca_name) ||
+        !ENGINE_set_destroy_function(e, ibmca_destroy) ||
+        !ENGINE_set_init_function(e, ibmca_init) ||
+        !ENGINE_set_finish_function(e, ibmca_finish) ||
+        !ENGINE_set_ctrl_function(e, ibmca_ctrl) ||
+        !ENGINE_set_cmd_defns(e, ibmca_cmd_defns))
+        return 0;
+
+    return 1;
+}
+
+static ENGINE *engine_ibmca(void)
+{
+      ENGINE *ret = ENGINE_new();
+      if (!ret)
+              return NULL;
+      if (!bind_helper(ret)) {
+              ENGINE_free(ret);
+              return NULL;
+      }
+      return ret;
+}
+
+void ENGINE_load_ibmca(void)
+{
+      /* Copied from eng_[openssl|dyn].c */
+      ENGINE *toadd = engine_ibmca();
+      if (!toadd)
+              return;
+      ENGINE_add(toadd);
+      ENGINE_free(toadd);
+      ERR_clear_error();
+}
+
+/*
+ * ENGINE calls this to find out how to deal with
+ * a particular NID in the ENGINE.
+ */
+static int ibmca_engine_ciphers(ENGINE *e, const EVP_CIPHER **cipher,
+                                const int **nids, int nid)
+{
+    int i;
+    if (!cipher)
+        return (ibmca_usable_ciphers(nids));
+
+    *cipher = NULL;
+    for (i = 0; i < size_cipher_list; i++)
+        if (nid == ibmca_cipher_lists.nids[i]) {
+            *cipher = (EVP_CIPHER *) ibmca_cipher_lists.crypto_meths[i];
+            break;
+        }
+
+    /* Check: how can *cipher be NULL? */
+    return (*cipher != NULL);
+}
+
+static int ibmca_usable_ciphers(const int **nids)
+{
+    if (nids)
+        *nids = ibmca_cipher_lists.nids;
+
+    return size_cipher_list;
+}
+
+static int ibmca_engine_digests(ENGINE *e, const EVP_MD **digest,
+                                const int **nids, int nid)
+{
+    int i;
+    if (!digest)
+        return (ibmca_usable_digests(nids));
+
+    *digest = NULL;
+    for (i = 0; i < size_digest_list; i++)
+        if (nid == ibmca_digest_lists.nids[i]) {
+            *digest = (EVP_MD *) ibmca_digest_lists.crypto_meths[i];
+            break;
+        }
+
+
+    return (*digest != NULL);
+}
+
+static int ibmca_usable_digests(const int **nids)
+{
+    if (nids)
+        *nids = ibmca_digest_lists.nids;
+
+    return size_digest_list;
+}
+
+static int ibmca_engine_pkey_meths(ENGINE *e, EVP_PKEY_METHOD **pmeth,
+                                   const int **nids, int nid)
+{
+    int i;
+
+    if (!pmeth)
+        return (ibmca_usable_pkey_meths(nids));
+
+    *pmeth = NULL;
+    for (i = 0; i < size_pkey_meths_list; i++) {
+        if (nid == ibmca_pkey_meths_lists.nids[i]) {
+            *pmeth = (EVP_PKEY_METHOD *)ibmca_pkey_meths_lists.crypto_meths[i];
+            break;
+        }
+    }
+
+    return (*pmeth != NULL);
+}
+
+static int ibmca_usable_pkey_meths(const int **nids)
+{
+    if (nids)
+        *nids = ibmca_pkey_meths_lists.nids;
+
+    return size_pkey_meths_list;
+}
+
+/* Random bytes are good */
+static int ibmca_rand_bytes(unsigned char *buf, int num)
+{
+    unsigned int rc;
+
+    rc = p_ica_random_number_generate(num, buf);
+    if (rc < 0) {
+        IBMCAerr(IBMCA_F_IBMCA_RAND_BYTES, IBMCA_R_REQUEST_FAILED);
+        return 0;
+    }
+
+    return 1;
+}
+
+static int ibmca_rand_status(void)
+{
+    return 1;
+}
+
+/*
+ * This stuff is needed if this ENGINE is being
+ * compiled into a self-contained shared-library.
+ */
+static int bind_fn(ENGINE *e, const char *id)
+{
+    if (id && (strcmp(id, engine_ibmca_id) != 0)) {
+        fprintf(stderr, "wrong engine id\n");
+        return 0;
+    }
+    if (!bind_helper(e)) {
+        fprintf(stderr, "bind failed\n");
+        return 0;
+    }
+
+    return 1;
+}
+
+IMPLEMENT_DYNAMIC_CHECK_FN()
+IMPLEMENT_DYNAMIC_BIND_FN(bind_fn)
+#endif                          /* !OPENSSL_NO_HW_IBMCA */
+#endif                          /* !OPENSSL_NO_HW */
diff -pruN 1.4.0-1/src/engine/e_ibmca_err.c 2.5.0-0ubuntu1/src/engine/e_ibmca_err.c
--- 1.4.0-1/src/engine/e_ibmca_err.c	1970-01-01 00:00:00.000000000 +0000
+++ 2.5.0-0ubuntu1/src/engine/e_ibmca_err.c	2025-04-16 08:09:24.000000000 +0000
@@ -0,0 +1,143 @@
+/*
+ * Copyright [2005-2018] International Business Machines Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <stdio.h>
+#include <openssl/err.h>
+#include "e_ibmca_err.h"
+
+/* BEGIN ERROR CODES */
+#ifndef OPENSSL_NO_ERR
+static ERR_STRING_DATA IBMCA_str_functs[] = {
+    {ERR_PACK(0, IBMCA_F_IBMCA_CTRL, 0), "IBMCA_CTRL"},
+    {ERR_PACK(0, IBMCA_F_IBMCA_FINISH, 0), "IBMCA_FINISH"},
+    {ERR_PACK(0, IBMCA_F_IBMCA_INIT, 0), "IBMCA_INIT"},
+    {ERR_PACK(0, IBMCA_F_IBMCA_MOD_EXP, 0), "IBMCA_MOD_EXP"},
+    {ERR_PACK(0, IBMCA_F_IBMCA_MOD_EXP_CRT, 0), "IBMCA_MOD_EXP_CRT"},
+    {ERR_PACK(0, IBMCA_F_IBMCA_RAND_BYTES, 0), "IBMCA_RAND_BYTES"},
+    {ERR_PACK(0, IBMCA_F_IBMCA_RSA_MOD_EXP, 0), "IBMCA_RSA_MOD_EXP"},
+    {ERR_PACK(0, IBMCA_F_IBMCA_DES_CIPHER, 0), "IBMCA_DES_CIPHER"},
+    {ERR_PACK(0, IBMCA_F_IBMCA_TDES_CIPHER, 0), "IBMCA_TDES_CIPHER"},
+    {ERR_PACK(0, IBMCA_F_IBMCA_SHA1_UPDATE, 0), "IBMCA_SHA1_UPDATE"},
+    {ERR_PACK(0, IBMCA_F_IBMCA_SHA1_FINAL, 0), "IBMCA_SHA1_FINAL"},
+    {ERR_PACK(0, IBMCA_F_IBMCA_AES_128_CIPHER, 0), "IBMCA_AES_128_CIPHER"},
+    {ERR_PACK(0, IBMCA_F_IBMCA_AES_192_CIPHER, 0), "IBMCA_AES_192_CIPHER"},
+    {ERR_PACK(0, IBMCA_F_IBMCA_AES_256_CIPHER, 0), "IBMCA_AES_256_CIPHER"},
+    {ERR_PACK(0, IBMCA_F_IBMCA_SHA256_UPDATE, 0), "IBMCA_SHA256_UPDATE"},
+    {ERR_PACK(0, IBMCA_F_IBMCA_SHA256_FINAL, 0), "IBMCA_SHA256_FINAL"},
+    {ERR_PACK(0, IBMCA_F_IBMCA_SHA512_UPDATE, 0), "IBMCA_SHA512_UPDATE"},
+    {ERR_PACK(0, IBMCA_F_IBMCA_SHA512_FINAL, 0), "IBMCA_SHA512_FINAL"},
+    {ERR_PACK(0, IBMCA_F_IBMCA_EC_KEY_GEN, 0), "IBMCA_EC_KEY_GEN"},
+    {ERR_PACK(0, IBMCA_F_IBMCA_ECDH_COMPUTE_KEY, 0), "IBMCA_ECDH_COMPUTE_KEY"},
+    {ERR_PACK(0, IBMCA_F_IBMCA_ECDSA_SIGN, 0), "IBMCA_ECDSA_SIGN"},
+    {ERR_PACK(0, IBMCA_F_IBMCA_ECDSA_SIGN_SIG, 0), "IBMCA_ECDSA_SIGN_SIG"},
+    {ERR_PACK(0, IBMCA_F_IBMCA_ECDSA_DO_SIGN, 0), "IBMCA_ECDSA_DO_SIGN"},
+    {ERR_PACK(0, IBMCA_F_IBMCA_ECDSA_VERIFY, 0), "IBMCA_ECDSA_VERIFY"},
+    {ERR_PACK(0, IBMCA_F_IBMCA_ECDSA_VERIFY_SIG, 0), "IBMCA_ECDSA_VERIFY_SIG"},
+    {ERR_PACK(0, IBMCA_F_ICA_EC_KEY_NEW, 0), "ICA_EC_KEY_NEW"},
+    {ERR_PACK(0, IBMCA_F_ICA_EC_KEY_INIT, 0), "ICA_EC_KEY_INIT"},
+    {ERR_PACK(0, IBMCA_F_ICA_EC_KEY_GENERATE, 0), "ICA_EC_KEY_GENERATE"},
+    {ERR_PACK(0, IBMCA_F_ICA_EC_KEY_GET_PUBLIC_KEY, 0), "ICA_EC_KEY_GET_PUBLIC_KEY"},
+    {ERR_PACK(0, IBMCA_F_ICA_EC_KEY_GET_PRIVATE_KEY, 0), "ICA_EC_KEY_GET_PRIVATE_KEY"},
+    {ERR_PACK(0, IBMCA_F_ICA_ECDH_DERIVE_SECRET, 0), "ICA_ECDH_DERIVE_SECRET"},
+    {ERR_PACK(0, IBMCA_F_ICA_ECDSA_SIGN, 0), "ICA_ECDSA_SIGN"},
+    {ERR_PACK(0, IBMCA_F_ICA_ECDSA_VERIFY, 0), "ICA_ECDSA_VERIFY"},
+    {ERR_PACK(0, IBMCA_F_ICA_ECDSA_VERIFY, 0), "IBMCA_X25519_KEYGEN"},
+    {ERR_PACK(0, IBMCA_F_ICA_ECDSA_VERIFY, 0), "IBMCA_X25519_DERIVE"},
+    {0, NULL}
+};
+
+static ERR_STRING_DATA IBMCA_str_reasons[] = {
+    {IBMCA_R_ALREADY_LOADED, "already loaded"},
+    {IBMCA_R_BN_CTX_FULL, "bn ctx full"},
+    {IBMCA_R_BN_EXPAND_FAIL, "bn expand fail"},
+    {IBMCA_R_CTRL_COMMAND_NOT_IMPLEMENTED, "ctrl command not implemented"},
+    {IBMCA_R_DSO_FAILURE, "dso failure"},
+    {IBMCA_R_MEXP_LENGTH_TO_LARGE, "mexp length to large"},
+    {IBMCA_R_MISSING_KEY_COMPONENTS, "missing key components"},
+    {IBMCA_R_NOT_INITIALISED, "not initialised"},
+    {IBMCA_R_NOT_LOADED, "not loaded"},
+    {IBMCA_R_OPERANDS_TO_LARGE, "operands to large"},
+    {IBMCA_R_OUTLEN_TO_LARGE, "outlen to large"},
+    {IBMCA_R_REQUEST_FAILED, "request failed"},
+    {IBMCA_R_UNDERFLOW_CONDITION, "underflow condition"},
+    {IBMCA_R_UNDERFLOW_KEYRECORD, "underflow keyrecord"},
+    {IBMCA_R_UNIT_FAILURE, "unit failure"},
+    {IBMCA_R_CIPHER_MODE_NOT_SUPPORTED, "cipher mode not supported"},
+    {IBMCA_R_EC_INVALID_PARM, "ec invalid parameter"},
+    {IBMCA_R_EC_UNSUPPORTED_CURVE, "ec unsupported curve"},
+    {IBMCA_R_EC_INTERNAL_ERROR, "ec internal error"},
+    {IBMCA_R_EC_ICA_EC_KEY_INIT, "ec ica ec key init"},
+    {IBMCA_R_EC_CURVE_DOES_NOT_SUPPORT_SIGNING, "ec curve does not support signing"},
+    {IBMCA_R_PKEY_INTERNAL_ERROR, "internal error"},
+    {IBMCA_R_PKEY_KEYGEN_FAILED, "keygen failed"},
+    {IBMCA_R_PKEY_KEYS_NOT_SET, "keys not set"},
+    {0, NULL}
+};
+
+#endif
+
+#ifdef IBMCA_LIB_NAME
+static ERR_STRING_DATA IBMCA_lib_name[] = {
+    {0, IBMCA_LIB_NAME},
+    {0, NULL}
+};
+#endif
+
+
+static int IBMCA_lib_error_code = 0;
+static int IBMCA_error_init = 1;
+
+void ERR_load_IBMCA_strings(void)
+{
+    if (IBMCA_lib_error_code == 0)
+        IBMCA_lib_error_code = ERR_get_next_error_library();
+
+    if (IBMCA_error_init) {
+        IBMCA_error_init = 0;
+#ifndef OPENSSL_NO_ERR
+        ERR_load_strings(IBMCA_lib_error_code, IBMCA_str_functs);
+        ERR_load_strings(IBMCA_lib_error_code, IBMCA_str_reasons);
+#endif
+
+#ifdef IBMCA_LIB_NAME
+        IBMCA_lib_name->error = ERR_PACK(IBMCA_lib_error_code, 0, 0);
+        ERR_load_strings(0, IBMCA_lib_name);
+#endif
+    }
+}
+
+void ERR_unload_IBMCA_strings(void)
+{
+    if (IBMCA_error_init == 0) {
+#ifndef OPENSSL_NO_ERR
+        ERR_unload_strings(IBMCA_lib_error_code, IBMCA_str_functs);
+        ERR_unload_strings(IBMCA_lib_error_code, IBMCA_str_reasons);
+#endif
+
+#ifdef IBMCA_LIB_NAME
+        ERR_unload_strings(0, IBMCA_lib_name);
+#endif
+        IBMCA_error_init = 1;
+    }
+}
+
+void ERR_IBMCA_error(int function, int reason, char *file, int line)
+{
+    if (IBMCA_lib_error_code == 0)
+        IBMCA_lib_error_code = ERR_get_next_error_library();
+    ERR_PUT_error(IBMCA_lib_error_code, function, reason, file, line);
+}
diff -pruN 1.4.0-1/src/engine/e_ibmca_err.h 2.5.0-0ubuntu1/src/engine/e_ibmca_err.h
--- 1.4.0-1/src/engine/e_ibmca_err.h	1970-01-01 00:00:00.000000000 +0000
+++ 2.5.0-0ubuntu1/src/engine/e_ibmca_err.h	2025-04-16 08:09:24.000000000 +0000
@@ -0,0 +1,100 @@
+/*
+ * Copyright [2005-2018] International Business Machines Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#ifndef HEADER_IBMCA_ERR_H
+#define HEADER_IBMCA_ERR_H
+
+/* BEGIN ERROR CODES */
+void ERR_load_IBMCA_strings(void);
+void ERR_unload_IBMCA_strings(void);
+void ERR_IBMCA_error(int function, int reason, char *file, int line);
+#define IBMCAerr(f,r) ERR_IBMCA_error((f),(r),__FILE__,__LINE__)
+
+/* Error codes for the IBMCA functions. */
+
+/* Function codes. */
+#define IBMCA_F_IBMCA_CTRL              100
+#define IBMCA_F_IBMCA_FINISH            101
+#define IBMCA_F_IBMCA_INIT              102
+#define IBMCA_F_IBMCA_MOD_EXP           103
+#define IBMCA_F_IBMCA_MOD_EXP_CRT       104
+#define IBMCA_F_IBMCA_RAND_BYTES        105
+#define IBMCA_F_IBMCA_RSA_MOD_EXP       106
+#define IBMCA_F_IBMCA_DES_CIPHER        107
+#define IBMCA_F_IBMCA_TDES_CIPHER       108
+#define IBMCA_F_IBMCA_SHA1_UPDATE       109
+#define IBMCA_F_IBMCA_SHA1_FINAL        110
+#define IBMCA_F_IBMCA_AES_128_CIPHER    111
+#define IBMCA_F_IBMCA_AES_192_CIPHER    112
+#define IBMCA_F_IBMCA_AES_256_CIPHER    113
+#define IBMCA_F_IBMCA_SHA256_UPDATE     114
+#define IBMCA_F_IBMCA_SHA256_FINAL      115
+#define IBMCA_F_IBMCA_SHA512_UPDATE     116
+#define IBMCA_F_IBMCA_SHA512_FINAL      117
+#define IBMCA_F_IBMCA_EC_KEY_GEN	120
+#define IBMCA_F_IBMCA_ECDH_COMPUTE_KEY	121
+#define IBMCA_F_IBMCA_ECDSA_SIGN	122
+#define IBMCA_F_IBMCA_ECDSA_SIGN_SIG	123
+#define IBMCA_F_IBMCA_ECDSA_DO_SIGN	124
+#define IBMCA_F_IBMCA_ECDSA_VERIFY	125
+#define IBMCA_F_IBMCA_ECDSA_VERIFY_SIG	126
+#define IBMCA_F_ICA_EC_KEY_NEW		127
+#define IBMCA_F_ICA_EC_KEY_INIT		128
+#define IBMCA_F_ICA_EC_KEY_GENERATE	129
+#define IBMCA_F_ICA_EC_KEY_GET_PUBLIC_KEY	130
+#define IBMCA_F_ICA_EC_KEY_GET_PRIVATE_KEY	131
+#define IBMCA_F_ICA_ECDH_DERIVE_SECRET	132
+#define IBMCA_F_ICA_ECDSA_SIGN		133
+#define IBMCA_F_ICA_ECDSA_VERIFY	134
+#define IBMCA_F_IBMCA_X25519_KEYGEN	140
+#define IBMCA_F_IBMCA_X25519_DERIVE	141
+#define IBMCA_F_IBMCA_X448_KEYGEN	142
+#define IBMCA_F_IBMCA_X448_DERIVE	143
+#define IBMCA_F_IBMCA_ED25519_KEYGEN	144
+#define IBMCA_F_IBMCA_ED448_KEYGEN	145
+#define IBMCA_F_IBMCA_ED25519_SIGN	146
+#define IBMCA_F_IBMCA_ED448_SIGN	147
+#define IBMCA_F_IBMCA_ED25519_VERIFY	148
+#define IBMCA_F_IBMCA_ED448_VERIFY	149
+
+/* Reason codes. */
+#define IBMCA_R_ALREADY_LOADED                  100
+#define IBMCA_R_BN_CTX_FULL                     101
+#define IBMCA_R_BN_EXPAND_FAIL                  102
+#define IBMCA_R_CTRL_COMMAND_NOT_IMPLEMENTED    103
+#define IBMCA_R_DSO_FAILURE                     104
+#define IBMCA_R_MEXP_LENGTH_TO_LARGE            110
+#define IBMCA_R_MISSING_KEY_COMPONENTS          105
+#define IBMCA_R_NOT_INITIALISED                 106
+#define IBMCA_R_NOT_LOADED                      107
+#define IBMCA_R_OPERANDS_TO_LARGE               111
+#define IBMCA_R_OUTLEN_TO_LARGE                 112
+#define IBMCA_R_REQUEST_FAILED                  108
+#define IBMCA_R_UNDERFLOW_CONDITION             113
+#define IBMCA_R_UNDERFLOW_KEYRECORD             114
+#define IBMCA_R_UNIT_FAILURE                    109
+#define IBMCA_R_CIPHER_MODE_NOT_SUPPORTED       115
+#define IBMCA_R_EC_INVALID_PARM			120
+#define IBMCA_R_EC_UNSUPPORTED_CURVE		121
+#define IBMCA_R_EC_INTERNAL_ERROR		122
+#define IBMCA_R_EC_ICA_EC_KEY_INIT		123
+#define IBMCA_R_EC_CURVE_DOES_NOT_SUPPORT_SIGNING	159
+#define IBMCA_R_PKEY_INTERNAL_ERROR		160
+#define IBMCA_R_PKEY_KEYGEN_FAILED		161
+#define IBMCA_R_PKEY_KEYS_NOT_SET		162
+
+#endif
diff -pruN 1.4.0-1/src/engine/ibmca-engine-opensslconfig.in 2.5.0-0ubuntu1/src/engine/ibmca-engine-opensslconfig.in
--- 1.4.0-1/src/engine/ibmca-engine-opensslconfig.in	1970-01-01 00:00:00.000000000 +0000
+++ 2.5.0-0ubuntu1/src/engine/ibmca-engine-opensslconfig.in	2025-04-16 08:09:24.000000000 +0000
@@ -0,0 +1,153 @@
+#!/usr/bin/perl
+
+#
+# Copyright 2022 International Business Machines Corp.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+#
+# Generate openssl.cnf from the system config file with added engine section
+# for ibmca engine.
+#
+# USE WITH CARE: No automation can replace human knowledge and understanding
+#                of the desired configuration!
+#
+
+use strict;
+use warnings;
+
+sub generate()
+{
+    my ($osslconfpath);
+    my ($tmp, $ih, $line, $oh, $defaultcnfsect, $indefaultsect, $enginesect);
+
+    $osslconfpath = `openssl version -d` || die "Please install openssl binary";
+    $osslconfpath =~ s/OPENSSLDIR: \"([^\"]*)\"$/$1/ || die "Failed to extract OpenSSL configuration directory";
+    chomp $osslconfpath;
+
+    open($ih, "<", "$osslconfpath/openssl.cnf") or die "Cannot open $osslconfpath/openssl.cnf";
+    open($oh, ">", "openssl.cnf.ibmca") or die "Cannot open openssl.cnf.ibmca";
+
+    $defaultcnfsect = undef;
+    $indefaultsect = 0;
+    $enginesect = undef;
+    while ($line = <$ih>) {
+        if ($line =~ /openssl_conf\s*=\s*(.*)/) {
+            $defaultcnfsect = $1;
+            chomp $defaultcnfsect;
+        }
+        if ($indefaultsect) {
+            if ($line =~ /\[\s*\w+\s*\]/) {
+                if (!$enginesect) {
+                    print $oh "engines = engine_section\n"
+                }
+                $indefaultsect = 0;
+            } elsif ($line =~ /^\s*engines\s*=\s*(\w+)\s*/) {
+                $enginesect = $1;
+                chomp $enginesect;
+            }
+        }
+        print $oh "$line";
+        if ($defaultcnfsect && $line =~ /\[\s*$defaultcnfsect\s*\]/) {
+            $indefaultsect = 1;
+        }
+        if ($enginesect && $line =~ /\[\s*$enginesect\s*\]/) {
+            print $oh "ibmca = ibmca_section\n"
+        }
+    }
+    if (!$defaultcnfsect) {
+        print $oh, qq|
+openssl_conf = openssl_init
+
+[openssl_init]
+engines = engine_section
+|;
+    }
+    if (!$enginesect) {
+        print $oh qq|
+[engine_section]
+ibmca = ibmca_section
+|;
+    }
+    print $oh qq|
+[ibmca_section]
+# The openssl engine path for ibmca.so.
+# Set the dynamic_path to where the ibmca.so engine
+# resides on the system.
+dynamic_path = @libdir@/ibmca.so
+engine_id = ibmca
+init = 1
+
+#
+# The following ibmca algorithms will be enabled by these parameters
+# to the default_algorithms line. Any combination of these is valid,
+# with "ALL" denoting the same as all of them in a comma separated
+# list.
+#
+# Note: Algorithms denoted by CIPHERS, DIGESTS, EC (since IBM z15 for certain
+# curves), and PKEY are already accelerated by OpenSSL itself using CPACF.
+# Therefore, do not accelerate them using the IBMCA engine. This would actually
+# make them slower.
+#
+# Moreover, ibmca's CIPHER and DIGEST implementations do not
+# support the processing of messages in arbitrary chunk sizes.
+# All chunks, except the final one, are required to be a multiple
+# of the primitive's block size.
+#
+# RSA
+# - RSA encrypt, decrypt, sign and verify, key lengths 512-4096
+#
+# DH
+# - DH key exchange
+#
+# DSA
+# - DSA sign and verify
+#
+# RAND
+# - Hardware random number generation
+#
+# ECDSA (OpenSSL < 1.1.0)
+# - Elliptic Curve DSA sign and verify
+#
+# ECDH (OpenSSL < 1.1.0)
+# - Elliptic Curve DH key exchange
+#
+# EC (OpenSSL >= 1.1.0)
+# - Elliptic Curve DSA sign and verify, Elliptic Curve DH key exchange
+#
+# CIPHERS
+# - DES-ECB, DES-CBC, DES-CFB, DES-OFB,
+#   DES-EDE3, DES-EDE3-CBC, DES-EDE3-CFB, DES-EDE3-OFB,
+#   AES-128-ECB, AES-128-CBC, AES-128-CFB, AES-128-OFB, id-aes128-GCM,
+#   AES-192-ECB, AES-192-CBC, AES-192-CFB, AES-192-OFB, id-aes192-GCM,
+#   AES-256-ECB, AES-256-CBC, AES-256-CFB, AES-256-OFB, id-aes256-GCM ciphers
+#
+# DIGESTS
+# - SHA1, SHA256, SHA512 digests
+#
+# PKEY_CRYPTO
+# - X25519, X448, ED25519, ED448
+
+default_algorithms = RSA,DH,DSA,RAND
+|;
+    close($ih);
+    close($oh);
+    print qq|
+Successfully generated openssl.cnf.ibmca file.  Please review this configuration
+and, if you are happy with the changes, replace $osslconfpath/openssl.cnf with
+this file.
+|;
+}
+
+generate();
diff -pruN 1.4.0-1/src/engine/ibmca.h 2.5.0-0ubuntu1/src/engine/ibmca.h
--- 1.4.0-1/src/engine/ibmca.h	1970-01-01 00:00:00.000000000 +0000
+++ 2.5.0-0ubuntu1/src/engine/ibmca.h	2025-04-16 08:09:24.000000000 +0000
@@ -0,0 +1,687 @@
+/*
+ * Copyright [2005-2018] International Business Machines Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <errno.h>
+#include <string.h>
+#include <openssl/ec.h>
+#include <openssl/ecdh.h>
+#include <openssl/ecdsa.h>
+#include <ica_api.h>
+
+#if OPENSSL_VERSION_NUMBER < 0x10100000L
+ #define OLDER_OPENSSL
+#endif
+
+/*
+ * Here is a DEBUG_PRINTF macro which expands to nothing
+ * at production level and is active only when the
+ * ibmca build is configured with --enable-debug
+ */
+#ifdef DEBUG
+#  define DEBUG_PRINTF(...) fprintf(stderr, __VA_ARGS__)
+#else
+#  define DEBUG_PRINTF(...) do{} while(0)
+#endif
+
+/* COMPAT MACROS */
+#ifdef OLDER_OPENSSL
+ #define EVP_CIPHER_CTX_get_cipher_data(ctx)    ((ctx)->cipher_data)
+ #define EVP_CIPHER_CTX_original_iv(ctx)        ((ctx)->oiv)
+ #define EVP_CIPHER_CTX_iv_noconst(ctx)         ((ctx)->iv)
+ #define EVP_CIPHER_CTX_encrypting(ctx)         ((ctx)->encrypt)
+ #define EVP_CIPHER_CTX_buf_noconst(ctx)        ((ctx)->buf)
+ #define EVP_CIPHER_CTX_key_length(ctx)         ((ctx)->cipher->key_len)
+ #define EVP_MD_CTX_md_data(ctx)                ((ctx)->md_data)
+#else
+ #define EVP_CTRL_GCM_SET_IVLEN                 EVP_CTRL_AEAD_SET_IVLEN
+ #define EVP_CTRL_GCM_SET_TAG                   EVP_CTRL_AEAD_SET_TAG
+ #define EVP_CTRL_GCM_GET_TAG                   EVP_CTRL_AEAD_GET_TAG
+#endif
+
+
+#if !defined(NID_aes_128_gcm) || \
+    !defined(NID_aes_192_gcm) || \
+    !defined(NID_aes_256_gcm)
+ #ifndef OPENSSL_NO_AES_GCM
+  #define OPENSSL_NO_AES_GCM
+ #endif
+#endif
+#ifndef EVP_AEAD_TLS1_AAD_LEN
+ #define EVP_AEAD_TLS1_AAD_LEN              13
+#endif
+#ifndef EVP_MD_FLAG_PKEY_METHOD_SIGNATURE
+ #define EVP_MD_FLAG_PKEY_METHOD_SIGNATURE  0
+#endif
+
+
+/******************************* Cipher stuff *********************************/
+typedef struct ibmca_des_context {
+    unsigned char key[sizeof(ica_des_key_triple_t)];
+} ICA_DES_CTX;
+typedef ICA_DES_CTX ICA_TDES_CTX;
+
+#define AES_128_KEYLEN  AES_KEY_LEN128
+typedef struct ibmca_aes_128_context {
+    unsigned char key[sizeof(ica_aes_key_len_128_t)];
+} ICA_AES_128_CTX;
+
+#define AES_192_KEYLEN  AES_KEY_LEN192
+typedef struct ibmca_aes_192_context {
+    unsigned char key[sizeof(ica_aes_key_len_192_t)];
+} ICA_AES_192_CTX;
+
+#define AES_256_KEYLEN  AES_KEY_LEN256
+typedef struct ibmca_aes_256_context {
+    unsigned char key[sizeof(ica_aes_key_len_256_t)];
+} ICA_AES_256_CTX;
+
+typedef struct ibmca_aes_gcm_context {
+    unsigned char key[32];
+    int key_set;
+    int iv_set;
+
+    unsigned char tag[16];
+    unsigned char subkey[16];
+    unsigned char icb[16];
+    unsigned char ucb[16];
+    unsigned long long ptlen;
+    unsigned long long aadlen;
+
+    unsigned char *iv;
+    int ivlen;
+    int taglen;
+    int iv_gen;
+    int tls_aadlen;
+
+} ICA_AES_GCM_CTX;
+
+#if defined(NID_aes_128_cfb128) && ! defined (NID_aes_128_cfb)
+#define NID_aes_128_cfb NID_aes_128_cfb128
+#endif
+
+#if defined(NID_aes_128_ofb128) && ! defined (NID_aes_128_ofb)
+#define NID_aes_128_ofb NID_aes_128_ofb128
+#endif
+
+#if defined(NID_aes_192_cfb128) && ! defined (NID_aes_192_cfb)
+#define NID_aes_192_cfb NID_aes_192_cfb128
+#endif
+
+#if defined(NID_aes_192_ofb128) && ! defined (NID_aes_192_ofb)
+#define NID_aes_192_ofb NID_aes_192_ofb128
+#endif
+
+#if defined(NID_aes_256_cfb128) && ! defined (NID_aes_256_cfb)
+#define NID_aes_256_cfb NID_aes_256_cfb128
+#endif
+
+#if defined(NID_aes_256_ofb128) && ! defined (NID_aes_256_ofb)
+#define NID_aes_256_ofb NID_aes_256_ofb128
+#endif
+
+#if defined(NID_des_ofb64) && ! defined (NID_des_ofb)
+#define NID_des_ofb NID_des_ofb64
+#endif
+
+#if defined(NID_des_ede3_ofb64) && ! defined (NID_des_ede3_ofb)
+#define NID_des_ede3_ofb NID_des_ede3_ofb64
+#endif
+
+#if defined(NID_des_cfb64) && ! defined (NID_des_cfb)
+#define NID_des_cfb NID_des_cfb64
+#endif
+
+#if defined(NID_des_ede3_cfb64) && ! defined (NID_des_ede3_cfb)
+#define NID_des_ede3_cfb NID_des_ede3_cfb64
+#endif
+
+const EVP_CIPHER *ibmca_des_ecb();
+const EVP_CIPHER *ibmca_des_cbc();
+const EVP_CIPHER *ibmca_des_ofb();
+const EVP_CIPHER *ibmca_des_cfb();
+const EVP_CIPHER *ibmca_tdes_ecb();
+const EVP_CIPHER *ibmca_tdes_cbc();
+const EVP_CIPHER *ibmca_tdes_ofb();
+const EVP_CIPHER *ibmca_tdes_cfb();
+const EVP_CIPHER *ibmca_aes_128_ecb();
+const EVP_CIPHER *ibmca_aes_128_cbc();
+const EVP_CIPHER *ibmca_aes_128_ofb();
+const EVP_CIPHER *ibmca_aes_128_cfb();
+const EVP_CIPHER *ibmca_aes_192_ecb();
+const EVP_CIPHER *ibmca_aes_192_cbc();
+const EVP_CIPHER *ibmca_aes_192_ofb();
+const EVP_CIPHER *ibmca_aes_192_cfb();
+const EVP_CIPHER *ibmca_aes_256_ecb();
+const EVP_CIPHER *ibmca_aes_256_cbc();
+const EVP_CIPHER *ibmca_aes_256_ofb();
+const EVP_CIPHER *ibmca_aes_256_cfb();
+#ifndef OPENSSL_NO_AES_GCM
+const EVP_CIPHER *ibmca_aes_128_gcm();
+const EVP_CIPHER *ibmca_aes_192_gcm();
+const EVP_CIPHER *ibmca_aes_256_gcm();
+#endif
+
+#ifndef OLDER_OPENSSL
+void ibmca_des_ecb_destroy();
+void ibmca_des_cbc_destroy();
+void ibmca_des_ofb_destroy();
+void ibmca_des_cfb_destroy();
+void ibmca_tdes_ecb_destroy();
+void ibmca_tdes_cbc_destroy();
+void ibmca_tdes_ofb_destroy();
+void ibmca_tdes_cfb_destroy();
+void ibmca_aes_128_ecb_destroy();
+void ibmca_aes_128_cbc_destroy();
+void ibmca_aes_128_ofb_destroy();
+void ibmca_aes_128_cfb_destroy();
+void ibmca_aes_192_ecb_destroy();
+void ibmca_aes_192_cbc_destroy();
+void ibmca_aes_192_ofb_destroy();
+void ibmca_aes_192_cfb_destroy();
+void ibmca_aes_256_ecb_destroy();
+void ibmca_aes_256_cbc_destroy();
+void ibmca_aes_256_ofb_destroy();
+void ibmca_aes_256_cfb_destroy();
+void ibmca_aes_128_gcm_destroy();
+void ibmca_aes_192_gcm_destroy();
+void ibmca_aes_256_gcm_destroy();
+#endif
+
+/******************************* Digest stuff *********************************/
+#ifndef OPENSSL_NO_SHA1
+#define SHA_BLOCK_SIZE 64
+typedef struct ibmca_sha1_ctx {
+    sha_context_t c;
+    unsigned char tail[SHA_BLOCK_SIZE];
+    unsigned int tail_len;
+} IBMCA_SHA_CTX;
+
+const EVP_MD *ibmca_sha1();
+
+#ifndef OLDER_OPENSSL
+void ibmca_sha1_destroy();
+#endif
+#endif
+
+#ifndef OPENSSL_NO_SHA256
+#define SHA256_BLOCK_SIZE 64
+typedef struct ibmca_sha256_ctx {
+    sha256_context_t c;
+    unsigned char tail[SHA256_BLOCK_SIZE];
+    unsigned int tail_len;
+} IBMCA_SHA256_CTX;
+
+const EVP_MD *ibmca_sha256();
+
+#ifndef OLDER_OPENSSL
+void ibmca_sha256_destroy();
+#endif
+#endif
+
+#ifndef OPENSSL_NO_SHA512
+#define SHA512_BLOCK_SIZE 128
+typedef struct ibmca_sha512_ctx {
+    sha512_context_t c;
+    unsigned char tail[SHA512_BLOCK_SIZE];
+    unsigned int tail_len;
+} IBMCA_SHA512_CTX;
+
+const EVP_MD *ibmca_sha512();
+
+#ifndef OLDER_OPENSSL
+void ibmca_sha512_destroy();
+#endif
+#endif
+
+/******************************** BIGNUM stuff ********************************/
+int ibmca_mod_exp(BIGNUM *r, const BIGNUM *a, const BIGNUM *p,
+                  const BIGNUM *m, BN_CTX *ctx);
+
+
+/********************************* RSA stuff **********************************/
+#ifndef OPENSSL_NO_RSA
+RSA_METHOD *ibmca_rsa();
+#ifndef OLDER_OPENSSL
+void ibmca_rsa_destroy(void);
+#endif
+#endif
+
+extern ica_adapter_handle_t ibmca_handle;
+
+
+
+/********************************* DSA stuff **********************************/
+#ifndef OPENSSL_NO_DSA
+DSA_METHOD *ibmca_dsa();
+#ifndef OLDER_OPENSSL
+void ibmca_dsa_destroy(void);
+#endif
+#endif
+
+
+
+/********************************** DH stuff **********************************/
+#ifndef OPENSSL_NO_DH
+DH_METHOD *ibmca_dh();
+#ifndef OLDER_OPENSSL
+void ibmca_dh_destroy(void);
+#endif
+#endif
+
+
+/********************************** EC stuff **********************************/
+
+/* Either enable or disable ALL ECC */
+#ifndef OPENSSL_NO_EC
+ #if defined(OPENSSL_NO_ECDH) || defined(OPENSSL_NO_ECDSA)
+  #define OPENSSL_NO_EC
+ #endif
+#endif
+
+#define IBMCA_EC_MAX_D_LEN	66
+#define IBMCA_EC_MAX_Q_LEN	(2 * IBMCA_EC_MAX_D_LEN)
+#define IBMCA_EC_MAX_SIG_LEN	IBMCA_EC_MAX_Q_LEN
+#define IBMCA_EC_MAX_Z_LEN	IBMCA_EC_MAX_D_LEN
+
+#ifndef OPENSSL_NO_EC
+int ibmca_ec_init(void);
+void ibmca_ec_destroy(void);
+
+int ibmca_ecdh_compute_key(unsigned char **pout, size_t *poutlen,
+			   const EC_POINT *pub_key, const EC_KEY *ecdh);
+ECDSA_SIG *ibmca_ecdsa_sign_sig(const unsigned char *dgst, int dgst_len,
+				const BIGNUM *in_kinv, const BIGNUM *in_r,
+				EC_KEY *eckey);
+int ibmca_ecdsa_verify_sig(const unsigned char *dgst, int dgst_len,
+			   const ECDSA_SIG *sig, EC_KEY *eckey);
+ #ifdef OLDER_OPENSSL
+extern ECDSA_METHOD *ibmca_ecdsa;
+extern ECDH_METHOD *ibmca_ecdh;
+extern const ECDSA_METHOD *ossl_ecdsa;
+extern const ECDH_METHOD *ossl_ecdh;
+
+int ibmca_older_ecdh_compute_key(void *out, size_t len,
+				 const EC_POINT *pub_key,
+				 EC_KEY *ecdh,
+				 void *(*KDF)(const void *in, size_t inlen,
+					      void *out, size_t *outlen));
+ECDSA_SIG *ibmca_older_ecdsa_do_sign(const unsigned char *dgst, int dlen,
+				     const BIGNUM *, const BIGNUM *,
+				     EC_KEY *eckey);
+int ibmca_older_ecdsa_do_verify(const unsigned char *dgst, int dgst_len,
+				const ECDSA_SIG *sig, EC_KEY *eckey);
+
+/*
+ * APIs which are missing in openssl 1.0.2.
+ */
+ECDH_METHOD *ECDH_METHOD_new(const ECDH_METHOD *meth);
+void ECDH_METHOD_set_compute_key(ECDH_METHOD *meth,
+				 int (*compute_key)(void *out, size_t len,
+						    const EC_POINT *pub_key,
+						    EC_KEY *ecdh,
+						    void *(*KDF)(const void *in,
+								 size_t inlen,
+								 void *out,
+								 size_t *outlen)));
+void ECDH_METHOD_get_compute_key(const ECDH_METHOD *meth,
+                                 int (**compute_key)(void *out, size_t len,
+                                                     const EC_POINT *pub_key,
+                                                     EC_KEY *ecdh,
+                                                     void *(*KDF)(const void *in,
+                                                                  size_t inlen,
+                                                                  void *out,
+                                                                  size_t *outlen)));
+void ECDH_METHOD_set_name(ECDH_METHOD *meth, char *name);
+void ECDH_METHOD_free(ECDH_METHOD *meth);
+
+void ECDSA_METHOD_get_sign(const ECDSA_METHOD *meth,
+                           int (**psign_setup)(EC_KEY *eckey, BN_CTX *ctx_in,
+                                               BIGNUM **kinvp, BIGNUM **rp),
+                           ECDSA_SIG *(**psign_sig)(const unsigned char *dgst,
+                                                    int dgst_len,
+                                                    const BIGNUM *in_kinv,
+                                                    const BIGNUM *in_r,
+                                                    EC_KEY *eckey));
+
+void ECDSA_METHOD_get_verify(const ECDSA_METHOD *meth,
+                             int (**pverify_sig)(const unsigned char *dgst,
+                                                 int dgst_len,
+                                                 const ECDSA_SIG *sig,
+                                                 EC_KEY *eckey));
+
+ #else
+extern EC_KEY_METHOD *ibmca_ec;
+extern const EC_KEY_METHOD *ossl_ec;
+
+int ibmca_ecdsa_sign(int type, const unsigned char *dgst, int dlen,
+		     unsigned char *sig_array, unsigned int *siglen,
+		     const BIGNUM *kinv, const BIGNUM *r, EC_KEY *eckey);
+int ibmca_ecdsa_verify(int type, const unsigned char *dgst, int dgst_len,
+		       const unsigned char *sigbuf, int sig_len,
+		       EC_KEY *eckey);
+ #endif
+#endif
+
+const EVP_PKEY_METHOD *ibmca_x25519(void);
+const EVP_PKEY_METHOD *ibmca_x448(void);
+const EVP_PKEY_METHOD *ibmca_ed25519(void);
+const EVP_PKEY_METHOD *ibmca_ed448(void);
+void ibmca_x25519_destroy(void);
+void ibmca_x448_destroy(void);
+void ibmca_ed25519_destroy(void);
+void ibmca_ed448_destroy(void);
+
+/******************************* Libica stuff *********************************/
+/*
+ * These are the function pointers that are (un)set when the library has
+ * successfully (un)loaded.
+ */
+typedef unsigned int (*ica_get_functionlist_t)(libica_func_list_element *,
+                                               unsigned int *);
+typedef void         (*ica_set_fallback_mode_t)(int);
+typedef unsigned int (*ica_open_adapter_t)(ica_adapter_handle_t *);
+typedef unsigned int (*ica_close_adapter_t)(ica_adapter_handle_t);
+typedef unsigned int (*ica_rsa_mod_expo_t)(ica_adapter_handle_t,
+                                           unsigned char *,
+                                           ica_rsa_key_mod_expo_t *,
+                                           unsigned char *);
+typedef unsigned int (*ica_rsa_crt_t)(ica_adapter_handle_t, unsigned char *,
+                                      ica_rsa_key_crt_t *, unsigned char *);
+typedef unsigned int (*ica_random_number_generate_t)(unsigned int,
+                                                     unsigned char *);
+typedef unsigned int (*ica_sha1_t)(unsigned int, unsigned int, unsigned char *,
+                                   sha_context_t *, unsigned char *);
+typedef unsigned int (*ica_sha256_t)(unsigned int, unsigned int,
+                                     unsigned char *, sha256_context_t *,
+                                     unsigned char *);
+typedef unsigned int (*ica_sha512_t)(unsigned int, unsigned int,
+                                     unsigned char *, sha512_context_t *,
+                                     unsigned char *);
+typedef unsigned int (*ica_des_ecb_t)(const unsigned char *in_data,
+                                      unsigned char *out_data,
+                                      unsigned long data_length,
+                                      unsigned char *key,
+                                      unsigned int direction);
+typedef unsigned int (*ica_des_cbc_t)(const unsigned char *in_data,
+                                      unsigned char *out_data,
+                                      unsigned long data_length,
+                                      unsigned char *key,
+                                      unsigned char *iv,
+                                      unsigned int direction);
+typedef unsigned int (*ica_des_cfb_t)(const unsigned char *in_data,
+                                      unsigned char *out_data,
+                                      unsigned long data_length,
+                                      const unsigned char *key,
+                                      unsigned char *iv,
+                                      unsigned int lcfb,
+                                      unsigned int direction);
+typedef unsigned int (*ica_des_ofb_t)(const unsigned char *in_data,
+                                      unsigned char *out_data,
+                                      unsigned long data_length,
+                                      const unsigned char *key,
+                                      unsigned char *iv,
+                                      unsigned int direction);
+typedef unsigned int (*ica_3des_ecb_t)(const unsigned char *in_data,
+                                       unsigned char *out_data,
+                                       unsigned long data_length,
+                                       unsigned char *key,
+                                       unsigned int direction);
+typedef unsigned int (*ica_3des_cbc_t)(const unsigned char *in_data,
+                                       unsigned char *out_data,
+                                       unsigned long data_length,
+                                       unsigned char *key,
+                                       unsigned char *iv,
+                                       unsigned int direction);
+typedef unsigned int (*ica_3des_cfb_t)(const unsigned char *, unsigned char *,
+                                       unsigned long, const unsigned char *,
+                                       unsigned char *, unsigned int,
+                                       unsigned int);
+typedef unsigned int (*ica_3des_ofb_t)(const unsigned char *in_data,
+                                       unsigned char *out_data,
+                                       unsigned long data_length,
+                                       const unsigned char *key,
+                                       unsigned char *iv,
+                                       unsigned int direction);
+typedef unsigned int (*ica_aes_ecb_t)(const unsigned char *in_data,
+                                      unsigned char *out_data,
+                                      unsigned long data_length,
+                                      unsigned char *key,
+                                      unsigned int key_length,
+                                      unsigned int direction);
+typedef unsigned int (*ica_aes_cbc_t)(const unsigned char *in_data,
+                                      unsigned char *out_data,
+                                      unsigned long data_length,
+                                      unsigned char *key,
+                                      unsigned int key_length,
+                                      unsigned char *iv,
+                                      unsigned int direction);
+typedef unsigned int (*ica_aes_ofb_t)(const unsigned char *in_data,
+                                      unsigned char *out_data,
+                                      unsigned long data_length,
+                                      const unsigned char *key,
+                                      unsigned int key_length,
+                                      unsigned char *iv,
+                                      unsigned int direction);
+typedef unsigned int (*ica_aes_cfb_t)(const unsigned char *in_data,
+                                      unsigned char *out_data,
+                                      unsigned long data_length,
+                                      const unsigned char *key,
+                                      unsigned int key_length,
+                                      unsigned char *iv, unsigned int lcfb,
+                                      unsigned int direction);
+typedef unsigned int (*ica_aes_gcm_initialize_t)(const unsigned char *iv,
+                                                 unsigned int iv_length,
+                                                 unsigned char *key,
+                                                 unsigned int key_length,
+                                                 unsigned char *icb,
+                                                 unsigned char *ucb,
+                                                 unsigned char *subkey,
+                                                 unsigned int direction);
+typedef unsigned int (*ica_aes_gcm_intermediate_t)(unsigned char *plaintext,
+                                                   unsigned long
+                                                            plaintext_length,
+                                                   unsigned char *ciphertext,
+                                                   unsigned char *ucb,
+                                                   unsigned char *aad,
+                                                   unsigned long aad_length,
+                                                   unsigned char *tag,
+                                                   unsigned int tag_length,
+                                                   unsigned char *key,
+                                                   unsigned int key_length,
+                                                   unsigned char *subkey,
+                                                   unsigned int direction);
+typedef unsigned int (*ica_aes_gcm_last_t)(unsigned char *icb,
+                                           unsigned long aad_length,
+                                           unsigned long ciph_length,
+                                           unsigned char *tag,
+                                           unsigned char *final_tag,
+                                           unsigned int final_tag_length,
+                                           unsigned char *key,
+                                           unsigned int key_length,
+                                           unsigned char *subkey,
+                                           unsigned int direction);
+
+#ifndef OPENSSL_NO_EC
+typedef ICA_EC_KEY* (*ica_ec_key_new_t)(unsigned int nid,
+					unsigned int *privlen);
+typedef int (*ica_ec_key_init_t)(const unsigned char *X,
+				 const unsigned char *Y,
+				 const unsigned char *D, ICA_EC_KEY *key);
+typedef int (*ica_ec_key_generate_t)(ica_adapter_handle_t adapter_handle,
+				     ICA_EC_KEY *key);
+typedef int (*ica_ecdh_derive_secret_t)(ica_adapter_handle_t adapter_handle,
+					const ICA_EC_KEY *privkey_A,
+					const ICA_EC_KEY *pubkey_B,
+					unsigned char *z,
+					unsigned int z_length);
+typedef int (*ica_ecdsa_sign_t)(ica_adapter_handle_t adapter_handle,
+				const ICA_EC_KEY *privkey,
+				const unsigned char *hash,
+				unsigned int hash_length,
+				unsigned char *signature,
+				unsigned int signature_length);
+typedef int (*ica_ecdsa_verify_t)(ica_adapter_handle_t adapter_handle,
+				  const ICA_EC_KEY *pubkey,
+				  const unsigned char *hash,
+				  unsigned int hash_length,
+				  const unsigned char *signature,
+				  unsigned int signature_length);
+typedef int (*ica_ec_key_get_public_key_t)(ICA_EC_KEY *key, unsigned char *q,
+					   unsigned int *q_len);
+typedef int (*ica_ec_key_get_private_key_t)(ICA_EC_KEY *key, unsigned char *d,
+					    unsigned int *d_len);
+typedef void (*ica_ec_key_free_t)(ICA_EC_KEY *key);
+#endif
+
+typedef
+int (*ica_x25519_ctx_new_t)(ICA_X25519_CTX **ctx);
+typedef
+int (*ica_x448_ctx_new_t)(ICA_X448_CTX **ctx);
+typedef
+int (*ica_ed25519_ctx_new_t)(ICA_ED25519_CTX **ctx);
+typedef
+int (*ica_ed448_ctx_new_t)(ICA_ED448_CTX **ctx);
+typedef
+int (*ica_x25519_key_set_t)(ICA_X25519_CTX *ctx, const unsigned char priv[32],
+		       const unsigned char pub[32]);
+typedef
+int (*ica_x448_key_set_t)(ICA_X448_CTX *ctx, const unsigned char priv[56],
+		     const unsigned char pub[56]);
+typedef
+int (*ica_ed25519_key_set_t)(ICA_ED25519_CTX *ctx, const unsigned char priv[32],
+			const unsigned char pub[32]);
+typedef
+int (*ica_ed448_key_set_t)(ICA_ED448_CTX *ctx, const unsigned char priv[56],
+		      const unsigned char pub[56]);
+typedef
+int (*ica_x25519_key_get_t)(ICA_X25519_CTX *ctx, unsigned char priv[32],
+		       unsigned char pub[32]);
+typedef
+int (*ica_x448_key_get_t)(ICA_X448_CTX *ctx, unsigned char priv[56],
+		     unsigned char pub[56]);
+typedef
+int (*ica_ed25519_key_get_t)(ICA_ED25519_CTX *ctx, unsigned char priv[32],
+			unsigned char pub[32]);
+typedef
+int (*ica_ed448_key_get_t)(ICA_ED448_CTX *ctx, unsigned char priv[57],
+		      unsigned char pub[57]);
+typedef
+int (*ica_x25519_key_gen_t)(ICA_X25519_CTX *ctx);
+typedef
+int (*ica_x448_key_gen_t)(ICA_X448_CTX *ctx);
+typedef
+int (*ica_ed25519_key_gen_t)(ICA_ED25519_CTX *ctx);
+typedef
+int (*ica_ed448_key_gen_t)(ICA_ED448_CTX *ctx);
+typedef
+int (*ica_x25519_derive_t)(ICA_X25519_CTX *ctx,
+		      unsigned char shared_secret[32],
+		      const unsigned char peer_pub[32]);
+typedef
+int (*ica_x448_derive_t)(ICA_X448_CTX *ctx,
+		    unsigned char shared_secret[56],
+		    const unsigned char peer_pub[56]);
+typedef
+int (*ica_ed25519_sign_t)(ICA_ED25519_CTX *ctx, unsigned char sig[64],
+		     const unsigned char *msg, size_t msglen);
+typedef
+int (*ica_ed448_sign_t)(ICA_ED448_CTX *ctx, unsigned char sig[114],
+		   const unsigned char *msg, size_t msglen);
+typedef
+int (*ica_ed25519_verify_t)(ICA_ED25519_CTX *ctx, const unsigned char sig[64],
+		       const unsigned char *msg, size_t msglen);
+typedef
+int (*ica_ed448_verify_t)(ICA_ED448_CTX *ctx, const unsigned char sig[114],
+		     const unsigned char *msg, size_t msglen);
+typedef
+int (*ica_x25519_ctx_del_t)(ICA_X25519_CTX **ctx);
+typedef
+int (*ica_x448_ctx_del_t)(ICA_X448_CTX **ctx);
+typedef
+int (*ica_ed25519_ctx_del_t)(ICA_ED25519_CTX **ctx);
+typedef
+int (*ica_ed448_ctx_del_t)(ICA_ED448_CTX **ctx);
+
+typedef void (*ica_cleanup_t)(void);
+typedef void (*ica_allow_external_gcm_iv_in_fips_mode_t)(int allow);
+
+/* entry points into libica, filled out at DSO load time */
+extern ica_get_functionlist_t           p_ica_get_functionlist;
+extern ica_set_fallback_mode_t          p_ica_set_fallback_mode;
+extern ica_open_adapter_t               p_ica_open_adapter;
+extern ica_close_adapter_t              p_ica_close_adapter;
+extern ica_rsa_mod_expo_t               p_ica_rsa_mod_expo;
+extern ica_random_number_generate_t     p_ica_random_number_generate;
+extern ica_rsa_crt_t                    p_ica_rsa_crt;
+extern ica_sha1_t                       p_ica_sha1;
+extern ica_sha256_t                     p_ica_sha256;
+extern ica_sha512_t                     p_ica_sha512;
+extern ica_des_ecb_t                    p_ica_des_ecb;
+extern ica_des_cbc_t                    p_ica_des_cbc;
+extern ica_des_ofb_t                    p_ica_des_ofb;
+extern ica_des_cfb_t                    p_ica_des_cfb;
+extern ica_3des_ecb_t                   p_ica_3des_ecb;
+extern ica_3des_cbc_t                   p_ica_3des_cbc;
+extern ica_3des_cfb_t                   p_ica_3des_cfb;
+extern ica_3des_ofb_t                   p_ica_3des_ofb;
+extern ica_aes_ecb_t                    p_ica_aes_ecb;
+extern ica_aes_cbc_t                    p_ica_aes_cbc;
+extern ica_aes_ofb_t                    p_ica_aes_ofb;
+extern ica_aes_cfb_t                    p_ica_aes_cfb;
+#ifndef OPENSSL_NO_AES_GCM
+extern ica_aes_gcm_initialize_t         p_ica_aes_gcm_initialize;
+extern ica_aes_gcm_intermediate_t       p_ica_aes_gcm_intermediate;
+extern ica_aes_gcm_last_t               p_ica_aes_gcm_last;
+#endif
+#ifndef OPENSSL_NO_EC
+extern ica_ec_key_new_t			p_ica_ec_key_new;
+extern ica_ec_key_init_t		p_ica_ec_key_init;
+extern ica_ec_key_generate_t		p_ica_ec_key_generate;
+extern ica_ecdh_derive_secret_t		p_ica_ecdh_derive_secret;
+extern ica_ecdsa_sign_t			p_ica_ecdsa_sign;
+extern ica_ecdsa_verify_t		p_ica_ecdsa_verify;
+extern ica_ec_key_get_public_key_t	p_ica_ec_key_get_public_key;
+extern ica_ec_key_get_private_key_t	p_ica_ec_key_get_private_key;
+extern ica_ec_key_free_t		p_ica_ec_key_free;
+#endif
+extern ica_x25519_ctx_new_t		p_ica_x25519_ctx_new;
+extern ica_x448_ctx_new_t		p_ica_x448_ctx_new;
+extern ica_ed25519_ctx_new_t		p_ica_ed25519_ctx_new;
+extern ica_ed448_ctx_new_t		p_ica_ed448_ctx_new;
+extern ica_x25519_key_set_t		p_ica_x25519_key_set;
+extern ica_x448_key_set_t		p_ica_x448_key_set;
+extern ica_ed25519_key_set_t		p_ica_ed25519_key_set;
+extern ica_ed448_key_set_t		p_ica_ed448_key_set;
+extern ica_x25519_key_get_t		p_ica_x25519_key_get;
+extern ica_x448_key_get_t		p_ica_x448_key_get;
+extern ica_ed25519_key_get_t		p_ica_ed25519_key_get;
+extern ica_ed448_key_get_t		p_ica_ed448_key_get;
+extern ica_x25519_key_gen_t		p_ica_x25519_key_gen;
+extern ica_x448_key_gen_t		p_ica_x448_key_gen;
+extern ica_ed25519_key_gen_t		p_ica_ed25519_key_gen;
+extern ica_ed448_key_gen_t		p_ica_ed448_key_gen;
+extern ica_x25519_derive_t		p_ica_x25519_derive;
+extern ica_x448_derive_t		p_ica_x448_derive;
+extern ica_ed25519_sign_t		p_ica_ed25519_sign;
+extern ica_ed448_sign_t			p_ica_ed448_sign;
+extern ica_ed25519_verify_t		p_ica_ed25519_verify;
+extern ica_ed448_verify_t		p_ica_ed448_verify;
+extern ica_x25519_ctx_del_t		p_ica_x25519_ctx_del;
+extern ica_x448_ctx_del_t		p_ica_x448_ctx_del;
+extern ica_ed25519_ctx_del_t		p_ica_ed25519_ctx_del;
+extern ica_ed448_ctx_del_t		p_ica_ed448_ctx_del;
+extern ica_cleanup_t            p_ica_cleanup;
diff -pruN 1.4.0-1/src/engine/ibmca.map 2.5.0-0ubuntu1/src/engine/ibmca.map
--- 1.4.0-1/src/engine/ibmca.map	1970-01-01 00:00:00.000000000 +0000
+++ 2.5.0-0ubuntu1/src/engine/ibmca.map	2025-04-16 08:09:24.000000000 +0000
@@ -0,0 +1,9 @@
+IBMCA_2.0.0 {
+    global:
+        v_check;
+        bind_engine;
+        ENGINE_load_ibmca;
+
+    local:
+        *;
+};
diff -pruN 1.4.0-1/src/engine/ibmca_cipher.c 2.5.0-0ubuntu1/src/engine/ibmca_cipher.c
--- 1.4.0-1/src/engine/ibmca_cipher.c	1970-01-01 00:00:00.000000000 +0000
+++ 2.5.0-0ubuntu1/src/engine/ibmca_cipher.c	2025-04-16 08:09:24.000000000 +0000
@@ -0,0 +1,821 @@
+/*
+ * Copyright [2005-2021] International Business Machines Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <string.h>
+#include <openssl/evp.h>
+#include <openssl/rand.h>
+#include "ibmca.h"
+#include "e_ibmca_err.h"
+
+static int ibmca_init_key(EVP_CIPHER_CTX *ctx, const unsigned char *key,
+                          const unsigned char *iv, int enc)
+{
+    ICA_DES_CTX *pCtx = (ICA_DES_CTX *) EVP_CIPHER_CTX_get_cipher_data(ctx);
+
+    memcpy(pCtx->key, key, EVP_CIPHER_CTX_key_length(ctx));
+
+    return 1;
+}
+
+static int ibmca_cipher_cleanup(EVP_CIPHER_CTX *ctx)
+{
+    return 1;
+}
+
+
+#define IMPLEMENT_IBMCA_DES_CIPHER_FN(name, NAME)                       \
+static int ibmca_##name##_cipher(EVP_CIPHER_CTX *ctx,                   \
+                                 unsigned char *out,                    \
+                                 const unsigned char *in, size_t len)   \
+{                                                                       \
+    ICA_##NAME##_CTX *c =                                               \
+        (ICA_##NAME##_CTX *)EVP_CIPHER_CTX_get_cipher_data(ctx);        \
+    unsigned char *iv = EVP_CIPHER_CTX_iv_noconst(ctx);                 \
+    const int mode = EVP_CIPHER_CTX_mode(ctx);                          \
+    const int enc = EVP_CIPHER_CTX_encrypting(ctx) ?                    \
+                                ICA_ENCRYPT : ICA_DECRYPT;              \
+    int rv;                                                             \
+                                                                        \
+    switch (mode) {                                                     \
+    case EVP_CIPH_ECB_MODE:                                             \
+        rv = p_ica_##name##_ecb(in, out, len, c->key, enc);             \
+        break;                                                          \
+    case EVP_CIPH_CBC_MODE:                                             \
+        rv = p_ica_##name##_cbc(in, out, len, c->key, iv, enc);         \
+        break;                                                          \
+    case EVP_CIPH_CFB_MODE:                                             \
+        rv = p_ica_##name##_cfb(in, out, len, c->key, iv, 8, enc);      \
+        break;                                                          \
+    case EVP_CIPH_OFB_MODE:                                             \
+        rv = p_ica_##name##_ofb(in, out, len, c->key, iv, enc);         \
+        break;                                                          \
+    default:                                                            \
+        IBMCAerr(IBMCA_F_IBMCA_##NAME##_CIPHER,                         \
+                 IBMCA_R_CIPHER_MODE_NOT_SUPPORTED);                    \
+        return 0;                                                       \
+    }                                                                   \
+    if (rv) {                                                           \
+        IBMCAerr(IBMCA_F_IBMCA_##NAME##_CIPHER,                         \
+            IBMCA_R_REQUEST_FAILED);                                    \
+        return 0;                                                       \
+    }                                                                   \
+    return 1;                                                           \
+}
+
+IMPLEMENT_IBMCA_DES_CIPHER_FN(des, DES)
+IMPLEMENT_IBMCA_DES_CIPHER_FN(3des, TDES)
+
+
+
+#ifdef OLDER_OPENSSL
+# define DECLARE_DES_EVP(mode, block_size, key_len, iv_len, flags,      \
+                         ctx_size, init, do_cipher, cleanup,            \
+                         set_asn1_parameters, get_asn1_parameters)      \
+const EVP_CIPHER des_##mode = {                                         \
+    NID_des_##mode,                                                     \
+    block_size,                                                         \
+    key_len,                                                            \
+    iv_len,                                                             \
+    flags,                                                              \
+    init,                                                               \
+    do_cipher,                                                          \
+    cleanup,                                                            \
+    ctx_size,                                                           \
+    set_asn1_parameters,                                                \
+    get_asn1_parameters,                                                \
+    NULL,                                                               \
+    NULL                                                                \
+};                                                                      \
+const EVP_CIPHER *ibmca_des_##mode(void)                                \
+{                                                                       \
+    return &des_##mode;                                                 \
+}
+
+#else
+# define DECLARE_DES_EVP(mode, block_size, key_len, iv_len, flags,      \
+                         ctx_size, init, do_cipher, cleanup,            \
+                         set_asn1_parameters, get_asn1_parameters)      \
+static EVP_CIPHER *des_##mode = NULL;                                   \
+const EVP_CIPHER *ibmca_des_##mode(void)                                \
+{                                                                       \
+    EVP_CIPHER *cipher;                                                 \
+                                                                        \
+    if (des_##mode != NULL)                                             \
+        goto done;                                                      \
+                                                                        \
+    if ((cipher = EVP_CIPHER_meth_new(NID_des_##mode,                   \
+                                      block_size, key_len)) == NULL     \
+         || !EVP_CIPHER_meth_set_iv_length(cipher, iv_len)              \
+         || !EVP_CIPHER_meth_set_flags(cipher, flags)                   \
+         || !EVP_CIPHER_meth_set_init(cipher, init)                     \
+         || !EVP_CIPHER_meth_set_do_cipher(cipher, do_cipher)           \
+         || !EVP_CIPHER_meth_set_cleanup(cipher, cleanup)               \
+         || !EVP_CIPHER_meth_set_impl_ctx_size(cipher, ctx_size)        \
+         || !EVP_CIPHER_meth_set_set_asn1_params(cipher,                \
+                                                 set_asn1_parameters)   \
+         || !EVP_CIPHER_meth_set_get_asn1_params(cipher,                \
+                                                 get_asn1_parameters)) {\
+        EVP_CIPHER_meth_free(cipher);                                   \
+        cipher = NULL;                                                  \
+    }                                                                   \
+    des_##mode = cipher;                                                \
+done:                                                                   \
+    return des_##mode;                                                  \
+}                                                                       \
+                                                                        \
+void ibmca_des_##mode##_destroy(void)                                   \
+{                                                                       \
+    EVP_CIPHER_meth_free(des_##mode);                                   \
+    des_##mode = NULL;                                                  \
+}
+#endif
+
+DECLARE_DES_EVP(ecb, sizeof(ica_des_vector_t), sizeof(ica_des_key_single_t),
+                sizeof(ica_des_vector_t), EVP_CIPH_ECB_MODE,
+                sizeof(struct ibmca_des_context), ibmca_init_key,
+                ibmca_des_cipher, ibmca_cipher_cleanup,
+                EVP_CIPHER_set_asn1_iv, EVP_CIPHER_get_asn1_iv)
+DECLARE_DES_EVP(cbc, sizeof(ica_des_vector_t), sizeof(ica_des_key_single_t),
+                sizeof(ica_des_vector_t), EVP_CIPH_CBC_MODE,
+                sizeof(struct ibmca_des_context), ibmca_init_key,
+                ibmca_des_cipher, ibmca_cipher_cleanup,
+                EVP_CIPHER_set_asn1_iv, EVP_CIPHER_get_asn1_iv)
+DECLARE_DES_EVP(ofb, 1, sizeof(ica_des_key_single_t),
+                sizeof(ica_des_vector_t), EVP_CIPH_OFB_MODE,
+                sizeof(struct ibmca_des_context), ibmca_init_key,
+                ibmca_des_cipher, ibmca_cipher_cleanup,
+                EVP_CIPHER_set_asn1_iv, EVP_CIPHER_get_asn1_iv)
+DECLARE_DES_EVP(cfb, 1, sizeof(ica_des_key_single_t),
+                sizeof(ica_des_vector_t), EVP_CIPH_CFB_MODE,
+                sizeof(struct ibmca_des_context), ibmca_init_key,
+                ibmca_des_cipher, ibmca_cipher_cleanup,
+                EVP_CIPHER_set_asn1_iv, EVP_CIPHER_get_asn1_iv)
+
+
+
+#ifdef OLDER_OPENSSL
+# define DECLARE_TDES_EVP(mode, block_size, key_len, iv_len, flags,     \
+                          ctx_size, init, do_cipher, cleanup,           \
+                          set_asn1_parameters, get_asn1_parameters)     \
+const EVP_CIPHER tdes_##mode = {                                        \
+    NID_des_ede3_##mode,                                                \
+    block_size,                                                         \
+    key_len,                                                            \
+    iv_len,                                                             \
+    flags,                                                              \
+    init,                                                               \
+    do_cipher,                                                          \
+    cleanup,                                                            \
+    ctx_size,                                                           \
+    set_asn1_parameters,                                                \
+    get_asn1_parameters,                                                \
+    NULL,                                                               \
+    NULL                                                                \
+};                                                                      \
+const EVP_CIPHER *ibmca_tdes_##mode(void)                               \
+{                                                                       \
+    return &tdes_##mode;                                                \
+}
+
+#else
+# define DECLARE_TDES_EVP(mode, block_size, key_len, iv_len, flags,     \
+                          ctx_size, init, do_cipher, cleanup,           \
+                          set_asn1_parameters, get_asn1_parameters)     \
+static EVP_CIPHER *tdes_##mode = NULL;                                  \
+const EVP_CIPHER *ibmca_tdes_##mode(void)                               \
+{                                                                       \
+    EVP_CIPHER *cipher;                                                 \
+                                                                        \
+    if (tdes_##mode != NULL)                                            \
+        goto done;                                                      \
+                                                                        \
+    if ((cipher = EVP_CIPHER_meth_new(NID_des_ede3_##mode,              \
+                                      block_size, key_len)) == NULL     \
+         || !EVP_CIPHER_meth_set_iv_length(cipher, iv_len)              \
+         || !EVP_CIPHER_meth_set_flags(cipher, flags)                   \
+         || !EVP_CIPHER_meth_set_init(cipher, init)                     \
+         || !EVP_CIPHER_meth_set_do_cipher(cipher, do_cipher)           \
+         || !EVP_CIPHER_meth_set_cleanup(cipher, cleanup)               \
+         || !EVP_CIPHER_meth_set_impl_ctx_size(cipher, ctx_size)        \
+         || !EVP_CIPHER_meth_set_set_asn1_params(cipher,                \
+                                                 set_asn1_parameters)   \
+         || !EVP_CIPHER_meth_set_get_asn1_params(cipher,                \
+                                                 get_asn1_parameters)) {\
+        EVP_CIPHER_meth_free(cipher);                                   \
+        cipher = NULL;                                                  \
+    }                                                                   \
+    tdes_##mode = cipher;                                               \
+done:                                                                   \
+    return tdes_##mode;                                                 \
+}                                                                       \
+                                                                        \
+void ibmca_tdes_##mode##_destroy(void)                                  \
+{                                                                       \
+    EVP_CIPHER_meth_free(tdes_##mode);                                  \
+    tdes_##mode = NULL;                                                 \
+}
+#endif
+
+DECLARE_TDES_EVP(ecb, sizeof(ica_des_vector_t), sizeof(ica_des_key_triple_t),
+                 sizeof(ica_des_vector_t), EVP_CIPH_ECB_MODE | EVP_CIPH_FLAG_FIPS,
+                 sizeof(struct ibmca_des_context), ibmca_init_key,
+                 ibmca_3des_cipher, ibmca_cipher_cleanup,
+                 EVP_CIPHER_set_asn1_iv, EVP_CIPHER_get_asn1_iv)
+DECLARE_TDES_EVP(cbc, sizeof(ica_des_vector_t), sizeof(ica_des_key_triple_t),
+                 sizeof(ica_des_vector_t), EVP_CIPH_CBC_MODE | EVP_CIPH_FLAG_FIPS,
+                 sizeof(struct ibmca_des_context), ibmca_init_key,
+                 ibmca_3des_cipher, ibmca_cipher_cleanup,
+                 EVP_CIPHER_set_asn1_iv, EVP_CIPHER_get_asn1_iv)
+DECLARE_TDES_EVP(ofb, 1, sizeof(ica_des_key_triple_t),
+                 sizeof(ica_des_vector_t), EVP_CIPH_OFB_MODE | EVP_CIPH_FLAG_FIPS,
+                 sizeof(struct ibmca_des_context), ibmca_init_key,
+                 ibmca_3des_cipher, ibmca_cipher_cleanup,
+                 EVP_CIPHER_set_asn1_iv, EVP_CIPHER_get_asn1_iv)
+DECLARE_TDES_EVP(cfb, 1, sizeof(ica_des_key_triple_t),
+                 sizeof(ica_des_vector_t), EVP_CIPH_CFB_MODE | EVP_CIPH_FLAG_FIPS,
+                 sizeof(struct ibmca_des_context), ibmca_init_key,
+                 ibmca_3des_cipher, ibmca_cipher_cleanup,
+                 EVP_CIPHER_set_asn1_iv, EVP_CIPHER_get_asn1_iv)
+
+
+#ifndef OPENSSL_NO_AES_GCM
+static int ibmca_gcm_aad(ICA_AES_GCM_CTX *ctx, const unsigned char *aad,
+                         size_t len, int enc, int keylen)
+{
+    uint64_t alen = ctx->aadlen;
+
+    if (ctx->ptlen)
+        return -2;
+
+    alen += len;
+    if (alen > (1ULL << 61) || (sizeof(len) == 8 && alen < len))
+        return -1;
+
+    ctx->aadlen = alen;
+
+    /* ctx->taglen is not set at this time... and is not needed. The
+     * function only checks, if it's a valid gcm tag length. So we chose 16.
+     */
+    return !(p_ica_aes_gcm_intermediate(NULL, 0, NULL, ctx->ucb,
+                                        (unsigned char *) aad, len,
+                                        ctx->tag, 16, ctx->key, keylen,
+                                        ctx->subkey, enc));
+}
+
+static int ibmca_aes_gcm(ICA_AES_GCM_CTX *ctx, const unsigned char *in,
+                         unsigned char *out, size_t len, int enc, int keylen)
+{
+    uint64_t mlen = ctx->ptlen;
+    unsigned char *pt, *ct;
+    int rv;
+
+    mlen += len;
+    if (mlen > ((1ULL << 36) - 32) || (sizeof(len) == 8 && mlen < len))
+        return 0;
+
+    ctx->ptlen = mlen;
+
+    if (enc) {
+        pt = (unsigned char *) in;
+        ct = out;
+    } else {
+        ct = (unsigned char *) in;
+        pt = out;
+    }
+
+    /* ctx->taglen is not set at this time... and is not needed. The
+     * function only checks, if it's a valid gcm tag length. So we chose 16.
+     */
+    rv = p_ica_aes_gcm_intermediate(pt, len, ct, ctx->ucb, NULL, 0,
+                                      ctx->tag, 16, ctx->key, keylen,
+                                      ctx->subkey, enc);
+    if (rv)
+        return 0;
+
+    return 1;
+}
+
+static int ibmca_aes_gcm_init_key(EVP_CIPHER_CTX *ctx,
+                                  const unsigned char *key,
+                                  const unsigned char *iv, int enc)
+{
+    ICA_AES_GCM_CTX *gctx =
+        (ICA_AES_GCM_CTX *) EVP_CIPHER_CTX_get_cipher_data(ctx);
+    const int gkeylen = EVP_CIPHER_CTX_key_length(ctx);
+
+    if (!iv && !key)
+        return 1;
+
+    if (key) {
+        memcpy(gctx->key, key, gkeylen);
+
+        if (iv == NULL && gctx->iv_set)
+            iv = gctx->iv;
+
+        if (iv) {
+            memset(gctx->icb, 0, sizeof(gctx->icb));
+            memset(gctx->tag, 0, sizeof(gctx->tag));
+            gctx->aadlen = 0;
+            gctx->ptlen = 0;
+            if (p_ica_aes_gcm_initialize(iv, gctx->ivlen,
+                                         gctx->key, gkeylen,
+                                         gctx->icb, gctx->ucb,
+                                         gctx->subkey, enc))
+                return 0;
+
+            gctx->iv_set = 1;
+        }
+        gctx->key_set = 1;
+    } else {
+        if (gctx->key_set) {
+            memset(gctx->icb, 0, sizeof(gctx->icb));
+            memset(gctx->tag, 0, sizeof(gctx->tag));
+            gctx->aadlen = 0;
+            gctx->ptlen = 0;
+            if (p_ica_aes_gcm_initialize(iv, gctx->ivlen,
+                                         gctx->key, gkeylen,
+                                         gctx->icb, gctx->ucb,
+                                         gctx->subkey, enc))
+                return 0;
+        } else {
+            memcpy(gctx->iv, iv, gctx->ivlen);
+        }
+        gctx->iv_set = 1;
+        gctx->iv_gen = 0;
+    }
+    return 1;
+}
+
+static int ibmca_aes_gcm_setiv(EVP_CIPHER_CTX *c)
+{
+    ICA_AES_GCM_CTX *gctx =
+        (ICA_AES_GCM_CTX *) EVP_CIPHER_CTX_get_cipher_data(c);
+    const int gkeylen = EVP_CIPHER_CTX_key_length(c);
+    int enc = EVP_CIPHER_CTX_encrypting(c);
+
+    if (!gctx->key_set)
+        return 0;
+
+    memset(gctx->icb, 0, sizeof(gctx->icb));
+    memset(gctx->tag, 0, sizeof(gctx->tag));
+    gctx->aadlen = 0;
+    gctx->ptlen = 0;
+    return !(p_ica_aes_gcm_initialize(gctx->iv, gctx->ivlen, gctx->key,
+                                      gkeylen, gctx->icb, gctx->ucb,
+                                      gctx->subkey, enc));
+}
+
+static int ibmca_aes_gcm_ctrl(EVP_CIPHER_CTX *c, int type, int arg, void *ptr)
+{
+    ICA_AES_GCM_CTX *gctx =
+        (ICA_AES_GCM_CTX *) EVP_CIPHER_CTX_get_cipher_data(c);
+    unsigned char *iv_noconst = EVP_CIPHER_CTX_iv_noconst(c);
+    unsigned char *buf_noconst = EVP_CIPHER_CTX_buf_noconst(c);
+    int enc = EVP_CIPHER_CTX_encrypting(c);
+    EVP_CIPHER_CTX *out;
+    ICA_AES_GCM_CTX *gctx_out;
+    unsigned char *iv_noconst_out;
+    unsigned int len;
+
+    switch (type) {
+    case EVP_CTRL_INIT:
+        gctx->key_set = 0;
+        gctx->iv_set = 0;
+        gctx->ivlen = EVP_CIPHER_CTX_iv_length(c);
+        gctx->iv = iv_noconst;
+        gctx->taglen = -1;
+        gctx->iv_gen = 0;
+        gctx->tls_aadlen = -1;
+        return 1;
+    case EVP_CTRL_GCM_SET_IVLEN:
+        if (arg <= 0)
+            return 0;
+        if ((arg > EVP_MAX_IV_LENGTH) && (arg > gctx->ivlen)) {
+            if (gctx->iv != iv_noconst)
+                OPENSSL_free(gctx->iv);
+            gctx->iv = OPENSSL_malloc(arg);
+            if (gctx->iv == NULL)
+                return 0;
+        }
+        gctx->ivlen = arg;
+        return 1;
+    case EVP_CTRL_GCM_SET_TAG:
+        if (arg <= 0 || arg > 16 || enc)
+            return 0;
+        memcpy(buf_noconst, ptr, arg);
+        gctx->taglen = arg;
+        return 1;
+    case EVP_CTRL_GCM_GET_TAG:
+        if (arg <= 0 || arg > 16 || !enc || gctx->taglen < 0)
+            return 0;
+        memcpy(ptr, buf_noconst, arg);
+        return 1;
+    case EVP_CTRL_GCM_SET_IV_FIXED:
+        if (arg == -1) {
+            memcpy(gctx->iv, ptr, gctx->ivlen);
+            gctx->iv_gen = 1;
+            return 1;
+        }
+        if ((arg < 4) || (gctx->ivlen - arg) < 8)
+            return 0;
+        if (arg)
+            memcpy(gctx->iv, ptr, arg);
+        if (enc && RAND_bytes(gctx->iv + arg, gctx->ivlen - arg) <= 0)
+            return 0;
+        gctx->iv_gen = 1;
+        return 1;
+    case EVP_CTRL_GCM_IV_GEN:
+        if (gctx->iv_gen == 0 || gctx->key_set == 0)
+            return 0;
+        if (!ibmca_aes_gcm_setiv(c))
+            return 0;
+        if (arg <= 0 || arg > gctx->ivlen)
+            arg = gctx->ivlen;
+        memcpy(ptr, gctx->iv + gctx->ivlen - arg, arg);
+        ++*(uint64_t *) (gctx->iv + gctx->ivlen - 8);
+        gctx->iv_set = 1;
+        return 1;
+    case EVP_CTRL_GCM_SET_IV_INV:
+        if (gctx->iv_gen == 0 || gctx->key_set == 0 || enc)
+            return 0;
+        memcpy(gctx->iv + gctx->ivlen - arg, ptr, arg);
+        if (!ibmca_aes_gcm_setiv(c))
+            return 0;
+        gctx->iv_set = 1;
+        return 1;
+    case EVP_CTRL_AEAD_TLS1_AAD:
+        if (arg != EVP_AEAD_TLS1_AAD_LEN)
+            return 0;
+        memcpy(buf_noconst, ptr, arg);
+        gctx->tls_aadlen = arg;
+        len = buf_noconst[arg - 2] << 8 | buf_noconst[arg - 1];
+        if (len < EVP_GCM_TLS_EXPLICIT_IV_LEN)
+            return 0;
+        len -= EVP_GCM_TLS_EXPLICIT_IV_LEN;
+        if (!enc) {
+            if (len < EVP_GCM_TLS_TAG_LEN)
+                return 0;
+            len -= EVP_GCM_TLS_TAG_LEN;
+        }
+        buf_noconst[arg - 2] = len >> 8;
+        buf_noconst[arg - 1] = len & 0xff;
+        return EVP_GCM_TLS_TAG_LEN;
+    case EVP_CTRL_COPY:{
+            out = ptr;
+            gctx_out = (ICA_AES_GCM_CTX *)
+                EVP_CIPHER_CTX_get_cipher_data(out);
+            iv_noconst_out = EVP_CIPHER_CTX_iv_noconst(out);
+            if (gctx->iv == iv_noconst) {
+                gctx_out->iv = iv_noconst_out;
+            } else {
+                gctx_out->iv = OPENSSL_malloc(gctx->ivlen);
+                if (gctx_out->iv == NULL)
+                    return 0;
+                memcpy(gctx_out->iv, gctx->iv, gctx->ivlen);
+            }
+            return 1;
+        }
+    default:
+        return -1;
+    }
+}
+
+static int ibmca_gcm_tag(EVP_CIPHER_CTX *ctx, unsigned char *out,
+                         const unsigned char *in, int taglen)
+{
+    ICA_AES_GCM_CTX *gctx =
+        (ICA_AES_GCM_CTX *) EVP_CIPHER_CTX_get_cipher_data(ctx);
+    int enc = EVP_CIPHER_CTX_encrypting(ctx);
+    const int gkeylen = EVP_CIPHER_CTX_key_length(ctx);
+
+    if (p_ica_aes_gcm_last(gctx->icb, gctx->aadlen, gctx->ptlen,
+                           gctx->tag, (unsigned char *) in, taglen,
+                           gctx->key, gkeylen, gctx->subkey, enc))
+        return 0;
+
+    if (out)
+        memcpy(out, gctx->tag, taglen);
+
+    return 1;
+}
+
+static int ibmca_aes_gcm_tls_cipher(EVP_CIPHER_CTX *ctx, unsigned char *out,
+                                    const unsigned char *in, size_t len)
+{
+    ICA_AES_GCM_CTX *gctx =
+        (ICA_AES_GCM_CTX *) EVP_CIPHER_CTX_get_cipher_data(ctx);
+    unsigned char *buf = EVP_CIPHER_CTX_buf_noconst(ctx);
+    int enc = EVP_CIPHER_CTX_encrypting(ctx);
+    const int keylen = EVP_CIPHER_CTX_key_length(ctx);
+    int rv = -1;
+
+    if (out != in || len < (EVP_GCM_TLS_EXPLICIT_IV_LEN + EVP_GCM_TLS_TAG_LEN))
+        return -1;
+    if (EVP_CIPHER_CTX_ctrl(ctx, enc ? EVP_CTRL_GCM_IV_GEN :
+                            EVP_CTRL_GCM_SET_IV_INV,
+                            EVP_GCM_TLS_EXPLICIT_IV_LEN, out) <= 0)
+        goto err;
+
+    if (!ibmca_gcm_aad(gctx, buf, gctx->tls_aadlen, enc, keylen))
+        goto err;
+
+    in += EVP_GCM_TLS_EXPLICIT_IV_LEN;
+    out += EVP_GCM_TLS_EXPLICIT_IV_LEN;
+    len -= EVP_GCM_TLS_EXPLICIT_IV_LEN + EVP_GCM_TLS_TAG_LEN;
+
+    if (!ibmca_aes_gcm(gctx, in, out, len, enc, keylen))
+        goto err;
+
+    if (enc) {
+        out += len;
+        if (!ibmca_gcm_tag(ctx, out, NULL, EVP_GCM_TLS_TAG_LEN)) {
+            goto err;
+        }
+        rv = len + EVP_GCM_TLS_EXPLICIT_IV_LEN + EVP_GCM_TLS_TAG_LEN;
+    } else {
+        if (!ibmca_gcm_tag(ctx, buf, in + len, EVP_GCM_TLS_TAG_LEN)) {
+            OPENSSL_cleanse(out, len);
+            goto err;
+        }
+        rv = len;
+    }
+err:
+    gctx->iv_set = 0;
+    gctx->tls_aadlen = -1;
+    return rv;
+}
+
+static int ibmca_aes_gcm_cipher(EVP_CIPHER_CTX *ctx, unsigned char *out,
+                                const unsigned char *in, size_t len)
+{
+    ICA_AES_GCM_CTX *gctx =
+        (ICA_AES_GCM_CTX *) EVP_CIPHER_CTX_get_cipher_data(ctx);
+    unsigned char *buf = EVP_CIPHER_CTX_buf_noconst(ctx);
+    int enc = EVP_CIPHER_CTX_encrypting(ctx);
+    const int keylen = EVP_CIPHER_CTX_key_length(ctx);
+
+    if (!gctx->key_set)
+        return -1;
+
+    if (gctx->tls_aadlen >= 0)
+        return ibmca_aes_gcm_tls_cipher(ctx, out, in, len);
+
+    if (!gctx->iv_set)
+        return -1;
+
+    if (in) {
+        if (out == NULL) {
+            if (!ibmca_gcm_aad(gctx, in, len, enc, keylen))
+                return -1;
+        } else {
+            if (!ibmca_aes_gcm(gctx, in, out, len, enc, keylen))
+                return -1;
+        }
+        return len;
+    } else {
+        if (enc) {
+            gctx->taglen = 16;
+            if (!ibmca_gcm_tag(ctx, buf, NULL, gctx->taglen))
+                return -1;
+        } else {
+            if (gctx->taglen < 0)
+                return -1;
+            if (!ibmca_gcm_tag(ctx, NULL, buf, gctx->taglen))
+                return -1;
+        }
+        gctx->iv_set = 0;
+        return 0;
+    }
+}
+#endif
+
+
+#define IMPLEMENT_IBMCA_AES_CIPHER_FN(name, NAME)                       \
+static int ibmca_##name##_cipher(EVP_CIPHER_CTX *ctx,                   \
+                                 unsigned char *out,                    \
+                                 const unsigned char *in, size_t len)   \
+{                                                                       \
+    ICA_##NAME##_CTX *c =                                               \
+        (ICA_##NAME##_CTX *)EVP_CIPHER_CTX_get_cipher_data(ctx);        \
+    unsigned char *iv = EVP_CIPHER_CTX_iv_noconst(ctx);                 \
+    const int mode = EVP_CIPHER_CTX_mode(ctx);                          \
+    const int enc = EVP_CIPHER_CTX_encrypting(ctx) ?                    \
+                                ICA_ENCRYPT : ICA_DECRYPT;              \
+    int rv;                                                             \
+                                                                        \
+    switch (mode) {                                                     \
+    case EVP_CIPH_ECB_MODE:                                             \
+        rv = p_ica_aes_ecb(in, out, len, c->key, NAME##_KEYLEN, enc);   \
+        break;                                                          \
+    case EVP_CIPH_CBC_MODE:                                             \
+        rv = p_ica_aes_cbc(in, out, len, c->key, NAME##_KEYLEN,         \
+                           iv, enc);                                    \
+        break;                                                          \
+    case EVP_CIPH_CFB_MODE:                                             \
+        rv = p_ica_aes_cfb(in, out, len, c->key, NAME##_KEYLEN,         \
+                           iv, 16, enc);                                \
+        break;                                                          \
+    case EVP_CIPH_OFB_MODE:                                             \
+        rv = p_ica_aes_ofb(in, out, len, c->key, NAME##_KEYLEN,         \
+                           iv, enc);                                    \
+        break;                                                          \
+    default:                                                            \
+        IBMCAerr(IBMCA_F_IBMCA_##NAME##_CIPHER,                         \
+                 IBMCA_R_CIPHER_MODE_NOT_SUPPORTED);                    \
+        return 0;                                                       \
+    }                                                                   \
+    if (rv) {                                                           \
+        IBMCAerr(IBMCA_F_IBMCA_##NAME##_CIPHER,                         \
+                 IBMCA_R_REQUEST_FAILED);                               \
+        return 0;                                                       \
+    }                                                                   \
+                                                                        \
+    return 1;                                                           \
+}
+
+IMPLEMENT_IBMCA_AES_CIPHER_FN(aes_128, AES_128)
+IMPLEMENT_IBMCA_AES_CIPHER_FN(aes_192, AES_192)
+IMPLEMENT_IBMCA_AES_CIPHER_FN(aes_256, AES_256)
+
+
+
+#ifdef OLDER_OPENSSL
+# define DECLARE_AES_EVP(kbits, mode, block_size, key_len, iv_len,      \
+                         flags, ctx_size, init, do_cipher, cleanup,     \
+                         set_asn1_parameters, get_asn1_parameters, ctrl)\
+const EVP_CIPHER aes_##kbits##_##mode = {                               \
+    NID_aes_##kbits##_##mode,                                           \
+    block_size,                                                         \
+    key_len,                                                            \
+    iv_len,                                                             \
+    flags,                                                              \
+    init,                                                               \
+    do_cipher,                                                          \
+    cleanup,                                                            \
+    ctx_size,                                                           \
+    set_asn1_parameters,                                                \
+    get_asn1_parameters,                                                \
+    ctrl,                                                               \
+    NULL                                                                \
+};                                                                      \
+const EVP_CIPHER *ibmca_aes_##kbits##_##mode(void)                      \
+{                                                                       \
+    return &aes_##kbits##_##mode;                                       \
+}
+
+#else
+# define DECLARE_AES_EVP(kbits, mode, block_size, key_len, iv_len,      \
+                         flags, ctx_size, init, do_cipher, cleanup,     \
+                         set_asn1_parameters, get_asn1_parameters, ctrl)\
+static EVP_CIPHER *aes_##kbits##_##mode = NULL;                         \
+const EVP_CIPHER *ibmca_aes_##kbits##_##mode(void)                      \
+{                                                                       \
+    EVP_CIPHER *cipher;                                                 \
+                                                                        \
+    if (aes_##kbits##_##mode != NULL)                                   \
+        goto done;                                                      \
+                                                                        \
+    if ((cipher = EVP_CIPHER_meth_new(NID_aes_##kbits##_##mode,         \
+                                      block_size, key_len)) == NULL     \
+         || !EVP_CIPHER_meth_set_iv_length(cipher, iv_len)              \
+         || !EVP_CIPHER_meth_set_flags(cipher, flags)                   \
+         || !EVP_CIPHER_meth_set_init(cipher, init)                     \
+         || !EVP_CIPHER_meth_set_do_cipher(cipher, do_cipher)           \
+         || !EVP_CIPHER_meth_set_cleanup(cipher, cleanup)               \
+         || !EVP_CIPHER_meth_set_impl_ctx_size(cipher, ctx_size)        \
+         || !EVP_CIPHER_meth_set_set_asn1_params(cipher,                \
+                                                 set_asn1_parameters)   \
+         || !EVP_CIPHER_meth_set_get_asn1_params(cipher,                \
+                                                 get_asn1_parameters)   \
+         || !EVP_CIPHER_meth_set_ctrl(cipher, ctrl)) {                  \
+        EVP_CIPHER_meth_free(cipher);                                   \
+        cipher = NULL;                                                  \
+    }                                                                   \
+    aes_##kbits##_##mode = cipher;                                      \
+done:                                                                   \
+    return aes_##kbits##_##mode;                                        \
+}                                                                       \
+                                                                        \
+void ibmca_aes_##kbits##_##mode##_destroy(void)                         \
+{                                                                       \
+    EVP_CIPHER_meth_free(aes_##kbits##_##mode);                         \
+    aes_##kbits##_##mode = NULL;                                        \
+}
+#endif
+
+DECLARE_AES_EVP(128, ecb, sizeof(ica_aes_vector_t),
+                sizeof(ica_aes_key_len_128_t), sizeof(ica_aes_vector_t),
+                EVP_CIPH_ECB_MODE | EVP_CIPH_FLAG_FIPS, sizeof(ICA_AES_128_CTX),
+                ibmca_init_key, ibmca_aes_128_cipher, ibmca_cipher_cleanup,
+                EVP_CIPHER_set_asn1_iv, EVP_CIPHER_get_asn1_iv, NULL)
+DECLARE_AES_EVP(128, cbc, sizeof(ica_aes_vector_t),
+                sizeof(ica_aes_key_len_128_t), sizeof(ica_aes_vector_t),
+                EVP_CIPH_CBC_MODE | EVP_CIPH_FLAG_FIPS, sizeof(ICA_AES_128_CTX),
+                ibmca_init_key, ibmca_aes_128_cipher, ibmca_cipher_cleanup,
+                EVP_CIPHER_set_asn1_iv, EVP_CIPHER_get_asn1_iv, NULL)
+DECLARE_AES_EVP(128, ofb, 1, sizeof(ica_aes_key_len_128_t),
+                sizeof(ica_aes_vector_t), EVP_CIPH_OFB_MODE | EVP_CIPH_FLAG_FIPS,
+                sizeof(ICA_AES_128_CTX), ibmca_init_key,
+                ibmca_aes_128_cipher, ibmca_cipher_cleanup,
+                EVP_CIPHER_set_asn1_iv, EVP_CIPHER_get_asn1_iv, NULL)
+DECLARE_AES_EVP(128, cfb, 1, sizeof(ica_aes_key_len_128_t),
+                sizeof(ica_aes_vector_t), EVP_CIPH_CFB_MODE | EVP_CIPH_FLAG_FIPS,
+                sizeof(ICA_AES_128_CTX), ibmca_init_key,
+                ibmca_aes_128_cipher, ibmca_cipher_cleanup,
+                EVP_CIPHER_set_asn1_iv, EVP_CIPHER_get_asn1_iv, NULL)
+#ifndef OPENSSL_NO_AES_GCM
+DECLARE_AES_EVP(128, gcm, 1, sizeof(ica_aes_key_len_128_t),
+                sizeof(ica_aes_vector_t) - sizeof(uint32_t),
+                EVP_CIPH_GCM_MODE | EVP_CIPH_FLAG_DEFAULT_ASN1
+                | EVP_CIPH_CUSTOM_IV | EVP_CIPH_FLAG_CUSTOM_CIPHER
+                | EVP_CIPH_ALWAYS_CALL_INIT | EVP_CIPH_CTRL_INIT
+                | EVP_CIPH_CUSTOM_COPY | EVP_CIPH_FLAG_AEAD_CIPHER
+		| EVP_CIPH_FLAG_FIPS,
+                sizeof(ICA_AES_GCM_CTX),
+                ibmca_aes_gcm_init_key, ibmca_aes_gcm_cipher, NULL, NULL,
+                NULL, ibmca_aes_gcm_ctrl)
+#endif
+
+DECLARE_AES_EVP(192, ecb, sizeof(ica_aes_vector_t),
+                sizeof(ica_aes_key_len_192_t), sizeof(ica_aes_vector_t),
+                EVP_CIPH_ECB_MODE | EVP_CIPH_FLAG_FIPS, sizeof(ICA_AES_192_CTX),
+                ibmca_init_key, ibmca_aes_192_cipher, ibmca_cipher_cleanup,
+                EVP_CIPHER_set_asn1_iv, EVP_CIPHER_get_asn1_iv, NULL)
+DECLARE_AES_EVP(192, cbc, sizeof(ica_aes_vector_t),
+                sizeof(ica_aes_key_len_192_t), sizeof(ica_aes_vector_t),
+                EVP_CIPH_CBC_MODE | EVP_CIPH_FLAG_FIPS, sizeof(ICA_AES_192_CTX),
+                ibmca_init_key, ibmca_aes_192_cipher, ibmca_cipher_cleanup,
+                EVP_CIPHER_set_asn1_iv, EVP_CIPHER_get_asn1_iv, NULL)
+DECLARE_AES_EVP(192, ofb, 1, sizeof(ica_aes_key_len_192_t),
+                sizeof(ica_aes_vector_t), EVP_CIPH_OFB_MODE | EVP_CIPH_FLAG_FIPS,
+                sizeof(ICA_AES_192_CTX), ibmca_init_key,
+                ibmca_aes_192_cipher, ibmca_cipher_cleanup,
+                EVP_CIPHER_set_asn1_iv, EVP_CIPHER_get_asn1_iv, NULL)
+DECLARE_AES_EVP(192, cfb, 1, sizeof(ica_aes_key_len_192_t),
+                sizeof(ica_aes_vector_t), EVP_CIPH_CFB_MODE | EVP_CIPH_FLAG_FIPS,
+                sizeof(ICA_AES_192_CTX), ibmca_init_key,
+                ibmca_aes_192_cipher, ibmca_cipher_cleanup,
+                EVP_CIPHER_set_asn1_iv, EVP_CIPHER_get_asn1_iv, NULL)
+#ifndef OPENSSL_NO_AES_GCM
+DECLARE_AES_EVP(192, gcm, 1, sizeof(ica_aes_key_len_192_t),
+                sizeof(ica_aes_vector_t) - sizeof(uint32_t),
+                EVP_CIPH_GCM_MODE | EVP_CIPH_FLAG_DEFAULT_ASN1
+                | EVP_CIPH_CUSTOM_IV | EVP_CIPH_FLAG_CUSTOM_CIPHER
+                | EVP_CIPH_ALWAYS_CALL_INIT | EVP_CIPH_CTRL_INIT
+                | EVP_CIPH_CUSTOM_COPY | EVP_CIPH_FLAG_AEAD_CIPHER
+		| EVP_CIPH_FLAG_FIPS,
+                sizeof(ICA_AES_GCM_CTX),
+                ibmca_aes_gcm_init_key, ibmca_aes_gcm_cipher, NULL, NULL,
+                NULL, ibmca_aes_gcm_ctrl)
+#endif
+
+DECLARE_AES_EVP(256, ecb, sizeof(ica_aes_vector_t),
+                sizeof(ica_aes_key_len_256_t), sizeof(ica_aes_vector_t),
+                EVP_CIPH_ECB_MODE | EVP_CIPH_FLAG_FIPS, sizeof(ICA_AES_256_CTX),
+                ibmca_init_key, ibmca_aes_256_cipher, ibmca_cipher_cleanup,
+                EVP_CIPHER_set_asn1_iv, EVP_CIPHER_get_asn1_iv, NULL)
+DECLARE_AES_EVP(256, cbc, sizeof(ica_aes_vector_t),
+                sizeof(ica_aes_key_len_256_t), sizeof(ica_aes_vector_t),
+                EVP_CIPH_CBC_MODE | EVP_CIPH_FLAG_FIPS, sizeof(ICA_AES_256_CTX),
+                ibmca_init_key, ibmca_aes_256_cipher, ibmca_cipher_cleanup,
+                EVP_CIPHER_set_asn1_iv, EVP_CIPHER_get_asn1_iv, NULL)
+DECLARE_AES_EVP(256, ofb, 1, sizeof(ica_aes_key_len_256_t),
+                sizeof(ica_aes_vector_t), EVP_CIPH_OFB_MODE | EVP_CIPH_FLAG_FIPS,
+                sizeof(ICA_AES_256_CTX), ibmca_init_key,
+                ibmca_aes_256_cipher, ibmca_cipher_cleanup,
+                EVP_CIPHER_set_asn1_iv, EVP_CIPHER_get_asn1_iv, NULL)
+DECLARE_AES_EVP(256, cfb, 1, sizeof(ica_aes_key_len_256_t),
+                sizeof(ica_aes_vector_t), EVP_CIPH_CFB_MODE | EVP_CIPH_FLAG_FIPS,
+                sizeof(ICA_AES_256_CTX), ibmca_init_key,
+                ibmca_aes_256_cipher, ibmca_cipher_cleanup,
+                EVP_CIPHER_set_asn1_iv, EVP_CIPHER_get_asn1_iv, NULL)
+#ifndef OPENSSL_NO_AES_GCM
+DECLARE_AES_EVP(256, gcm, 1, sizeof(ica_aes_key_len_256_t),
+                sizeof(ica_aes_vector_t) - sizeof(uint32_t),
+                EVP_CIPH_GCM_MODE | EVP_CIPH_FLAG_DEFAULT_ASN1
+                | EVP_CIPH_CUSTOM_IV | EVP_CIPH_FLAG_CUSTOM_CIPHER
+                | EVP_CIPH_ALWAYS_CALL_INIT | EVP_CIPH_CTRL_INIT
+                | EVP_CIPH_CUSTOM_COPY | EVP_CIPH_FLAG_AEAD_CIPHER
+		| EVP_CIPH_FLAG_FIPS,
+                sizeof(ICA_AES_GCM_CTX),
+                ibmca_aes_gcm_init_key, ibmca_aes_gcm_cipher, NULL, NULL,
+                NULL, ibmca_aes_gcm_ctrl)
+#endif
+
+
+
+
diff -pruN 1.4.0-1/src/engine/ibmca_dh.c 2.5.0-0ubuntu1/src/engine/ibmca_dh.c
--- 1.4.0-1/src/engine/ibmca_dh.c	1970-01-01 00:00:00.000000000 +0000
+++ 2.5.0-0ubuntu1/src/engine/ibmca_dh.c	2025-04-16 08:09:24.000000000 +0000
@@ -0,0 +1,97 @@
+/*
+ * Copyright [2005-2018] International Business Machines Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <openssl/dh.h>
+#include "ibmca.h"
+#include <stdio.h>
+
+#ifndef OPENSSL_NO_DH
+
+static int (*ibmca_mod_exp_dh_backup)(DH const *dh, BIGNUM *r,
+                                      const BIGNUM *a, const BIGNUM *p,
+                                      const BIGNUM *m, BN_CTX *ctx,
+                                      BN_MONT_CTX *m_ctx);
+
+/* This function is aliased to mod_exp (with the dh and mont dropped). */
+static int ibmca_mod_exp_dh(DH const *dh, BIGNUM *r,
+                            const BIGNUM *a, const BIGNUM *p,
+                            const BIGNUM *m, BN_CTX *ctx, BN_MONT_CTX *m_ctx)
+{
+	if (!ibmca_mod_exp(r, a, p, m, ctx) && ibmca_mod_exp_dh_backup)
+		return ibmca_mod_exp_dh_backup(dh, r, a, p, m, ctx, m_ctx);
+	return 1;
+}
+
+
+#ifdef OLDER_OPENSSL
+static DH_METHOD dh_m = {
+    "Ibmca DH method",          /* name */
+    NULL,                       /* generate_key */
+    NULL,                       /* compute_key */
+    ibmca_mod_exp_dh,           /* bn_mod_exp */
+    NULL,                       /* init */
+    NULL,                       /* finish */
+    DH_FLAG_FIPS_METHOD,        /* flags */
+    NULL                        /* app_data */
+};
+
+DH_METHOD *ibmca_dh(void)
+{
+    const DH_METHOD *meth1 = DH_OpenSSL();
+
+    ibmca_mod_exp_dh_backup = meth1->bn_mod_exp;
+    dh_m.generate_key = meth1->generate_key;
+    dh_m.compute_key = meth1->compute_key;
+
+    return &dh_m;
+}
+
+#else
+static DH_METHOD *dh_m = NULL;
+DH_METHOD *ibmca_dh(void)
+{
+    const DH_METHOD *meth1;
+    DH_METHOD *method;
+
+    if (dh_m != NULL)
+        goto done;
+
+    if ((method = DH_meth_new("Ibmca DH method", 0)) == NULL
+        || (meth1 = DH_OpenSSL()) == NULL
+	    || (ibmca_mod_exp_dh_backup = DH_meth_get_bn_mod_exp(meth1)) == NULL
+        || !DH_meth_set_generate_key(method, DH_meth_get_generate_key(meth1))
+        || !DH_meth_set_compute_key(method, DH_meth_get_compute_key(meth1))
+        || !DH_meth_set_bn_mod_exp(method, ibmca_mod_exp_dh)
+        || !DH_meth_set_flags(method, DH_FLAG_FIPS_METHOD)) {
+        DH_meth_free(method);
+        method = NULL;
+        meth1 = NULL;
+    }
+
+    dh_m = method;
+
+done:
+    return dh_m;
+}
+
+void ibmca_dh_destroy(void)
+{
+    DH_meth_free(dh_m);
+}
+#endif
+
+#endif                          /* end OPENSSL_NO_DH */
diff -pruN 1.4.0-1/src/engine/ibmca_digest.c 2.5.0-0ubuntu1/src/engine/ibmca_digest.c
--- 1.4.0-1/src/engine/ibmca_digest.c	1970-01-01 00:00:00.000000000 +0000
+++ 2.5.0-0ubuntu1/src/engine/ibmca_digest.c	2025-04-16 08:09:24.000000000 +0000
@@ -0,0 +1,604 @@
+/*
+ * Copyright [2005-2018] International Business Machines Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <string.h>
+#include <openssl/evp.h>
+#include <openssl/rsa.h>
+#include "ibmca.h"
+#include "e_ibmca_err.h"
+
+#ifndef OPENSSL_NO_SHA1
+static int ibmca_sha1_init(EVP_MD_CTX *ctx)
+{
+    IBMCA_SHA_CTX *ibmca_sha_ctx = (IBMCA_SHA_CTX *) EVP_MD_CTX_md_data(ctx);
+
+    memset((unsigned char *) ibmca_sha_ctx, 0, sizeof(*ibmca_sha_ctx));
+
+    return 1;
+}
+
+static int ibmca_sha1_update(EVP_MD_CTX *ctx,
+                             const void *in_data, unsigned long inlen)
+{
+    IBMCA_SHA_CTX *ibmca_sha_ctx = (IBMCA_SHA_CTX *) EVP_MD_CTX_md_data(ctx);
+    unsigned int message_part = SHA_MSG_PART_MIDDLE, fill_size = 0;
+    unsigned long in_data_len = inlen;
+    unsigned char tmp_hash[SHA_HASH_LENGTH];
+
+    if (in_data_len == 0)
+        return 1;
+
+    if (ibmca_sha_ctx->c.runningLength == 0 && ibmca_sha_ctx->tail_len == 0) {
+        message_part = SHA_MSG_PART_FIRST;
+
+        ibmca_sha_ctx->tail_len = in_data_len & 0x3f;
+        if (ibmca_sha_ctx->tail_len) {
+            in_data_len &= ~0x3f;
+            memcpy(ibmca_sha_ctx->tail,
+                   in_data + in_data_len, ibmca_sha_ctx->tail_len);
+        }
+    } else if (ibmca_sha_ctx->c.runningLength == 0
+               && ibmca_sha_ctx->tail_len > 0) {
+        /* Here we need to fill out the temporary tail buffer until
+         * it has 64 bytes in it, then call ica_sha1 on that buffer.
+         * If there weren't enough bytes passed in to fill it out,
+         * just copy in what we can and return success without calling
+         * ica_sha1. - KEY
+         */
+
+        fill_size = SHA_BLOCK_SIZE - ibmca_sha_ctx->tail_len;
+        if (fill_size < in_data_len) {
+            memcpy(ibmca_sha_ctx->tail + ibmca_sha_ctx->tail_len, in_data,
+                   fill_size);
+
+            /* Submit the filled out tail buffer */
+            if (p_ica_sha1((unsigned int) SHA_MSG_PART_FIRST,
+                           (unsigned int) SHA_BLOCK_SIZE, ibmca_sha_ctx->tail,
+                           &ibmca_sha_ctx->c, tmp_hash)) {
+
+                IBMCAerr(IBMCA_F_IBMCA_SHA1_UPDATE, IBMCA_R_REQUEST_FAILED);
+                return 0;
+            }
+        } else {
+            memcpy(ibmca_sha_ctx->tail + ibmca_sha_ctx->tail_len,
+                   in_data, in_data_len);
+            ibmca_sha_ctx->tail_len += in_data_len;
+
+            return 1;
+        }
+
+        /* We had to use 'fill_size' bytes from in_data to fill out the
+         * empty part of save data, so adjust in_data_len
+         */
+        in_data_len -= fill_size;
+
+        ibmca_sha_ctx->tail_len = in_data_len & 0x3f;
+        if (ibmca_sha_ctx->tail_len) {
+            in_data_len &= ~0x3f;
+            memcpy(ibmca_sha_ctx->tail,
+                   in_data + fill_size + in_data_len, ibmca_sha_ctx->tail_len);
+            /* fill_size is added to in_data down below */
+
+        }
+    } else if (ibmca_sha_ctx->c.runningLength > 0) {
+        if (ibmca_sha_ctx->tail_len) {
+            fill_size = SHA_BLOCK_SIZE - ibmca_sha_ctx->tail_len;
+            if (fill_size < in_data_len) {
+                memcpy(ibmca_sha_ctx->tail + ibmca_sha_ctx->tail_len,
+                       in_data, fill_size);
+
+                /* Submit the filled out save buffer */
+                if (p_ica_sha1(message_part,
+                               (unsigned int) SHA_BLOCK_SIZE,
+                               ibmca_sha_ctx->tail, &ibmca_sha_ctx->c,
+                               tmp_hash)) {
+
+                    IBMCAerr(IBMCA_F_IBMCA_SHA1_UPDATE, IBMCA_R_REQUEST_FAILED);
+                    return 0;
+                }
+            } else {
+                memcpy(ibmca_sha_ctx->tail + ibmca_sha_ctx->tail_len,
+                       in_data, in_data_len);
+                ibmca_sha_ctx->tail_len += in_data_len;
+
+                return 1;
+            }
+
+            /*
+             * We had to use some of the data from in_data to
+             * fill out the empty part of save data, so adjust
+             * in_data_len
+             */
+            in_data_len -= fill_size;
+
+            ibmca_sha_ctx->tail_len = in_data_len & 0x3f;
+            if (ibmca_sha_ctx->tail_len) {
+                in_data_len &= ~0x3f;
+                memcpy(ibmca_sha_ctx->tail,
+                       in_data + fill_size + in_data_len,
+                       ibmca_sha_ctx->tail_len);
+            }
+        } else {
+            /* This is the odd case, where we need to go ahead and
+             * send the first X * 64 byte chunks in to be processed
+             * and copy the last <64 byte area into the tail. -KEY
+             */
+            ibmca_sha_ctx->tail_len = in_data_len & 0x3f;
+            if (ibmca_sha_ctx->tail_len) {
+                in_data_len &= ~0x3f;
+                memcpy(ibmca_sha_ctx->tail, in_data + in_data_len,
+                       ibmca_sha_ctx->tail_len);
+            }
+        }
+    }
+
+    /* If the data passed in was <64 bytes, in_data_len will be 0 */
+    if (in_data_len &&
+        p_ica_sha1(message_part,
+                   (unsigned int) in_data_len,
+                   (unsigned char *) (in_data + fill_size), &ibmca_sha_ctx->c,
+                   tmp_hash)) {
+
+        IBMCAerr(IBMCA_F_IBMCA_SHA1_UPDATE, IBMCA_R_REQUEST_FAILED);
+        return 0;
+    }
+
+    return 1;
+}
+
+static int ibmca_sha1_final(EVP_MD_CTX *ctx, unsigned char *md)
+{
+    IBMCA_SHA_CTX *ibmca_sha_ctx = (IBMCA_SHA_CTX *) EVP_MD_CTX_md_data(ctx);
+    unsigned int message_part = 0;
+
+    if (ibmca_sha_ctx->c.runningLength)
+        message_part = SHA_MSG_PART_FINAL;
+    else
+        message_part = SHA_MSG_PART_ONLY;
+
+    if (p_ica_sha1(message_part,
+                   ibmca_sha_ctx->tail_len,
+                   (unsigned char *) ibmca_sha_ctx->tail,
+                   &ibmca_sha_ctx->c, md)) {
+
+        IBMCAerr(IBMCA_F_IBMCA_SHA1_FINAL, IBMCA_R_REQUEST_FAILED);
+        return 0;
+    }
+
+    return 1;
+}
+
+static int ibmca_sha1_cleanup(EVP_MD_CTX *ctx)
+{
+    return 1;
+}
+#endif                          /* OPENSSL_NO_SHA1 */
+
+
+#ifndef OPENSSL_NO_SHA256
+static int ibmca_sha256_init(EVP_MD_CTX *ctx)
+{
+    IBMCA_SHA256_CTX *ibmca_sha256_ctx =
+        (IBMCA_SHA256_CTX *) EVP_MD_CTX_md_data(ctx);
+
+    memset((unsigned char *) ibmca_sha256_ctx, 0, sizeof(*ibmca_sha256_ctx));
+
+    return 1;
+}
+
+static int ibmca_sha256_update(EVP_MD_CTX *ctx, const void *in_data,
+                               unsigned long inlen)
+{
+    IBMCA_SHA256_CTX *ibmca_sha256_ctx =
+        (IBMCA_SHA256_CTX *) EVP_MD_CTX_md_data(ctx);
+    unsigned int message_part = SHA_MSG_PART_MIDDLE, fill_size = 0;
+    unsigned long in_data_len = inlen;
+    unsigned char tmp_hash[SHA256_HASH_LENGTH];
+
+    if (in_data_len == 0)
+        return 1;
+
+    if (ibmca_sha256_ctx->c.runningLength == 0
+        && ibmca_sha256_ctx->tail_len == 0) {
+        message_part = SHA_MSG_PART_FIRST;
+
+        ibmca_sha256_ctx->tail_len = in_data_len & 0x3f;
+        if (ibmca_sha256_ctx->tail_len) {
+            in_data_len &= ~0x3f;
+            memcpy(ibmca_sha256_ctx->tail, in_data + in_data_len,
+                   ibmca_sha256_ctx->tail_len);
+        }
+    } else if (ibmca_sha256_ctx->c.runningLength == 0
+               && ibmca_sha256_ctx->tail_len > 0) {
+        /* Here we need to fill out the temporary tail buffer
+         * until it has 64 bytes in it, then call ica_sha256 on
+         * that buffer.  If there weren't enough bytes passed
+         * in to fill it out, just copy in what we can and
+         * return success without calling ica_sha256. - KEY */
+
+        fill_size = SHA256_BLOCK_SIZE - ibmca_sha256_ctx->tail_len;
+        if (fill_size < in_data_len) {
+            memcpy(ibmca_sha256_ctx->tail
+                   + ibmca_sha256_ctx->tail_len, in_data, fill_size);
+
+            /* Submit the filled out tail buffer */
+            if (p_ica_sha256((unsigned int) SHA_MSG_PART_FIRST,
+                             (unsigned int) SHA256_BLOCK_SIZE,
+                             ibmca_sha256_ctx->tail,
+                             &ibmca_sha256_ctx->c, tmp_hash)) {
+                IBMCAerr(IBMCA_F_IBMCA_SHA256_UPDATE, IBMCA_R_REQUEST_FAILED);
+                return 0;
+            }
+        } else {
+            memcpy(ibmca_sha256_ctx->tail
+                   + ibmca_sha256_ctx->tail_len, in_data, in_data_len);
+            ibmca_sha256_ctx->tail_len += in_data_len;
+            return 1;
+        }
+
+        /* We had to use 'fill_size' bytes from in_data to fill out the
+         * empty part of save data, so adjust in_data_len */
+        in_data_len -= fill_size;
+
+        ibmca_sha256_ctx->tail_len = in_data_len & 0x3f;
+        if (ibmca_sha256_ctx->tail_len) {
+            in_data_len &= ~0x3f;
+            memcpy(ibmca_sha256_ctx->tail,
+                   in_data + fill_size + in_data_len,
+                   ibmca_sha256_ctx->tail_len);
+            /* fill_size is added to in_data down below */
+        }
+    } else if (ibmca_sha256_ctx->c.runningLength > 0) {
+        if (ibmca_sha256_ctx->tail_len) {
+            fill_size = SHA256_BLOCK_SIZE - ibmca_sha256_ctx->tail_len;
+            if (fill_size < in_data_len) {
+                memcpy(ibmca_sha256_ctx->tail
+                       + ibmca_sha256_ctx->tail_len, in_data, fill_size);
+
+                /* Submit the filled out save buffer */
+                if (p_ica_sha256(message_part,
+                                 (unsigned int) SHA256_BLOCK_SIZE,
+                                 ibmca_sha256_ctx->tail,
+                                 &ibmca_sha256_ctx->c, tmp_hash)) {
+                    IBMCAerr(IBMCA_F_IBMCA_SHA256_UPDATE,
+                             IBMCA_R_REQUEST_FAILED);
+                    return 0;
+                }
+            } else {
+                memcpy(ibmca_sha256_ctx->tail
+                       + ibmca_sha256_ctx->tail_len, in_data, in_data_len);
+                ibmca_sha256_ctx->tail_len += in_data_len;
+                return 1;
+            }
+
+            /*
+             * We had to use some of the data from in_data to
+             * fill out the empty part of save data, so adjust
+             * in_data_len
+             */
+            in_data_len -= fill_size;
+
+            ibmca_sha256_ctx->tail_len = in_data_len & 0x3f;
+            if (ibmca_sha256_ctx->tail_len) {
+                in_data_len &= ~0x3f;
+                memcpy(ibmca_sha256_ctx->tail,
+                       in_data + fill_size + in_data_len,
+                       ibmca_sha256_ctx->tail_len);
+            }
+        } else {
+            /* This is the odd case, where we need to go
+             * ahead and send the first X * 64 byte chunks
+             * in to be processed and copy the last <64
+             * byte area into the tail. -KEY */
+            ibmca_sha256_ctx->tail_len = in_data_len & 0x3f;
+            if (ibmca_sha256_ctx->tail_len) {
+                in_data_len &= ~0x3f;
+                memcpy(ibmca_sha256_ctx->tail,
+                       in_data + in_data_len, ibmca_sha256_ctx->tail_len);
+            }
+        }
+    }
+
+    /* If the data passed in was <64 bytes, in_data_len will be 0 */
+    if (in_data_len &&
+        p_ica_sha256(message_part,
+                     (unsigned int) in_data_len,
+                     (unsigned char *) (in_data + fill_size),
+                     &ibmca_sha256_ctx->c, tmp_hash)) {
+        IBMCAerr(IBMCA_F_IBMCA_SHA256_UPDATE, IBMCA_R_REQUEST_FAILED);
+        return 0;
+    }
+
+    return 1;
+}
+
+static int ibmca_sha256_final(EVP_MD_CTX *ctx, unsigned char *md)
+{
+    IBMCA_SHA256_CTX *ibmca_sha256_ctx =
+        (IBMCA_SHA256_CTX *) EVP_MD_CTX_md_data(ctx);
+    unsigned int message_part = 0;
+
+    if (ibmca_sha256_ctx->c.runningLength)
+        message_part = SHA_MSG_PART_FINAL;
+    else
+        message_part = SHA_MSG_PART_ONLY;
+
+    if (p_ica_sha256(message_part,
+                     ibmca_sha256_ctx->tail_len,
+                     (unsigned char *) ibmca_sha256_ctx->tail,
+                     &ibmca_sha256_ctx->c, md)) {
+        IBMCAerr(IBMCA_F_IBMCA_SHA256_FINAL, IBMCA_R_REQUEST_FAILED);
+        return 0;
+    }
+
+    return 1;
+}
+
+static int ibmca_sha256_cleanup(EVP_MD_CTX *ctx)
+{
+    return 1;
+}
+#endif                          /* OPENSSL_NO_SHA256 */
+
+
+#ifndef OPENSSL_NO_SHA512
+static int ibmca_sha512_init(EVP_MD_CTX *ctx)
+{
+    IBMCA_SHA512_CTX *ibmca_sha512_ctx =
+        (IBMCA_SHA512_CTX *) EVP_MD_CTX_md_data(ctx);
+
+    memset((unsigned char *) ibmca_sha512_ctx, 0, sizeof(*ibmca_sha512_ctx));
+
+    return 1;
+}
+
+static int ibmca_sha512_update(EVP_MD_CTX *ctx, const void *in_data,
+                               unsigned long inlen)
+{
+    IBMCA_SHA512_CTX *ibmca_sha512_ctx =
+        (IBMCA_SHA512_CTX *) EVP_MD_CTX_md_data(ctx);
+    unsigned int message_part = SHA_MSG_PART_MIDDLE, fill_size = 0;
+    unsigned long in_data_len = inlen;
+    unsigned char tmp_hash[SHA512_HASH_LENGTH];
+
+    if (in_data_len == 0)
+        return 1;
+
+    if (ibmca_sha512_ctx->c.runningLengthLow == 0
+        && ibmca_sha512_ctx->tail_len == 0) {
+        message_part = SHA_MSG_PART_FIRST;
+
+        ibmca_sha512_ctx->tail_len = in_data_len & 0x7f;
+        if (ibmca_sha512_ctx->tail_len) {
+            in_data_len &= ~0x7f;
+            memcpy(ibmca_sha512_ctx->tail, in_data + in_data_len,
+                   ibmca_sha512_ctx->tail_len);
+        }
+    } else if (ibmca_sha512_ctx->c.runningLengthLow == 0
+               && ibmca_sha512_ctx->tail_len > 0) {
+        /* Here we need to fill out the temporary tail buffer
+         * until it has 128 bytes in it, then call ica_sha512 on
+         * that buffer.  If there weren't enough bytes passed
+         * in to fill it out, just copy in what we can and
+         * return success without calling ica_sha512.
+         */
+
+        fill_size = SHA512_BLOCK_SIZE - ibmca_sha512_ctx->tail_len;
+        if (fill_size < in_data_len) {
+            memcpy(ibmca_sha512_ctx->tail
+                   + ibmca_sha512_ctx->tail_len, in_data, fill_size);
+
+            /* Submit the filled out tail buffer */
+            if (p_ica_sha512((unsigned int) SHA_MSG_PART_FIRST,
+                             (unsigned int) SHA512_BLOCK_SIZE,
+                             ibmca_sha512_ctx->tail,
+                             &ibmca_sha512_ctx->c, tmp_hash)) {
+                IBMCAerr(IBMCA_F_IBMCA_SHA512_UPDATE, IBMCA_R_REQUEST_FAILED);
+                return 0;
+            }
+        } else {
+            memcpy(ibmca_sha512_ctx->tail
+                   + ibmca_sha512_ctx->tail_len, in_data, in_data_len);
+            ibmca_sha512_ctx->tail_len += in_data_len;
+            return 1;
+        }
+
+        /* We had to use 'fill_size' bytes from in_data to fill out the
+         * empty part of save data, so adjust in_data_len
+         */
+        in_data_len -= fill_size;
+
+        ibmca_sha512_ctx->tail_len = in_data_len & 0x7f;
+        if (ibmca_sha512_ctx->tail_len) {
+            in_data_len &= ~0x7f;
+            memcpy(ibmca_sha512_ctx->tail,
+                   in_data + fill_size + in_data_len,
+                   ibmca_sha512_ctx->tail_len);
+            /* fill_size is added to in_data down below */
+        }
+    } else if (ibmca_sha512_ctx->c.runningLengthLow > 0) {
+        if (ibmca_sha512_ctx->tail_len) {
+            fill_size = SHA512_BLOCK_SIZE - ibmca_sha512_ctx->tail_len;
+            if (fill_size < in_data_len) {
+                memcpy(ibmca_sha512_ctx->tail
+                       + ibmca_sha512_ctx->tail_len, in_data, fill_size);
+
+                /* Submit the filled out save buffer */
+                if (p_ica_sha512(message_part,
+                                 (unsigned int) SHA512_BLOCK_SIZE,
+                                 ibmca_sha512_ctx->tail,
+                                 &ibmca_sha512_ctx->c, tmp_hash)) {
+                    IBMCAerr(IBMCA_F_IBMCA_SHA512_UPDATE,
+                             IBMCA_R_REQUEST_FAILED);
+                    return 0;
+                }
+            } else {
+                memcpy(ibmca_sha512_ctx->tail
+                       + ibmca_sha512_ctx->tail_len, in_data, in_data_len);
+                ibmca_sha512_ctx->tail_len += in_data_len;
+                return 1;
+            }
+
+            /*
+             * We had to use some of the data from in_data to
+             * fill out the empty part of save data, so adjust
+             * in_data_len
+             */
+            in_data_len -= fill_size;
+
+            ibmca_sha512_ctx->tail_len = in_data_len & 0x7f;
+            if (ibmca_sha512_ctx->tail_len) {
+                in_data_len &= ~0x7f;
+                memcpy(ibmca_sha512_ctx->tail,
+                       in_data + fill_size + in_data_len,
+                       ibmca_sha512_ctx->tail_len);
+            }
+        } else {
+            /* This is the odd case, where we need to go
+             * ahead and send the first X * 128 byte chunks
+             * in to be processed and copy the last <128
+             * byte area into the tail.
+             */
+            ibmca_sha512_ctx->tail_len = in_data_len & 0x7f;
+            if (ibmca_sha512_ctx->tail_len) {
+                in_data_len &= ~0x7f;
+                memcpy(ibmca_sha512_ctx->tail,
+                       in_data + in_data_len, ibmca_sha512_ctx->tail_len);
+            }
+        }
+    }
+
+    /* If the data passed in was <128 bytes, in_data_len will be 0 */
+    if (in_data_len &&
+        p_ica_sha512(message_part, (unsigned int) in_data_len,
+                     (unsigned char *) (in_data + fill_size),
+                     &ibmca_sha512_ctx->c, tmp_hash)) {
+        IBMCAerr(IBMCA_F_IBMCA_SHA512_UPDATE, IBMCA_R_REQUEST_FAILED);
+        return 0;
+    }
+
+    return 1;
+}
+
+static int ibmca_sha512_final(EVP_MD_CTX *ctx, unsigned char *md)
+{
+    IBMCA_SHA512_CTX *ibmca_sha512_ctx =
+        (IBMCA_SHA512_CTX *) EVP_MD_CTX_md_data(ctx);
+    unsigned int message_part = 0;
+
+    if (ibmca_sha512_ctx->c.runningLengthLow)
+        message_part = SHA_MSG_PART_FINAL;
+    else
+        message_part = SHA_MSG_PART_ONLY;
+
+    if (p_ica_sha512(message_part, ibmca_sha512_ctx->tail_len,
+                     (unsigned char *) ibmca_sha512_ctx->tail,
+                     &ibmca_sha512_ctx->c, md)) {
+        IBMCAerr(IBMCA_F_IBMCA_SHA512_FINAL, IBMCA_R_REQUEST_FAILED);
+        return 0;
+    }
+
+    return 1;
+}
+
+static int ibmca_sha512_cleanup(EVP_MD_CTX *ctx)
+{
+    return 1;
+}
+#endif                          /* OPENSSL_NO_SHA512 */
+
+
+#ifdef OLDER_OPENSSL
+# define DECLARE_SHA_EVP(type, pkey_type, md_size, flags,               \
+                         block_size, ctx_size, init, update,            \
+                         final, copy, cleanup)                          \
+static const EVP_MD type##_md = {                                       \
+    NID_##type,                                                         \
+    NID_##pkey_type,                                                    \
+    md_size,                                                            \
+    EVP_MD_FLAG_PKEY_METHOD_SIGNATURE|flags,                            \
+    init,                                                               \
+    update,                                                             \
+    final,                                                              \
+    copy,                                                               \
+    cleanup,                                                            \
+    EVP_PKEY_RSA_method,                                                \
+    block_size,                                                         \
+    ctx_size                                                            \
+};                                                                      \
+const EVP_MD *ibmca_##type(void)                                        \
+{                                                                       \
+    return &type##_md;                                                  \
+}
+
+#else
+# define DECLARE_SHA_EVP(type, pkey_type, md_size, flags,               \
+                         block_size, ctx_size, init, update,            \
+                         final, copy, cleanup)                          \
+static EVP_MD *type##_md = NULL;                                        \
+const EVP_MD *ibmca_##type(void)                                        \
+{                                                                       \
+    EVP_MD *md;                                                         \
+                                                                        \
+    if (type##_md != NULL)                                              \
+        goto done;                                                      \
+                                                                        \
+    if ((md = EVP_MD_meth_new(NID_##type, NID_##pkey_type)) == NULL     \
+         || !EVP_MD_meth_set_result_size(md, md_size)                   \
+         || !EVP_MD_meth_set_input_blocksize(md, block_size)            \
+         || !EVP_MD_meth_set_app_datasize(md, ctx_size)                 \
+         || !EVP_MD_meth_set_flags(md, flags)                           \
+         || !EVP_MD_meth_set_init(md, init)                             \
+         || !EVP_MD_meth_set_update(md, update)                         \
+         || !EVP_MD_meth_set_final(md, final)                           \
+         || !EVP_MD_meth_set_cleanup(md, cleanup)) {                    \
+        EVP_MD_meth_free(md);                                           \
+        md = NULL;                                                      \
+    }                                                                   \
+    type##_md = md;                                                     \
+done:                                                                   \
+    return type##_md;                                                   \
+}                                                                       \
+                                                                        \
+void ibmca_##type##_destroy(void)                                       \
+{                                                                       \
+    EVP_MD_meth_free(type##_md);                                        \
+    type##_md = NULL;                                                   \
+}
+#endif
+
+#ifndef OPENSSL_NO_SHA1
+DECLARE_SHA_EVP(sha1, sha1WithRSAEncryption, SHA_HASH_LENGTH,
+                EVP_MD_FLAG_FIPS, SHA_BLOCK_SIZE,
+                sizeof(EVP_MD *) + sizeof(struct ibmca_sha1_ctx),
+                ibmca_sha1_init, ibmca_sha1_update, ibmca_sha1_final,
+                NULL, ibmca_sha1_cleanup)
+#endif
+#ifndef OPENSSL_NO_SHA256
+DECLARE_SHA_EVP(sha256, sha256WithRSAEncryption, SHA256_HASH_LENGTH,
+                EVP_MD_FLAG_FIPS, SHA256_BLOCK_SIZE,
+                sizeof(EVP_MD *) + sizeof(struct ibmca_sha256_ctx),
+                ibmca_sha256_init, ibmca_sha256_update, ibmca_sha256_final,
+                NULL, ibmca_sha256_cleanup)
+#endif
+#ifndef OPENSSL_NO_SHA512
+DECLARE_SHA_EVP(sha512, sha512WithRSAEncryption, SHA512_HASH_LENGTH,
+                EVP_MD_FLAG_FIPS, SHA512_BLOCK_SIZE,
+                sizeof(EVP_MD *) + sizeof(struct ibmca_sha512_ctx),
+                ibmca_sha512_init, ibmca_sha512_update, ibmca_sha512_final,
+                NULL, ibmca_sha512_cleanup)
+#endif
diff -pruN 1.4.0-1/src/engine/ibmca_dsa.c 2.5.0-0ubuntu1/src/engine/ibmca_dsa.c
--- 1.4.0-1/src/engine/ibmca_dsa.c	1970-01-01 00:00:00.000000000 +0000
+++ 2.5.0-0ubuntu1/src/engine/ibmca_dsa.c	2025-04-16 08:09:24.000000000 +0000
@@ -0,0 +1,143 @@
+/*
+ * Copyright [2005-2021] International Business Machines Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <openssl/dsa.h>
+#include "ibmca.h"
+
+#ifndef OPENSSL_NO_DSA
+
+/* This code was liberated and adapted from the commented-out code in
+ * dsa_ossl.c. Because of the unoptimised form of the Ibmca acceleration
+ * (it doesn't have a CRT form for RSA), this function means that an
+ * Ibmca system running with a DSA server certificate can handshake
+ * around 5 or 6 times faster/more than an equivalent system running with
+ * RSA. Just check out the "signs" statistics from the RSA and DSA parts
+ * of "openssl speed -engine ibmca dsa1024 rsa1024". */
+#ifdef OLDER_OPENSSL
+static int ibmca_dsa_mod_exp(DSA *dsa, BIGNUM *rr, BIGNUM *a1,
+                             BIGNUM *p1, BIGNUM *a2, BIGNUM *p2,
+                             BIGNUM *m, BN_CTX *ctx, BN_MONT_CTX *in_mont)
+#else
+static int ibmca_dsa_mod_exp(DSA *dsa, BIGNUM *rr, const BIGNUM *a1,
+                             const BIGNUM *p1, const BIGNUM *a2,
+                             const BIGNUM *p2, const BIGNUM *m,
+                             BN_CTX *ctx, BN_MONT_CTX *in_mont)
+#endif
+{
+    BIGNUM *t;
+    int to_return = 0;
+
+    t = BN_new();
+    /* let rr = a1 ^ p1 mod m */
+    if (!ibmca_mod_exp(rr, a1, p1, m, ctx))
+        goto end;
+    /* let t = a2 ^ p2 mod m */
+    if (!ibmca_mod_exp(t, a2, p2, m, ctx))
+        goto end;
+    /* let rr = rr * t mod m */
+    if (!BN_mod_mul(rr, rr, t, m, ctx))
+        goto end;
+
+    to_return = 1;
+
+end:
+    BN_free(t);
+
+    if (!to_return)
+        return BN_mod_exp2_mont(rr, a1, p1, a2, p2, m, ctx, in_mont);
+    return to_return;
+}
+
+#ifdef OLDER_OPENSSL
+
+static int ibmca_mod_exp_dsa(DSA *dsa, BIGNUM *r, BIGNUM *a,
+                             const BIGNUM *p, const BIGNUM *m,
+                             BN_CTX *ctx, BN_MONT_CTX *m_ctx)
+#else
+
+static int ibmca_mod_exp_dsa(DSA *dsa, BIGNUM *r, const BIGNUM *a,
+                             const BIGNUM *p, const BIGNUM *m,
+                             BN_CTX *ctx, BN_MONT_CTX *m_ctx)
+#endif
+{
+    if (!ibmca_mod_exp(r, a, p, m, ctx))
+        return BN_mod_exp_mont(r, a, p, m, ctx, m_ctx);
+    return 1;
+}
+
+
+#ifdef OLDER_OPENSSL
+static DSA_METHOD dsa_m = {
+    "Ibmca DSA method",         /* name */
+    NULL,                       /* dsa_do_sign */
+    NULL,                       /* dsa_sign_setup */
+    NULL,                       /* dsa_do_verify */
+    ibmca_dsa_mod_exp,          /* dsa_mod_exp */
+    ibmca_mod_exp_dsa,          /* bn_mod_exp */
+    NULL,                       /* init */
+    NULL,                       /* finish */
+    DSA_FLAG_FIPS_METHOD,       /* flags */
+    NULL                        /* app_data */
+};
+
+DSA_METHOD *ibmca_dsa(void)
+{
+    const DSA_METHOD *meth1 = DSA_OpenSSL();
+
+    dsa_m.dsa_do_sign = meth1->dsa_do_sign;
+    dsa_m.dsa_sign_setup = meth1->dsa_sign_setup;
+    dsa_m.dsa_do_verify = meth1->dsa_do_verify;
+
+    return &dsa_m;
+}
+
+#else
+static DSA_METHOD *dsa_m = NULL;
+DSA_METHOD *ibmca_dsa(void)
+{
+    const DSA_METHOD *meth1;
+    DSA_METHOD *method;
+
+    if (dsa_m != NULL)
+        goto done;
+
+    if ((method = DSA_meth_new("Ibmca DSA method", 0)) == NULL
+        || (meth1 = DSA_OpenSSL()) == NULL
+        || !DSA_meth_set_sign(method, DSA_meth_get_sign(meth1))
+        || !DSA_meth_set_sign_setup(method, DSA_meth_get_sign_setup(meth1))
+        || !DSA_meth_set_verify(method, DSA_meth_get_verify(meth1))
+        || !DSA_meth_set_mod_exp(method, ibmca_dsa_mod_exp)
+        || !DSA_meth_set_bn_mod_exp(method, ibmca_mod_exp_dsa)
+        || !DSA_meth_set_flags(method, DSA_FLAG_FIPS_METHOD)) {
+        DSA_meth_free(method);
+        method = NULL;
+        meth1 = NULL;
+    }
+
+    dsa_m = method;
+
+done:
+    return dsa_m;
+}
+
+void ibmca_dsa_destroy(void)
+{
+    DSA_meth_free(dsa_m);
+    dsa_m = NULL;
+}
+#endif
+#endif                          /* endif OPENSSL_NO_DSA */
diff -pruN 1.4.0-1/src/engine/ibmca_ec.c 2.5.0-0ubuntu1/src/engine/ibmca_ec.c
--- 1.4.0-1/src/engine/ibmca_ec.c	1970-01-01 00:00:00.000000000 +0000
+++ 2.5.0-0ubuntu1/src/engine/ibmca_ec.c	2025-04-16 08:09:24.000000000 +0000
@@ -0,0 +1,1025 @@
+/*
+ * Copyright [2005-2018] International Business Machines Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <stdlib.h>
+#include <pthread.h>
+#include <openssl/err.h>
+#include "ibmca.h"
+#include "e_ibmca_err.h"
+
+#ifndef OPENSSL_NO_EC
+
+ica_ec_key_new_t		p_ica_ec_key_new;
+ica_ec_key_init_t		p_ica_ec_key_init;
+ica_ec_key_generate_t		p_ica_ec_key_generate;
+ica_ecdh_derive_secret_t        p_ica_ecdh_derive_secret;
+ica_ecdsa_sign_t		p_ica_ecdsa_sign;
+ica_ecdsa_verify_t		p_ica_ecdsa_verify;
+ica_ec_key_get_public_key_t	p_ica_ec_key_get_public_key;
+ica_ec_key_get_private_key_t	p_ica_ec_key_get_private_key;
+ica_ec_key_free_t		p_ica_ec_key_free;
+
+int ec_ex_data_index = -1;
+pthread_mutex_t ec_ex_data_mutex = PTHREAD_MUTEX_INITIALIZER;
+
+struct ibmca_ec_ex_data {
+    ICA_EC_KEY *ica_key;
+    int nid;
+    unsigned int privlen;
+};
+
+void ibmca_ec_ex_data_free(void *parent, void *ptr, CRYPTO_EX_DATA *ad,
+                           int idx, long argl, void *argp)
+{
+    struct ibmca_ec_ex_data *data = ptr;
+
+    if (data == NULL)
+        return;
+
+    p_ica_ec_key_free(data->ica_key);
+    OPENSSL_free(data);
+}
+
+int ibmca_ec_ex_data_dup(CRYPTO_EX_DATA *to, const CRYPTO_EX_DATA *from,
+#ifdef OPENSSL_VERSION_PREREQ
+                         void **pptr, int idx, long argl, void *argp)
+#else
+                         void *from_d, int idx, long argl, void *argp)
+#endif
+{
+#ifndef OPENSSL_VERSION_PREREQ
+    void **pptr = (void **)from_d;
+#endif
+    struct ibmca_ec_ex_data *from_data;
+    struct ibmca_ec_ex_data *to_data;
+    unsigned char Q[2 * IBMCA_EC_MAX_D_LEN];
+    unsigned char D[IBMCA_EC_MAX_D_LEN];
+    unsigned int len;
+    int rc;
+
+    if (pptr == NULL)
+        return 0;
+
+    from_data = (struct ibmca_ec_ex_data *)*pptr;
+    if (from_data == NULL)
+        return 0;
+
+    to_data = OPENSSL_zalloc(sizeof(*to_data));
+    if (to_data == NULL) {
+       return 0;
+    }
+
+    to_data->nid = from_data->nid;
+    to_data->ica_key = p_ica_ec_key_new(to_data->nid, &to_data->privlen);
+    if (to_data->ica_key == NULL) {
+        IBMCAerr(IBMCA_F_ICA_EC_KEY_NEW, IBMCA_R_EC_ICA_EC_KEY_INIT);
+        goto error;
+    }
+
+    if (p_ica_ec_key_get_private_key(from_data->ica_key, D, &len) == 0) {
+        rc = p_ica_ec_key_init(NULL, NULL, D, to_data->ica_key);
+        if (rc != 0) {
+            IBMCAerr(IBMCA_F_ICA_EC_KEY_INIT, rc);
+            goto error;
+        }
+    }
+
+    if (p_ica_ec_key_get_public_key(from_data->ica_key, Q, &len) == 0) {
+        rc = p_ica_ec_key_init(Q, Q + to_data->privlen, NULL, to_data->ica_key);
+        if (rc != 0) {
+            IBMCAerr(IBMCA_F_ICA_EC_KEY_INIT, rc);
+            goto error;
+        }
+    }
+
+    *pptr = to_data;
+
+    return 1;
+
+error:
+    if (to_data->ica_key != NULL)
+        p_ica_ec_key_free(to_data->ica_key);
+    OPENSSL_free(to_data);
+
+    return 0;
+}
+
+static ICA_EC_KEY *ibmca_ec_make_ica_key(EC_KEY *ec_key, int *nid,
+                                         unsigned int *privlen)
+{
+    ICA_EC_KEY *icakey;
+    const EC_GROUP *group;
+    const EC_POINT *q;
+    const BIGNUM *bn_d;
+    BIGNUM *bn_x = NULL, *bn_y = NULL;
+    unsigned char D[IBMCA_EC_MAX_D_LEN];
+    unsigned char X[IBMCA_EC_MAX_D_LEN];
+    unsigned char Y[IBMCA_EC_MAX_D_LEN];
+    int rc, n;
+
+    /* Get group */
+    if ((group = EC_KEY_get0_group(ec_key)) == NULL) {
+        IBMCAerr(IBMCA_F_ICA_EC_KEY_NEW, IBMCA_R_EC_INVALID_PARM);
+        return NULL;
+    }
+
+    /* Get curve nid */
+    *nid = EC_GROUP_get_curve_name(group);
+    if (*nid <= 0) {
+        IBMCAerr(IBMCA_F_ICA_EC_KEY_NEW, IBMCA_R_EC_UNSUPPORTED_CURVE);
+        return NULL;
+    }
+
+    /* Create ICA_EC_KEY object */
+    icakey = p_ica_ec_key_new(*nid, privlen);
+    if (icakey == NULL) {
+        IBMCAerr(IBMCA_F_ICA_EC_KEY_NEW, IBMCA_R_EC_ICA_EC_KEY_INIT);
+        return NULL;
+    }
+
+    /* Get private (D) value from EC_KEY (if available) */
+    bn_d = EC_KEY_get0_private_key(ec_key);
+    if (bn_d != NULL) {
+        /* Format (D) as char array, with leading zeros if necessary */
+        n = *privlen - BN_num_bytes(bn_d);
+        memset(D, 0, n);
+        BN_bn2bin(bn_d, &(D[n]));
+
+        /* Initialize private ICA_EC_KEY */
+        rc = p_ica_ec_key_init(NULL, NULL, D, icakey);
+        if (rc != 0) {
+            IBMCAerr(IBMCA_F_ICA_EC_KEY_INIT, rc);
+            goto error;
+        }
+    }
+
+    /* Get public (X and Y) values from EC_KEY (if available) */
+    q = EC_KEY_get0_public_key(ec_key);
+    if (q != NULL) {
+        /* Provide public key (X,Y) */
+        bn_x = BN_new();
+        bn_y = BN_new();
+        if (!EC_POINT_get_affine_coordinates_GFp(group, q, bn_x, bn_y, NULL)) {
+            IBMCAerr(IBMCA_F_ICA_EC_KEY_NEW, IBMCA_R_EC_INTERNAL_ERROR);
+            goto error;
+        }
+
+        /* Format (X) as char array with leading nulls if necessary */
+        n = *privlen - BN_num_bytes(bn_x);
+        memset(X, 0, n);
+        BN_bn2bin(bn_x, &X[n]);
+
+        /* Format (Y) as char array with leading nulls if necessary */
+        n = *privlen - BN_num_bytes(bn_y);
+        memset(Y, 0, n);
+        BN_bn2bin(bn_y, &Y[n]);
+
+        /* Initialize public ICA_EC_KEY */
+        rc = p_ica_ec_key_init(X, Y, NULL, icakey);
+        if (rc != 0) {
+            IBMCAerr(IBMCA_F_ICA_EC_KEY_INIT, rc);
+            goto error;
+        }
+    }
+
+out:
+    BN_clear_free(bn_x);
+    BN_clear_free(bn_y);
+
+    return icakey;
+
+error:
+    p_ica_ec_key_free(icakey);
+    icakey = NULL;
+    goto out;
+}
+
+static ICA_EC_KEY *ibmca_ec_make_and_cache_ica_key(EC_KEY *ec_key,
+                                                   unsigned int *privlen)
+{
+    struct ibmca_ec_ex_data *data, *data2;
+
+    data = OPENSSL_zalloc(sizeof(*data));
+    if (data == NULL)
+        return NULL;
+
+    data->ica_key = ibmca_ec_make_ica_key(ec_key, &data->nid, &data->privlen);
+    if (data->ica_key == NULL) {
+        OPENSSL_free(data);
+        return NULL;
+    }
+
+    /*
+     * Note that another thread could have allocated and set the ex_data for
+     * that key after the caller of this function has checked if there is
+     * already ex_data available for the key. So once we have the lock, we
+     * check again, and if there now is ex_data available, we return the
+     * ICA key from the ex_data, and free the one just created.
+     */
+    if (pthread_mutex_lock(&ec_ex_data_mutex) != 0) {
+        IBMCAerr(IBMCA_F_ICA_EC_KEY_NEW, IBMCA_R_EC_INTERNAL_ERROR);
+        return NULL;
+    }
+
+    data2 = EC_KEY_get_ex_data(ec_key, ec_ex_data_index);
+    if (data2 != NULL) {
+        pthread_mutex_unlock(&ec_ex_data_mutex);
+
+        p_ica_ec_key_free(data->ica_key);
+        OPENSSL_free(data);
+
+        *privlen = data2->privlen;
+
+        return data2->ica_key;
+    }
+
+    if (EC_KEY_set_ex_data(ec_key, ec_ex_data_index, data) != 1) {
+        IBMCAerr(IBMCA_F_ICA_EC_KEY_NEW, IBMCA_R_EC_INTERNAL_ERROR);
+        pthread_mutex_unlock(&ec_ex_data_mutex);
+        OPENSSL_free(data);
+        return NULL;
+    }
+
+    pthread_mutex_unlock(&ec_ex_data_mutex);
+
+    *privlen = data->privlen;
+
+    return data->ica_key;
+}
+
+int ibmca_ec_init(void)
+{
+#ifdef OLDER_OPENSSL
+    return 0;
+#else
+    ec_ex_data_index = EC_KEY_get_ex_new_index(0, NULL, NULL,
+                                               ibmca_ec_ex_data_dup,
+                                               ibmca_ec_ex_data_free);
+    if (ec_ex_data_index < 0) {
+        IBMCAerr(IBMCA_F_IBMCA_INIT, IBMCA_R_EC_INTERNAL_ERROR);
+        return 0;
+    }
+
+    return 1;
+#endif
+}
+
+void ibmca_ec_destroy(void)
+{
+ #ifdef OLDER_OPENSSL
+    if (ibmca_ecdh)
+        ECDH_METHOD_free(ibmca_ecdh);
+    if (ibmca_ecdh)
+        ECDSA_METHOD_free(ibmca_ecdsa);
+ #else
+    if (ec_ex_data_index >= 0) {
+        CRYPTO_free_ex_index(CRYPTO_EX_INDEX_EC_KEY, ec_ex_data_index);
+        ec_ex_data_index = -1;
+    }
+    if (ibmca_ec)
+        EC_KEY_METHOD_free(ibmca_ec);
+ #endif
+}
+
+/**
+ * ECDH key derivation method, replaces ossl_ecdh_compute_key.
+ *
+ * @return 1 success
+ *         0 error
+ */
+int ibmca_ecdh_compute_key(unsigned char **pout, size_t *poutlen,
+                           const EC_POINT *pub_key, const EC_KEY *ecdh)
+{
+    ICA_EC_KEY *ica_pubkey = NULL, *ica_privkey = NULL;
+    const EC_GROUP *group;
+    BIGNUM *bn_x = NULL, *bn_y = NULL;
+    unsigned int n, privlen;
+    unsigned char X[IBMCA_EC_MAX_D_LEN];
+    unsigned char Y[IBMCA_EC_MAX_D_LEN];
+    unsigned char *z_buf = NULL;
+    int rc, ret = 0, nid;
+ #ifndef OLDER_OPENSSL
+    int (*compute_key_sw)(unsigned char **pout, size_t *poutlen,
+                          const EC_POINT *pub_key, const EC_KEY *ecdh) = NULL;
+ #endif
+    struct ibmca_ec_ex_data *data = EC_KEY_get_ex_data(ecdh, ec_ex_data_index);
+
+    /* Get group from EC_KEY */
+    if ((group = EC_KEY_get0_group(ecdh)) == NULL) {
+        IBMCAerr(IBMCA_F_IBMCA_ECDH_COMPUTE_KEY, IBMCA_R_EC_INVALID_PARM);
+        return 0;
+    }
+
+    /* Determine curve nid */
+    nid = EC_GROUP_get_curve_name(group);
+    if (nid <= 0) {
+        IBMCAerr(IBMCA_F_IBMCA_ECDH_COMPUTE_KEY, IBMCA_R_EC_UNSUPPORTED_CURVE);
+        return 0;
+    }
+
+    if (data != NULL) {
+        ica_privkey = data->ica_key;
+        privlen = data->privlen;
+        goto do_derive;
+    }
+
+    /* Create ICA_EC_KEY object for private key */
+    ERR_set_mark();
+    ica_privkey = ibmca_ec_make_and_cache_ica_key((EC_KEY*)ecdh, &privlen);
+    ERR_pop_to_mark();
+    if (ica_privkey == NULL) {
+        /* This curve is not supported by libica. */
+    #ifdef OLDER_OPENSSL
+        return 0;
+    #else
+        /*
+         * EC_KEY_METHOD_get_compute_key misses the const-qualifier of the
+         * parameter in some openssl versions.
+         */
+        EC_KEY_METHOD_get_compute_key((EC_KEY_METHOD *)ossl_ec,
+                                      &compute_key_sw);
+        if (compute_key_sw == NULL) {
+            IBMCAerr(IBMCA_F_IBMCA_ECDH_COMPUTE_KEY,
+                     IBMCA_R_EC_INTERNAL_ERROR);
+            return 0;
+        }
+
+        return compute_key_sw(pout, poutlen, pub_key, ecdh);
+    #endif
+    }
+
+do_derive:
+    /* Create ICA_EC_KEY object for public key */
+    ica_pubkey = p_ica_ec_key_new(nid, &privlen);
+    if (ica_pubkey == NULL) {
+        /* This curve is not supported by libica. */
+ #ifdef OLDER_OPENSSL
+        return 0;
+ #else
+        /*
+         * EC_KEY_METHOD_get_compute_key misses the const-qualifier of the
+         * parameter in some openssl versions.
+         */
+        EC_KEY_METHOD_get_compute_key((EC_KEY_METHOD *)ossl_ec,
+                                      &compute_key_sw);
+        if (compute_key_sw == NULL) {
+            IBMCAerr(IBMCA_F_IBMCA_ECDH_COMPUTE_KEY,
+                     IBMCA_R_EC_INTERNAL_ERROR);
+            return 0;
+        }
+
+        return compute_key_sw(pout, poutlen, pub_key, ecdh);
+ #endif
+    }
+
+    /* Get (X,Y) from EC_POINT */
+    bn_x = BN_new();
+    bn_y = BN_new();
+    if (!EC_POINT_get_affine_coordinates_GFp(group, pub_key, bn_x, bn_y, NULL)) {
+        IBMCAerr(IBMCA_F_IBMCA_ECDH_COMPUTE_KEY, IBMCA_R_EC_INTERNAL_ERROR);
+        goto end;
+    }
+
+    /* Format (X) as char array, with leading zeros if necessary */
+    n = privlen - BN_num_bytes(bn_x);
+    memset(X, 0, n);
+    BN_bn2bin(bn_x, &(X[n]));
+
+    /* Format (Y) as char array, with leading zeros if necessary */
+    n = privlen - BN_num_bytes(bn_y);
+    memset(Y, 0, n);
+    BN_bn2bin(bn_y, &(Y[n]));
+
+    /* Initialize public ICA_EC_KEY with (X,Y) */
+    rc = p_ica_ec_key_init(X, Y, NULL, ica_pubkey);
+    if (rc != 0) {
+        IBMCAerr(IBMCA_F_ICA_EC_KEY_INIT, rc);
+        goto end;
+    }
+
+    /* Allocate memory for shared secret z, will be freed by caller */
+    if ((z_buf = OPENSSL_malloc(privlen)) == NULL) {
+        IBMCAerr(IBMCA_F_IBMCA_ECDH_COMPUTE_KEY, IBMCA_R_EC_INTERNAL_ERROR);
+        goto end;
+    }
+
+    /* Calculate shared secret z */
+    rc = p_ica_ecdh_derive_secret(ibmca_handle, ica_privkey, ica_pubkey, z_buf,
+                                  privlen);
+    if (rc != 0) {
+        /* Possibly no suitable adapter. */
+        OPENSSL_free(z_buf);
+
+ #ifdef OLDER_OPENSSL
+        goto end;
+ #else
+        /*
+         * EC_KEY_METHOD_get_compute_key misses the const-qualifier of the
+         * parameter in some openssl versions.
+         */
+        EC_KEY_METHOD_get_compute_key((EC_KEY_METHOD *)ossl_ec,
+                                      &compute_key_sw);
+        if (compute_key_sw == NULL) {
+            IBMCAerr(IBMCA_F_ICA_ECDH_DERIVE_SECRET, rc);
+            goto end;
+        }
+
+        ret = compute_key_sw(pout, poutlen, pub_key, ecdh);
+        goto end;
+ #endif
+    }
+
+    *pout = z_buf;
+    *poutlen = privlen;
+
+    ret = 1;
+
+end:
+    p_ica_ec_key_free(ica_pubkey);
+    BN_clear_free(bn_x);
+    BN_clear_free(bn_y);
+    return ret;
+}
+
+/**
+ * ECDSA signing method (replaces ossl_ecdsa_sign_sig).
+ *
+ * @return pointer to a ECDSA_SIG structure or NULL if an error occurred
+ */
+ECDSA_SIG *ibmca_ecdsa_sign_sig(const unsigned char *dgst, int dgst_len,
+                const BIGNUM *in_kinv, const BIGNUM *in_r,
+                EC_KEY *eckey)
+{
+    ECDSA_SIG *sig = NULL;
+    ICA_EC_KEY *icakey = NULL;
+    unsigned int privlen;
+    BIGNUM *r, *s, *kinv;
+    unsigned char sigret[IBMCA_EC_MAX_SIG_LEN];
+    int rc;
+ #ifndef OLDER_OPENSSL
+    int (*sign_sw)(int type, const unsigned char *dgst, int dlen,
+                    unsigned char *sig, unsigned int *siglen,
+                    const BIGNUM *kinv, const BIGNUM *r, EC_KEY *eckey) = NULL;
+ #endif
+    int (*sign_setup_sw)(EC_KEY *eckey, BN_CTX *ctx_in, BIGNUM **kinvp,
+                         BIGNUM **rp) = NULL;
+    ECDSA_SIG *(*sign_sig_sw)(const unsigned char *dgst, int dgst_len,
+                              const BIGNUM *in_kinv, const BIGNUM *in_r,
+                              EC_KEY *eckey) = NULL;
+    BN_CTX *ctx;
+    struct ibmca_ec_ex_data *data = EC_KEY_get_ex_data(eckey, ec_ex_data_index);
+
+    /* Check parms: precomputed (k,r) are not supported by ibmca */
+    if (in_kinv != NULL || in_r != NULL) {
+        IBMCAerr(IBMCA_F_IBMCA_ECDSA_SIGN_SIG, IBMCA_R_EC_INVALID_PARM);
+        return NULL;
+    }
+
+    /* Check if key usable */
+ #ifndef OLDER_OPENSSL
+    if (!EC_KEY_can_sign(eckey)) {
+        IBMCAerr(IBMCA_F_IBMCA_ECDSA_SIGN_SIG,
+                 IBMCA_R_EC_CURVE_DOES_NOT_SUPPORT_SIGNING);
+        return NULL;
+    }
+ #endif
+
+    if (data != NULL) {
+        icakey = data->ica_key;
+        privlen = data->privlen;
+        goto do_sign;
+    }
+
+    /* Create ICA_EC_KEY object */
+    ERR_set_mark();
+    icakey = ibmca_ec_make_and_cache_ica_key(eckey, &privlen);
+    ERR_pop_to_mark();
+    if (icakey == NULL) {
+        /* This curve is not supported by libica. */
+ #ifdef OLDER_OPENSSL
+        ECDSA_METHOD_get_sign(ossl_ecdsa, &sign_setup_sw, &sign_sig_sw);
+ #else
+        /*
+         * EC_KEY_METHOD_get_sign misses the const-qualifier of the
+         * parameter in some openssl versions.
+         */
+        EC_KEY_METHOD_get_sign((EC_KEY_METHOD *)ossl_ec, &sign_sw,
+                               &sign_setup_sw, &sign_sig_sw);
+ #endif
+        if (sign_sig_sw == NULL || sign_setup_sw == NULL) {
+            IBMCAerr(IBMCA_F_IBMCA_ECDSA_SIGN_SIG,
+                     IBMCA_R_EC_INTERNAL_ERROR);
+            return NULL;
+        }
+
+        kinv = NULL;
+        r = NULL;
+        ctx = BN_CTX_new();
+        sign_setup_sw(eckey, ctx, &kinv, &r);
+        BN_CTX_free(ctx);
+        sig = sign_sig_sw(dgst, dgst_len, kinv, r, eckey);
+        BN_clear_free(kinv);
+        BN_clear_free(r);
+        return sig;
+    }
+
+do_sign:
+    /* Call libica signing routine */
+    rc = p_ica_ecdsa_sign(ibmca_handle, icakey, dgst, dgst_len, sigret,
+                          sizeof(sigret));
+    if (rc != 0) {
+        /* Possibly no adapter. */
+ #ifdef OLDER_OPENSSL
+        ECDSA_METHOD_get_sign(ossl_ecdsa, &sign_setup_sw, &sign_sig_sw);
+ #else
+        /*
+         * EC_KEY_METHOD_get_sign misses the const-qualifier of the
+         * parameter in some openssl versions.
+         */
+        EC_KEY_METHOD_get_sign((EC_KEY_METHOD *)ossl_ec, &sign_sw,
+                               &sign_setup_sw, &sign_sig_sw);
+ #endif
+        if (sign_sig_sw == NULL || sign_setup_sw == NULL) {
+            IBMCAerr(IBMCA_F_IBMCA_ECDSA_SIGN_SIG,
+                     IBMCA_R_EC_INTERNAL_ERROR);
+            return NULL;
+        }
+
+        kinv = NULL;
+        r = NULL;
+        ctx = BN_CTX_new();
+        sign_setup_sw(eckey, ctx, &kinv, &r);
+        BN_CTX_free(ctx);
+        sig = sign_sig_sw(dgst, dgst_len, kinv, r, eckey);
+        BN_clear_free(kinv);
+        BN_clear_free(r);
+        goto end2;
+    }
+
+    /* Construct ECDSA_SIG object from char array */
+    r = BN_bin2bn(sigret, privlen, NULL);
+    s = BN_bin2bn(sigret + privlen, privlen, NULL);
+    sig = ECDSA_SIG_new();
+
+ #ifndef OLDER_OPENSSL
+    if (sig)
+        ECDSA_SIG_set0(sig, r, s);
+ #else
+    if (sig) {
+        BN_free(sig->r);
+        sig->r = r;
+        BN_free(sig->s);
+        sig->s = s;
+    }
+ #endif
+
+end2:
+    return sig;
+}
+
+/**
+ * ECDSA verify method (replaces ossl_ecdsa_verify_sig).
+ *
+ * @return
+ *      1: correct signature
+ *      0: incorrect signature
+ *     -1: error
+ */
+int ibmca_ecdsa_verify_sig(const unsigned char *dgst, int dgst_len,
+                           const ECDSA_SIG *sig, EC_KEY *eckey)
+{
+    unsigned char sig_array[IBMCA_EC_MAX_Q_LEN];
+    BIGNUM *bn_x = NULL, *bn_y = NULL;
+    const BIGNUM *bn_r, *bn_s;
+    unsigned int privlen;
+    ICA_EC_KEY *icakey = NULL;
+    int rc, n;
+    int ret = -1;
+#ifndef OLDER_OPENSSL
+    int (*verify_sw)(int type, const unsigned char *dgst, int dgst_len,
+                     const unsigned char *sigbuf, int sig_len, EC_KEY *eckey) = NULL;
+#endif
+    int (*verify_sig_sw)(const unsigned char *dgst, int dgst_len,
+                         const ECDSA_SIG *sig, EC_KEY *eckey) = NULL;
+    struct ibmca_ec_ex_data *data = EC_KEY_get_ex_data(eckey, ec_ex_data_index);
+
+    /* Check parms */
+    if (eckey == NULL || sig == NULL) {
+        IBMCAerr(IBMCA_F_IBMCA_ECDSA_VERIFY_SIG, EC_R_MISSING_PARAMETERS);
+        return ret;
+    }
+
+    /* Check if key usable */
+#ifndef OLDER_OPENSSL
+    if (!EC_KEY_can_sign(eckey)) {
+        IBMCAerr(IBMCA_F_IBMCA_ECDSA_VERIFY_SIG, IBMCA_R_EC_CURVE_DOES_NOT_SUPPORT_SIGNING);
+        return ret;
+    }
+ #endif
+
+    if (data != NULL) {
+        icakey = data->ica_key;
+        privlen = data->privlen;
+        goto do_verify;
+    }
+
+    /* Create ICA_EC_KEY object */
+    ERR_set_mark();
+    icakey = ibmca_ec_make_and_cache_ica_key(eckey, &privlen);
+    ERR_pop_to_mark();
+    if (icakey == NULL) {
+        /* This curve is not supported by libica. */
+ #ifdef OLDER_OPENSSL
+        ECDSA_METHOD_get_verify(ossl_ecdsa, &verify_sig_sw);
+ #else
+        /*
+         * EC_KEY_METHOD_get_verify misses the const-qualifier of the
+         * parameter in some openssl versions.
+         */
+        EC_KEY_METHOD_get_verify((EC_KEY_METHOD *)ossl_ec, &verify_sw,
+                                 &verify_sig_sw);
+ #endif
+        if (verify_sig_sw == NULL) {
+            IBMCAerr(IBMCA_F_IBMCA_ECDSA_VERIFY_SIG,
+                     IBMCA_R_EC_INTERNAL_ERROR);
+            return ret;
+        }
+
+        return verify_sig_sw(dgst, dgst_len, sig, eckey);
+    }
+
+do_verify:
+    /* Get (r,s) from ECDSA_SIG */
+ #ifdef OLDER_OPENSSL
+    bn_r = sig->r;
+    bn_s = sig->s;
+ #else
+    ECDSA_SIG_get0(sig, &bn_r, &bn_s);
+ #endif
+
+    /* Format r as byte array with leading 0x00's if necessary */
+    n = privlen - BN_num_bytes(bn_r);
+    memset(sig_array, 0, n);
+    BN_bn2bin(bn_r, &(sig_array[n]));
+
+    /* Format s as byte array with leading 0x00's if necessary */
+    n = privlen - BN_num_bytes(bn_s);
+    memset(&(sig_array[privlen]), 0, n);
+    BN_bn2bin(bn_s, &(sig_array[privlen+n]));
+
+    /* Call libica verify routine */
+    rc = p_ica_ecdsa_verify(ibmca_handle, icakey, dgst, dgst_len, sig_array,
+                            2 * privlen);
+    switch (rc) {
+    case 0:
+        ret = 1; /* signature valid */
+        break;
+    case EFAULT:
+        ret = 0; /* signature invalid */
+        break;
+    default:
+        /* Possibly no suitable adapter. */
+ #ifdef OLDER_OPENSSL
+        ECDSA_METHOD_get_verify(ossl_ecdsa, &verify_sig_sw);
+ #else
+        /*
+         * EC_KEY_METHOD_get_verify misses the const-qualifier of the
+         * parameter in some openssl versions.
+         */
+        EC_KEY_METHOD_get_verify((EC_KEY_METHOD *)ossl_ec, &verify_sw,
+                                 &verify_sig_sw);
+ #endif
+        if (verify_sig_sw == NULL) {
+            IBMCAerr(IBMCA_F_IBMCA_ECDSA_VERIFY_SIG,
+                     IBMCA_R_EC_INTERNAL_ERROR);
+            goto end;
+        }
+
+        ret = verify_sig_sw(dgst, dgst_len, sig, eckey);
+        break;
+    }
+
+end:
+    BN_clear_free(bn_x);
+    BN_clear_free(bn_y);
+
+    return ret;
+}
+
+/* --- OLDER_OPENSSL section --- */
+ #ifdef OLDER_OPENSSL
+
+ECDSA_METHOD *ibmca_ecdsa = NULL;
+ECDH_METHOD *ibmca_ecdh = NULL;
+
+/*
+ * This structure is opaque in openssl. However, get/set methods are missing
+ * so we copy its definition and write our own.
+ */
+struct ecdh_method {
+    const char *name;
+    int (*compute_key)(void *out, size_t len, const EC_POINT *pub_key,
+                       EC_KEY *ecdh, void *(*KDF)(const void *in,
+                                                  size_t inlen, void *out,
+                                                  size_t *outlen));
+    int flags;
+    void *app_data;
+};
+
+struct ecdsa_method {
+    const char *name;
+    ECDSA_SIG *(*sign_sig)(const unsigned char *dgst, int dgst_len,
+                           const BIGNUM *inv, const BIGNUM *rp,
+                           EC_KEY *eckey);
+    int (*sign_setup)(EC_KEY *eckey, BN_CTX *ctx, BIGNUM **kinv,
+                      BIGNUM **r);
+    int (*verify_sig)(const unsigned char *dgst, int dgst_len,
+                      const ECDSA_SIG *sig, EC_KEY *eckey);
+    int flags;
+    void *app_data;
+};
+
+/**
+ * ECDH key derivation method, replaces ossl_ecdh_compute_key for older openssl.
+ *
+ * @return 1 success
+ *         0 error
+ */
+int ibmca_older_ecdh_compute_key(void *out, size_t outlen,
+                                 const EC_POINT *pub_key,
+                                 EC_KEY *ecdh, void *(*KDF) (const void *in,
+                                                             size_t inlen,
+                                                             void *out,
+                                                             size_t *outlen))
+{
+    int rc = 0;
+    unsigned char *temp_p = NULL;
+    size_t temp_len = 0;
+    int (*compute_key_sw)(void *out, size_t len, const EC_POINT *pub_key,
+                          EC_KEY *ecdh, void *(*KDF)(const void *in,
+                                                     size_t inlen,
+                                                     void *out,
+                                                     size_t *outlen)) = NULL;
+
+    rc = ibmca_ecdh_compute_key(&temp_p, &temp_len, pub_key, ecdh);
+    if (!rc) {
+        ECDH_METHOD_get_compute_key(ossl_ecdh, &compute_key_sw);
+        rc = compute_key_sw == NULL ? 0 : compute_key_sw(out, outlen, pub_key,
+                                                         ecdh, KDF);
+        goto end;
+    }
+
+    if (outlen < temp_len) {
+        rc = 0;
+        goto end;
+    }
+
+    if (KDF != NULL) {
+        if (KDF(temp_p, temp_len, out, &outlen) == NULL) {
+            rc = 0;
+            goto end;
+        }
+        rc = outlen;
+    } else {
+        if (outlen > temp_len)
+            outlen = temp_len;
+        memcpy(out, temp_p, outlen);
+        rc = outlen;
+    }
+
+end:
+    OPENSSL_free(temp_p);
+    return rc;
+}
+
+/**
+ * ECDSA sign method, replaces ecdsa_do_sign for older openssl.
+ */
+ECDSA_SIG *ibmca_older_ecdsa_do_sign(const unsigned char *dgst, int dgst_len,
+                                     const BIGNUM *in_kinv, const BIGNUM *in_r,
+                                     EC_KEY *eckey)
+{
+    if (in_kinv != NULL || in_r != NULL) {
+        IBMCAerr(IBMCA_F_IBMCA_ECDSA_DO_SIGN, IBMCA_R_EC_INVALID_PARM);
+        return NULL;
+    }
+
+    return ibmca_ecdsa_sign_sig(dgst, dgst_len, NULL, NULL, eckey);
+}
+
+/**
+ * ECDSA verify method, replaces ecdsa_do_verify for older openssl.
+ *
+ * @return 1 success
+ *         0 error
+ */
+int ibmca_older_ecdsa_do_verify(const unsigned char *dgst, int dgst_len,
+                                const ECDSA_SIG *sig, EC_KEY *eckey)
+{
+    return ibmca_ecdsa_verify_sig(dgst, dgst_len, sig, eckey);
+}
+
+/*
+ * APIs which are missing in openssl 1.0.2.
+ */
+ECDH_METHOD *ECDH_METHOD_new(const ECDH_METHOD *meth)
+{
+    ECDH_METHOD *out;
+
+    out = OPENSSL_malloc(sizeof(*out));
+    if (out == NULL)
+        return NULL;
+
+    if (meth)
+        memcpy(out, meth, sizeof(*out));
+    else
+        memset(out, 0, sizeof(*out));
+
+    return out;
+}
+
+void ECDH_METHOD_set_compute_key(ECDH_METHOD *meth,
+                                 int (*compute_key)(void *out, size_t len,
+                                                    const EC_POINT *pub_key,
+                                                    EC_KEY *ecdh,
+                                                    void *(*KDF)(const void *in,
+                                                                 size_t inlen,
+                                                                 void *out,
+                                                                 size_t *outlen)))
+{
+    meth->compute_key = compute_key;
+}
+
+void ECDH_METHOD_get_compute_key(const ECDH_METHOD *meth,
+                                 int (**compute_key)(void *out, size_t len,
+                                                     const EC_POINT *pub_key,
+                                                     EC_KEY *ecdh,
+                                                     void *(*KDF)(const void *in,
+                                                                  size_t inlen,
+                                                                  void *out,
+                                                                  size_t *outlen)))
+{
+    if (compute_key != NULL)
+        *compute_key = meth->compute_key;
+}
+
+void ECDH_METHOD_set_name(ECDH_METHOD *meth, char *name)
+{
+    meth->name = name;
+}
+
+void ECDH_METHOD_free(ECDH_METHOD *meth)
+{
+    OPENSSL_free(meth);
+}
+
+void ECDSA_METHOD_get_sign(const ECDSA_METHOD *meth,
+                           int (**psign_setup)(EC_KEY *eckey, BN_CTX *ctx_in,
+                                               BIGNUM **kinvp, BIGNUM **rp),
+                           ECDSA_SIG *(**psign_sig)(const unsigned char *dgst,
+                                                    int dgst_len,
+                                                    const BIGNUM *in_kinv,
+                                                    const BIGNUM *in_r,
+                                                    EC_KEY *eckey))
+{
+    if (psign_setup != NULL)
+        *psign_setup = meth->sign_setup;
+    if (psign_sig != NULL)
+        *psign_sig = meth->sign_sig;
+}
+
+void ECDSA_METHOD_get_verify(const ECDSA_METHOD *meth,
+                             int (**pverify_sig)(const unsigned char *dgst,
+                                                 int dgst_len,
+                                                 const ECDSA_SIG *sig,
+                                                 EC_KEY *eckey))
+{
+    if (pverify_sig != NULL)
+        *pverify_sig = meth->verify_sig;
+}
+
+/* --- !OLDER_OPENSSL section --- */
+ #else
+
+EC_KEY_METHOD *ibmca_ec = NULL;
+
+/**
+ * ECDSA signing method (replaces ossl_ecdsa_sign).
+ *
+ * returns 1 if success
+ *         0 if error
+ */
+int ibmca_ecdsa_sign(int type, const unsigned char *dgst, int dlen,
+             unsigned char *sig_array, unsigned int *siglen,
+             const BIGNUM *kinv, const BIGNUM *r, EC_KEY *eckey)
+{
+    ECDSA_SIG *sig;
+    const BIGNUM *bn_r, *bn_s;
+    const EC_GROUP *group;
+    int n, r_len, rc;
+
+    /* Check parms: precomputed (k,r) are not supported by ibmca */
+    if (kinv != NULL || r != NULL) {
+        IBMCAerr(IBMCA_F_IBMCA_ECDSA_SIGN, IBMCA_R_EC_INVALID_PARM);
+        return 0;
+    }
+
+    /* Create signature */
+    sig = ibmca_ecdsa_sign_sig(dgst, dlen, NULL, NULL, eckey);
+    if (sig == NULL) {
+        IBMCAerr(IBMCA_F_IBMCA_ECDSA_SIGN, IBMCA_R_EC_INTERNAL_ERROR);
+        *siglen = 0;
+        return 0;
+    }
+
+    /* Determine r-length */
+    if ((group = EC_KEY_get0_group(eckey)) == NULL) {
+        IBMCAerr(IBMCA_F_IBMCA_ECDSA_SIGN, IBMCA_R_EC_INTERNAL_ERROR);
+        rc = 0;
+        goto ret;
+    }
+    r_len = (EC_GROUP_get_degree(group) + 7) / 8;
+
+    /* Get (r,s) from ECDSA_SIG */
+    ECDSA_SIG_get0(sig, &bn_r, &bn_s);
+
+    /* Format r as byte array with leading 0x00's if necessary */
+    n = r_len - BN_num_bytes(bn_r);
+    memset(sig_array, 0, n);
+    BN_bn2bin(bn_r, &(sig_array[n]));
+
+    /* Format s as byte array with leading 0x00's if necessary */
+    n = r_len - BN_num_bytes(bn_s);
+    memset(&(sig_array[r_len]), 0, n);
+    BN_bn2bin(bn_s, &(sig_array[r_len + n]));
+
+    /* Create DER encoding */
+    *siglen = i2d_ECDSA_SIG(sig, &sig_array);
+
+    rc = 1;
+ret:
+    ECDSA_SIG_free(sig);
+    return rc;
+}
+
+/**
+ * ECDSA verify method (replaces ossl_ecdsa_verify). Just create an ECDSA_SIG object
+ * from given byte array and call ibmca_ecdsa_verify_sig.
+ *
+ * @return
+ *      1: correct signature
+ *      0: incorrect signature
+ *     -1: error
+ */
+int ibmca_ecdsa_verify(int type, const unsigned char *dgst, int dgst_len,
+                       const unsigned char *sigbuf, int sig_len, EC_KEY *eckey)
+{
+    ECDSA_SIG *s;
+    const unsigned char *p = sigbuf;
+    unsigned char *der = NULL;
+    int derlen = -1;
+    int ret = -1;
+
+    s = ECDSA_SIG_new();
+    if (s == NULL)
+        return ret;
+
+    if (d2i_ECDSA_SIG(&s, &p, sig_len) == NULL)
+        goto err;
+
+    /* Ensure signature uses DER and doesn't have trailing garbage */
+    derlen = i2d_ECDSA_SIG(s, &der);
+    if (derlen != sig_len || memcmp(sigbuf, der, derlen) != 0)
+        goto err;
+
+    ret = ibmca_ecdsa_verify_sig(dgst, dgst_len, s, eckey);
+
+err:
+    OPENSSL_clear_free(der, derlen);
+    ECDSA_SIG_free(s);
+
+    return ret;
+}
+
+ #endif
+
+#else
+
+/* non-empty compilation unit */
+static void *variable = &variable;
+
+#endif
diff -pruN 1.4.0-1/src/engine/ibmca_pkey.c 2.5.0-0ubuntu1/src/engine/ibmca_pkey.c
--- 1.4.0-1/src/engine/ibmca_pkey.c	1970-01-01 00:00:00.000000000 +0000
+++ 2.5.0-0ubuntu1/src/engine/ibmca_pkey.c	2025-04-16 08:09:24.000000000 +0000
@@ -0,0 +1,666 @@
+/*
+ * Copyright 2019-2021 International Business Machines Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <openssl/engine.h>
+#include <openssl/obj_mac.h>
+
+#include "ibmca.h"
+#include "e_ibmca_err.h"
+
+#include "openssl-compat.h"
+
+ica_x25519_ctx_new_t		p_ica_x25519_ctx_new;
+ica_x448_ctx_new_t		p_ica_x448_ctx_new;
+ica_ed25519_ctx_new_t		p_ica_ed25519_ctx_new;
+ica_ed448_ctx_new_t		p_ica_ed448_ctx_new;
+ica_x25519_key_set_t		p_ica_x25519_key_set;
+ica_x448_key_set_t		p_ica_x448_key_set;
+ica_ed25519_key_set_t		p_ica_ed25519_key_set;
+ica_ed448_key_set_t		p_ica_ed448_key_set;
+ica_x25519_key_get_t		p_ica_x25519_key_get;
+ica_x448_key_get_t		p_ica_x448_key_get;
+ica_ed25519_key_get_t		p_ica_ed25519_key_get;
+ica_ed448_key_get_t		p_ica_ed448_key_get;
+ica_x25519_key_gen_t		p_ica_x25519_key_gen;
+ica_x448_key_gen_t		p_ica_x448_key_gen;
+ica_ed25519_key_gen_t		p_ica_ed25519_key_gen;
+ica_ed448_key_gen_t		p_ica_ed448_key_gen;
+ica_x25519_derive_t		p_ica_x25519_derive;
+ica_x448_derive_t		p_ica_x448_derive;
+ica_ed25519_sign_t		p_ica_ed25519_sign;
+ica_ed448_sign_t		p_ica_ed448_sign;
+ica_ed25519_verify_t		p_ica_ed25519_verify;
+ica_ed448_verify_t		p_ica_ed448_verify;
+ica_x25519_ctx_del_t		p_ica_x25519_ctx_del;
+ica_x448_ctx_del_t		p_ica_x448_ctx_del;
+ica_ed25519_ctx_del_t		p_ica_ed25519_ctx_del;
+ica_ed448_ctx_del_t		p_ica_ed448_ctx_del;
+
+static EVP_PKEY_METHOD *ibmca_x25519_pmeth = NULL;
+static EVP_PKEY_METHOD *ibmca_x448_pmeth = NULL;
+static EVP_PKEY_METHOD *ibmca_ed25519_pmeth = NULL;
+static EVP_PKEY_METHOD *ibmca_ed448_pmeth = NULL;
+
+/* X25519 */
+
+static int ibmca_x25519_keygen(EVP_PKEY_CTX *c, EVP_PKEY *pkey)
+{
+    unsigned char priv[32], pub[32], *private = NULL;
+    ECX_KEY *key = NULL;
+    ICA_X25519_CTX *ctx = NULL;
+    int rc = 0;
+
+    if (p_ica_x25519_ctx_new(&ctx) != 0) {
+        IBMCAerr(IBMCA_F_IBMCA_X25519_KEYGEN, IBMCA_R_PKEY_INTERNAL_ERROR);
+        goto ret;
+    }
+    if (p_ica_x25519_key_gen(ctx) != 0) {
+        IBMCAerr(IBMCA_F_IBMCA_X25519_KEYGEN, IBMCA_R_PKEY_KEYGEN_FAILED);
+        goto ret;
+    }
+    if (p_ica_x25519_key_get(ctx, priv, pub) != 0) {
+        IBMCAerr(IBMCA_F_IBMCA_X25519_KEYGEN, IBMCA_R_PKEY_KEYGEN_FAILED);
+        goto ret;
+    }
+
+    key = ossl_ecx_key_new_simple(ECX_KEY_TYPE_X25519);
+    private = calloc(1, sizeof(priv));
+    if (key == NULL || private == NULL) {
+        IBMCAerr(IBMCA_F_IBMCA_X25519_KEYGEN, IBMCA_R_PKEY_KEYGEN_FAILED);
+        goto ret;
+    }
+
+    memcpy(private, priv, sizeof(priv));
+    ossl_ecx_copypubkey(key, pub, sizeof(pub));
+    ossl_ecx_set0_privkey(key, private);
+
+    EVP_PKEY_assign(pkey, NID_X25519, key);
+    rc = 1;
+ret:
+    if (rc == 0) {
+        free(key);
+        free(private);
+    }
+    if (ctx != NULL)
+        p_ica_x25519_ctx_del(&ctx);
+    return rc;
+}
+
+static int ibmca_x25519_ctrl(EVP_PKEY_CTX *ctx, int type, int p1, void *p2)
+{
+    if (type == EVP_PKEY_CTRL_PEER_KEY)
+        return 1;
+
+    return -2;
+}
+
+static int ibmca_x25519_derive(EVP_PKEY_CTX *pkey_ctx, unsigned char *key, size_t *keylen)
+{
+    ICA_X25519_CTX *ctx = NULL;
+    ECX_KEY *key_ecx = NULL, *peerkey_ecx = NULL;
+    EVP_PKEY *key_pkey = NULL, *peerkey_pkey = NULL;
+    int rc = 0;
+
+    *keylen = 32;
+    if (key == NULL) {
+        rc = 1;
+        goto ret;
+    }
+
+    key_pkey = EVP_PKEY_CTX_get0_pkey(pkey_ctx);
+    peerkey_pkey = EVP_PKEY_CTX_get0_peerkey(pkey_ctx);
+    if (key_pkey == NULL || peerkey_pkey == NULL) {
+        IBMCAerr(IBMCA_F_IBMCA_X25519_DERIVE, IBMCA_R_PKEY_KEYS_NOT_SET);
+        goto ret;
+    }
+
+    key_ecx = EVP_PKEY_get0(key_pkey);
+    peerkey_ecx = EVP_PKEY_get0(peerkey_pkey);
+    if (key_ecx == NULL || peerkey_ecx == NULL) {
+        IBMCAerr(IBMCA_F_IBMCA_X25519_DERIVE, IBMCA_R_PKEY_KEYS_NOT_SET);
+        goto ret;
+    }
+
+    if (p_ica_x25519_ctx_new(&ctx) != 0) {
+        IBMCAerr(IBMCA_F_IBMCA_X25519_DERIVE, IBMCA_R_PKEY_INTERNAL_ERROR);
+        goto ret;
+    }
+
+    if (p_ica_x25519_key_set(ctx, ossl_ecx_get0_privkey(key_ecx), NULL) != 0) {
+        IBMCAerr(IBMCA_F_IBMCA_X25519_DERIVE, IBMCA_R_PKEY_KEYS_NOT_SET);
+        goto ret;
+    }
+
+    if (p_ica_x25519_derive(ctx, key, ossl_ecx_get0_pubkey(peerkey_ecx)) != 0)
+        goto ret;
+
+    rc = 1;
+ret:
+    if (ctx != NULL)
+        p_ica_x25519_ctx_del(&ctx);
+    return rc;
+}
+
+/* X448 */
+
+static int ibmca_x448_keygen(EVP_PKEY_CTX *c, EVP_PKEY *pkey)
+{
+    unsigned char priv[56], pub[56], *private = NULL;
+    ECX_KEY *key = NULL;
+    ICA_X448_CTX *ctx = NULL;
+    int rc = 0;
+
+    if (p_ica_x448_ctx_new(&ctx) != 0) {
+        IBMCAerr(IBMCA_F_IBMCA_X448_KEYGEN, IBMCA_R_PKEY_INTERNAL_ERROR);
+        goto ret;
+    }
+    if (p_ica_x448_key_gen(ctx) != 0) {
+        IBMCAerr(IBMCA_F_IBMCA_X448_KEYGEN, IBMCA_R_PKEY_KEYGEN_FAILED);
+        goto ret;
+    }
+    if (p_ica_x448_key_get(ctx, priv, pub) != 0) {
+        IBMCAerr(IBMCA_F_IBMCA_X448_KEYGEN, IBMCA_R_PKEY_KEYGEN_FAILED);
+        goto ret;
+    }
+
+    key = ossl_ecx_key_new_simple(ECX_KEY_TYPE_X448);
+    private = calloc(1, sizeof(priv));
+    if (key == NULL || private == NULL) {
+        IBMCAerr(IBMCA_F_IBMCA_X448_KEYGEN, IBMCA_R_PKEY_KEYGEN_FAILED);
+        goto ret;
+    }
+
+    memcpy(private, priv, sizeof(priv));
+    ossl_ecx_copypubkey(key, pub, sizeof(pub));
+    ossl_ecx_set0_privkey(key, private);
+
+    EVP_PKEY_assign(pkey, NID_X448, key);
+    rc = 1;
+ret:
+    if (rc == 0) {
+        free(key);
+        free(private);
+    }
+    if (ctx != NULL)
+        p_ica_x448_ctx_del(&ctx);
+    return rc;
+}
+
+static int ibmca_x448_ctrl(EVP_PKEY_CTX *ctx, int type, int p1, void *p2)
+{
+    if (type == EVP_PKEY_CTRL_PEER_KEY)
+        return 1;
+
+    return -2;
+}
+
+static int ibmca_x448_derive(EVP_PKEY_CTX *pkey_ctx, unsigned char *key, size_t *keylen)
+{
+    ICA_X448_CTX *ctx = NULL;
+    ECX_KEY *key_ecx = NULL, *peerkey_ecx = NULL;
+    EVP_PKEY *key_pkey = NULL, *peerkey_pkey = NULL;
+    int rc = 0;
+
+    *keylen = 56;
+    if (key == NULL) {
+        rc = 1;
+        goto ret;
+    }
+
+    key_pkey = EVP_PKEY_CTX_get0_pkey(pkey_ctx);
+    peerkey_pkey = EVP_PKEY_CTX_get0_peerkey(pkey_ctx);
+    if (key_pkey == NULL || peerkey_pkey == NULL) {
+        IBMCAerr(IBMCA_F_IBMCA_X448_DERIVE, IBMCA_R_PKEY_KEYS_NOT_SET);
+        goto ret;
+    }
+
+    key_ecx = EVP_PKEY_get0(key_pkey);
+    peerkey_ecx = EVP_PKEY_get0(peerkey_pkey);
+    if (key_ecx == NULL || peerkey_ecx == NULL) {
+        IBMCAerr(IBMCA_F_IBMCA_X448_DERIVE, IBMCA_R_PKEY_KEYS_NOT_SET);
+        goto ret;
+    }
+
+    if (p_ica_x448_ctx_new(&ctx) != 0) {
+        IBMCAerr(IBMCA_F_IBMCA_X448_DERIVE, IBMCA_R_PKEY_INTERNAL_ERROR);
+        goto ret;
+    }
+
+    if (p_ica_x448_key_set(ctx, ossl_ecx_get0_privkey(key_ecx), NULL) != 0) {
+        IBMCAerr(IBMCA_F_IBMCA_X448_DERIVE, IBMCA_R_PKEY_KEYS_NOT_SET);
+        goto ret;
+    }
+
+    if (p_ica_x448_derive(ctx, key, ossl_ecx_get0_pubkey(peerkey_ecx)) != 0)
+        goto ret;
+
+    rc = 1;
+ret:
+    if (ctx != NULL)
+        p_ica_x448_ctx_del(&ctx);
+    return rc;
+}
+
+/* ED25519 */
+
+static int ibmca_ed25519_copy(EVP_PKEY_CTX *to, const EVP_PKEY_CTX *from)
+{
+    return 1;
+}
+
+static int ibmca_ed25519_keygen(EVP_PKEY_CTX *c, EVP_PKEY *pkey)
+{
+    unsigned char priv[32], pub[32], *private = NULL;
+    ECX_KEY *key = NULL;
+    ICA_ED25519_CTX *ctx = NULL;
+    int rc = 0;
+
+    if (p_ica_ed25519_ctx_new(&ctx) != 0) {
+        IBMCAerr(IBMCA_F_IBMCA_ED25519_KEYGEN, IBMCA_R_PKEY_INTERNAL_ERROR);
+        goto ret;
+    }
+    if (p_ica_ed25519_key_gen(ctx) != 0) {
+        IBMCAerr(IBMCA_F_IBMCA_ED25519_KEYGEN, IBMCA_R_PKEY_KEYGEN_FAILED);
+        goto ret;
+    }
+    if (p_ica_ed25519_key_get(ctx, priv, pub) != 0) {
+        IBMCAerr(IBMCA_F_IBMCA_ED25519_KEYGEN, IBMCA_R_PKEY_KEYGEN_FAILED);
+        goto ret;
+    }
+
+    key = ossl_ecx_key_new_simple(ECX_KEY_TYPE_ED25519);
+    private = calloc(1, sizeof(priv));
+    if (key == NULL || private == NULL) {
+        IBMCAerr(IBMCA_F_IBMCA_ED25519_KEYGEN, IBMCA_R_PKEY_KEYGEN_FAILED);
+        goto ret;
+    }
+
+    memcpy(private, priv, sizeof(priv));
+    ossl_ecx_copypubkey(key, pub, sizeof(pub));
+    ossl_ecx_set0_privkey(key, private);
+
+    EVP_PKEY_assign(pkey, NID_ED25519, key);
+    rc = 1;
+ret:
+    if (rc == 0) {
+        free(key);
+        free(private);
+    }
+    if (ctx != NULL)
+        p_ica_ed25519_ctx_del(&ctx);
+    return rc;
+}
+
+static int ibmca_ed25519_sign(EVP_MD_CTX *md_ctx, unsigned char *sig,
+                              size_t *siglen, const unsigned char *tbs,
+                              size_t tbslen)
+{
+    ICA_ED25519_CTX *ctx = NULL;
+    ECX_KEY *key_ecx = NULL;
+    EVP_PKEY *key_pkey = NULL;
+    int rc = 0;
+
+    if (sig == NULL) {
+        *siglen = 2 * 32;
+        return 1;
+    }
+
+    if (*siglen < 2 * 32)
+        goto ret;
+
+    key_pkey = EVP_PKEY_CTX_get0_pkey(EVP_MD_CTX_pkey_ctx(md_ctx));
+    if (key_pkey == NULL) {
+        IBMCAerr(IBMCA_F_IBMCA_ED25519_SIGN, IBMCA_R_PKEY_KEYS_NOT_SET);
+        goto ret;
+    }
+
+    key_ecx = EVP_PKEY_get0(key_pkey);
+    if (key_ecx == NULL) {
+        IBMCAerr(IBMCA_F_IBMCA_ED25519_SIGN, IBMCA_R_PKEY_KEYS_NOT_SET);
+        goto ret;
+    }
+
+    if (p_ica_ed25519_ctx_new(&ctx) != 0) {
+        IBMCAerr(IBMCA_F_IBMCA_ED25519_SIGN, IBMCA_R_PKEY_INTERNAL_ERROR);
+        goto ret;
+    }
+
+    if (p_ica_ed25519_key_set(ctx, ossl_ecx_get0_privkey(key_ecx), NULL) != 0) {
+        IBMCAerr(IBMCA_F_IBMCA_ED25519_SIGN, IBMCA_R_PKEY_KEYS_NOT_SET);
+        goto ret;
+    }
+
+    if (p_ica_ed25519_sign(ctx, sig, tbs, tbslen) != 0)
+        goto ret;
+
+    *siglen = 2 * 32;
+    rc = 1;
+ret:
+    if (ctx != NULL)
+        p_ica_ed25519_ctx_del(&ctx);
+    return rc;
+}
+
+static int ibmca_ed25519_verify(EVP_MD_CTX *md_ctx, const unsigned char *sig,
+                                size_t siglen, const unsigned char *tbv,
+                                size_t tbvlen)
+{
+    ICA_ED25519_CTX *ctx = NULL;
+    ECX_KEY *key_ecx = NULL;
+    EVP_PKEY *key_pkey = NULL;
+    int rc = 0;
+
+    if (sig == NULL || siglen != 2 * 32)
+        goto ret;
+
+    key_pkey = EVP_PKEY_CTX_get0_pkey(EVP_MD_CTX_pkey_ctx(md_ctx));
+    if (key_pkey == NULL) {
+        IBMCAerr(IBMCA_F_IBMCA_ED25519_VERIFY, IBMCA_R_PKEY_KEYS_NOT_SET);
+        goto ret;
+    }
+
+    key_ecx = EVP_PKEY_get0(key_pkey);
+    if (key_ecx == NULL) {
+        IBMCAerr(IBMCA_F_IBMCA_ED25519_VERIFY, IBMCA_R_PKEY_KEYS_NOT_SET);
+        goto ret;
+    }
+
+    if (p_ica_ed25519_ctx_new(&ctx) != 0) {
+        IBMCAerr(IBMCA_F_IBMCA_ED25519_VERIFY, IBMCA_R_PKEY_INTERNAL_ERROR);
+        goto ret;
+    }
+
+    if (p_ica_ed25519_key_set(ctx, NULL, ossl_ecx_get0_pubkey(key_ecx)) != 0) {
+        IBMCAerr(IBMCA_F_IBMCA_ED25519_VERIFY, IBMCA_R_PKEY_KEYS_NOT_SET);
+        goto ret;
+    }
+
+    if (p_ica_ed25519_verify(ctx, sig, tbv, tbvlen) != 0)
+        goto ret;
+
+    rc = 1;
+ret:
+    if (ctx != NULL)
+        p_ica_ed25519_ctx_del(&ctx);
+    return rc;
+}
+
+/* ED448 */
+
+static int ibmca_ed448_copy(EVP_PKEY_CTX *to, const EVP_PKEY_CTX *from)
+{
+    return 1;
+}
+
+static int ibmca_ed448_keygen(EVP_PKEY_CTX *c, EVP_PKEY *pkey)
+{
+    unsigned char priv[57], pub[57], *private = NULL;
+    ECX_KEY *key = NULL;
+    ICA_ED448_CTX *ctx = NULL;
+    int rc = 0;
+
+    if (p_ica_ed448_ctx_new(&ctx) != 0) {
+        IBMCAerr(IBMCA_F_IBMCA_ED448_KEYGEN, IBMCA_R_PKEY_INTERNAL_ERROR);
+        goto ret;
+    }
+    if (p_ica_ed448_key_gen(ctx) != 0) {
+        IBMCAerr(IBMCA_F_IBMCA_ED448_KEYGEN, IBMCA_R_PKEY_KEYGEN_FAILED);
+        goto ret;
+    }
+    if (p_ica_ed448_key_get(ctx, priv, pub) != 0) {
+        IBMCAerr(IBMCA_F_IBMCA_ED448_KEYGEN, IBMCA_R_PKEY_KEYGEN_FAILED);
+        goto ret;
+    }
+
+    key = ossl_ecx_key_new_simple(ECX_KEY_TYPE_ED448);
+    private = calloc(1, sizeof(priv));
+    if (key == NULL || private == NULL) {
+        IBMCAerr(IBMCA_F_IBMCA_ED448_KEYGEN, IBMCA_R_PKEY_KEYGEN_FAILED);
+        goto ret;
+    }
+
+    memcpy(private, priv, sizeof(priv));
+    ossl_ecx_copypubkey(key, pub, sizeof(pub));
+    ossl_ecx_set0_privkey(key, private);
+
+    EVP_PKEY_assign(pkey, NID_ED448, key);
+    rc = 1;
+ret:
+    if (rc == 0) {
+        free(key);
+        free(private);
+    }
+    if (ctx != NULL)
+        p_ica_ed448_ctx_del(&ctx);
+    return rc;
+}
+
+static int ibmca_ed448_sign(EVP_MD_CTX *md_ctx, unsigned char *sig,
+                              size_t *siglen, const unsigned char *tbs,
+                              size_t tbslen)
+{
+    ICA_ED448_CTX *ctx = NULL;
+    ECX_KEY *key_ecx = NULL;
+    EVP_PKEY *key_pkey = NULL;
+    int rc = 0;
+
+    if (sig == NULL) {
+        *siglen = 2 * 57;
+        return 1;
+    }
+
+    if (*siglen < 2 * 57)
+        goto ret;
+
+    key_pkey = EVP_PKEY_CTX_get0_pkey(EVP_MD_CTX_pkey_ctx(md_ctx));
+    if (key_pkey == NULL) {
+        IBMCAerr(IBMCA_F_IBMCA_ED448_SIGN, IBMCA_R_PKEY_KEYS_NOT_SET);
+        goto ret;
+    }
+
+    key_ecx = EVP_PKEY_get0(key_pkey);
+    if (key_ecx == NULL) {
+        IBMCAerr(IBMCA_F_IBMCA_ED448_SIGN, IBMCA_R_PKEY_KEYS_NOT_SET);
+        goto ret;
+    }
+
+    if (p_ica_ed448_ctx_new(&ctx) != 0) {
+        IBMCAerr(IBMCA_F_IBMCA_ED448_SIGN, IBMCA_R_PKEY_INTERNAL_ERROR);
+        goto ret;
+    }
+
+    if (p_ica_ed448_key_set(ctx, ossl_ecx_get0_privkey(key_ecx), NULL) != 0) {
+        IBMCAerr(IBMCA_F_IBMCA_ED448_SIGN, IBMCA_R_PKEY_KEYS_NOT_SET);
+        goto ret;
+    }
+
+    if (p_ica_ed448_sign(ctx, sig, tbs, tbslen) != 0)
+        goto ret;
+
+    *siglen = 2 * 57;
+    rc = 1;
+ret:
+    if (ctx != NULL)
+        p_ica_ed448_ctx_del(&ctx);
+    return rc;
+}
+
+static int ibmca_ed448_verify(EVP_MD_CTX *md_ctx, const unsigned char *sig,
+                                size_t siglen, const unsigned char *tbv,
+                                size_t tbvlen)
+{
+    ICA_ED448_CTX *ctx = NULL;
+    ECX_KEY *key_ecx = NULL;
+    EVP_PKEY *key_pkey = NULL;
+    int rc = 0;
+
+    if (sig == NULL || siglen != 2 * 57)
+        goto ret;
+
+    key_pkey = EVP_PKEY_CTX_get0_pkey(EVP_MD_CTX_pkey_ctx(md_ctx));
+    if (key_pkey == NULL) {
+        IBMCAerr(IBMCA_F_IBMCA_ED448_VERIFY, IBMCA_R_PKEY_KEYS_NOT_SET);
+        goto ret;
+    }
+
+    key_ecx = EVP_PKEY_get0(key_pkey);
+    if (key_ecx == NULL) {
+        IBMCAerr(IBMCA_F_IBMCA_ED448_VERIFY, IBMCA_R_PKEY_KEYS_NOT_SET);
+        goto ret;
+    }
+
+    if (p_ica_ed448_ctx_new(&ctx) != 0) {
+        IBMCAerr(IBMCA_F_IBMCA_ED448_VERIFY, IBMCA_R_PKEY_INTERNAL_ERROR);
+        goto ret;
+    }
+
+    if (p_ica_ed448_key_set(ctx, NULL, ossl_ecx_get0_pubkey(key_ecx)) != 0) {
+        IBMCAerr(IBMCA_F_IBMCA_ED448_VERIFY, IBMCA_R_PKEY_KEYS_NOT_SET);
+        goto ret;
+    }
+
+    if (p_ica_ed448_verify(ctx, sig, tbv, tbvlen) != 0)
+        goto ret;
+
+    rc = 1;
+ret:
+    if (ctx != NULL)
+        p_ica_ed448_ctx_del(&ctx);
+    return rc;
+}
+
+/* Methods */
+
+static int ibmca_ed_ctrl(EVP_PKEY_CTX *ctx, int type, int p1, void *p2)
+{
+    switch (type) {
+    case EVP_PKEY_CTRL_MD:
+        /* Only NULL allowed as digest */
+        if (p2 == NULL || (const EVP_MD *)p2 == EVP_md_null())
+            return 1;
+        return 0;
+
+    case EVP_PKEY_CTRL_DIGESTINIT:
+        return 1;
+    }
+    return -2;
+}
+
+const EVP_PKEY_METHOD *ibmca_x25519(void)
+{
+    if (ibmca_x25519_pmeth != NULL)
+        goto ret;
+
+    ibmca_x25519_pmeth = EVP_PKEY_meth_new(NID_X25519, 0);
+    if (ibmca_x25519_pmeth == NULL)
+        goto ret;
+
+    EVP_PKEY_meth_set_ctrl(ibmca_x25519_pmeth, ibmca_x25519_ctrl, NULL);
+    EVP_PKEY_meth_set_keygen(ibmca_x25519_pmeth, NULL, ibmca_x25519_keygen);
+    EVP_PKEY_meth_set_derive(ibmca_x25519_pmeth, NULL, ibmca_x25519_derive);
+
+ret:
+    return ibmca_x25519_pmeth;
+}
+
+const EVP_PKEY_METHOD *ibmca_x448(void)
+{
+    if (ibmca_x448_pmeth != NULL)
+        goto ret;
+
+    ibmca_x448_pmeth = EVP_PKEY_meth_new(NID_X448, 0);
+    if (ibmca_x448_pmeth == NULL)
+        goto ret;
+
+    EVP_PKEY_meth_set_ctrl(ibmca_x448_pmeth, ibmca_x448_ctrl, NULL);
+    EVP_PKEY_meth_set_keygen(ibmca_x448_pmeth, NULL, ibmca_x448_keygen);
+    EVP_PKEY_meth_set_derive(ibmca_x448_pmeth, NULL, ibmca_x448_derive);
+
+ret:
+    return ibmca_x448_pmeth;
+}
+
+const EVP_PKEY_METHOD *ibmca_ed25519(void)
+{
+    if (ibmca_ed25519_pmeth != NULL)
+        goto ret;
+
+    ibmca_ed25519_pmeth = EVP_PKEY_meth_new(NID_ED25519, EVP_PKEY_FLAG_SIGCTX_CUSTOM);
+    if (ibmca_ed25519_pmeth == NULL)
+        goto ret;
+
+    EVP_PKEY_meth_set_ctrl(ibmca_ed25519_pmeth, ibmca_ed_ctrl, NULL);
+    EVP_PKEY_meth_set_copy(ibmca_ed25519_pmeth, ibmca_ed25519_copy);
+    EVP_PKEY_meth_set_keygen(ibmca_ed25519_pmeth, NULL, ibmca_ed25519_keygen);
+    EVP_PKEY_meth_set_digestsign(ibmca_ed25519_pmeth, ibmca_ed25519_sign);
+    EVP_PKEY_meth_set_digestverify(ibmca_ed25519_pmeth, ibmca_ed25519_verify);
+
+ret:
+    return ibmca_ed25519_pmeth;
+}
+
+const EVP_PKEY_METHOD *ibmca_ed448(void)
+{
+    if (ibmca_ed448_pmeth != NULL)
+        goto ret;
+
+    ibmca_ed448_pmeth = EVP_PKEY_meth_new(NID_ED448, EVP_PKEY_FLAG_SIGCTX_CUSTOM);
+    if (ibmca_ed448_pmeth == NULL)
+        goto ret;
+
+    EVP_PKEY_meth_set_ctrl(ibmca_ed448_pmeth, ibmca_ed_ctrl, NULL);
+    EVP_PKEY_meth_set_copy(ibmca_ed448_pmeth, ibmca_ed448_copy);
+    EVP_PKEY_meth_set_keygen(ibmca_ed448_pmeth, NULL, ibmca_ed448_keygen);
+    EVP_PKEY_meth_set_digestsign(ibmca_ed448_pmeth, ibmca_ed448_sign);
+    EVP_PKEY_meth_set_digestverify(ibmca_ed448_pmeth, ibmca_ed448_verify);
+
+ret:
+    return ibmca_ed448_pmeth;
+}
+
+void ibmca_x25519_destroy(void)
+{
+    if (ibmca_x25519_pmeth != NULL) {
+        EVP_PKEY_meth_free(ibmca_x25519_pmeth);
+        ibmca_x25519_pmeth = NULL;
+    }
+}
+
+void ibmca_x448_destroy(void)
+{
+    if (ibmca_x448_pmeth != NULL) {
+        EVP_PKEY_meth_free(ibmca_x448_pmeth);
+        ibmca_x448_pmeth = NULL;
+    }
+}
+
+void ibmca_ed25519_destroy(void)
+{
+    if (ibmca_ed25519_pmeth != NULL) {
+        EVP_PKEY_meth_free(ibmca_ed25519_pmeth);
+        ibmca_ed25519_pmeth = NULL;
+    }
+}
+
+void ibmca_ed448_destroy(void)
+{
+    if (ibmca_ed448_pmeth != NULL) {
+        EVP_PKEY_meth_free(ibmca_ed448_pmeth);
+        ibmca_ed448_pmeth = NULL;
+    }
+}
diff -pruN 1.4.0-1/src/engine/ibmca_rsa.c 2.5.0-0ubuntu1/src/engine/ibmca_rsa.c
--- 1.4.0-1/src/engine/ibmca_rsa.c	1970-01-01 00:00:00.000000000 +0000
+++ 2.5.0-0ubuntu1/src/engine/ibmca_rsa.c	2025-04-16 08:09:24.000000000 +0000
@@ -0,0 +1,435 @@
+/*
+ * Copyright [2005-2021] International Business Machines Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <openssl/rsa.h>
+#include "ibmca.h"
+#include "e_ibmca_err.h"
+
+#include <stdio.h>
+
+/*
+ * Define compat functions for older OpenSSL versions
+ */
+#ifdef OLDER_OPENSSL
+void RSA_get0_key(const RSA *rsa, const BIGNUM **n,
+                  const BIGNUM **e, const BIGNUM **d)
+{
+    if (n != NULL)
+        *n = rsa->n;
+    if (e != NULL)
+        *e = rsa->e;
+    if (d != NULL)
+        *d = rsa->d;
+}
+
+void RSA_get0_factors(const RSA *rsa, const BIGNUM **p, const BIGNUM **q)
+{
+    if (p != NULL)
+        *p = rsa->p;
+    if (q != NULL)
+        *q = rsa->q;
+}
+
+void RSA_get0_crt_params(const RSA *rsa, const BIGNUM **dmp1,
+                         const BIGNUM **dmq1, const BIGNUM **iqmp)
+{
+    if (dmp1 != NULL)
+        *dmp1 = rsa->dmp1;
+    if (dmq1 != NULL)
+        *dmq1 = rsa->dmq1;
+    if (iqmp != NULL)
+        *iqmp = rsa->iqmp;
+}
+#endif
+
+int ibmca_mod_exp(BIGNUM * r, const BIGNUM * a, const BIGNUM * p,
+                  const BIGNUM * m, BN_CTX * ctx)
+{
+    /* r = (a^p) mod m
+     * r = output
+     * a = input
+     * p = exponent
+     * m = modulus
+     */
+
+    unsigned char *input = NULL, *output = NULL;
+    ica_rsa_key_mod_expo_t *key = NULL;
+    unsigned int rc;
+    int plen, mlen, inputlen;
+
+    /*
+     * make necessary memory allocations
+     * FIXME: Would it be possible to minimize memory allocation overhead by
+     * either allocating it all at once or having a static storage?
+     */
+    key = (ica_rsa_key_mod_expo_t *) calloc(1, sizeof(ica_rsa_key_mod_expo_t));
+    if (key == NULL) {
+        IBMCAerr(IBMCA_F_IBMCA_MOD_EXP, IBMCA_R_REQUEST_FAILED);
+        goto err;
+    }
+
+    key->key_length = mlen = BN_num_bytes(m);
+
+    key->modulus = (unsigned char *) calloc(1, key->key_length);
+    if (key->modulus == NULL) {
+        IBMCAerr(IBMCA_F_IBMCA_MOD_EXP, IBMCA_R_REQUEST_FAILED);
+        goto err;
+    }
+
+    plen = BN_num_bytes(p);
+
+    /* despite plen, key->exponent must be key->key_length in size */
+    key->exponent = (unsigned char *) calloc(1, key->key_length);
+    if (key->exponent == NULL) {
+        IBMCAerr(IBMCA_F_IBMCA_MOD_EXP, IBMCA_R_REQUEST_FAILED);
+        goto err;
+    }
+
+    inputlen = BN_num_bytes(a);
+
+    /* despite inputlen, input and output must be key->key_length in size */
+    input = (unsigned char *) calloc(1, key->key_length);
+    if (input == NULL) {
+        IBMCAerr(IBMCA_F_IBMCA_MOD_EXP, IBMCA_R_REQUEST_FAILED);
+        goto err;
+    }
+
+    output = (unsigned char *) calloc(1, key->key_length);
+    if (output == NULL) {
+        IBMCAerr(IBMCA_F_IBMCA_MOD_EXP, IBMCA_R_REQUEST_FAILED);
+        goto err;
+    }
+
+    /* Now convert from BIGNUM representation.
+     * Everything must be right-justified
+     */
+    BN_bn2bin(m, key->modulus);
+
+    BN_bn2bin(p, key->exponent + key->key_length - plen);
+
+    BN_bn2bin(a, input + key->key_length - inputlen);
+
+    /* execute the ica mod_exp call */
+    rc = p_ica_rsa_mod_expo(ibmca_handle, input, key, output);
+    if (rc != 0) {
+        goto err;
+    } else {
+        rc = 1;
+    }
+
+    /* Convert output to BIGNUM representation.
+     * right-justified output applies
+     */
+    /* BN_bin2bn((unsigned char *) (output + key->key_length - inputlen),
+     *           inputlen, r); */
+    BN_bin2bn((unsigned char *) output, key->key_length, r);
+
+    goto end;
+
+err:
+    rc = 0;                     /* error condition */
+
+end:
+    free(key->exponent);
+    free(key->modulus);
+    free(key);
+    free(input);
+    free(output);
+
+    return rc;
+}
+
+#ifndef OPENSSL_NO_RSA
+
+static int ibmca_mod_exp_crt(BIGNUM * r, const BIGNUM * a,
+                             const BIGNUM * p, const BIGNUM * q,
+                             const BIGNUM * dmp1, const BIGNUM * dmq1,
+                             const BIGNUM * iqmp, BN_CTX * ctx)
+{
+    /*
+     * r = output
+     * a = input
+     * p and q are themselves
+     * dmp1, dmq1 are dp and dq respectively
+     * iqmp is qInverse
+     */
+
+    ica_rsa_key_crt_t *key = NULL;
+    unsigned char *output = NULL, *input = NULL;
+    int rc;
+    int plen, qlen, dplen, dqlen, qInvlen;
+    int inputlen;
+
+    /*
+     * make necessary memory allocations
+     * FIXME: Would it be possible to minimize memory allocation overhead by
+     * either allocating it all at once or having a static storage?
+     */
+    key = (ica_rsa_key_crt_t *) calloc(1, sizeof(ica_rsa_key_crt_t));
+    if (key == NULL) {
+        IBMCAerr(IBMCA_F_IBMCA_MOD_EXP, IBMCA_R_REQUEST_FAILED);
+        goto err;
+    }
+
+    /* buffers pointed by p, q, dp, dq and qInverse in struct
+     * ica_rsa_key_crt_t must be of size key_legth/2 or larger.
+     * p, dp and qInverse have an additional 8-byte padding. */
+
+    plen = BN_num_bytes(p);
+    qlen = BN_num_bytes(q);
+    key->key_length = 2 * (plen > qlen ? plen : qlen);
+
+    key->p = (unsigned char *) calloc(1, (key->key_length / 2) + 8);
+    if (key->p == NULL) {
+        IBMCAerr(IBMCA_F_IBMCA_MOD_EXP, IBMCA_R_REQUEST_FAILED);
+        goto err;
+    }
+    dplen = BN_num_bytes(dmp1);
+    key->dp = (unsigned char *) calloc(1, (key->key_length / 2) + 8);
+    if (key->dp == NULL) {
+        IBMCAerr(IBMCA_F_IBMCA_MOD_EXP, IBMCA_R_REQUEST_FAILED);
+        goto err;
+    }
+
+    key->q = (unsigned char *) calloc(1, key->key_length / 2);
+    if (key->q == NULL) {
+        IBMCAerr(IBMCA_F_IBMCA_MOD_EXP, IBMCA_R_REQUEST_FAILED);
+        goto err;
+    }
+
+    dqlen = BN_num_bytes(dmq1);
+    key->dq = (unsigned char *) calloc(1, key->key_length / 2);
+    if (key->dq == NULL) {
+        IBMCAerr(IBMCA_F_IBMCA_MOD_EXP, IBMCA_R_REQUEST_FAILED);
+        goto err;
+    }
+
+    qInvlen = BN_num_bytes(iqmp);
+    key->qInverse = (unsigned char *) calloc(1, (key->key_length / 2) + 8);
+    if (key->qInverse == NULL) {
+        IBMCAerr(IBMCA_F_IBMCA_MOD_EXP, IBMCA_R_REQUEST_FAILED);
+        goto err;
+    }
+    inputlen = BN_num_bytes(a);
+    if (inputlen > key->key_length) {   /* input can't be larger than key */
+        IBMCAerr(IBMCA_F_IBMCA_MOD_EXP, IBMCA_R_REQUEST_FAILED);
+        goto err;
+    }
+
+    /* allocate input to the size of key_length in bytes, and
+     * pad front with zero if inputlen < key->key_length */
+    input = (unsigned char *) calloc(1, key->key_length);
+    if (input == NULL) {
+        IBMCAerr(IBMCA_F_IBMCA_MOD_EXP, IBMCA_R_REQUEST_FAILED);
+        goto err;
+    }
+
+    /* output must also be key_length in size */
+    output = (unsigned char *) calloc(1, key->key_length);
+    if (output == NULL) {
+        IBMCAerr(IBMCA_F_IBMCA_MOD_EXP, IBMCA_R_REQUEST_FAILED);
+        goto err;
+    }
+
+
+    /* Now convert from BIGNUM representation.
+     * p, dp and qInverse have an additional 8-byte padding,
+     * and everything must be right-justified */
+    BN_bn2bin(p, key->p + 8 + (key->key_length / 2) - plen);
+
+    BN_bn2bin(dmp1, key->dp + 8 + (key->key_length / 2) - dplen);
+
+    BN_bn2bin(q, key->q + (key->key_length / 2) - qlen);
+
+    BN_bn2bin(dmq1, key->dq + (key->key_length / 2) - dqlen);
+
+    BN_bn2bin(iqmp, key->qInverse + 8 + (key->key_length / 2) - qInvlen);
+
+    BN_bn2bin(a, input + key->key_length - inputlen);
+
+    /* execute the ica crt call */
+
+    rc = p_ica_rsa_crt(ibmca_handle, input, key, output);
+    if (rc != 0) {
+        //IBMCAerr(IBMCA_F_IBMCA_MOD_EXP, IBMCA_R_REQUEST_FAILED);
+        goto err;
+    } else {
+        rc = 1;
+    }
+
+    /* Convert output to BIGNUM representation */
+    /* BN_bin2bn((unsigned char *) (output + key->key_length - inputlen),
+     *           inputlen, r); */
+    BN_bin2bn((unsigned char *) output, key->key_length, r);
+
+    goto end;
+
+err:
+    rc = 0;                     /* error condition */
+
+end:
+    free(key->p);
+    free(key->q);
+    free(key->dp);
+    free(key->dq);
+    free(key->qInverse);
+    free(key);
+    free(input);
+    free(output);
+
+    return rc;
+}
+
+static int ibmca_rsa_init(RSA * rsa)
+{
+    /*
+     * Ensure that the MONT_CTXs for public and private keys are cached.
+     * This enables that ibmca_mod_exp_mont() is also called during
+     * (re-)setup of the RSA blinding factors.
+     */
+    RSA_set_flags(rsa, RSA_FLAG_CACHE_PUBLIC | RSA_FLAG_CACHE_PRIVATE);
+
+    return 1;
+}
+
+static int (*ibmca_rsa_mod_exp_backup)(BIGNUM * r0, const BIGNUM * I, RSA * rsa,
+                                       BN_CTX * ctx);
+
+static int ibmca_rsa_mod_exp(BIGNUM * r0, const BIGNUM * I, RSA * rsa,
+                             BN_CTX * ctx)
+{
+    int to_return = 0, usebackup = 1;
+    const BIGNUM *d, *n, *p, *q, *dmp1, *dmq1, *iqmp;
+
+    RSA_get0_key(rsa, &n, NULL, &d);
+    RSA_get0_factors(rsa, &p, &q);
+    RSA_get0_crt_params(rsa, &dmp1, &dmq1, &iqmp);
+    if (!p || !q || !dmp1 || !dmq1 || !iqmp) {
+        if (!d || !n) {
+            IBMCAerr(IBMCA_F_IBMCA_RSA_MOD_EXP, IBMCA_R_MISSING_KEY_COMPONENTS);
+            usebackup = 0;
+            goto err;
+        }
+        to_return = ibmca_mod_exp(r0, I, d, n, ctx);
+    } else {
+        to_return = ibmca_mod_exp_crt(r0, I, p, q, dmp1, dmq1, iqmp, ctx);
+    }
+
+err:
+    if (!to_return && usebackup && ibmca_rsa_mod_exp_backup)
+        return ibmca_rsa_mod_exp_backup(r0, I, rsa, ctx);
+    return to_return;
+}
+
+static int (*ibmca_mod_exp_mont_backup)(BIGNUM * r, const BIGNUM * a,
+                                        const BIGNUM * p, const BIGNUM * m,
+                                        BN_CTX * ctx, BN_MONT_CTX * m_ctx);
+
+
+/* This function is aliased to mod_exp (with the mont stuff dropped). */
+static int ibmca_mod_exp_mont(BIGNUM * r, const BIGNUM * a,
+                              const BIGNUM * p, const BIGNUM * m,
+                              BN_CTX * ctx, BN_MONT_CTX * m_ctx)
+{
+    if (!ibmca_mod_exp(r, a, p, m, ctx)) {
+        if (ibmca_mod_exp_mont_backup)
+            return ibmca_mod_exp_mont_backup(r, a, p, m, ctx, m_ctx);
+        return 0;
+    }
+    return 1;
+}
+
+#ifdef OLDER_OPENSSL
+static RSA_METHOD rsa_m = {
+    "Ibmca RSA method",         /* name */
+    NULL,                       /* rsa_pub_enc */
+    NULL,                       /* rsa_pub_dec */
+    NULL,                       /* rsa_priv_enc */
+    NULL,                       /* rsa_priv_dec */
+    ibmca_rsa_mod_exp,          /* rsa_mod_exp */
+    ibmca_mod_exp_mont,         /* bn_mod_exp */
+    ibmca_rsa_init,             /* init */
+    NULL,                       /* finish */
+    RSA_FLAG_FIPS_METHOD,       /* flags */
+    NULL,                       /* app_data */
+    NULL,                       /* rsa_sign */
+    NULL,                       /* rsa_verify */
+    NULL                        /* rsa_keygen */
+};
+
+RSA_METHOD *ibmca_rsa(void)
+{
+    /* We know that the "PKCS1_SSLeay()" functions hook properly
+     * to the ibmca-specific mod_exp and mod_exp_crt so we use
+     * those functions. NB: We don't use ENGINE_openssl() or
+     * anything "more generic" because something like the RSAref
+     * code may not hook properly, and if you own one of these here
+     * cards then you have the right to do RSA operations on it
+     * anyway! */
+    const RSA_METHOD *meth1 = RSA_PKCS1_SSLeay();
+
+    ibmca_rsa_mod_exp_backup = meth1->rsa_mod_exp;
+    ibmca_mod_exp_mont_backup = meth1->bn_mod_exp;
+    rsa_m.rsa_pub_enc = meth1->rsa_pub_enc;
+    rsa_m.rsa_pub_dec = meth1->rsa_pub_dec;
+    rsa_m.rsa_priv_enc = meth1->rsa_priv_enc;
+    rsa_m.rsa_priv_dec = meth1->rsa_priv_dec;
+
+    return &rsa_m;
+}
+
+#else
+static RSA_METHOD *rsa_m = NULL;
+RSA_METHOD *ibmca_rsa(void)
+{
+    const RSA_METHOD *meth1;
+    RSA_METHOD *method;
+
+    if (rsa_m != NULL)
+        goto done;
+
+    if ((method = RSA_meth_new("Ibmca RSA method", 0)) == NULL
+        || (meth1 = RSA_PKCS1_OpenSSL()) == NULL
+        || (ibmca_rsa_mod_exp_backup = RSA_meth_get_mod_exp(meth1)) == NULL
+        || (ibmca_mod_exp_mont_backup = RSA_meth_get_bn_mod_exp(meth1)) == NULL
+        || !RSA_meth_set_pub_enc(method, RSA_meth_get_pub_enc(meth1))
+        || !RSA_meth_set_pub_dec(method, RSA_meth_get_pub_dec(meth1))
+        || !RSA_meth_set_priv_enc(method, RSA_meth_get_priv_enc(meth1))
+        || !RSA_meth_set_priv_dec(method, RSA_meth_get_priv_dec(meth1))
+        || !RSA_meth_set_mod_exp(method, ibmca_rsa_mod_exp)
+        || !RSA_meth_set_bn_mod_exp(method, ibmca_mod_exp_mont)
+        || !RSA_meth_set_init(method, ibmca_rsa_init)
+        || !RSA_meth_set_flags(method, RSA_FLAG_FIPS_METHOD)) {
+        RSA_meth_free(method);
+        method = NULL;
+        meth1 = NULL;
+    }
+
+    rsa_m = method;
+
+done:
+    return rsa_m;
+}
+
+void ibmca_rsa_destroy(void)
+{
+    RSA_meth_free(rsa_m);
+    rsa_m = NULL;
+}
+#endif
+
+#endif                          /* endif OPENSSL_NO_RSA */
diff -pruN 1.4.0-1/src/engine/openssl-compat.h 2.5.0-0ubuntu1/src/engine/openssl-compat.h
--- 1.4.0-1/src/engine/openssl-compat.h	1970-01-01 00:00:00.000000000 +0000
+++ 2.5.0-0ubuntu1/src/engine/openssl-compat.h	2025-04-16 08:09:24.000000000 +0000
@@ -0,0 +1,151 @@
+/*
+ * Copyright 2022 International Business Machines Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef IBMCA_OPENSSL_COMPAT_H
+#define IBMCA_OPENSSL_COMPAT_H
+
+#include <openssl/opensslv.h>
+
+typedef enum {
+    ECX_KEY_TYPE_X25519,
+    ECX_KEY_TYPE_X448,
+    ECX_KEY_TYPE_ED25519,
+    ECX_KEY_TYPE_ED448
+} ECX_KEY_TYPE;
+
+#ifdef OPENSSL_VERSION_PREREQ
+/* This is 3.x */
+
+#  define X25519_KEYLEN         32
+#  define X448_KEYLEN           56
+#  define ED25519_KEYLEN        32
+#  define ED448_KEYLEN          57
+
+#  define MAX_KEYLEN  ED448_KEYLEN
+
+typedef int CRYPTO_REF_COUNT;
+
+typedef struct ecx_key_st {
+    OSSL_LIB_CTX *libctx;
+    char *propq;
+    unsigned int haspubkey:1;
+    unsigned char pubkey[MAX_KEYLEN];
+    unsigned char *privkey;
+    size_t keylen;
+    ECX_KEY_TYPE type;
+    CRYPTO_REF_COUNT references;
+    CRYPTO_RWLOCK *lock;
+} ECX_KEY;
+
+static inline ECX_KEY *ossl_ecx_key_new_simple(ECX_KEY_TYPE type)
+{
+    ECX_KEY *ret = OPENSSL_zalloc(sizeof(*ret));
+
+    if (ret == NULL)
+        return NULL;
+
+    ret->libctx = NULL;
+    ret->haspubkey = 0;
+    switch (type) {
+    case ECX_KEY_TYPE_X25519:
+        ret->keylen = X25519_KEYLEN;
+        break;
+    case ECX_KEY_TYPE_X448:
+        ret->keylen = X448_KEYLEN;
+        break;
+    case ECX_KEY_TYPE_ED25519:
+        ret->keylen = ED25519_KEYLEN;
+        break;
+    case ECX_KEY_TYPE_ED448:
+        ret->keylen = ED448_KEYLEN;
+        break;
+    }
+    ret->type = type;
+    ret->references = 1;
+
+    ret->lock = CRYPTO_THREAD_lock_new();
+    if (ret->lock == NULL)
+        goto err;
+    return ret;
+err:
+    OPENSSL_free(ret);
+    return NULL;
+}
+
+static inline void ossl_ecx_set0_privkey(ECX_KEY *key, unsigned char *privkey)
+{
+    key->privkey = privkey;
+}
+
+static inline unsigned char *ossl_ecx_get0_privkey(ECX_KEY *key)
+{
+    return key->privkey;
+}
+
+static inline unsigned char *ossl_ecx_get0_pubkey(ECX_KEY *key)
+{
+    return key->pubkey;
+}
+
+static inline void ossl_ecx_copypubkey(ECX_KEY *key, unsigned char *pubkey, size_t len)
+{
+    memcpy(key->pubkey, pubkey, len);
+    key->haspubkey = 1;
+}
+
+#else
+/* This is 1.1.x */
+
+#include <openssl/evp.h>
+
+/*
+ * copied from evp_int.h:
+ * missing set/get methods for opaque types.
+ */
+
+typedef struct {
+    unsigned char pub[57];
+    unsigned char *priv;
+} ECX_KEY;
+
+static inline ECX_KEY *ossl_ecx_key_new_simple(ECX_KEY_TYPE type)
+{
+    return calloc(1, sizeof(ECX_KEY));
+}
+
+static inline void ossl_ecx_set0_privkey(ECX_KEY *key, unsigned char *privkey)
+{
+    key->priv = privkey;
+}
+
+static inline unsigned char *ossl_ecx_get0_privkey(ECX_KEY *key)
+{
+    return key->priv;
+}
+
+static inline unsigned char *ossl_ecx_get0_pubkey(ECX_KEY *key)
+{
+    return key->pub;
+}
+
+static inline void ossl_ecx_copypubkey(ECX_KEY *key, unsigned char *pubkey, size_t len)
+{
+    memcpy(key->pub, pubkey, len);
+}
+
+#endif
+
+#endif
diff -pruN 1.4.0-1/src/engine/openssl.cnf.sample 2.5.0-0ubuntu1/src/engine/openssl.cnf.sample
--- 1.4.0-1/src/engine/openssl.cnf.sample	1970-01-01 00:00:00.000000000 +0000
+++ 2.5.0-0ubuntu1/src/engine/openssl.cnf.sample	2025-04-16 08:09:24.000000000 +0000
@@ -0,0 +1,77 @@
+#
+# OpenSSL example configuration file. This file will load the IBMCA engine
+# for all operations that the IBMCA engine implements for all apps that
+# have OpenSSL config support compiled into them.
+#
+# Adding OpenSSL config support is as simple as adding the following line to
+# the app:
+#
+# #define OPENSSL_LOAD_CONF	1
+#
+openssl_conf = openssl_def
+
+[openssl_def]
+engines = engine_section
+
+[engine_section]
+ibmca = ibmca_section
+
+[ibmca_section]
+# The openssl engine path for ibmca.so.
+# Set the dynamic_path to where the ibmca.so engine
+# resides on the system.
+dynamic_path = /usr/local/lib/ibmca.so
+engine_id = ibmca
+init = 1
+
+#
+# The following ibmca algorithms will be enabled by these parameters
+# to the default_algorithms line. Any combination of these is valid,
+# with "ALL" denoting the same as all of them in a comma separated
+# list.
+#
+# Note: Algorithms denoted by CIPHERS, DIGESTS, EC (since IBM z15 for certain
+# curves), and PKEY are already accelerated by OpenSSL itself using CPACF.
+# Therefore, do not accelerate them using the IBMCA engine. This would actually
+# make them slower.
+#
+# Moreover, ibmca's CIPHER and DIGEST implementations do not
+# support the processing of messages in arbitrary chunk sizes.
+# All chunks, except the final one, are required to be a multiple
+# of the primitive's block size.
+#
+# RSA
+# - RSA encrypt, decrypt, sign and verify, key lengths 512-4096
+#
+# DH
+# - DH key exchange
+#
+# DSA
+# - DSA sign and verify
+#
+# RAND
+# - Hardware random number generation
+#
+# ECDSA (OpenSSL < 1.1.0)
+# - Elliptic Curve DSA sign and verify
+#
+# ECDH (OpenSSL < 1.1.0)
+# - Elliptic Curve DH key exchange
+#
+# EC (OpenSSL >= 1.1.0)
+# - Elliptic Curve DSA sign and verify, Elliptic Curve DH key exchange
+#
+# CIPHERS
+# - DES-ECB, DES-CBC, DES-CFB, DES-OFB,
+#   DES-EDE3, DES-EDE3-CBC, DES-EDE3-CFB, DES-EDE3-OFB,
+#   AES-128-ECB, AES-128-CBC, AES-128-CFB, AES-128-OFB, id-aes128-GCM,
+#   AES-192-ECB, AES-192-CBC, AES-192-CFB, AES-192-OFB, id-aes192-GCM,
+#   AES-256-ECB, AES-256-CBC, AES-256-CFB, AES-256-OFB, id-aes256-GCM ciphers
+#
+# DIGESTS
+# - SHA1, SHA256, SHA512 digests
+#
+# PKEY_CRYPTO
+# - X25519, X448, ED25519, ED448
+
+default_algorithms = RSA,DH,DSA,RAND
diff -pruN 1.4.0-1/src/engine/test/Makefile.linux.in 2.5.0-0ubuntu1/src/engine/test/Makefile.linux.in
--- 1.4.0-1/src/engine/test/Makefile.linux.in	1970-01-01 00:00:00.000000000 +0000
+++ 2.5.0-0ubuntu1/src/engine/test/Makefile.linux.in	2025-04-16 08:09:24.000000000 +0000
@@ -0,0 +1,14 @@
+PTS = -O0 -g -Wall -fprofile-arcs -ftest-coverage -fPIC
+#OPTS = -O0 -g -Wall -m31 -D_LINUX_S390_
+OPTS = -O0 -g -Wall -D_LINUX_S390_ -std=gnu99
+
+TARGETS = ibmca_mechaList_test
+
+all: $(TARGETS)
+
+# Every target is created from a single .c file.
+%: %.c
+	gcc $(OPTS) -o $@ $^ -l@ICA@ -lcrypto
+
+clean:
+	rm -f $(TARGETS)
diff -pruN 1.4.0-1/src/engine/test/ibmca_mechaList_test.c 2.5.0-0ubuntu1/src/engine/test/ibmca_mechaList_test.c
--- 1.4.0-1/src/engine/test/ibmca_mechaList_test.c	1970-01-01 00:00:00.000000000 +0000
+++ 2.5.0-0ubuntu1/src/engine/test/ibmca_mechaList_test.c	2025-04-16 08:09:24.000000000 +0000
@@ -0,0 +1,370 @@
+/*
+ * Copyright [2015-2018] International Business Machines Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <openssl/engine.h>
+#include <ica_api.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <getopt.h>
+#include <dirent.h>
+#include <string.h>
+#include <stdbool.h>
+
+#define CIPH 1
+#define DIG  2
+#define SET 1
+#define UNSET 0
+
+
+typedef struct {
+    int nid;
+    int ica_id;
+    int dig_ciph;
+} id_map;
+
+#define AP_PATH  "/sys/devices/ap"
+#define IBMCA_PATH "/usr/lib64/openssl/engines/ibmca.so"
+
+
+id_map ica_to_ssl_map[] = {
+#ifndef OPENSSL_NO_SHA1
+    {NID_sha1, SHA1, DIG},
+#endif
+#ifndef OPENSSL_NO_SHA256
+    {NID_sha256, SHA256, DIG},
+#endif
+#ifndef OPENSSL_NO_SHA512
+    {NID_sha512, SHA512, DIG},
+#endif
+    {NID_des_ecb, DES_ECB, CIPH},
+    {NID_des_cbc, DES_CBC, CIPH},
+    {NID_des_ofb64, DES_OFB, CIPH},
+    {NID_des_cfb64, DES_CFB, CIPH},
+    {NID_des_ede3_ecb, DES3_ECB, CIPH},
+    {NID_des_ede3_cbc, DES3_CBC, CIPH},
+    {NID_des_ede3_ofb64, DES3_OFB, CIPH},
+    {NID_des_ede3_cfb64, DES3_CFB, CIPH},
+    {NID_aes_128_ecb, AES_ECB, CIPH},
+    {NID_aes_192_ecb, AES_ECB, CIPH},
+    {NID_aes_256_ecb, AES_ECB, CIPH},
+    {NID_aes_128_cbc, AES_CBC, CIPH},
+    {NID_aes_192_cbc, AES_CBC, CIPH},
+    {NID_aes_256_cbc, AES_CBC, CIPH},
+    {NID_aes_128_ofb128, AES_OFB, CIPH},
+    {NID_aes_192_ofb128, AES_OFB, CIPH},
+    {NID_aes_256_ofb128, AES_OFB, CIPH},
+    {NID_aes_128_cfb128, AES_CFB, CIPH},
+    {NID_aes_192_cfb128, AES_CFB, CIPH},
+    {NID_aes_256_cfb128, AES_CFB, CIPH},
+    {0, 0, 0}
+};
+
+ENGINE *eng;
+int failure = 0;
+
+int init_engine(char *id)
+{
+    ENGINE_load_builtin_engines();
+    eng = ENGINE_by_id("dynamic");
+    if (!eng) {
+        return 1;
+    }
+    if (!ENGINE_ctrl_cmd_string(eng, "SO_PATH", id, 0)) {
+        return 1;
+    }
+    if (!ENGINE_ctrl_cmd_string(eng, "LOAD", NULL, 0)) {
+        return 1;
+    }
+    if (!ENGINE_init(eng)) {
+        return 1;
+    }
+    if (!ENGINE_set_default_RSA(eng))
+        return 1;
+
+    ENGINE_set_default_DSA(eng);
+    ENGINE_set_default_RAND(eng);
+    ENGINE_set_default_DH(eng);
+    ENGINE_set_default_ciphers(eng);
+    ENGINE_set_default_digests(eng);
+
+    return 0;
+}
+
+void exit_engine()
+{
+    /* Release the functional reference from ENGINE_init() */
+    ENGINE_finish(eng);
+    /* Release the structural reference from ENGINE_by_id() */
+    ENGINE_free(eng);
+}
+
+void nid_failure(int nid, int set)
+{
+    failure++;
+    if (set == SET) {
+        fprintf(stderr, "ERROR: NID %d not set in Engine!\n", nid);
+    } else if (set == UNSET) {
+        fprintf(stderr, "ERROR: NID %d set despite missing hardware support!",
+                nid);
+    }
+}
+
+int is_crypto_card_loaded()
+{
+    DIR *sysDir;
+    FILE *file;
+    char dev[PATH_MAX] = AP_PATH;
+    struct dirent *direntp;
+    char *type = NULL;
+    size_t size;
+    char c;
+
+    if ((sysDir = opendir(dev)) == NULL)
+        return 0;
+
+    while ((direntp = readdir(sysDir)) != NULL) {
+        if (strstr(direntp->d_name, "card") != 0) {
+            snprintf(dev, PATH_MAX, "%s/%s/type", AP_PATH, direntp->d_name);
+
+            if ((file = fopen(dev, "r")) == NULL) {
+                closedir(sysDir);
+                return 0;
+            }
+
+            if (getline(&type, &size, file) == -1) {
+                fclose(file);
+                closedir(sysDir);
+                return 0;
+            }
+
+            /* ignore \n
+             * looking for CEX??A and CEX??C
+             * Skip type CEX??P cards
+             */
+            if (type[strlen(type) - 2] == 'P') {
+                free(type);
+                type = NULL;
+                fclose(file);
+                continue;
+            }
+            free(type);
+            type = NULL;
+            fclose(file);
+
+            snprintf(dev, PATH_MAX, "%s/%s/online", AP_PATH, direntp->d_name);
+
+            if ((file = fopen(dev, "r")) == NULL) {
+                closedir(sysDir);
+                return 0;
+            }
+            if ((c = fgetc(file)) == '1') {
+                fclose(file);
+                return 1;
+            }
+            fclose(file);
+        }
+    }
+    closedir(sysDir);
+
+    return 0;
+}
+
+void check_mech(int i, int j, libica_func_list_element * pmech_list)
+{
+    if (!(pmech_list[j].flags & (ICA_FLAG_SHW))) {
+        if (ica_to_ssl_map[i].dig_ciph == CIPH) {
+            if (ENGINE_get_cipher_engine(ica_to_ssl_map[i].nid)) {
+                nid_failure(ica_to_ssl_map[i].nid, UNSET);
+            } else {
+                printf("NID %d not found! SUCCESS\n", ica_to_ssl_map[i].nid);
+            }
+        } else {
+            if (ENGINE_get_digest_engine(ica_to_ssl_map[i].nid)) {
+                nid_failure(ica_to_ssl_map[i].nid, UNSET);
+            } else {
+                printf("NID %d not found! SUCCESS\n", ica_to_ssl_map[i].nid);
+            }
+        }
+    } else {
+        if (ica_to_ssl_map[i].dig_ciph == CIPH) {
+            if (!ENGINE_get_cipher_engine(ica_to_ssl_map[i].nid)) {
+                nid_failure(ica_to_ssl_map[i].nid, SET);
+            } else {
+                printf("NID %d found! SUCCESS %d\n", ica_to_ssl_map[i].nid, j);
+            }
+        } else {
+            if (!ENGINE_get_digest_engine(ica_to_ssl_map[i].nid)) {
+                nid_failure(ica_to_ssl_map[i].nid, SET);
+            } else {
+                printf("NID %d found! SUCCESS %d\n", ica_to_ssl_map[i].nid, j);
+            }
+        }
+    }
+}
+
+int main(int argc, char *argv[])
+{
+    int i, j, opt, option_index = 0;
+    int card_loaded;
+    unsigned int mech_len;
+    bool found = false;
+    libica_func_list_element *pmech_list = NULL;
+    char *engine_id = IBMCA_PATH;
+    struct option long_options[] = {
+        {"help", no_argument, 0, 'h'},
+        {"file", required_argument, 0, 'f'},
+        {0, 0, 0, 0}
+    };
+
+    while ((opt = getopt_long(argc, argv, "hf:",
+                              long_options, &option_index)) != -1) {
+        switch (opt) {
+        case 'f':
+            engine_id = optarg;
+            break;
+        case 'h':
+            printf("This test checks with the engine API of libcrypto if a\n"
+                   "crypto mechanism is enabled or not.\n"
+                   "If one mechanism is not found the NID is returned.\n"
+                   "The NID can be mapped to a name in the file \n"
+                   "/usr/include/openssl/obj_mac.h\n");
+
+            printf("Usage: %s [-f | --file ibmca.so] [-h | --help]\n", argv[0]);
+            exit(EXIT_SUCCESS);
+        default:               /* '?' */
+            fprintf(stderr, "Usage: %s [-t nsecs] [-n] name\n", argv[0]);
+            exit(EXIT_FAILURE);
+        }
+    }
+
+    printf("This test checks with the engine API of libcrypto if a\n"
+           "crypto mechanism is enabled or not.\n"
+           "If one mechanism is not found the NID is returned.\n"
+           "The NID can be mapped to a name in the file \n"
+           "/usr/include/openssl/obj_mac.h\n");
+    printf("IBMCA path: %s\n", engine_id);
+    printf("------------------------------------------------------------\n\n");
+
+    if (init_engine(engine_id)) {
+        fprintf(stderr, "Could not initialize Ibmca engine\n");
+        return EXIT_FAILURE;
+    }
+    if (ica_get_functionlist(NULL, &mech_len) != 0) {
+        perror("get_functionlist");
+        return EXIT_FAILURE;
+    }
+    pmech_list = malloc(sizeof(libica_func_list_element) * mech_len);
+    if (ica_get_functionlist(pmech_list, &mech_len) != 0) {
+        perror("get_functionlist");
+        free(pmech_list);
+        return EXIT_FAILURE;
+    }
+
+    card_loaded = is_crypto_card_loaded();
+    for (i = 0; ica_to_ssl_map[i].nid; i++) {
+        for (j = 0; j < mech_len; j++) {
+
+            if (ica_to_ssl_map[i].ica_id == pmech_list[j].mech_mode_id) {
+                found = true;
+                check_mech(i, j, pmech_list);
+                break;
+            }
+        }
+        assert(found);
+        found = false;
+
+    }
+
+    for (i = 0; i < mech_len; i++) {
+        if (pmech_list[i].mech_mode_id == P_RNG) {
+            if (pmech_list[i].flags & (ICA_FLAG_SHW)) {
+                if (!ENGINE_get_default_RAND()) {
+                    failure++;
+                    fprintf(stderr,
+                            "ERROR: Engine has no enabled PRNG support!\n");
+                } else {
+                    printf("PRNG Support found! SUCCESS\n");
+                }
+            } else {
+                if (ENGINE_get_default_RAND()) {
+                    failure++;
+                    fprintf(stderr, "ERROR: Engine has enabled PRNG support"
+                            ", despite no hardware support!\n");
+                } else {
+                    printf("PRNG Support not found! SUCCESS\n");
+                }
+            }
+
+        }
+        if (pmech_list[i].mech_mode_id == RSA_ME) {
+            if (card_loaded) {
+                if (!ENGINE_get_default_RSA()) {
+                    failure++;
+                    fprintf(stderr,
+                            "ERROR: Engine has no enabeled RSA support!\n");
+                } else {
+                    printf("RSA Support found! SUCCESS\n");
+                }
+                if (!ENGINE_get_default_DSA()) {
+                    failure++;
+                    fprintf(stderr,
+                            "ERROR: Engine has no enabled DSA support!\n");
+                } else {
+                    printf("DSA Support found! SUCCESS\n");
+                }
+                if (!ENGINE_get_default_DH()) {
+                    failure++;
+                    fprintf(stderr,
+                            "ERROR: Engine has no enabled DH support!\n");
+                } else {
+                    printf("DH Support found! SUCCESS\n");
+                }
+
+            } else {
+                if (ENGINE_get_default_RSA()) {
+                    failure++;
+                    fprintf(stderr, "ERROR: Engine has enabled RSA support,"
+                            "despite no hardware support!\n");
+                } else {
+                    printf("RSA Support not found! SUCCESS\n");
+                }
+                if (ENGINE_get_default_DSA()) {
+                    failure++;
+                    fprintf(stderr, "ERROR: Engine has no enabled DSA support,"
+                            "despite no hardware support!\n");
+                } else {
+                    printf("DSA Support not found! SUCCESS\n");
+                }
+                if (ENGINE_get_default_DH()) {
+                    failure++;
+                    fprintf(stderr, "ERROR: Engine has no enabled DH support,"
+                            "despite no hardware support!\n");
+                } else {
+                    printf("DH Support not found! SUCCESS\n");
+                }
+            }
+        }
+    }
+
+    printf("\n\n-----------------------------------------------------------\n");
+    printf("TEST Summary:\n" "Failure Counter:  %d\n", failure);
+    if (failure)
+        return EXIT_FAILURE;
+
+    return EXIT_SUCCESS;
+}
diff -pruN 1.4.0-1/src/openssl.cnf.sample 2.5.0-0ubuntu1/src/openssl.cnf.sample
--- 1.4.0-1/src/openssl.cnf.sample	2017-09-08 17:54:06.000000000 +0000
+++ 2.5.0-0ubuntu1/src/openssl.cnf.sample	1970-01-01 00:00:00.000000000 +0000
@@ -1,52 +0,0 @@
-#
-# OpenSSL example configuration file. This file will load the IBMCA engine
-# for all operations that the IBMCA engine implements for all apps that
-# have OpenSSL config support compiled into them.
-#
-# Adding OpenSSL config support is as simple as adding the following line to
-# the app:
-#
-# #define OPENSSL_LOAD_CONF	1
-#
-openssl_conf = openssl_def
-
-[openssl_def]
-engines = engine_section
-
-
-[engine_section]
-ibmca = ibmca_section
-
-
-[ibmca_section]
-
-# The openssl engine path for libibmca.so.
-# Set the dynamic_path to where the libibmca.so engine
-# resides on the system.
-dynamic_path = /usr/local/lib/libibmca.so
-engine_id = ibmca
-init = 1
-
-#
-# The following ibmca algorithms will be enabled by these parameters
-# to the default_algorithms line. Any combination of these is valid,
-# with "ALL" denoting the same as all of them in a comma separated
-# list.
-#
-# RSA
-# - RSA encrypt, decrypt, sign and verify, key lengths 512-4096
-#
-# RAND
-# - Hardware random number generation
-#
-# CIPHERS
-# - DES-ECB, DES-CBC, DES-CFB, DES-OFB, DES-EDE3, DES-EDE3-CBC, DES-EDE3-CFB,
-#   DES-EDE3-OFB, AES-128-ECB, AES-128-CBC, AES-128-CFB, AES-128-OFB,
-#   AES-192-ECB, AES-192-CBC, AES-192-CFB, AES-192-OFB, AES-256-ECB,
-#   AES-256-CBC, AES-256-CFB, AES-256-OFB symmetric crypto
-#
-# DIGESTS
-# - SHA1, SHA256, SHA512 digests
-#
-default_algorithms = ALL
-#default_algorithms = RAND,RSA,CIPHERS,DIGESTS
diff -pruN 1.4.0-1/src/provider/Makefile.am 2.5.0-0ubuntu1/src/provider/Makefile.am
--- 1.4.0-1/src/provider/Makefile.am	1970-01-01 00:00:00.000000000 +0000
+++ 2.5.0-0ubuntu1/src/provider/Makefile.am	2025-04-16 08:09:24.000000000 +0000
@@ -0,0 +1,30 @@
+VERSION = 2:5:0
+VERSION_STR = 2.5.0
+
+lib_LTLIBRARIES=ibmca-provider.la
+
+ibmca_provider_la_SOURCES=p_ibmca.c p_key.c p_context.c \
+			rsa_keymgmt.c rsa_asym_cipher.c rsa_padding.c \
+			rsa_signature.c ec_keymgmt.c ec_signature.c ec_keyexch.c \
+			dh_keymgmt.c dh_keyexch.c rsa_blinding.c rsa_sup_mul.c
+
+dist_ibmca_provider_la_SOURCES=p_ibmca.h constant_time.h
+
+ibmca_provider_la_CFLAGS=-DIBMCA_VERSION=\"${VERSION_STR}\" -DIBMCA_LOGDIR=\"$(logdir)\"
+
+if PROVIDER_FULL_LIBICA
+ibmca_provider_la_LIBADD=-ldl -lica
+else
+ibmca_provider_la_LIBADD=-ldl -lica-cex
+endif
+ibmca_provider_la_LDFLAGS=-module -version-number ${VERSION} -shared -no-undefined \
+		  -avoid-version -Wl,--version-script=${srcdir}/ibmca-provider.map
+
+EXTRA_DIST = openssl.cnf.provider.sample ibmca-provider-opensslconfig \
+	ibmca-provider.map
+
+ACLOCAL_AMFLAGS = -I m4
+
+SUBDIRS = doc
+
+noinst_SCRIPTS = ibmca-provider-opensslconfig
diff -pruN 1.4.0-1/src/provider/constant_time.h 2.5.0-0ubuntu1/src/provider/constant_time.h
--- 1.4.0-1/src/provider/constant_time.h	1970-01-01 00:00:00.000000000 +0000
+++ 2.5.0-0ubuntu1/src/provider/constant_time.h	2025-04-16 08:09:24.000000000 +0000
@@ -0,0 +1,425 @@
+/*
+ * Copyright 2014-2021 The OpenSSL Project Authors. All Rights Reserved.
+ * Copyright 2023 International Business Machines Corp.
+ *
+ * Licensed under the Apache License 2.0 (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ *
+ * Copied from OpenSSL include/internal/constant_time.h
+ * The only changes are to add this notice and the copyright statement above.
+ */
+
+#ifndef OSSL_INTERNAL_CONSTANT_TIME_H
+# define OSSL_INTERNAL_CONSTANT_TIME_H
+# pragma once
+
+# include <stdlib.h>
+# include <string.h>
+# include <openssl/e_os2.h>              /* For 'ossl_inline' */
+
+/*-
+ * The boolean methods return a bitmask of all ones (0xff...f) for true
+ * and 0 for false. This is useful for choosing a value based on the result
+ * of a conditional in constant time. For example,
+ *      if (a < b) {
+ *        c = a;
+ *      } else {
+ *        c = b;
+ *      }
+ * can be written as
+ *      unsigned int lt = constant_time_lt(a, b);
+ *      c = constant_time_select(lt, a, b);
+ */
+
+/* Returns the given value with the MSB copied to all the other bits. */
+static ossl_inline unsigned int constant_time_msb(unsigned int a);
+/* Convenience method for uint32_t. */
+static ossl_inline uint32_t constant_time_msb_32(uint32_t a);
+/* Convenience method for uint64_t. */
+static ossl_inline uint64_t constant_time_msb_64(uint64_t a);
+
+/* Returns 0xff..f if a < b and 0 otherwise. */
+static ossl_inline unsigned int constant_time_lt(unsigned int a,
+                                                 unsigned int b);
+/* Convenience method for getting an 8-bit mask. */
+static ossl_inline unsigned char constant_time_lt_8(unsigned int a,
+                                                    unsigned int b);
+/* Convenience method for uint64_t. */
+static ossl_inline uint64_t constant_time_lt_64(uint64_t a, uint64_t b);
+
+/* Returns 0xff..f if a >= b and 0 otherwise. */
+static ossl_inline unsigned int constant_time_ge(unsigned int a,
+                                                 unsigned int b);
+/* Convenience method for getting an 8-bit mask. */
+static ossl_inline unsigned char constant_time_ge_8(unsigned int a,
+                                                    unsigned int b);
+
+/* Returns 0xff..f if a == 0 and 0 otherwise. */
+static ossl_inline unsigned int constant_time_is_zero(unsigned int a);
+/* Convenience method for getting an 8-bit mask. */
+static ossl_inline unsigned char constant_time_is_zero_8(unsigned int a);
+/* Convenience method for getting a 32-bit mask. */
+static ossl_inline uint32_t constant_time_is_zero_32(uint32_t a);
+
+/* Returns 0xff..f if a == b and 0 otherwise. */
+static ossl_inline unsigned int constant_time_eq(unsigned int a,
+                                                 unsigned int b);
+/* Convenience method for getting an 8-bit mask. */
+static ossl_inline unsigned char constant_time_eq_8(unsigned int a,
+                                                    unsigned int b);
+/* Signed integers. */
+static ossl_inline unsigned int constant_time_eq_int(int a, int b);
+/* Convenience method for getting an 8-bit mask. */
+static ossl_inline unsigned char constant_time_eq_int_8(int a, int b);
+
+/*-
+ * Returns (mask & a) | (~mask & b).
+ *
+ * When |mask| is all 1s or all 0s (as returned by the methods above),
+ * the select methods return either |a| (if |mask| is nonzero) or |b|
+ * (if |mask| is zero).
+ */
+static ossl_inline unsigned int constant_time_select(unsigned int mask,
+                                                     unsigned int a,
+                                                     unsigned int b);
+/* Convenience method for unsigned chars. */
+static ossl_inline unsigned char constant_time_select_8(unsigned char mask,
+                                                        unsigned char a,
+                                                        unsigned char b);
+
+/* Convenience method for uint32_t. */
+static ossl_inline uint32_t constant_time_select_32(uint32_t mask, uint32_t a,
+                                                    uint32_t b);
+
+/* Convenience method for uint64_t. */
+static ossl_inline uint64_t constant_time_select_64(uint64_t mask, uint64_t a,
+                                                    uint64_t b);
+/* Convenience method for signed integers. */
+static ossl_inline int constant_time_select_int(unsigned int mask, int a,
+                                                int b);
+
+
+static ossl_inline unsigned int constant_time_msb(unsigned int a)
+{
+    return 0 - (a >> (sizeof(a) * 8 - 1));
+}
+
+
+static ossl_inline uint32_t constant_time_msb_32(uint32_t a)
+{
+    return 0 - (a >> 31);
+}
+
+static ossl_inline uint64_t constant_time_msb_64(uint64_t a)
+{
+    return 0 - (a >> 63);
+}
+
+static ossl_inline size_t constant_time_msb_s(size_t a)
+{
+    return 0 - (a >> (sizeof(a) * 8 - 1));
+}
+
+static ossl_inline unsigned int constant_time_lt(unsigned int a,
+                                                 unsigned int b)
+{
+    return constant_time_msb(a ^ ((a ^ b) | ((a - b) ^ b)));
+}
+
+static ossl_inline size_t constant_time_lt_s(size_t a, size_t b)
+{
+    return constant_time_msb_s(a ^ ((a ^ b) | ((a - b) ^ b)));
+}
+
+static ossl_inline unsigned char constant_time_lt_8(unsigned int a,
+                                                    unsigned int b)
+{
+    return (unsigned char)constant_time_lt(a, b);
+}
+
+static ossl_inline uint64_t constant_time_lt_64(uint64_t a, uint64_t b)
+{
+    return constant_time_msb_64(a ^ ((a ^ b) | ((a - b) ^ b)));
+}
+
+static ossl_inline unsigned int constant_time_ge(unsigned int a,
+                                                 unsigned int b)
+{
+    return ~constant_time_lt(a, b);
+}
+
+static ossl_inline size_t constant_time_ge_s(size_t a, size_t b)
+{
+    return ~constant_time_lt_s(a, b);
+}
+
+static ossl_inline unsigned char constant_time_ge_8(unsigned int a,
+                                                    unsigned int b)
+{
+    return (unsigned char)constant_time_ge(a, b);
+}
+
+static ossl_inline unsigned char constant_time_ge_8_s(size_t a, size_t b)
+{
+    return (unsigned char)constant_time_ge_s(a, b);
+}
+
+static ossl_inline unsigned int constant_time_is_zero(unsigned int a)
+{
+    return constant_time_msb(~a & (a - 1));
+}
+
+static ossl_inline size_t constant_time_is_zero_s(size_t a)
+{
+    return constant_time_msb_s(~a & (a - 1));
+}
+
+static ossl_inline unsigned char constant_time_is_zero_8(unsigned int a)
+{
+    return (unsigned char)constant_time_is_zero(a);
+}
+
+static ossl_inline uint32_t constant_time_is_zero_32(uint32_t a)
+{
+    return constant_time_msb_32(~a & (a - 1));
+}
+
+static ossl_inline uint64_t constant_time_is_zero_64(uint64_t a)
+{
+    return constant_time_msb_64(~a & (a - 1));
+}
+
+static ossl_inline unsigned int constant_time_eq(unsigned int a,
+                                                 unsigned int b)
+{
+    return constant_time_is_zero(a ^ b);
+}
+
+static ossl_inline size_t constant_time_eq_s(size_t a, size_t b)
+{
+    return constant_time_is_zero_s(a ^ b);
+}
+
+static ossl_inline unsigned char constant_time_eq_8(unsigned int a,
+                                                    unsigned int b)
+{
+    return (unsigned char)constant_time_eq(a, b);
+}
+
+static ossl_inline unsigned char constant_time_eq_8_s(size_t a, size_t b)
+{
+    return (unsigned char)constant_time_eq_s(a, b);
+}
+
+static ossl_inline unsigned int constant_time_eq_int(int a, int b)
+{
+    return constant_time_eq((unsigned)(a), (unsigned)(b));
+}
+
+static ossl_inline unsigned char constant_time_eq_int_8(int a, int b)
+{
+    return constant_time_eq_8((unsigned)(a), (unsigned)(b));
+}
+
+/*
+ * Returns the value unmodified, but avoids optimizations.
+ * The barriers prevent the compiler from narrowing down the
+ * possible value range of the mask and ~mask in the select
+ * statements, which avoids the recognition of the select
+ * and turning it into a conditional load or branch.
+ */
+static ossl_inline unsigned int value_barrier(unsigned int a)
+{
+#if !defined(OPENSSL_NO_ASM) && defined(__GNUC__)
+    unsigned int r;
+    __asm__("" : "=r"(r) : "0"(a));
+#else
+    volatile unsigned int r = a;
+#endif
+    return r;
+}
+
+/* Convenience method for uint32_t. */
+static ossl_inline uint32_t value_barrier_32(uint32_t a)
+{
+#if !defined(OPENSSL_NO_ASM) && defined(__GNUC__)
+    uint32_t r;
+    __asm__("" : "=r"(r) : "0"(a));
+#else
+    volatile uint32_t r = a;
+#endif
+    return r;
+}
+
+/* Convenience method for uint64_t. */
+static ossl_inline uint64_t value_barrier_64(uint64_t a)
+{
+#if !defined(OPENSSL_NO_ASM) && defined(__GNUC__)
+    uint64_t r;
+    __asm__("" : "=r"(r) : "0"(a));
+#else
+    volatile uint64_t r = a;
+#endif
+    return r;
+}
+
+/* Convenience method for size_t. */
+static ossl_inline size_t value_barrier_s(size_t a)
+{
+#if !defined(OPENSSL_NO_ASM) && defined(__GNUC__)
+    size_t r;
+    __asm__("" : "=r"(r) : "0"(a));
+#else
+    volatile size_t r = a;
+#endif
+    return r;
+}
+
+static ossl_inline unsigned int constant_time_select(unsigned int mask,
+                                                     unsigned int a,
+                                                     unsigned int b)
+{
+    return (value_barrier(mask) & a) | (value_barrier(~mask) & b);
+}
+
+static ossl_inline size_t constant_time_select_s(size_t mask,
+                                                 size_t a,
+                                                 size_t b)
+{
+    return (value_barrier_s(mask) & a) | (value_barrier_s(~mask) & b);
+}
+
+static ossl_inline unsigned char constant_time_select_8(unsigned char mask,
+                                                        unsigned char a,
+                                                        unsigned char b)
+{
+    return (unsigned char)constant_time_select(mask, a, b);
+}
+
+static ossl_inline int constant_time_select_int(unsigned int mask, int a,
+                                                int b)
+{
+    return (int)constant_time_select(mask, (unsigned)(a), (unsigned)(b));
+}
+
+static ossl_inline int constant_time_select_int_s(size_t mask, int a, int b)
+{
+    return (int)constant_time_select((unsigned)mask, (unsigned)(a),
+                                      (unsigned)(b));
+}
+
+static ossl_inline uint32_t constant_time_select_32(uint32_t mask, uint32_t a,
+                                                    uint32_t b)
+{
+    return (value_barrier_32(mask) & a) | (value_barrier_32(~mask) & b);
+}
+
+static ossl_inline uint64_t constant_time_select_64(uint64_t mask, uint64_t a,
+                                                    uint64_t b)
+{
+    return (value_barrier_64(mask) & a) | (value_barrier_64(~mask) & b);
+}
+
+/*
+ * mask must be 0xFFFFFFFF or 0x00000000.
+ *
+ * if (mask) {
+ *     uint32_t tmp = *a;
+ *
+ *     *a = *b;
+ *     *b = tmp;
+ * }
+ */
+static ossl_inline void constant_time_cond_swap_32(uint32_t mask, uint32_t *a,
+                                                   uint32_t *b)
+{
+    uint32_t xor = *a ^ *b;
+
+    xor &= mask;
+    *a ^= xor;
+    *b ^= xor;
+}
+
+/*
+ * mask must be 0xFFFFFFFF or 0x00000000.
+ *
+ * if (mask) {
+ *     uint64_t tmp = *a;
+ *
+ *     *a = *b;
+ *     *b = tmp;
+ * }
+ */
+static ossl_inline void constant_time_cond_swap_64(uint64_t mask, uint64_t *a,
+                                                   uint64_t *b)
+{
+    uint64_t xor = *a ^ *b;
+
+    xor &= mask;
+    *a ^= xor;
+    *b ^= xor;
+}
+
+/*
+ * mask must be 0xFF or 0x00.
+ * "constant time" is per len.
+ *
+ * if (mask) {
+ *     unsigned char tmp[len];
+ *
+ *     memcpy(tmp, a, len);
+ *     memcpy(a, b);
+ *     memcpy(b, tmp);
+ * }
+ */
+static ossl_inline void constant_time_cond_swap_buff(unsigned char mask,
+                                                     unsigned char *a,
+                                                     unsigned char *b,
+                                                     size_t len)
+{
+    size_t i;
+    unsigned char tmp;
+
+    for (i = 0; i < len; i++) {
+        tmp = a[i] ^ b[i];
+        tmp &= mask;
+        a[i] ^= tmp;
+        b[i] ^= tmp;
+    }
+}
+
+/*
+ * table is a two dimensional array of bytes. Each row has rowsize elements.
+ * Copies row number idx into out. rowsize and numrows are not considered
+ * private.
+ */
+static ossl_inline void constant_time_lookup(void *out,
+                                             const void *table,
+                                             size_t rowsize,
+                                             size_t numrows,
+                                             size_t idx)
+{
+    size_t i, j;
+    const unsigned char *tablec = (const unsigned char *)table;
+    unsigned char *outc = (unsigned char *)out;
+    unsigned char mask;
+
+    memset(out, 0, rowsize);
+
+    /* Note idx may underflow - but that is well defined */
+    for (i = 0; i < numrows; i++, idx--) {
+        mask = (unsigned char)constant_time_is_zero_s(idx);
+        for (j = 0; j < rowsize; j++)
+            *(outc + j) |= constant_time_select_8(mask, *(tablec++), 0);
+    }
+}
+
+/*
+ * Expected usage pattern is to unconditionally set error and then
+ * wipe it if there was no actual error. |clear| is 1 or 0.
+ */
+void err_clear_last_constant_time(int clear);
+
+#endif                          /* OSSL_INTERNAL_CONSTANT_TIME_H */
diff -pruN 1.4.0-1/src/provider/dh_keyexch.c 2.5.0-0ubuntu1/src/provider/dh_keyexch.c
--- 1.4.0-1/src/provider/dh_keyexch.c	1970-01-01 00:00:00.000000000 +0000
+++ 2.5.0-0ubuntu1/src/provider/dh_keyexch.c	2025-04-16 08:09:24.000000000 +0000
@@ -0,0 +1,816 @@
+/*
+ * Copyright [2021-2022] International Business Machines Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <stddef.h>
+#include <err.h>
+#include <strings.h>
+#include <string.h>
+
+#include <openssl/evp.h>
+#include <openssl/dh.h>
+#include <openssl/kdf.h>
+#include <openssl/core.h>
+#include <openssl/core_dispatch.h>
+#include <openssl/core_names.h>
+#include <openssl/params.h>
+#include <openssl/prov_ssl.h>
+
+#include "p_ibmca.h"
+
+static OSSL_FUNC_keyexch_newctx_fn ibmca_keyexch_dh_newctx;
+static OSSL_FUNC_keyexch_init_fn ibmca_keyexch_dh_init;
+static OSSL_FUNC_keyexch_set_peer_fn ibmca_keyexch_dh_set_peer;
+static OSSL_FUNC_keyexch_derive_fn ibmca_keyexch_dh_derive;
+static OSSL_FUNC_keyexch_set_ctx_params_fn ibmca_keyexch_dh_set_ctx_params;
+static OSSL_FUNC_keyexch_settable_ctx_params_fn
+                                        ibmca_keyexch_dh_settable_ctx_params;
+static OSSL_FUNC_keyexch_get_ctx_params_fn ibmca_keyexch_dh_get_ctx_params;
+static OSSL_FUNC_keyexch_gettable_ctx_params_fn
+                                       ibmca_keyexch_dh_gettable_ctx_params;
+
+static void ibmca_keyexch_dh_free_cb(struct ibmca_op_ctx *ctx);
+static int ibmca_keyexch_dh_dup_cb(const struct ibmca_op_ctx *ctx,
+                                   struct ibmca_op_ctx *new_ctx);
+static int ibmca_keyexch_dh_set_ctx_params(void *vctx,
+                                           const OSSL_PARAM params[]);
+
+static void *ibmca_keyexch_dh_newctx(void *vprovctx)
+{
+    const struct ibmca_prov_ctx *provctx = vprovctx;
+    struct ibmca_op_ctx *opctx;
+
+    if (provctx == NULL)
+        return NULL;
+
+    ibmca_debug_ctx(provctx, "provctx: %p", provctx);
+
+    opctx = ibmca_op_newctx(provctx, NULL, EVP_PKEY_DH,
+                            ibmca_keyexch_dh_free_cb,
+                            ibmca_keyexch_dh_dup_cb);
+    if (opctx == NULL) {
+        ibmca_debug_ctx(provctx, "ERROR: ibmca_op_newctx failed");
+        return NULL;
+    }
+
+    ibmca_debug_ctx(provctx, "opctx: %p", opctx);
+
+    return opctx;
+}
+
+static void ibmca_keyexch_dh_free_cb(struct ibmca_op_ctx *ctx)
+{
+    if (ctx == NULL)
+        return;
+
+    ibmca_debug_op_ctx(ctx, "ctx: %p", ctx);
+
+    if (ctx->dh.derive.peer_key != NULL)
+        ibmca_keymgmt_free(ctx->dh.derive.peer_key);
+    ctx->dh.derive.peer_key = NULL;
+
+    ctx->dh.derive.pad = false;
+
+    ctx->dh.derive.kdf_type = EVP_PKEY_DH_KDF_NONE;
+
+    if (ctx->dh.derive.kdf_md != NULL)
+        EVP_MD_free(ctx->dh.derive.kdf_md);
+    ctx->dh.derive.kdf_md = NULL;
+
+    ctx->dh.derive.kdf_outlen = 0;
+
+    if (ctx->dh.derive.kdf_ukm != NULL)
+        P_CLEAR_FREE(ctx->provctx, ctx->dh.derive.kdf_ukm,
+                     ctx->dh.derive.kdf_ukmlen);
+    ctx->dh.derive.kdf_ukm = NULL;
+    ctx->dh.derive.kdf_ukmlen = 0;
+
+    if (ctx->dh.derive.kdf_cekalg != NULL)
+        P_FREE(ctx->provctx, ctx->dh.derive.kdf_cekalg);
+    ctx->dh.derive.kdf_cekalg = NULL;
+}
+
+static int ibmca_keyexch_dh_dup_cb(const struct ibmca_op_ctx *ctx,
+                                   struct ibmca_op_ctx *new_ctx)
+{
+    if (ctx == NULL)
+        return 0;
+
+    ibmca_debug_op_ctx(ctx, "ctx: %p new_ctx: %p", ctx, new_ctx);
+
+    if (ctx->dh.derive.peer_key != NULL) {
+        new_ctx->dh.derive.peer_key = ctx->dh.derive.peer_key;
+        ibmca_keymgmt_upref(new_ctx->dh.derive.peer_key);
+    }
+
+    new_ctx->dh.derive.pad = ctx->dh.derive.pad;
+
+    new_ctx->dh.derive.kdf_type = ctx->dh.derive.kdf_type;
+
+    new_ctx->dh.derive.kdf_md = ctx->dh.derive.kdf_md;
+    if (new_ctx->dh.derive.kdf_md != NULL) {
+        if (EVP_MD_up_ref(new_ctx->dh.derive.kdf_md) == 0) {
+            put_error_op_ctx(ctx, IBMCA_ERR_INTERNAL_ERROR,
+                             "EVP_MD_up_ref failed");
+            return 0;
+        }
+    }
+
+    new_ctx->dh.derive.kdf_outlen = ctx->dh.derive.kdf_outlen;
+
+    if (ctx->dh.derive.kdf_ukm != NULL && ctx->dh.derive.kdf_ukmlen > 0) {
+        new_ctx->dh.derive.kdf_ukm = P_MEMDUP(ctx->provctx,
+                                              ctx->dh.derive.kdf_ukm,
+                                              ctx->dh.derive.kdf_ukmlen);
+        if (new_ctx->dh.derive.kdf_ukm == NULL) {
+            put_error_op_ctx(ctx, IBMCA_ERR_MALLOC_FAILED, "P_MEMDUP failed");
+            return 0;
+        }
+        new_ctx->dh.derive.kdf_ukmlen = ctx->dh.derive.kdf_ukmlen;
+    }
+
+    if (ctx->dh.derive.kdf_cekalg != NULL) {
+        new_ctx->dh.derive.kdf_cekalg = P_STRDUP(ctx->provctx,
+                                                 ctx->dh.derive.kdf_cekalg);
+        if (new_ctx->dh.derive.kdf_cekalg == NULL) {
+            put_error_op_ctx(ctx, IBMCA_ERR_MALLOC_FAILED, "P_STRDUP failed");
+            return 0;
+        }
+    }
+
+    return 1;
+}
+
+static int ibmca_keyexch_dh_init(void *vctx, void *vkey,
+                                 const OSSL_PARAM params[])
+{
+    struct ibmca_op_ctx *ctx = vctx;
+    struct ibmca_key *key = vkey;
+    const OSSL_PARAM *p;
+
+    if (ctx == NULL || key == NULL)
+        return 0;
+
+    ibmca_debug_op_ctx(ctx, "ctx: %p key: %p", ctx, key);
+    for (p = params; p != NULL && p->key != NULL; p++)
+        ibmca_debug_op_ctx(ctx, "param: %s", p->key);
+
+    if (ibmca_op_init(ctx, key, EVP_PKEY_OP_DERIVE) == 0) {
+        ibmca_debug_op_ctx(ctx, "ERROR: ibmca_op_init failed");
+        return 0;
+    }
+
+    /* Set up defaults for this context */
+    ibmca_keyexch_dh_free_cb(ctx);
+
+    if (params != NULL) {
+        if (ibmca_keyexch_dh_set_ctx_params(ctx, params) == 0) {
+            ibmca_debug_op_ctx(ctx,
+                    "ERROR: ibmca_keyexch_dh_set_ctx_params failed");
+            return 0;
+        }
+    }
+
+    return 1;
+}
+
+static int ibmca_keyexch_dh_set_peer(void *vctx, void *vkey)
+{
+    struct ibmca_op_ctx *ctx = vctx;
+    struct ibmca_key *key = vkey;
+
+    if (ctx == NULL || key == NULL)
+        return 0;
+
+    ibmca_debug_op_ctx(ctx, "ctx: %p key: %p", ctx, key);
+
+    if (key->type != ctx->key->type) {
+        put_error_op_ctx(ctx, IBMCA_ERR_INVALID_PARAM,
+                         "Peer key is not an DH or DHX key");
+        return 0;
+    }
+
+    if (ctx->key->match(ctx->key, key,
+                        OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS) != 1) {
+        put_error_op_ctx(ctx, IBMCA_ERR_INVALID_PARAM,
+                         "Peer key uses a different DH parameters");
+        return 0;
+    }
+
+    if (key->has(key, OSSL_KEYMGMT_SELECT_PUBLIC_KEY) != 1) {
+        put_error_op_ctx(ctx, IBMCA_ERR_INVALID_PARAM,
+                         "Peer key does not contain a public DH key");
+        return 0;
+    }
+
+    if (ctx->dh.derive.peer_key != NULL)
+        ibmca_keymgmt_free(ctx->dh.derive.peer_key);
+
+    ctx->dh.derive.peer_key = key;
+    ibmca_keymgmt_upref(key);
+
+    return 1;
+}
+
+static int ibmca_keyexch_dh_derive_plain_fallback(struct ibmca_op_ctx *ctx,
+                                                  unsigned char *secret,
+                                                  size_t outlen)
+{
+    EVP_PKEY *pkey = NULL;
+    EVP_PKEY *peer = NULL;
+    EVP_PKEY_CTX *pctx = NULL;
+    size_t keylen;
+    int rc = 0;
+
+    ibmca_debug_op_ctx(ctx, "ctx: %p secret: %p outlen: %lu", ctx, secret,
+                       outlen);
+
+    pkey = ibmca_new_fallback_pkey(ctx->key);
+    if (pkey == NULL) {
+        ibmca_debug_op_ctx(ctx,"ERROR: ibmca_new_fallback_pkey failed");
+        goto out;
+    }
+
+    peer = ibmca_new_fallback_pkey(ctx->dh.derive.peer_key);
+    if (peer == NULL) {
+        ibmca_debug_op_ctx(ctx,"ERROR: ibmca_new_fallback_pkey failed");
+        goto out;
+    }
+
+    pctx = ibmca_new_fallback_pkey_ctx(ctx->provctx, pkey, NULL);
+    if (pctx == NULL) {
+        ibmca_debug_op_ctx(ctx,"ERROR: ibmca_new_fallback_pkey_ctx failed");
+        goto out;
+    }
+
+    if (EVP_PKEY_derive_init(pctx) != 1) {
+        put_error_op_ctx(ctx, IBMCA_ERR_INTERNAL_ERROR,
+                         "EVP_PKEY_derive_init failed");
+        goto out;
+    }
+
+    if (ibmca_check_fallback_provider(ctx->provctx, pctx) != 1) {
+        ibmca_debug_op_ctx(ctx, "ERROR: ibmca_check_fallback_provider failed");
+        goto out;
+    }
+
+    if (EVP_PKEY_derive_set_peer(pctx, peer) != 1 ||
+        EVP_PKEY_CTX_set_dh_pad(pctx, 1) != 1 ||
+        EVP_PKEY_CTX_set_dh_kdf_type(pctx, EVP_PKEY_DH_KDF_NONE) != 1) {
+        put_error_op_ctx(ctx, IBMCA_ERR_INTERNAL_ERROR,
+                         "EVP_PKEY_derive_set_peer/EVP_PKEY_CTX_set_dh_pad/EVP_PKEY_CTX_set_dh_kdf_type failed");
+        goto out;
+    }
+
+    keylen = outlen;
+    if (EVP_PKEY_derive(pctx, secret, &keylen) != 1 ||
+        keylen != outlen) {
+        put_error_op_ctx(ctx, IBMCA_ERR_INTERNAL_ERROR,
+                         "EVP_PKEY_derive failed");
+        goto out;
+    }
+
+    rc = 1;
+
+out:
+    if (pkey != NULL)
+        EVP_PKEY_free(pkey);
+    if (peer != NULL)
+        EVP_PKEY_free(peer);
+    if (pctx != NULL)
+        EVP_PKEY_CTX_free(pctx);
+
+    return rc;
+}
+
+static int ibmca_keyexch_dh_derive_plain(struct ibmca_op_ctx *ctx,
+                                         unsigned char *secret,
+                                         size_t *secretlen, size_t outlen,
+                                         bool pad)
+{
+    int rc = 0;
+    unsigned char *buf, *pub;
+    bool use_tbuf = false;
+    size_t prime_size, i;
+    ica_rsa_key_mod_expo_t mod_exp;
+    BIGNUM *z = NULL, *pminus1 = NULL;
+
+    ibmca_debug_op_ctx(ctx, "ctx: %p secret: %p outlen: %lu pad: %d", ctx,
+                       secret, outlen, pad);
+
+    prime_size = ctx->key->get_max_param_size(ctx->key);
+    if (secret == NULL) {
+        *secretlen = prime_size;
+        rc = 1;
+        goto out;
+    }
+
+    *secretlen = outlen < prime_size ? outlen : prime_size;
+
+    if (ibmca_op_alloc_tbuf(ctx, 4 * prime_size) != 1) {
+        ibmca_debug_op_ctx(ctx, "ERROR: ibmca_op_alloc_tbuf failed");
+        goto out;
+    }
+
+    if (*secretlen == prime_size) {
+        buf = secret;
+    } else {
+        buf = ctx->tbuf;
+        use_tbuf = true;
+    }
+
+    /* Z = pub_key^priv_key mod p */
+    mod_exp.key_length = prime_size;
+    mod_exp.modulus = ctx->tbuf + prime_size;
+    mod_exp.exponent = ctx->tbuf + 2 * prime_size;
+    pub = ctx->tbuf + 3 * prime_size;
+
+    if (BN_bn2binpad(ctx->key->dh.ffc_params.p, mod_exp.modulus,
+                     prime_size) <= 0 ||
+        BN_bn2binpad(ctx->key->dh.priv, mod_exp.exponent, prime_size) <= 0 ||
+        BN_bn2binpad(ctx->dh.derive.peer_key->dh.pub, pub, prime_size) <= 0) {
+        put_error_op_ctx(ctx, IBMCA_ERR_INTERNAL_ERROR, "BN_bn2binpad failed");
+        goto out;
+    }
+
+    rc = ica_rsa_mod_expo(ctx->provctx->ica_adapter, pub, &mod_exp, buf);
+    if (rc == 0) {
+        z = BN_secure_new();
+        if (z == NULL) {
+            put_error_op_ctx(ctx, IBMCA_ERR_MALLOC_FAILED,
+                             "BN_secure_new failed");
+            goto out;
+        }
+        z = BN_bin2bn(buf, prime_size, z);
+        pminus1 = BN_new();
+        if (z == NULL || pminus1 == NULL ||
+            BN_copy(pminus1, ctx->key->dh.ffc_params.p) == NULL ||
+            BN_sub_word(pminus1, 1) == 0) {
+            put_error_op_ctx(ctx, IBMCA_ERR_INTERNAL_ERROR,
+                             "BN_bin2bn/BN_copy/BN_sub_word failed");
+            goto out;
+        }
+        /* Error if z <= 1 or z = p - 1 */
+        if (BN_cmp(z, BN_value_one()) <= 0 ||
+            BN_cmp(z, pminus1) == 0) {
+            put_error_op_ctx(ctx, IBMCA_ERR_INVALID_PARAM, "invalid secret");
+            goto out;
+        }
+    } else {
+        ibmca_debug_op_ctx(ctx, "ica_rsa_mod_expo failed with: %s",
+                           strerror(rc));
+
+        rc = ibmca_keyexch_dh_derive_plain_fallback(ctx, buf, prime_size);
+        if (rc != 1) {
+            ibmca_debug_op_ctx(ctx,
+                               "ERROR: ibmca_keyexch_dh_derive_plain_fallback failed");
+            rc = 0;
+            goto out;
+        }
+    }
+
+    if (use_tbuf)
+        memcpy(secret, ctx->tbuf, *secretlen);
+
+    if (pad == false) {
+        for (i = 0; i < *secretlen && secret[i] == 0; i++)
+            ;
+        if (i > 0 && i < *secretlen) {
+            memmove(secret, secret + i, *secretlen - i);
+            P_CLEANSE(ctx->provctx, secret + *secretlen - i, i);
+            *secretlen = *secretlen - i;
+        }
+    }
+
+    rc = 1;
+
+out:
+    if (rc != 1)
+        *secretlen = 0;
+    if (use_tbuf && ctx->tbuf != NULL)
+        P_CLEANSE(ctx->provctx, ctx->tbuf, ctx->tbuf_len);
+    if (z != NULL)
+        BN_clear_free(z);
+    if (pminus1 != NULL)
+        BN_free(pminus1);
+
+    ibmca_debug_op_ctx(ctx, "secretlen: %lu", *secretlen);
+
+    return rc;
+}
+
+static int ibmca_keyexch_dh_kdf_x942(const struct ibmca_prov_ctx *provctx,
+                                     const unsigned char *z, size_t z_len,
+                                     EVP_MD *md, const unsigned char *ukm,
+                                     size_t ukm_len, const char *cek_alg,
+                                     unsigned char *out, size_t outlen)
+{
+    int rc = 0;
+    OSSL_PARAM params[5];
+    OSSL_PARAM *p;
+    EVP_KDF *kdf = NULL;
+    EVP_KDF_CTX *kctx = NULL;
+
+    kdf = EVP_KDF_fetch(provctx->libctx, OSSL_KDF_NAME_X942KDF_ASN1, NULL);
+    if (kdf == NULL) {
+        put_error_ctx(provctx, IBMCA_ERR_INTERNAL_ERROR,
+                      "Failed to fetch KDF '%s'", OSSL_KDF_NAME_X942KDF_ASN1);
+        goto out;
+    }
+
+    kctx = EVP_KDF_CTX_new(kdf);
+    if (kctx == NULL) {
+        put_error_ctx(provctx, IBMCA_ERR_INTERNAL_ERROR,
+                      "EVP_KDF_CTX_new failed");
+        goto out;
+    }
+
+    p = params;
+    *p++ = OSSL_PARAM_construct_utf8_string(OSSL_KDF_PARAM_DIGEST,
+                                            (char *)EVP_MD_get0_name(md), 0);
+    *p++ = OSSL_PARAM_construct_octet_string(OSSL_KDF_PARAM_KEY,
+                                             (void *)z, z_len);
+    *p++ = OSSL_PARAM_construct_octet_string(OSSL_KDF_PARAM_INFO,
+                                             (void *)ukm, ukm_len);
+    *p++ = OSSL_PARAM_construct_utf8_string(OSSL_KDF_PARAM_CEK_ALG,
+                                            (char *)cek_alg, 0);
+    *p = OSSL_PARAM_construct_end();
+
+    rc = EVP_KDF_derive(kctx, out, outlen, params);
+    if (rc <= 0) {
+        put_error_ctx(provctx, IBMCA_ERR_INTERNAL_ERROR,
+                      "EVP_KDF_derive failed");
+        goto out;
+    }
+
+    rc = 1;
+
+out:
+    if (kctx != NULL)
+        EVP_KDF_CTX_free(kctx);
+    if (kdf != NULL)
+        EVP_KDF_free(kdf);
+
+    return rc;
+
+}
+
+static int ibmca_keyexch_dh_derive_x942_kdf(struct ibmca_op_ctx *ctx,
+                                            unsigned char *secret,
+                                            size_t *secretlen, size_t outlen)
+{
+    int rc = 0;
+    size_t len, prime_size;
+
+    ibmca_debug_op_ctx(ctx, "ctx: %p secret: %p outlen: %lu", ctx, secret,
+                       outlen);
+
+    *secretlen = ctx->dh.derive.kdf_outlen;
+
+    if (secret == NULL) {
+        rc = 1;
+        goto out;
+    }
+
+    if (outlen < *secretlen) {
+        put_error_op_ctx(ctx, IBMCA_ERR_INVALID_PARAM,
+                         "Output buffer too small");
+        goto out;
+    }
+
+    prime_size = ctx->key->get_max_param_size(ctx->key);
+
+    if (ibmca_op_alloc_tbuf(ctx, prime_size * 4) != 1) {
+        ibmca_debug_op_ctx(ctx, "ERROR: ibmca_op_alloc_tbuf failed");
+        goto out;
+    }
+
+    rc = ibmca_keyexch_dh_derive_plain(ctx, ctx->tbuf, &len, prime_size, true);
+    if (rc != 1) {
+        ibmca_debug_op_ctx(ctx,
+                           "ERROR: ibmca_keyexch_dh_derive_plain failed");
+        goto out;
+    }
+
+    rc = ibmca_keyexch_dh_kdf_x942(ctx->provctx, ctx->tbuf, len,
+                                   ctx->dh.derive.kdf_md,
+                                   ctx->dh.derive.kdf_ukm,
+                                   ctx->dh.derive.kdf_ukmlen,
+                                   ctx->dh.derive.kdf_cekalg,
+                                   secret, ctx->dh.derive.kdf_outlen);
+    if (rc != 1) {
+        ibmca_debug_op_ctx(ctx, "ERROR: ibmca_keyexch_dh_kdf_x942 failed");
+        goto out;
+    }
+
+    rc = 1;
+
+out:
+    if (rc != 1)
+        *secretlen = 0;
+
+    ibmca_debug_op_ctx(ctx, "secretlen: %lu", *secretlen);
+
+    if (ctx->tbuf != NULL)
+        P_CLEANSE(ctx->provctx, ctx->tbuf, ctx->tbuf_len);
+
+    return rc;
+}
+
+static int ibmca_keyexch_dh_derive(void *vctx,  unsigned char *secret,
+                                   size_t *secretlen, size_t outlen)
+{
+    struct ibmca_op_ctx *ctx = vctx;
+
+    if (ctx == NULL || secretlen == NULL)
+        return 0;
+
+    ibmca_debug_op_ctx(ctx, "ctx: %p secret: %p outlen: %lu", ctx, secret,
+                       outlen);
+
+    switch (ctx->dh.derive.kdf_type) {
+    case EVP_PKEY_DH_KDF_X9_42:
+        return ibmca_keyexch_dh_derive_x942_kdf(ctx, secret, secretlen, outlen);
+    case EVP_PKEY_DH_KDF_NONE:
+    default:
+        return ibmca_keyexch_dh_derive_plain(ctx, secret, secretlen, outlen,
+                                             ctx->dh.derive.pad);
+    }
+
+    return 0;
+}
+
+static int ibmca_keyexch_dh_get_ctx_params(void *vctx, OSSL_PARAM params[])
+{
+    struct ibmca_op_ctx *ctx = vctx;
+    OSSL_PARAM *p;
+    const char *name;
+    int rc;
+
+    if (ctx == NULL)
+        return 0;
+
+    ibmca_debug_op_ctx(ctx, "ctx: %p", ctx);
+    for (p = params; p != NULL && p->key != NULL; p++)
+        ibmca_debug_op_ctx(ctx, "param: %s", p->key);
+
+    /* OSSL_EXCHANGE_PARAM_KDF_TYPE */
+    switch (ctx->dh.derive.kdf_type) {
+    case EVP_PKEY_DH_KDF_X9_42:
+        name = OSSL_KDF_NAME_X942KDF_ASN1;
+        break;
+    case EVP_PKEY_DH_KDF_NONE:
+    default:
+        name = "";
+        break;
+    }
+    rc = ibmca_param_build_set_utf8(ctx->provctx, NULL, params,
+                                    OSSL_EXCHANGE_PARAM_KDF_TYPE, name);
+    if (rc == 0)
+       return 0;
+
+    /* OSSL_EXCHANGE_PARAM_KDF_DIGEST */
+    if (ctx->dh.derive.kdf_md != NULL)
+        name = EVP_MD_get0_name(ctx->dh.derive.kdf_md);
+    else
+        name = "";
+    rc = ibmca_param_build_set_utf8(ctx->provctx, NULL, params,
+                                    OSSL_EXCHANGE_PARAM_KDF_DIGEST, name);
+    if (rc == 0)
+       return 0;
+
+    /* OSSL_EXCHANGE_PARAM_KDF_OUTLEN */
+    rc = ibmca_param_build_set_size_t(ctx->provctx, NULL, params,
+                                      OSSL_EXCHANGE_PARAM_KDF_OUTLEN,
+                                      ctx->dh.derive.kdf_outlen);
+    if (rc == 0)
+       return 0;
+
+    /* OSSL_EXCHANGE_PARAM_KDF_UKM */
+    rc = ibmca_param_build_set_octet_ptr(ctx->provctx, NULL, params,
+                                         OSSL_EXCHANGE_PARAM_KDF_UKM,
+                                         ctx->dh.derive.kdf_ukm,
+                                         ctx->dh.derive.kdf_ukmlen);
+    if (rc == 0)
+       return 0;
+
+    /* OSSL_KDF_PARAM_CEK_ALG */
+    rc = ibmca_param_build_set_utf8(ctx->provctx, NULL, params,
+                                    OSSL_KDF_PARAM_CEK_ALG,
+                                    ctx->dh.derive.kdf_cekalg);
+    if (rc == 0)
+       return 0;
+
+    return 1;
+}
+
+static int ibmca_keyexch_dh_set_ctx_params(void *vctx,
+                                           const OSSL_PARAM params[])
+{
+    struct ibmca_op_ctx *ctx = vctx;
+    const OSSL_PARAM *p;
+    const char *name, *props = NULL;
+    unsigned int value;
+    int rc;
+    void *ukm = NULL;
+    size_t ukmlen;
+    EVP_MD *md;
+
+    if (ctx == NULL)
+        return 0;
+
+    ibmca_debug_op_ctx(ctx, "ctx: %p", ctx);
+    for (p = params; p != NULL && p->key != NULL; p++)
+        ibmca_debug_op_ctx(ctx, "param: %s", p->key);
+
+    /* OSSL_EXCHANGE_PARAM_PAD */
+    rc = ibmca_param_get_uint(ctx->provctx, params,
+                              OSSL_EXCHANGE_PARAM_PAD, &value);
+    if (rc == 0)
+        return 0;
+    if (rc > 0)
+        ctx->dh.derive.pad = (value != 0);
+
+    /* OSSL_EXCHANGE_PARAM_KDF_TYPE */
+    rc = ibmca_param_get_utf8(ctx->provctx, params,
+                              OSSL_EXCHANGE_PARAM_KDF_TYPE, &name);
+    if (rc == 0)
+        return 0;
+    if (rc > 0) {
+        if (name[0] == '\0') {
+            ctx->dh.derive.kdf_type = EVP_PKEY_DH_KDF_NONE;
+        } else if (strcmp(name, OSSL_KDF_NAME_X942KDF_ASN1) == 0) {
+            ctx->dh.derive.kdf_type = EVP_PKEY_DH_KDF_X9_42;
+        } else {
+            put_error_op_ctx(ctx, IBMCA_ERR_INVALID_PARAM,
+                             "DH '%s': '%s' is not supported",
+                             OSSL_EXCHANGE_PARAM_KDF_TYPE, name);
+            return 0;
+        }
+    }
+
+    /* OSSL_EXCHANGE_PARAM_KDF_DIGEST_PROPS */
+    rc = ibmca_param_get_utf8(ctx->provctx, params,
+                              OSSL_EXCHANGE_PARAM_KDF_DIGEST_PROPS, &props);
+    if (rc == 0)
+        return 0;
+
+    /* OSSL_EXCHANGE_PARAM_KDF_DIGEST */
+    rc = ibmca_param_get_utf8(ctx->provctx, params,
+                             OSSL_EXCHANGE_PARAM_KDF_DIGEST, &name);
+    if (rc == 0)
+        return 0;
+    if (rc > 0) {
+        md = EVP_MD_fetch(ctx->provctx->libctx, name,
+                          props != NULL ? props : ctx->propq);
+        if (md == NULL) {
+            put_error_op_ctx(ctx, IBMCA_ERR_INVALID_PARAM,
+                              "DH '%s': '%s' could not be fetched",
+                              OSSL_EXCHANGE_PARAM_KDF_DIGEST, name);
+            return 0;
+        }
+
+        if ((EVP_MD_get_flags(md) & EVP_MD_FLAG_XOF) != 0) {
+            put_error_op_ctx(ctx, IBMCA_ERR_INVALID_PARAM,
+                             "XOF Digest '%s' is not allowed", name);
+            EVP_MD_free(md);
+            return 0;
+        }
+
+        if (ctx->dh.derive.kdf_md != NULL)
+            EVP_MD_free(ctx->dh.derive.kdf_md);
+        ctx->dh.derive.kdf_md = md;
+    }
+
+    /* OSSL_EXCHANGE_PARAM_KDF_OUTLEN */
+    rc = ibmca_param_get_size_t(ctx->provctx, params,
+                                OSSL_EXCHANGE_PARAM_KDF_OUTLEN,
+                                &ctx->dh.derive.kdf_outlen);
+    if (rc == 0)
+        return 0;
+
+    /* OSSL_EXCHANGE_PARAM_KDF_UKM */
+    rc = ibmca_param_get_octet_string(ctx->provctx, params,
+                                      OSSL_EXCHANGE_PARAM_KDF_UKM,
+                                      &ukm, &ukmlen);
+    if (rc == 0)
+        return 0;
+    if (rc > 0) {
+        if (ctx->dh.derive.kdf_ukm != NULL)
+            P_CLEAR_FREE(ctx->provctx, ctx->dh.derive.kdf_ukm,
+                         ctx->dh.derive.kdf_ukmlen);
+        ctx->dh.derive.kdf_ukm = ukm;
+        ctx->dh.derive.kdf_ukmlen = ukmlen;
+    }
+
+    /* OSSL_KDF_PARAM_CEK_ALG */
+    rc = ibmca_param_get_utf8(ctx->provctx, params,
+                              OSSL_KDF_PARAM_CEK_ALG, &name);
+    if (rc == 0)
+        return 0;
+    if (rc > 0) {
+        ctx->dh.derive.kdf_cekalg = P_STRDUP(ctx->provctx, name);
+        if (ctx->dh.derive.kdf_cekalg == NULL) {
+            put_error_op_ctx(ctx, IBMCA_ERR_MALLOC_FAILED, "P_STRDUP failed");
+            return 0;
+        }
+    }
+
+    return 1;
+}
+
+static const OSSL_PARAM ibmca_keyexch_dh_gettable_params[] = {
+    OSSL_PARAM_utf8_string(OSSL_EXCHANGE_PARAM_KDF_TYPE, NULL, 0),
+    OSSL_PARAM_utf8_string(OSSL_EXCHANGE_PARAM_KDF_DIGEST, NULL, 0),
+    OSSL_PARAM_size_t(OSSL_EXCHANGE_PARAM_KDF_OUTLEN, NULL),
+    OSSL_PARAM_DEFN(OSSL_EXCHANGE_PARAM_KDF_UKM, OSSL_PARAM_OCTET_PTR, NULL, 0),
+    OSSL_PARAM_utf8_string(OSSL_KDF_PARAM_CEK_ALG, NULL, 0),
+    OSSL_PARAM_END
+};
+
+static const OSSL_PARAM *ibmca_keyexch_dh_gettable_ctx_params(
+                                                void *vctx, void *vprovctx)
+{
+    const struct ibmca_op_ctx *ctx = vctx;
+    const struct ibmca_prov_ctx *provctx = vprovctx;
+    const OSSL_PARAM *p;
+
+    ibmca_debug_ctx(provctx, "ctx: %p", ctx);
+
+    for (p = ibmca_keyexch_dh_gettable_params;
+                                    p != NULL && p->key != NULL; p++)
+        ibmca_debug_ctx(provctx, "param: %s", p->key);
+
+    return ibmca_keyexch_dh_gettable_params;
+}
+
+static const OSSL_PARAM ibmca_keyexch_dh_settable_params[] = {
+    OSSL_PARAM_int(OSSL_EXCHANGE_PARAM_PAD, NULL),
+    OSSL_PARAM_utf8_string(OSSL_EXCHANGE_PARAM_KDF_TYPE, NULL, 0),
+    OSSL_PARAM_utf8_string(OSSL_EXCHANGE_PARAM_KDF_DIGEST, NULL, 0),
+    OSSL_PARAM_utf8_string(OSSL_EXCHANGE_PARAM_KDF_DIGEST_PROPS, NULL, 0),
+    OSSL_PARAM_size_t(OSSL_EXCHANGE_PARAM_KDF_OUTLEN, NULL),
+    OSSL_PARAM_octet_string(OSSL_EXCHANGE_PARAM_KDF_UKM, NULL, 0),
+    OSSL_PARAM_utf8_string(OSSL_KDF_PARAM_CEK_ALG, NULL, 0),
+    OSSL_PARAM_END
+};
+
+static const OSSL_PARAM *ibmca_keyexch_dh_settable_ctx_params(
+                                                void *vctx, void *vprovctx)
+{
+    const struct ibmca_op_ctx *ctx = vctx;
+    const struct ibmca_prov_ctx *provctx = vprovctx;
+    const OSSL_PARAM *p;
+
+    ibmca_debug_ctx(provctx, "ctx: %p", ctx);
+
+    for (p = ibmca_keyexch_dh_settable_params; p != NULL && p->key != NULL; p++)
+        ibmca_debug_ctx(provctx, "param: %s", p->key);
+
+    return ibmca_keyexch_dh_settable_params;
+}
+
+static const OSSL_DISPATCH ibmca_dh_keyexch_functions[] = {
+    /* Context management */
+    { OSSL_FUNC_KEYEXCH_NEWCTX, (void (*)(void))ibmca_keyexch_dh_newctx },
+    { OSSL_FUNC_KEYEXCH_FREECTX, (void (*)(void))ibmca_op_freectx },
+    { OSSL_FUNC_KEYEXCH_DUPCTX, (void (*)(void))ibmca_op_dupctx },
+
+    /* Shared secret derivation */
+    { OSSL_FUNC_KEYEXCH_INIT, (void (*)(void))ibmca_keyexch_dh_init },
+    { OSSL_FUNC_KEYEXCH_SET_PEER, (void (*)(void))ibmca_keyexch_dh_set_peer },
+    { OSSL_FUNC_KEYEXCH_DERIVE, (void (*)(void))ibmca_keyexch_dh_derive },
+
+    /* Key Exchange parameters */
+    { OSSL_FUNC_KEYEXCH_SET_CTX_PARAMS,
+        (void (*)(void))ibmca_keyexch_dh_set_ctx_params },
+    { OSSL_FUNC_KEYEXCH_SETTABLE_CTX_PARAMS,
+        (void (*)(void))ibmca_keyexch_dh_settable_ctx_params },
+    { OSSL_FUNC_KEYEXCH_GET_CTX_PARAMS,
+            (void (*)(void))ibmca_keyexch_dh_get_ctx_params },
+    { OSSL_FUNC_KEYEXCH_GETTABLE_CTX_PARAMS,
+        (void (*)(void))ibmca_keyexch_dh_gettable_ctx_params },
+
+    { 0, NULL }
+};
+
+const OSSL_ALGORITHM ibmca_dh_keyexch[] = {
+    { "DH:dhKeyAgreement:1.2.840.113549.1.3.1", NULL,
+      ibmca_dh_keyexch_functions, "IBMCA DH key exchange implementation" },
+    { NULL, NULL, NULL, NULL }
+};
diff -pruN 1.4.0-1/src/provider/dh_keymgmt.c 2.5.0-0ubuntu1/src/provider/dh_keymgmt.c
--- 1.4.0-1/src/provider/dh_keymgmt.c	1970-01-01 00:00:00.000000000 +0000
+++ 2.5.0-0ubuntu1/src/provider/dh_keymgmt.c	2025-04-16 08:09:24.000000000 +0000
@@ -0,0 +1,2120 @@
+/*
+ * Copyright [2021-2022] International Business Machines Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <stddef.h>
+#include <err.h>
+#include <strings.h>
+#include <string.h>
+#include <errno.h>
+
+#include <openssl/evp.h>
+#include <openssl/bn.h>
+#include <openssl/dh.h>
+#include <openssl/core.h>
+#include <openssl/core_dispatch.h>
+#include <openssl/core_names.h>
+#include <openssl/params.h>
+#include <openssl/param_build.h>
+#include <openssl/prov_ssl.h>
+
+#include "p_ibmca.h"
+
+static OSSL_FUNC_keymgmt_new_fn ibmca_keymgmt_dh_new;
+static OSSL_FUNC_keymgmt_new_fn ibmca_keymgmt_dhx_new;
+static OSSL_FUNC_keymgmt_gen_init_fn ibmca_keymgmt_dh_gen_init;
+static OSSL_FUNC_keymgmt_gen_init_fn ibmca_keymgmt_dhx_gen_init;
+static OSSL_FUNC_keymgmt_gen_set_template_fn ibmca_keymgmt_dh_gen_set_template;
+static OSSL_FUNC_keymgmt_gen_set_params_fn ibmca_keymgmt_dh_gen_set_params;
+static OSSL_FUNC_keymgmt_gen_settable_params_fn
+                                        ibmca_keymgmt_dh_gen_settable_params;
+static OSSL_FUNC_keymgmt_gen_settable_params_fn
+                                        ibmca_keymgmt_dhx_gen_settable_params;
+static OSSL_FUNC_keymgmt_gen_fn ibmca_keymgmt_dh_gen;
+static OSSL_FUNC_keymgmt_has_fn ibmca_keymgmt_dh_has;
+static OSSL_FUNC_keymgmt_match_fn ibmca_keymgmt_dh_match;
+static OSSL_FUNC_keymgmt_validate_fn ibmca_keymgmt_dh_validate;
+static OSSL_FUNC_keymgmt_query_operation_name_fn
+                                        ibmca_keymgmt_dh_query_operation_name;
+static OSSL_FUNC_keymgmt_get_params_fn ibmca_keymgmt_dh_get_params;
+static OSSL_FUNC_keymgmt_gettable_params_fn ibmca_keymgmt_dh_gettable_params;
+static OSSL_FUNC_keymgmt_set_params_fn ibmca_keymgmt_dh_set_params;
+static OSSL_FUNC_keymgmt_settable_params_fn ibmca_keymgmt_dh_settable_params;
+static OSSL_FUNC_keymgmt_export_fn ibmca_keymgmt_dh_export;
+static OSSL_FUNC_keymgmt_export_types_fn ibmca_keymgmt_dh_imexport_types;
+static OSSL_FUNC_keymgmt_import_fn ibmca_keymgmt_dh_import;
+
+static void ibmca_keymgmt_dh_free_cb(struct ibmca_key *key);
+static int ibmca_keymgmt_dh_dup_cb(const struct ibmca_key *key,
+                                   struct ibmca_key *new_key);
+static size_t ibmca_keymgmt_dh_get_prime_size(const struct ibmca_key *key);
+
+static struct ibmca_key *ibmca_keymgmt_dh_new_type(
+                                       const struct ibmca_prov_ctx *provctx,
+                                       int type,  const char *algorithm)
+{
+    struct ibmca_key *key;
+
+    if (provctx == NULL)
+        return NULL;
+
+    ibmca_debug_ctx(provctx, "provctx: %p type: %d algorithm: '%s'", provctx,
+                    type, algorithm);
+
+    key = ibmca_keymgmt_new(provctx, type, algorithm,
+                            ibmca_keymgmt_dh_free_cb,
+                            ibmca_keymgmt_dh_dup_cb,
+                            ibmca_keymgmt_dh_get_prime_size,
+                            ibmca_keymgmt_dh_export,
+                            ibmca_keymgmt_dh_import,
+                            ibmca_keymgmt_dh_has,
+                            ibmca_keymgmt_dh_match);
+    if (key == NULL) {
+        ibmca_debug_ctx(provctx, "ERROR: ibmca_keymgmt_new failed");
+        return NULL;
+    }
+
+    key->dh.ffc_params.group_nid = NID_undef;
+    key->dh.ffc_params.length = 0;
+    key->dh.ffc_params.seed = NULL;
+    key->dh.ffc_params.seed_len = 0;
+    key->dh.ffc_params.gindex = -1;
+    key->dh.ffc_params.pcounter = -1;
+    key->dh.ffc_params.hindex = 0;
+    key->dh.ffc_params.validate_pq = true;
+    key->dh.ffc_params.validate_g = true;
+    key->dh.ffc_params.validate_legacy = false;
+
+    return key;
+}
+static void *ibmca_keymgmt_dh_new(void *vprovctx)
+{
+    const struct ibmca_prov_ctx *provctx = vprovctx;
+
+    return ibmca_keymgmt_dh_new_type(provctx, EVP_PKEY_DH, "DH");
+}
+
+static void *ibmca_keymgmt_dhx_new(void *vprovctx)
+{
+    const struct ibmca_prov_ctx *provctx = vprovctx;
+
+    return ibmca_keymgmt_dh_new_type(provctx, EVP_PKEY_DHX, "DHX");
+}
+
+static void ibmca_keymgmt_dh_free_cb(struct ibmca_key *key)
+{
+    if (key == NULL)
+        return;
+
+    ibmca_debug_key(key, "key: %p", key);
+
+    if (key->dh.priv != NULL)
+        BN_clear_free(key->dh.priv);
+    key->dh.priv = NULL;
+
+    if (key->dh.pub != NULL)
+        BN_free(key->dh.pub);
+    key->dh.pub = NULL;
+
+    if (key->dh.ffc_params.p != NULL)
+        BN_free(key->dh.ffc_params.p);
+    key->dh.ffc_params.p = NULL;
+
+    if (key->dh.ffc_params.q != NULL)
+        BN_free(key->dh.ffc_params.q);
+    key->dh.ffc_params.q = NULL;
+
+    if (key->dh.ffc_params.g != NULL)
+        BN_free(key->dh.ffc_params.g);
+    key->dh.ffc_params.g = NULL;
+
+    if (key->dh.ffc_params.cofactor != NULL)
+        BN_free(key->dh.ffc_params.cofactor);
+    key->dh.ffc_params.cofactor = NULL;
+
+    if (key->dh.ffc_params.seed != NULL)
+        P_FREE(key->provctx, key->dh.ffc_params.seed);
+    key->dh.ffc_params.seed = NULL;
+    key->dh.ffc_params.seed_len = 0;
+
+    if (key->dh.ffc_params.mdname != NULL)
+        P_FREE(key->provctx, (char *)key->dh.ffc_params.mdname);
+    key->dh.ffc_params.mdname = NULL;
+
+    if (key->dh.ffc_params.mdprops != NULL)
+        P_FREE(key->provctx, (char *)key->dh.ffc_params.mdprops);
+    key->dh.ffc_params.mdprops = NULL;
+
+    key->dh.ffc_params.group_nid = NID_undef;
+    key->dh.ffc_params.length = 0;
+    key->dh.ffc_params.gindex = -1;
+    key->dh.ffc_params.pcounter = -1;
+    key->dh.ffc_params.hindex = 0;
+    key->dh.ffc_params.validate_pq = true;
+    key->dh.ffc_params.validate_g = true;
+    key->dh.ffc_params.validate_legacy = false;
+}
+
+static int ibmca_keymgmt_dh_dup_params(const struct ibmca_key *key,
+                                       struct ibmca_key *new_key)
+{
+    if (key == NULL || new_key == NULL)
+        return 0;
+
+    ibmca_debug_key(key, "key: %p new_key: %p", key, new_key);
+
+    if (key->dh.ffc_params.p != NULL) {
+        new_key->dh.ffc_params.p = BN_dup(key->dh.ffc_params.p);
+        if (new_key->dh.ffc_params.p == NULL) {
+            put_error_key(key, IBMCA_ERR_INTERNAL_ERROR, "BN_dup failed");
+            return 0;
+        }
+    }
+
+    if (key->dh.ffc_params.q != NULL) {
+        new_key->dh.ffc_params.q = BN_dup(key->dh.ffc_params.q);
+        if (new_key->dh.ffc_params.q == NULL) {
+            put_error_key(key, IBMCA_ERR_INTERNAL_ERROR, "BN_dup failed");
+            return 0;
+        }
+    }
+
+    if (key->dh.ffc_params.g != NULL) {
+        new_key->dh.ffc_params.g = BN_dup(key->dh.ffc_params.g);
+        if (new_key->dh.ffc_params.g == NULL) {
+            put_error_key(key, IBMCA_ERR_INTERNAL_ERROR, "BN_dup failed");
+            return 0;
+        }
+    }
+
+    if (key->dh.ffc_params.cofactor != NULL) {
+        new_key->dh.ffc_params.cofactor = BN_dup(key->dh.ffc_params.cofactor);
+        if (new_key->dh.ffc_params.cofactor == NULL) {
+            put_error_key(key, IBMCA_ERR_INTERNAL_ERROR, "BN_dup failed");
+            return 0;
+        }
+    }
+
+    if (key->dh.ffc_params.seed != NULL && key->dh.ffc_params.seed_len > 0) {
+        new_key->dh.ffc_params.seed = P_MEMDUP(key->provctx,
+                                               key->dh.ffc_params.seed,
+                                               key->dh.ffc_params.seed_len);
+        if (new_key->dh.ffc_params.seed == NULL) {
+            put_error_key(key, IBMCA_ERR_INTERNAL_ERROR, "BN_dup failed");
+            return 0;
+        }
+        new_key->dh.ffc_params.seed_len = key->dh.ffc_params.seed_len;
+    }
+
+    new_key->dh.ffc_params.group_nid = key->dh.ffc_params.group_nid;
+    new_key->dh.ffc_params.length = key->dh.ffc_params.length;
+    new_key->dh.ffc_params.gindex = key->dh.ffc_params.gindex;
+    new_key->dh.ffc_params.pcounter = key->dh.ffc_params.pcounter;
+    new_key->dh.ffc_params.hindex = key->dh.ffc_params.hindex;
+    new_key->dh.ffc_params.validate_pq = key->dh.ffc_params.validate_pq;
+    new_key->dh.ffc_params.validate_g = key->dh.ffc_params.validate_g;
+    new_key->dh.ffc_params.validate_legacy = key->dh.ffc_params.validate_legacy;
+
+    return 1;
+}
+
+static int ibmca_keymgmt_dh_dup_cb(const struct ibmca_key *key,
+                                   struct ibmca_key *new_key)
+{
+    if (key == NULL || new_key == NULL)
+        return 0;
+
+    ibmca_debug_key(key, "key: %p new_key: %p", key, new_key);
+
+    if (key->dh.priv != NULL) {
+        new_key->dh.priv = BN_dup(key->dh.priv);
+        if (new_key->dh.priv == NULL) {
+            put_error_key(key, IBMCA_ERR_INTERNAL_ERROR, "BN_dup failed");
+            return 0;
+        }
+    }
+
+    if (key->dh.pub != NULL) {
+        new_key->dh.pub = BN_dup(key->dh.pub);
+        if (new_key->dh.pub == NULL) {
+            put_error_key(key, IBMCA_ERR_INTERNAL_ERROR, "BN_dup failed");
+            return 0;
+        }
+    }
+
+    return ibmca_keymgmt_dh_dup_params(key, new_key);
+}
+
+static size_t ibmca_keymgmt_dh_get_prime_bits(const struct ibmca_key *key)
+{
+    if (key->dh.ffc_params.p != NULL)
+        return BN_num_bits(key->dh.ffc_params.p);
+
+    put_error_key(key, IBMCA_ERR_INVALID_PARAM, "No DH parameters available");
+    return 0;
+}
+
+static size_t ibmca_keymgmt_dh_get_prime_size(const struct ibmca_key *key)
+{
+    return (ibmca_keymgmt_dh_get_prime_bits(key) + 7) / 8;
+}
+
+static int ibmca_keymgmt_dh_get_security_bits(const struct ibmca_key *key)
+{
+    int n;
+
+    if (key->dh.ffc_params.p != NULL)
+        n =  BN_num_bits(key->dh.ffc_params.p);
+    else
+        n = key->dh.ffc_params.length;
+    if (n == 0)
+        n = -1;
+
+    if (key->dh.ffc_params.p != NULL)
+        return BN_security_bits(BN_num_bits(key->dh.ffc_params.p), n);
+
+    put_error_key(key, IBMCA_ERR_INVALID_PARAM, "No DH parameters available");
+    return -1;
+}
+
+static void ibmca_keymgmt_dh_gen_free_cb(struct ibmca_op_ctx *ctx)
+{
+    if (ctx == NULL)
+        return;
+
+    ibmca_debug_op_ctx(ctx, "ctx: %p", ctx);
+
+    if (ctx->dh.gen.pctx != NULL)
+        EVP_PKEY_CTX_free(ctx->dh.gen.pctx);
+    ctx->dh.gen.pctx = NULL;
+
+    ctx->dh.gen.selection = 0;
+    ctx->dh.gen.priv_len = 0;
+}
+
+static int ibmca_keymgmt_dh_gen_dup_cb(const struct ibmca_op_ctx *ctx,
+                                       struct ibmca_op_ctx *new_ctx)
+{
+    if (ctx == NULL)
+        return 0;
+
+    ibmca_debug_op_ctx(ctx, "ctx: %p new_ctx: %p", ctx, new_ctx);
+
+    if (ctx->dh.gen.pctx != NULL) {
+        new_ctx->dh.gen.pctx = EVP_PKEY_CTX_dup(ctx->dh.gen.pctx);
+        if (new_ctx->dh.gen.pctx == NULL) {
+            put_error_op_ctx(ctx, IBMCA_ERR_INTERNAL_ERROR,
+                             "EVP_PKEY_CTX_dup failed");
+            return 0;
+        }
+    }
+
+    new_ctx->dh.gen.selection = ctx->dh.gen.selection;
+    new_ctx->dh.gen.priv_len = ctx->dh.gen.priv_len;
+
+    return 1;
+}
+
+static int ibmca_keymgmt_dh_gen_init_pctx(struct ibmca_op_ctx *genctx,
+                                          int operation, EVP_PKEY *templ_pkey)
+{
+    const char *algorithm = NULL;
+
+    ibmca_debug_op_ctx(genctx, "genctx: %p operation: %d templ_pkey: %p", genctx,
+                       operation, templ_pkey);
+
+    switch (genctx->type) {
+    case EVP_PKEY_DH:
+        algorithm = "DH";
+        break;
+    case EVP_PKEY_DHX:
+        algorithm = "DHX";
+        break;
+    default:
+        put_error_op_ctx(genctx, IBMCA_ERR_INTERNAL_ERROR,
+                         "Invalid context type");
+        return 0;
+    }
+
+    genctx->dh.gen.pctx = ibmca_new_fallback_pkey_ctx(genctx->provctx,
+                                                      templ_pkey,
+                                                      algorithm);
+    if (genctx->dh.gen.pctx == NULL) {
+        ibmca_debug_op_ctx(genctx, "ERROR: ibmca_new_fallback_pkey_ctx failed");
+        return 0;
+    }
+
+    switch (operation) {
+    case EVP_PKEY_OP_KEYGEN:
+        if (EVP_PKEY_keygen_init(genctx->dh.gen.pctx) != 1) {
+            put_error_op_ctx(genctx, IBMCA_ERR_INTERNAL_ERROR,
+                            "EVP_PKEY_keygen_init failed");
+            goto error;
+        }
+        break;
+    case EVP_PKEY_OP_PARAMGEN:
+        if (EVP_PKEY_paramgen_init(genctx->dh.gen.pctx) != 1) {
+            put_error_op_ctx(genctx, IBMCA_ERR_INTERNAL_ERROR,
+                            "EVP_PKEY_paramgen_init failed");
+            goto error;
+        }
+        break;
+    default:
+        put_error_op_ctx(genctx, IBMCA_ERR_INTERNAL_ERROR,
+                                 "Invalid operation type");
+        goto error;
+    }
+
+    if (ibmca_check_fallback_provider(genctx->provctx,
+                                      genctx->dh.gen.pctx) != 1) {
+        ibmca_debug_op_ctx(genctx,
+                           "ERROR: ibmca_check_fallback_provider failed");
+        return 0;
+    }
+
+    return 1;
+
+error:
+    EVP_PKEY_CTX_free(genctx->dh.gen.pctx);
+    genctx->dh.gen.pctx = NULL;
+
+    return 0;
+}
+
+static struct ibmca_op_ctx *ibmca_keymgmt_dh_gen_init_type(
+                                            const struct ibmca_prov_ctx *provctx,
+                                            int selection,
+                                            const OSSL_PARAM params[],
+                                            int type)
+{
+    struct ibmca_op_ctx *ctx;
+    const OSSL_PARAM *p;
+
+    if (provctx == NULL)
+        return NULL;
+
+    ibmca_debug_ctx(provctx, "provctx: %p selection: 0x%x type: %d", provctx,
+                    selection, type);
+    for (p = params; p != NULL && p->key != NULL; p++)
+        ibmca_debug_ctx(provctx, "param: %s", p->key);
+
+    if ((selection & (OSSL_KEYMGMT_SELECT_KEYPAIR |
+                      OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS)) == 0)
+        return NULL;
+
+    ctx = ibmca_keymgmt_gen_init(provctx, type,
+                                 ibmca_keymgmt_dh_gen_free_cb,
+                                 ibmca_keymgmt_dh_gen_dup_cb);
+    if (ctx == NULL) {
+        ibmca_debug_ctx(provctx, "ERROR: ibmca_keymgmt_gen_init failed");
+        return NULL;
+    }
+
+    ibmca_keymgmt_dh_gen_free_cb(ctx);
+
+    ctx->dh.gen.selection = selection;
+
+    if (ibmca_keymgmt_dh_gen_init_pctx(ctx, EVP_PKEY_OP_PARAMGEN, NULL) != 1) {
+        ibmca_debug_ctx(provctx,
+                        "ERROR: ibmca_keymgmt_dh_gen_init_pctx failed");
+        goto error;
+    }
+
+    if (params != NULL) {
+        if (ibmca_keymgmt_dh_gen_set_params(ctx, params) == 0) {
+            ibmca_debug_ctx(provctx,
+                            "ERROR: ibmca_keymgmt_dh_gen_set_params failed");
+            goto error;
+        }
+    }
+
+    return ctx;
+
+error:
+    ibmca_op_freectx(ctx);
+    return NULL;
+}
+
+static void *ibmca_keymgmt_dh_gen_init(void *vprovctx, int selection,
+                                       const OSSL_PARAM params[])
+{
+    const struct ibmca_prov_ctx *provctx = vprovctx;
+
+    return ibmca_keymgmt_dh_gen_init_type(provctx, selection, params,
+                                          EVP_PKEY_DH);
+}
+
+static void *ibmca_keymgmt_dhx_gen_init(void *vprovctx, int selection,
+                                        const OSSL_PARAM params[])
+{
+    const struct ibmca_prov_ctx *provctx = vprovctx;
+
+    return ibmca_keymgmt_dh_gen_init_type(provctx, selection, params,
+                                          EVP_PKEY_DHX);
+}
+
+static int ibmca_keymgmt_dh_gen_set_template(void *vgenctx, void *vtempl)
+{
+    struct ibmca_op_ctx *genctx = vgenctx;
+    struct ibmca_key *templ = vtempl;
+
+    if (genctx == NULL || templ == NULL)
+        return 0;
+
+    ibmca_debug_op_ctx(genctx, "genctx: %p templ: %p", genctx, templ);
+
+    if (genctx->type != templ->type) {
+        put_error_op_ctx(genctx, IBMCA_ERR_INVALID_PARAM,
+                         "invalid template key type");
+        return 0;
+    }
+
+    /* Don't generate DH parameters */
+    if (genctx->dh.gen.pctx != NULL)
+        EVP_PKEY_CTX_free(genctx->dh.gen.pctx);
+    genctx->dh.gen.pctx = NULL;
+
+    ibmca_keymgmt_upref(templ);
+    genctx->key = templ;
+
+    return 1;
+}
+
+static int ibmca_keymgmt_dh_gen_set_params(void *vgenctx,
+                                           const OSSL_PARAM params[])
+{
+    struct ibmca_op_ctx *genctx = vgenctx;
+    const OSSL_PARAM *p;
+    int rc;
+
+    if (genctx == NULL)
+        return 0;
+
+    ibmca_debug_op_ctx(genctx, "genctx: %p", genctx);
+    for (p = params; p != NULL && p->key != NULL; p++)
+        ibmca_debug_op_ctx(genctx, "param: %s", p->key);
+
+    if (genctx->dh.gen.pctx == NULL) {
+        put_error_op_ctx(genctx, IBMCA_ERR_INVALID_PARAM,
+                         "Can not set parameters when template is used");
+        return 0;
+    }
+
+    if (EVP_PKEY_CTX_set_params(genctx->dh.gen.pctx, params) != 1) {
+        put_error_op_ctx(genctx, IBMCA_ERR_INVALID_PARAM,
+                         "EVP_PKEY_CTX_set_params failed");
+        return 0;
+    }
+
+    /*  OSSL_PKEY_PARAM_DH_PRIV_LEN */
+    rc = ibmca_param_get_int(genctx->provctx, params,
+                             OSSL_PKEY_PARAM_DH_PRIV_LEN,
+                             &genctx->dh.gen.priv_len);
+    if (rc == 0)
+        return 0;
+
+    return 1;
+}
+
+static const OSSL_PARAM ibmca_dh_op_ctx_settable_params[] = {
+    OSSL_PARAM_utf8_string(OSSL_PKEY_PARAM_FFC_TYPE, NULL, 0),
+    OSSL_PARAM_utf8_string(OSSL_PKEY_PARAM_GROUP_NAME, NULL, 0),
+    OSSL_PARAM_int(OSSL_PKEY_PARAM_DH_PRIV_LEN, NULL),
+    OSSL_PARAM_size_t(OSSL_PKEY_PARAM_FFC_PBITS, NULL),
+    OSSL_PARAM_int(OSSL_PKEY_PARAM_DH_GENERATOR, NULL),
+    OSSL_PARAM_END
+};
+
+static const OSSL_PARAM ibmca_dhx_op_ctx_settable_params[] = {
+    OSSL_PARAM_utf8_string(OSSL_PKEY_PARAM_FFC_TYPE, NULL, 0),
+    OSSL_PARAM_utf8_string(OSSL_PKEY_PARAM_GROUP_NAME, NULL, 0),
+    OSSL_PARAM_int(OSSL_PKEY_PARAM_DH_PRIV_LEN, NULL),
+    OSSL_PARAM_size_t(OSSL_PKEY_PARAM_FFC_PBITS, NULL),
+    OSSL_PARAM_size_t(OSSL_PKEY_PARAM_FFC_QBITS, NULL),
+    OSSL_PARAM_utf8_string(OSSL_PKEY_PARAM_FFC_DIGEST, NULL, 0),
+    OSSL_PARAM_utf8_string(OSSL_PKEY_PARAM_FFC_DIGEST_PROPS, NULL, 0),
+    OSSL_PARAM_int(OSSL_PKEY_PARAM_FFC_GINDEX, NULL),
+    OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_FFC_SEED, NULL, 0),
+    OSSL_PARAM_int(OSSL_PKEY_PARAM_FFC_PCOUNTER, NULL),
+    OSSL_PARAM_int(OSSL_PKEY_PARAM_FFC_H, NULL),
+    OSSL_PARAM_END
+};
+
+static const OSSL_PARAM *ibmca_keymgmt_dh_gen_settable_params(void *vgenctx,
+                                                              void *vprovctx)
+{
+    const struct ibmca_op_ctx *genctx = vgenctx;
+    const struct ibmca_prov_ctx *provctx = vprovctx;
+    const OSSL_PARAM *params, *p;
+
+    UNUSED(genctx);
+
+    if (provctx == NULL)
+        return NULL;
+
+    params = ibmca_dh_op_ctx_settable_params;
+    for (p = params; p != NULL && p->key != NULL; p++)
+        ibmca_debug_ctx(provctx, "param: %s", p->key);
+
+    return params;
+}
+
+static const OSSL_PARAM *ibmca_keymgmt_dhx_gen_settable_params(void *vgenctx,
+                                                               void *vprovctx)
+{
+    const struct ibmca_op_ctx *genctx = vgenctx;
+    const struct ibmca_prov_ctx *provctx = vprovctx;
+    const OSSL_PARAM *params, *p;
+
+    UNUSED(genctx);
+
+    if (provctx == NULL)
+        return NULL;
+
+    params = ibmca_dhx_op_ctx_settable_params;
+    for (p = params; p != NULL && p->key != NULL; p++)
+        ibmca_debug_ctx(provctx, "param: %s", p->key);
+
+    return params;
+}
+
+static int ibmca_keymgmt_dh_gen_fallback(struct ibmca_op_ctx *genctx,
+                                         struct ibmca_key *key,
+                                         OSSL_CALLBACK *osslcb, void *cbarg)
+{
+    struct ibmca_keygen_cb_data cbdata;
+    EVP_PKEY_CTX *pctx = NULL;
+    EVP_PKEY *pkey = NULL;
+    EVP_PKEY *params = NULL;
+    int rc = 0;
+
+    ibmca_debug_op_ctx(genctx, "genctx: %p", genctx);
+
+    params = ibmca_new_fallback_pkey(key);
+    if (params == NULL) {
+        ibmca_debug_op_ctx(genctx,"ERROR: ibmca_new_fallback_pkey failed");
+        goto out;
+    }
+
+    pctx = ibmca_new_fallback_pkey_ctx(genctx->provctx, params, NULL);
+    if (pctx == NULL) {
+        ibmca_debug_op_ctx(genctx, "ERROR: ibmca_new_fallback_pkey_ctx failed");
+        goto out;
+    }
+
+    if (EVP_PKEY_keygen_init(pctx) != 1) {
+        put_error_key(key, IBMCA_ERR_INTERNAL_ERROR,
+                     "EVP_PKEY_keygen_init failed");
+        goto out;
+    }
+
+    if (ibmca_check_fallback_provider(genctx->provctx, pctx) != 1) {
+        ibmca_debug_op_ctx(genctx,
+                           "ERROR: ibmca_check_fallback_provider failed");
+        goto out;
+    }
+
+    if (osslcb != NULL) {
+        cbdata.osslcb = osslcb;
+        cbdata.cbarg = cbarg;
+        EVP_PKEY_CTX_set_cb(pctx, ibmca_keygen_cb);
+        EVP_PKEY_CTX_set_app_data(pctx, &cbdata);
+    }
+
+    if (EVP_PKEY_generate(pctx, &pkey) != 1) {
+        put_error_key(key, IBMCA_ERR_INTERNAL_ERROR,
+                      "EVP_PKEY_generate failed");
+        goto out;
+    }
+
+    rc = ibmca_import_from_fallback_pkey(key, pkey, OSSL_KEYMGMT_SELECT_ALL);
+    if (rc != 1) {
+        ibmca_debug_op_ctx(genctx,
+                           "ERROR: ibmca_import_from_fallback_pkey failed");
+        goto out;
+    }
+
+    rc = 1;
+
+out:
+    if (pctx != NULL)
+        EVP_PKEY_CTX_free(pctx);
+    if (params != NULL)
+        EVP_PKEY_free(params);
+    if (pkey != NULL)
+        EVP_PKEY_free(pkey);
+
+    return rc;
+}
+
+static int ibmca_keymgmt_dh_gen_priv_key(struct ibmca_key *key, BN_CTX *bn_ctx,
+                                             int priv_len, int strength)
+{
+    int rc = 0, qbits = BN_num_bits(key->dh.ffc_params.q);
+    BIGNUM *m, *two_powN = NULL;
+
+    ibmca_debug_key(key, "key: %p priv_len: %d strength: %d", key, priv_len,
+                    strength);
+
+    /* Generate a private key in the interval [1, min(2 ^ N - 1, q - 1)]. */
+
+    if (priv_len == 0)
+        priv_len = qbits;
+    if (strength == 0)
+        strength = priv_len / 2;
+
+    if (priv_len < 2 * strength || priv_len > qbits) {
+        put_error_key(key, IBMCA_ERR_INVALID_PARAM, "priv_len is invalid");
+        return 0;
+    }
+
+    two_powN = BN_new();
+    if (two_powN == NULL ||
+        BN_lshift(two_powN, BN_value_one(), priv_len) != 1) {
+        put_error_key(key, IBMCA_ERR_INTERNAL_ERROR, "BN_new/BN_lshift failed");
+        goto out;
+    }
+
+    m = (BN_cmp(two_powN, key->dh.ffc_params.q) > 0) ?
+                                            key->dh.ffc_params.q : two_powN;
+
+    key->dh.priv = BN_secure_new();
+    if (key->dh.priv == NULL) {
+        put_error_key(key, IBMCA_ERR_MALLOC_FAILED, "BN_secure_new failed");
+        goto out;
+    }
+
+    do {
+        if (BN_priv_rand_range_ex(key->dh.priv, two_powN, 0, bn_ctx) != 1 ||
+            BN_add_word(key->dh.priv, 1) != 1) {
+            put_error_key(key, IBMCA_ERR_INTERNAL_ERROR,
+                          "BN_priv_rand_range_ex/BN_add_word failed");
+            goto out;
+        }
+
+        if (BN_cmp(key->dh.priv, m) < 0)
+            break;
+    } while (1);
+
+    rc = 1;
+
+out:
+    if (two_powN != NULL)
+        BN_free(two_powN);
+    if (rc != 1 && key->dh.priv != NULL) {
+        BN_clear_free(key->dh.priv);
+        key->dh.priv = NULL;
+    }
+
+    return rc;
+}
+
+static int ibmca_keymgmt_dh_gen_libica(struct ibmca_key *key, int priv_len,
+                                       OSSL_CALLBACK *osslcb, void *cbarg)
+{
+    OSSL_PARAM cb_params[] = { OSSL_PARAM_END, OSSL_PARAM_END, OSSL_PARAM_END };
+    int rc = 0, p, n, max_strength, len;
+    ica_rsa_key_mod_expo_t mod_exp;
+    BN_CTX *bn_ctx = NULL;
+    size_t prime_size = 0;
+    unsigned char *buf = NULL, *pub;
+
+    ibmca_debug_key(key, "key: %p priv_len: %d", key, priv_len);
+
+    cb_params[0] = OSSL_PARAM_construct_int(OSSL_GEN_PARAM_POTENTIAL, &p);
+    cb_params[1] = OSSL_PARAM_construct_int(OSSL_GEN_PARAM_ITERATION, &n);
+
+    if (ibmca_keymgmt_dh_has(key,
+                             OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS) != 1) {
+        put_error_key(key, IBMCA_ERR_INTERNAL_ERROR,
+                      "required DH parameters not available");
+        return 0;
+    }
+
+    ibmca_debug_key(key, "group_nid: %d", key->dh.ffc_params.group_nid);
+
+    /* Generate private key */
+
+    p = 0;
+    n = 0;
+    if (osslcb != NULL && osslcb(cb_params, cbarg) == 0) {
+        put_error_key(key, IBMCA_ERR_INTERNAL_ERROR, "osslcb failed");
+        goto out;
+    }
+
+    bn_ctx = BN_CTX_new_ex(key->provctx->libctx);
+    if (bn_ctx == NULL) {
+        put_error_key(key, IBMCA_ERR_INTERNAL_ERROR, "BN_CTX_new_ex failed");
+        return 0;
+    }
+
+    if (key->dh.priv != NULL)
+        BN_free(key->dh.priv);
+    key->dh.priv = NULL;
+    if (key->dh.pub != NULL)
+        BN_free(key->dh.pub);
+    key->dh.pub = NULL;
+
+    if (key->dh.ffc_params.group_nid != NID_undef) {
+        /* named group */
+        if (key->dh.ffc_params.q == NULL) {
+            put_error_key(key, IBMCA_ERR_INTERNAL_ERROR,
+                          "required DH parameters not available");
+            goto out;
+        }
+
+        if (priv_len > BN_num_bits(key->dh.ffc_params.q)) {
+            put_error_key(key, IBMCA_ERR_INTERNAL_ERROR, "priv_len too large");
+            goto out;
+        }
+
+        max_strength = ibmca_keymgmt_dh_get_security_bits(key);
+
+        if (ibmca_keymgmt_dh_gen_priv_key(key, bn_ctx, priv_len,
+                                          max_strength) != 1) {
+            ibmca_debug_key(key, "ERROR: ibmca_keymgmt_dh_gen_priv_key failed");
+            goto out;
+        }
+    } else if (key->dh.ffc_params.q == NULL) {
+        /* secret exponent length, must satisfy 2^(l-1) <= p */
+        if (priv_len >= BN_num_bits(key->dh.ffc_params.p)) {
+            put_error_key(key, IBMCA_ERR_INTERNAL_ERROR, "priv_len too large");
+            goto out;
+        }
+        len = priv_len > 0 ? priv_len : BN_num_bits(key->dh.ffc_params.p) - 1;
+
+        key->dh.priv = BN_secure_new();
+        if (key->dh.priv == NULL) {
+            put_error_key(key, IBMCA_ERR_MALLOC_FAILED, "BN_secure_new failed");
+            goto out;
+        }
+
+        if (BN_priv_rand_ex(key->dh.priv, len, BN_RAND_TOP_ONE,
+                            BN_RAND_BOTTOM_ANY, 0, bn_ctx) != 1) {
+            put_error_key(key, IBMCA_ERR_INTERNAL_ERROR,
+                          "BN_priv_rand_ex failed");
+            BN_clear_free(key->dh.priv);
+            key->dh.priv = NULL;
+            goto out;
+        }
+
+        /*
+         * Check for one known case where g is a quadratic non-residue:
+         * for g = 2: p % 8 == 3
+         */
+        if (BN_is_word(key->dh.ffc_params.g, DH_GENERATOR_2) &&
+            !BN_is_bit_set(key->dh.ffc_params.p, 2)) {
+             /* clear bit 0, since it won't be a secret anyway */
+             if (BN_clear_bit(key->dh.priv, 0) != 1) {
+                 put_error_key(key, IBMCA_ERR_INTERNAL_ERROR,
+                               "BN_clear_bit failed");
+                 BN_clear_free(key->dh.priv);
+                 key->dh.priv = NULL;
+                 goto out;
+             }
+        }
+    } else {
+        if (ibmca_keymgmt_dh_gen_priv_key(key, bn_ctx,
+                                          BN_num_bits(key->dh.ffc_params.q),
+                                          key->provctx->fips ? 112 : 80) != 1) {
+            ibmca_debug_key(key, "ERROR: ibmca_keymgmt_dh_gen_priv_key failed");
+            goto out;
+        }
+    }
+
+    /* Generate public key */
+
+    p = 1;
+    n = 0;
+    if (osslcb != NULL && osslcb(cb_params, cbarg) == 0) {
+        put_error_key(key, IBMCA_ERR_INTERNAL_ERROR, "osslcb failed");
+        goto out;
+    }
+
+    prime_size = ibmca_keymgmt_dh_get_prime_size(key);
+    buf = P_SECURE_ZALLOC(key->provctx, prime_size * 4);
+    if (buf == NULL) {
+        put_error_key(key, IBMCA_ERR_MALLOC_FAILED,
+                      "Failed to allocate mod-expo buffer");
+        goto out;
+    }
+
+    /* pub_key = g^priv_key mod p */
+    mod_exp.key_length = prime_size;
+    mod_exp.modulus = buf + prime_size;
+    mod_exp.exponent = buf + 2 * prime_size;
+    pub = buf + 3 * prime_size;
+
+    if (BN_bn2binpad(key->dh.ffc_params.p, mod_exp.modulus, prime_size) <= 0 ||
+        BN_bn2binpad(key->dh.priv, mod_exp.exponent, prime_size) <= 0 ||
+        BN_bn2binpad(key->dh.ffc_params.g, buf, prime_size) <= 0) {
+        put_error_key(key, IBMCA_ERR_INTERNAL_ERROR, "BN_bn2binpad failed");
+        goto out;
+    }
+
+    rc = ica_rsa_mod_expo(key->provctx->ica_adapter, buf, &mod_exp, pub);
+    if (rc != 0) {
+        ibmca_debug_key(key, "ica_rsa_mod_expo failed with: %s", strerror(rc));
+        rc = 0;
+        goto out;
+    }
+
+    key->dh.pub = BN_bin2bn(pub, prime_size, NULL);
+    if (key->dh.pub == NULL)  {
+        put_error_key(key, IBMCA_ERR_INTERNAL_ERROR, "BN_bin2bn failed");
+        goto out;
+    }
+
+    p = 3;
+    n = 0;
+    if (osslcb != NULL && osslcb(cb_params, cbarg) == 0) {
+        put_error_key(key, IBMCA_ERR_INTERNAL_ERROR, "osslcb failed");
+        goto out;
+    }
+
+    rc = 1;
+
+out:
+    if (bn_ctx != NULL)
+        BN_CTX_free(bn_ctx);
+    if (buf != NULL)
+        P_CLEAR_FREE(key->provctx, buf, prime_size * 4);
+
+    return rc;
+}
+
+static void *ibmca_keymgmt_dh_gen(void *vgenctx, OSSL_CALLBACK *osslcb,
+                                  void *cbarg)
+{
+    struct ibmca_op_ctx *genctx = vgenctx;
+    struct ibmca_key *key = NULL;
+    struct ibmca_keygen_cb_data cbdata;
+    EVP_PKEY *pkey = NULL;
+    int rc;
+
+    if (genctx == NULL)
+        return NULL;
+
+    ibmca_debug_op_ctx(genctx, "genctx: %p", genctx);
+
+    key = ibmca_keymgmt_new(genctx->provctx, genctx->type,
+                            genctx->type == EVP_PKEY_DHX ? "DHX" : "DH",
+                            ibmca_keymgmt_dh_free_cb,
+                            ibmca_keymgmt_dh_dup_cb,
+                            ibmca_keymgmt_dh_get_prime_size,
+                            ibmca_keymgmt_dh_export,
+                            ibmca_keymgmt_dh_import,
+                            ibmca_keymgmt_dh_has,
+                            ibmca_keymgmt_dh_match);
+    if (key == NULL) {
+        ibmca_debug_op_ctx(genctx, "ERROR: ibmca_keymgmt_new failed");
+        return NULL;
+    }
+
+    if (genctx->dh.gen.pctx != NULL) {
+        /* Generate DH parameters only */
+        if (osslcb != NULL) {
+            cbdata.osslcb = osslcb;
+            cbdata.cbarg = cbarg;
+            EVP_PKEY_CTX_set_cb(genctx->dh.gen.pctx, ibmca_keygen_cb);
+            EVP_PKEY_CTX_set_app_data(genctx->dh.gen.pctx, &cbdata);
+        }
+
+        if (EVP_PKEY_generate(genctx->dh.gen.pctx, &pkey) != 1) {
+            put_error_key(key, IBMCA_ERR_INTERNAL_ERROR,
+                          "EVP_PKEY_generate failed");
+            goto error;
+        }
+
+        rc = ibmca_import_from_fallback_pkey(key, pkey,
+                                             OSSL_KEYMGMT_SELECT_ALL_PARAMETERS);
+        if (rc != 1) {
+            ibmca_debug_op_ctx(genctx,
+                               "ERROR: ibmca_import_from_fallback_pkey failed");
+            goto error;
+        }
+    } else if (genctx->key != NULL) {
+        /* Copy parameters from template key */
+        if (ibmca_keymgmt_dh_dup_params(genctx->key, key) != 1) {
+            ibmca_debug_op_ctx(genctx,
+                               "ERROR: ibmca_keymgmt_dh_dup_params failed");
+            goto error;
+        }
+    } else {
+        put_error_key(key, IBMCA_ERR_INTERNAL_ERROR,
+                      "Neither parmagen context nor template key available");
+        goto error;
+    }
+
+    if (genctx->dh.gen.selection & OSSL_KEYMGMT_SELECT_KEYPAIR) {
+        if (ibmca_keymgmt_dh_gen_libica(key, genctx->dh.gen.priv_len, osslcb,
+                                        cbarg) != 1) {
+            ibmca_debug_op_ctx(genctx,
+                               "ERROR: ibmca_keymgmt_dh_gen_libica failed");
+
+            if (ibmca_keymgmt_dh_gen_fallback(genctx, key,
+                                              osslcb, cbarg) != 1) {
+                ibmca_debug_op_ctx(genctx,
+                                   "ERROR: ibmca_keymgmt_dh_gen_fallback failed");
+                goto error;
+            }
+        }
+     }
+
+    goto out;
+
+error:
+    ibmca_keymgmt_free(key);
+    key = NULL;
+
+out:
+    if (pkey != NULL)
+        EVP_PKEY_free(pkey);
+
+    return key;
+}
+
+static int ibmca_keymgmt_dh_has(const void *vkey, int selection)
+{
+    const struct ibmca_key *key = vkey;
+    int ok = 1;
+
+    if (key == NULL)
+        return 0;
+
+    ibmca_debug_key(key, "key: %p selection: 0x%x", key, selection);
+
+    if ((selection & OSSL_KEYMGMT_SELECT_PUBLIC_KEY) != 0)
+        ok = ok && (key->dh.pub != NULL);
+
+    if ((selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) != 0)
+        ok = ok && (key->dh.priv != NULL);
+
+    if ((selection & OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS) != 0) {
+        ok = ok && (key->dh.ffc_params.p != NULL &&
+                    key->dh.ffc_params.g != NULL);
+        if (key->type == EVP_PKEY_DHX)
+            ok = ok && (key->dh.ffc_params.q != NULL);
+    }
+
+    ibmca_debug_key(key, "ok: %d", ok);
+
+    return ok;
+}
+
+static int ibmca_keymgmt_dh_match(const void *vkey1, const void *vkey2,
+                                  int selection)
+{
+    const struct ibmca_key *key1 = vkey1;
+    const struct ibmca_key *key2 = vkey2;
+    int ok = 1, checked = 0;
+
+    if (key1 == NULL || key2 == NULL)
+        return 0;
+
+    ibmca_debug_key(key1, "key1: %p key2: %p selection: 0x%x", key1, key2,
+                    selection);
+
+    if (ibmca_keymgmt_match(key1, key2) == 0)
+        return 0;
+
+    if ((selection & OSSL_KEYMGMT_SELECT_KEYPAIR) != 0) {
+        if ((selection & OSSL_KEYMGMT_SELECT_PUBLIC_KEY) != 0) {
+            if (key1->dh.pub != NULL || key2->dh.pub != NULL) {
+                ok = ok && (BN_cmp(key1->dh.pub, key2->dh.pub) == 0);
+                checked = 1;
+            }
+        }
+
+        if (!checked && (selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) != 0) {
+            if (key1->dh.priv != NULL || key2->dh.priv != NULL) {
+                ok = ok && (BN_cmp(key1->dh.priv, key2->dh.priv) == 0);
+                checked = 1;
+            }
+        }
+
+        ok = ok && checked;
+    }
+
+    if ((selection & OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS) != 0) {
+        ok = ok && (BN_cmp(key1->dh.ffc_params.p, key2->dh.ffc_params.p) == 0);
+        ok = ok && (BN_cmp(key1->dh.ffc_params.g, key2->dh.ffc_params.g) == 0);
+        if (key1->type == EVP_PKEY_DHX)
+            ok = ok &&
+                 (BN_cmp(key1->dh.ffc_params.q, key2->dh.ffc_params.q) == 0);
+    }
+
+    ibmca_debug_key(key1, "ok: %d", ok);
+
+    return ok;
+}
+
+static int ibmca_keymgmt_dh_validate(const void *vkey, int selection,
+                                     int checktype)
+{
+    struct ibmca_key *key = (struct ibmca_key *)vkey;
+    EVP_PKEY *pkey = NULL;
+    EVP_PKEY_CTX *pctx = NULL;
+    int rc = 0;
+
+    if (key == NULL)
+        return 0;
+
+    ibmca_debug_key(key, "key: %p selection: 0x%x checktype: 0x%x", key,
+                    selection, checktype);
+
+    pkey = ibmca_new_fallback_pkey(key);
+    if (pkey == NULL)
+        goto out;
+
+    pctx = ibmca_new_fallback_pkey_ctx(key->provctx, pkey, NULL);
+    if (pctx == NULL)
+        goto out;
+
+    if ((selection & OSSL_KEYMGMT_SELECT_KEYPAIR)
+                            == OSSL_KEYMGMT_SELECT_KEYPAIR) {
+        rc = EVP_PKEY_check(pctx);
+    } else if ((selection & OSSL_KEYMGMT_SELECT_PUBLIC_KEY) != 0) {
+        rc = EVP_PKEY_public_check(pctx);
+    } else if ((selection & OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS) != 0) {
+        rc = EVP_PKEY_param_check(pctx);
+    }
+
+out:
+    ibmca_debug_key(key, "valid: %d", rc);
+
+    if (pctx != NULL)
+        EVP_PKEY_CTX_free(pctx);
+    if (pkey != NULL)
+        EVP_PKEY_free(pkey);
+
+    return rc;
+}
+
+static const char *ibmca_keymgmt_dh_query_operation_name(int operation_id)
+{
+    switch (operation_id) {
+    case OSSL_OP_KEYEXCH:
+        return "DH"; /* DHX also uses "DH" as operation name for KEYEXCH */
+    }
+
+    return NULL;
+}
+
+static const OSSL_PARAM ibmca_dh_gettable_params[] = {
+    OSSL_PARAM_int(OSSL_PKEY_PARAM_BITS, NULL),
+    OSSL_PARAM_int(OSSL_PKEY_PARAM_SECURITY_BITS, NULL),
+    OSSL_PARAM_int(OSSL_PKEY_PARAM_MAX_SIZE, NULL),
+    OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_ENCODED_PUBLIC_KEY, NULL, 0),
+    OSSL_PARAM_BN(OSSL_PKEY_PARAM_PUB_KEY, NULL, 0),
+    OSSL_PARAM_BN(OSSL_PKEY_PARAM_PRIV_KEY, NULL, 0),
+    OSSL_PARAM_BN(OSSL_PKEY_PARAM_FFC_P, NULL, 0),
+    OSSL_PARAM_BN(OSSL_PKEY_PARAM_FFC_G, NULL, 0),
+    OSSL_PARAM_BN(OSSL_PKEY_PARAM_FFC_COFACTOR, NULL, 0),
+    OSSL_PARAM_int(OSSL_PKEY_PARAM_FFC_GINDEX, NULL),
+    OSSL_PARAM_int(OSSL_PKEY_PARAM_FFC_PCOUNTER, NULL),
+    OSSL_PARAM_int(OSSL_PKEY_PARAM_FFC_H, NULL),
+    OSSL_PARAM_int(OSSL_PKEY_PARAM_DH_PRIV_LEN, NULL),
+    OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_FFC_SEED, NULL, 0),
+    OSSL_PARAM_utf8_string(OSSL_PKEY_PARAM_GROUP_NAME, NULL, 0),
+    OSSL_PARAM_END
+};
+
+static const OSSL_PARAM *ibmca_keymgmt_dh_gettable_params(void *vprovctx)
+{
+    const struct ibmca_prov_ctx *provctx = vprovctx;
+    const OSSL_PARAM *p;
+
+    if (provctx == NULL)
+        return NULL;
+
+    for (p = ibmca_dh_gettable_params; p != NULL && p->key != NULL; p++)
+        ibmca_debug_ctx(provctx, "param: %s", p->key);
+
+    return ibmca_dh_gettable_params;
+}
+
+static int ibmca_keymgmt_dh_get_params(void *vkey, OSSL_PARAM params[])
+{
+    struct ibmca_key *key = vkey;
+    OSSL_PARAM *parm;
+    const char *name;
+    unsigned char *enc = NULL;
+    size_t size;
+    int rc;
+
+    if (key == NULL)
+        return 0;
+
+    ibmca_debug_key(key, "key: %p", key);
+    for (parm = params; parm != NULL && parm->key != NULL; parm++)
+        ibmca_debug_key(key, "param: %s", parm->key);
+
+    /* OSSL_PKEY_PARAM_BITS */
+    if (OSSL_PARAM_locate(params, OSSL_PKEY_PARAM_BITS) != NULL) {
+        size = ibmca_keymgmt_dh_get_prime_bits(key);
+        if (size == 0)
+            return 0;
+
+        rc = ibmca_param_build_set_int(key->provctx, NULL, params,
+                                       OSSL_PKEY_PARAM_BITS, size);
+        if (rc == 0)
+            return 0;
+    }
+
+    /* OSSL_PKEY_PARAM_SECURITY_BITS */
+    if (OSSL_PARAM_locate(params, OSSL_PKEY_PARAM_SECURITY_BITS) != NULL) {
+        size = ibmca_keymgmt_dh_get_security_bits(key);
+        if (size == 0)
+            return 0;
+
+        rc = ibmca_param_build_set_int(key->provctx, NULL, params,
+                                       OSSL_PKEY_PARAM_SECURITY_BITS, size);
+        if (rc == 0)
+            return 0;
+    }
+
+    /* OSSL_PKEY_PARAM_MAX_SIZE */
+    if (OSSL_PARAM_locate(params, OSSL_PKEY_PARAM_MAX_SIZE) != NULL) {
+        size = ibmca_keymgmt_dh_get_prime_size(key);
+        if (size == 0)
+            return 0;
+
+        rc = ibmca_param_build_set_int(key->provctx, NULL, params,
+                                       OSSL_PKEY_PARAM_MAX_SIZE, size);
+        if (rc == 0)
+            return 0;
+    }
+
+    /* OSSL_PKEY_PARAM_ENCODED_PUBLIC_KEY */
+    if (OSSL_PARAM_locate(params, OSSL_PKEY_PARAM_ENCODED_PUBLIC_KEY) != NULL &&
+        key->dh.pub != NULL) {
+        size = ibmca_keymgmt_dh_get_prime_size(key);
+        if (size == 0)
+            return 0;
+
+        enc = P_ZALLOC(key->provctx, size);
+        if (enc == NULL) {
+            put_error_key(key, IBMCA_ERR_MALLOC_FAILED,
+                          "Failed to allocate encoded pubkey buffer");
+            return 0;
+        }
+        if (BN_bn2binpad(key->dh.pub, enc, size) <= 0) {
+            put_error_key(key, IBMCA_ERR_INTERNAL_ERROR, "BN_bn2binpad failed");
+            P_FREE(key->provctx, enc);
+            return 0;
+        }
+
+        rc = ibmca_param_build_set_octet_ptr(key->provctx, NULL, params,
+                                             OSSL_PKEY_PARAM_ENCODED_PUBLIC_KEY,
+                                             enc, size);
+        P_FREE(key->provctx, enc);
+        if (rc == 0)
+            return 0;
+    }
+
+    /* OSSL_PKEY_PARAM_PRIV_KEY */
+    if (OSSL_PARAM_locate(params, OSSL_PKEY_PARAM_PRIV_KEY) != NULL &&
+        key->dh.priv != NULL) {
+        rc = ibmca_param_build_set_bn(key->provctx, NULL, params,
+                                      OSSL_PKEY_PARAM_PRIV_KEY,
+                                      key->dh.priv);
+        if (rc == 0)
+            return 0;
+    }
+
+    /* OSSL_PKEY_PARAM_PUB_KEY */
+    if (OSSL_PARAM_locate(params, OSSL_PKEY_PARAM_PUB_KEY) != NULL &&
+        key->dh.pub != NULL) {
+        rc = ibmca_param_build_set_bn(key->provctx, NULL, params,
+                                      OSSL_PKEY_PARAM_PUB_KEY,
+                                      key->dh.pub);
+        if (rc == 0)
+            return 0;
+    }
+
+    /* OSSL_PKEY_PARAM_FFC_P */
+    if (OSSL_PARAM_locate(params, OSSL_PKEY_PARAM_FFC_P) != NULL &&
+        key->dh.ffc_params.p != NULL) {
+        rc = ibmca_param_build_set_bn(key->provctx, NULL, params,
+                                      OSSL_PKEY_PARAM_FFC_P,
+                                      key->dh.ffc_params.p);
+        if (rc == 0)
+            return 0;
+    }
+
+    /* OSSL_PKEY_PARAM_FFC_Q */
+    if (OSSL_PARAM_locate(params, OSSL_PKEY_PARAM_FFC_Q) != NULL &&
+        key->dh.ffc_params.q != NULL) {
+        rc = ibmca_param_build_set_bn(key->provctx, NULL, params,
+                                      OSSL_PKEY_PARAM_FFC_Q,
+                                      key->dh.ffc_params.q);
+        if (rc == 0)
+            return 0;
+    }
+
+    /* OSSL_PKEY_PARAM_FFC_G */
+    if (OSSL_PARAM_locate(params, OSSL_PKEY_PARAM_FFC_G) != NULL &&
+        key->dh.ffc_params.g != NULL) {
+        rc = ibmca_param_build_set_bn(key->provctx, NULL, params,
+                                      OSSL_PKEY_PARAM_FFC_G,
+                                      key->dh.ffc_params.g);
+        if (rc == 0)
+            return 0;
+    }
+
+    /* OSSL_PKEY_PARAM_FFC_COFACTOR */
+    if (OSSL_PARAM_locate(params, OSSL_PKEY_PARAM_FFC_COFACTOR) != NULL &&
+        key->dh.ffc_params.cofactor != NULL) {
+        rc = ibmca_param_build_set_bn(key->provctx, NULL, params,
+                                      OSSL_PKEY_PARAM_FFC_COFACTOR,
+                                      key->dh.ffc_params.cofactor);
+        if (rc == 0)
+            return 0;
+    }
+
+    /* OSSL_PKEY_PARAM_FFC_GINDEX */
+    if (OSSL_PARAM_locate(params, OSSL_PKEY_PARAM_FFC_GINDEX) != NULL) {
+        rc = ibmca_param_build_set_int(key->provctx, NULL, params,
+                                       OSSL_PKEY_PARAM_FFC_GINDEX,
+                                       key->dh.ffc_params.gindex);
+        if (rc == 0)
+            return 0;
+    }
+
+    /* OSSL_PKEY_PARAM_FFC_PCOUNTER */
+    if (OSSL_PARAM_locate(params, OSSL_PKEY_PARAM_FFC_PCOUNTER) != NULL) {
+        rc = ibmca_param_build_set_int(key->provctx, NULL, params,
+                                       OSSL_PKEY_PARAM_FFC_PCOUNTER,
+                                       key->dh.ffc_params.pcounter);
+        if (rc == 0)
+            return 0;
+    }
+
+    /* OSSL_PKEY_PARAM_FFC_H */
+    if (OSSL_PARAM_locate(params, OSSL_PKEY_PARAM_FFC_H) != NULL) {
+        rc = ibmca_param_build_set_int(key->provctx, NULL, params,
+                                       OSSL_PKEY_PARAM_FFC_H,
+                                       key->dh.ffc_params.hindex);
+        if (rc == 0)
+            return 0;
+    }
+
+    /* OSSL_PKEY_PARAM_FFC_SEED */
+    if (OSSL_PARAM_locate(params, OSSL_PKEY_PARAM_FFC_SEED) != NULL &&
+        key->dh.ffc_params.seed != NULL && key->dh.ffc_params.seed_len > 0) {
+        rc = ibmca_param_build_set_octet_ptr(key->provctx, NULL, params,
+                                             OSSL_PKEY_PARAM_FFC_SEED,
+                                             key->dh.ffc_params.seed,
+                                             key->dh.ffc_params.seed_len);
+        if (rc == 0)
+            return 0;
+    }
+
+    /* OSSL_PKEY_PARAM_GROUP_NAME */
+    if (OSSL_PARAM_locate(params, OSSL_PKEY_PARAM_GROUP_NAME) != NULL &&
+        key->dh.ffc_params.group_nid != NID_undef) {
+        name = OBJ_nid2sn(key->dh.ffc_params.group_nid);
+        rc = ibmca_param_build_set_utf8(key->provctx, NULL, params,
+                                        OSSL_PKEY_PARAM_GROUP_NAME, name);
+        if (rc == 0)
+            return 0;
+    }
+
+    /* OSSL_PKEY_PARAM_FFC_VALIDATE_PQ */
+    if (OSSL_PARAM_locate(params, OSSL_PKEY_PARAM_FFC_VALIDATE_PQ) != NULL) {
+        rc = ibmca_param_build_set_int(key->provctx, NULL, params,
+                                       OSSL_PKEY_PARAM_FFC_VALIDATE_PQ,
+                                       key->dh.ffc_params.validate_pq);
+        if (rc == 0)
+            return 0;
+    }
+
+    /* OSSL_PKEY_PARAM_FFC_VALIDATE_G */
+    if (OSSL_PARAM_locate(params, OSSL_PKEY_PARAM_FFC_VALIDATE_G) != NULL) {
+        rc = ibmca_param_build_set_int(key->provctx, NULL, params,
+                                       OSSL_PKEY_PARAM_FFC_VALIDATE_G,
+                                       key->dh.ffc_params.validate_g);
+        if (rc == 0)
+            return 0;
+    }
+
+    /* OSSL_PKEY_PARAM_FFC_VALIDATE_LEGACY */
+    if (OSSL_PARAM_locate(params, OSSL_PKEY_PARAM_FFC_VALIDATE_LEGACY)
+                                                                    != NULL) {
+        rc = ibmca_param_build_set_int(key->provctx, NULL, params,
+                                       OSSL_PKEY_PARAM_FFC_VALIDATE_LEGACY,
+                                       key->dh.ffc_params.validate_legacy);
+        if (rc == 0)
+            return 0;
+    }
+
+    /* OSSL_PKEY_PARAM_FFC_DIGEST */
+    if (OSSL_PARAM_locate(params, OSSL_PKEY_PARAM_FFC_DIGEST) != NULL &&
+        key->dh.ffc_params.mdname != NULL) {
+        rc = ibmca_param_build_set_utf8(key->provctx, NULL, params,
+                                        OSSL_PKEY_PARAM_FFC_DIGEST,
+                                        key->dh.ffc_params.mdname);
+        if (rc == 0)
+            return 0;
+    }
+
+    /* OSSL_PKEY_PARAM_FFC_DIGEST_PROPS */
+    if (OSSL_PARAM_locate(params, OSSL_PKEY_PARAM_FFC_DIGEST_PROPS) != NULL &&
+        key->dh.ffc_params.mdname != NULL) {
+        rc = ibmca_param_build_set_utf8(key->provctx, NULL, params,
+                                        OSSL_PKEY_PARAM_FFC_DIGEST_PROPS,
+                                        key->dh.ffc_params.mdprops);
+        if (rc == 0)
+            return 0;
+    }
+
+    /* OSSL_PKEY_PARAM_DH_PRIV_LEN */
+    if (OSSL_PARAM_locate(params, OSSL_PKEY_PARAM_DH_PRIV_LEN) != NULL &&
+        key->dh.ffc_params.length > 0) {
+        rc = ibmca_param_build_set_int(key->provctx, NULL, params,
+                                       OSSL_PKEY_PARAM_DH_PRIV_LEN,
+                                       key->dh.ffc_params.length);
+        if (rc == 0)
+            return 0;
+    }
+
+    return 1;
+}
+
+static const OSSL_PARAM ibmca_dh_settable_params[] = {
+    OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_ENCODED_PUBLIC_KEY, NULL, 0),
+    OSSL_PARAM_END
+};
+
+static const OSSL_PARAM *ibmca_keymgmt_dh_settable_params(void *vprovctx)
+{
+    const struct ibmca_prov_ctx *provctx = vprovctx;
+    const OSSL_PARAM *p;
+
+    if (provctx == NULL)
+        return NULL;
+
+    for (p = ibmca_dh_settable_params; p != NULL && p->key != NULL; p++)
+        ibmca_debug_ctx(provctx, "param: %s", p->key);
+
+    return ibmca_dh_settable_params;
+}
+
+static int ibmca_keymgmt_dh_set_params(void *vkey, const OSSL_PARAM params[])
+{
+    struct ibmca_key *key = vkey;
+    const OSSL_PARAM *parm;
+    unsigned char *enc = NULL;
+    size_t size = 0;
+    int rc;
+
+    if (key == NULL)
+        return 0;
+
+    ibmca_debug_key(key, "key: %p", key);
+    for (parm = params; parm != NULL && parm->key != NULL; parm++)
+        ibmca_debug_key(key, "param: %s", parm->key);
+
+    /* OSSL_PKEY_PARAM_ENCODED_PUBLIC_KEY */
+    rc = ibmca_param_get_octet_string(key->provctx, params,
+                                      OSSL_PKEY_PARAM_ENCODED_PUBLIC_KEY,
+                                      (void **)&enc, &size);
+    if (rc == 0)
+        return 0;
+    if (rc > 0) {
+        if (key->dh.pub != NULL)
+            BN_free(key->dh.pub);
+        key->dh.pub = BN_bin2bn(enc, size, NULL);
+        if (key->dh.pub == NULL) {
+            put_error_key(key, IBMCA_ERR_INTERNAL_ERROR, "BN_bin2bn failed");
+            return 0;
+        }
+        /* public key must be same size as p */
+        if (BN_is_zero(key->dh.pub) ||
+            size != ibmca_keymgmt_dh_get_prime_size(key)) {
+            BN_free(key->dh.pub);
+            key->dh.pub = NULL;
+            put_error_key(key, IBMCA_ERR_INVALID_PARAM,
+                          "DH '%s' invalid public key",
+                          OSSL_PKEY_PARAM_ENCODED_PUBLIC_KEY);
+            return 0;
+        }
+
+        ibmca_clean_fallback_pkey_cache(key);
+    }
+
+    return 1;
+}
+
+static const OSSL_PARAM ibmca_keymgmt_dh_imexport_dom_params[] = {
+    OSSL_PARAM_BN(OSSL_PKEY_PARAM_FFC_P, NULL, 0),
+    OSSL_PARAM_BN(OSSL_PKEY_PARAM_FFC_Q, NULL, 0),
+    OSSL_PARAM_BN(OSSL_PKEY_PARAM_FFC_G, NULL, 0),
+    OSSL_PARAM_BN(OSSL_PKEY_PARAM_FFC_COFACTOR, NULL, 0),
+    OSSL_PARAM_int(OSSL_PKEY_PARAM_FFC_GINDEX, NULL),
+    OSSL_PARAM_int(OSSL_PKEY_PARAM_FFC_PCOUNTER, NULL),
+    OSSL_PARAM_int(OSSL_PKEY_PARAM_FFC_H, NULL),
+    OSSL_PARAM_int(OSSL_PKEY_PARAM_DH_PRIV_LEN, NULL),
+    OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_FFC_SEED, NULL, 0),
+    OSSL_PARAM_utf8_string(OSSL_PKEY_PARAM_GROUP_NAME, NULL, 0),
+    OSSL_PARAM_END
+};
+
+static const OSSL_PARAM ibmca_keymgmt_dh_imexport_priv_key_dom_params[] = {
+    OSSL_PARAM_BN(OSSL_PKEY_PARAM_PRIV_KEY, NULL, 0),
+    OSSL_PARAM_BN(OSSL_PKEY_PARAM_FFC_P, NULL, 0),
+    OSSL_PARAM_BN(OSSL_PKEY_PARAM_FFC_Q, NULL, 0),
+    OSSL_PARAM_BN(OSSL_PKEY_PARAM_FFC_G, NULL, 0),
+    OSSL_PARAM_BN(OSSL_PKEY_PARAM_FFC_COFACTOR, NULL, 0),
+    OSSL_PARAM_int(OSSL_PKEY_PARAM_FFC_GINDEX, NULL),
+    OSSL_PARAM_int(OSSL_PKEY_PARAM_FFC_PCOUNTER, NULL),
+    OSSL_PARAM_int(OSSL_PKEY_PARAM_FFC_H, NULL),
+    OSSL_PARAM_int(OSSL_PKEY_PARAM_DH_PRIV_LEN, NULL),
+    OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_FFC_SEED, NULL, 0),
+    OSSL_PARAM_utf8_string(OSSL_PKEY_PARAM_GROUP_NAME, NULL, 0),
+    OSSL_PARAM_END
+};
+
+static const OSSL_PARAM ibmca_keymgmt_dh_imexport_pub_key_dom_params[] = {
+    OSSL_PARAM_BN(OSSL_PKEY_PARAM_PUB_KEY, NULL, 0),
+    OSSL_PARAM_BN(OSSL_PKEY_PARAM_FFC_P, NULL, 0),
+    OSSL_PARAM_BN(OSSL_PKEY_PARAM_FFC_Q, NULL, 0),
+    OSSL_PARAM_BN(OSSL_PKEY_PARAM_FFC_G, NULL, 0),
+    OSSL_PARAM_BN(OSSL_PKEY_PARAM_FFC_COFACTOR, NULL, 0),
+    OSSL_PARAM_int(OSSL_PKEY_PARAM_FFC_GINDEX, NULL),
+    OSSL_PARAM_int(OSSL_PKEY_PARAM_FFC_PCOUNTER, NULL),
+    OSSL_PARAM_int(OSSL_PKEY_PARAM_FFC_H, NULL),
+    OSSL_PARAM_int(OSSL_PKEY_PARAM_DH_PRIV_LEN, NULL),
+    OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_FFC_SEED, NULL, 0),
+    OSSL_PARAM_utf8_string(OSSL_PKEY_PARAM_GROUP_NAME, NULL, 0),
+    OSSL_PARAM_END
+};
+
+static const OSSL_PARAM ibmca_keymgmt_dh_imexport_key_pair_dom_params[] = {
+    OSSL_PARAM_BN(OSSL_PKEY_PARAM_PUB_KEY, NULL, 0),
+    OSSL_PARAM_BN(OSSL_PKEY_PARAM_PRIV_KEY, NULL, 0),
+    OSSL_PARAM_BN(OSSL_PKEY_PARAM_FFC_P, NULL, 0),
+    OSSL_PARAM_BN(OSSL_PKEY_PARAM_FFC_Q, NULL, 0),
+    OSSL_PARAM_BN(OSSL_PKEY_PARAM_FFC_G, NULL, 0),
+    OSSL_PARAM_BN(OSSL_PKEY_PARAM_FFC_COFACTOR, NULL, 0),
+    OSSL_PARAM_int(OSSL_PKEY_PARAM_FFC_GINDEX, NULL),
+    OSSL_PARAM_int(OSSL_PKEY_PARAM_FFC_PCOUNTER, NULL),
+    OSSL_PARAM_int(OSSL_PKEY_PARAM_FFC_H, NULL),
+    OSSL_PARAM_int(OSSL_PKEY_PARAM_DH_PRIV_LEN, NULL),
+    OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_FFC_SEED, NULL, 0),
+    OSSL_PARAM_utf8_string(OSSL_PKEY_PARAM_GROUP_NAME, NULL, 0),
+    OSSL_PARAM_END
+};
+
+static const OSSL_PARAM ibmca_keymgmt_dh_imexport_key_pair[] = {
+    OSSL_PARAM_BN(OSSL_PKEY_PARAM_PUB_KEY, NULL, 0),
+    OSSL_PARAM_BN(OSSL_PKEY_PARAM_PRIV_KEY, NULL, 0),
+    OSSL_PARAM_END
+};
+
+static const OSSL_PARAM ibmca_keymgmt_dh_imexport_public_key[] = {
+    OSSL_PARAM_BN(OSSL_PKEY_PARAM_PUB_KEY, NULL, 0),
+    OSSL_PARAM_END
+};
+
+static const OSSL_PARAM ibmca_keymgmt_dh_imexport_private_key[] = {
+    OSSL_PARAM_BN(OSSL_PKEY_PARAM_PRIV_KEY, NULL, 0),
+    OSSL_PARAM_END
+};
+
+static const OSSL_PARAM *ibmca_keymgmt_dh_imexport_types(int selection)
+{
+    selection &= (OSSL_KEYMGMT_SELECT_KEYPAIR |
+                  OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS);
+
+    switch (selection) {
+    case OSSL_KEYMGMT_SELECT_PRIVATE_KEY:
+        return ibmca_keymgmt_dh_imexport_private_key;
+    case OSSL_KEYMGMT_SELECT_PUBLIC_KEY:
+        return ibmca_keymgmt_dh_imexport_public_key;
+    case OSSL_KEYMGMT_SELECT_KEYPAIR:
+        return ibmca_keymgmt_dh_imexport_key_pair;
+    case OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS:
+        return ibmca_keymgmt_dh_imexport_dom_params;
+    case OSSL_KEYMGMT_SELECT_PRIVATE_KEY | OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS:
+        return ibmca_keymgmt_dh_imexport_priv_key_dom_params;
+    case OSSL_KEYMGMT_SELECT_PUBLIC_KEY | OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS:
+        return ibmca_keymgmt_dh_imexport_pub_key_dom_params;
+    case OSSL_KEYMGMT_SELECT_KEYPAIR | OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS:
+        return ibmca_keymgmt_dh_imexport_key_pair_dom_params;
+    }
+
+    return NULL;
+}
+
+static int ibmca_keymgmt_dh_export(void *vkey, int selection,
+                                   OSSL_CALLBACK *param_callback, void *cbarg)
+{
+    struct ibmca_key *key = vkey;
+    OSSL_PARAM_BLD *bld;
+    OSSL_PARAM *params = NULL;
+    const char *name;
+    int rc = 1;
+
+    if (key == NULL || param_callback == NULL)
+        return 0;
+
+    ibmca_debug_key(key, "key: %p selection: 0x%x", key, selection);
+
+    bld = OSSL_PARAM_BLD_new();
+    if (bld == NULL) {
+        put_error_key(key, IBMCA_ERR_MALLOC_FAILED,
+                      "OSSL_PARAM_BLD_new failed");
+        return 0;
+    }
+
+    /* Domain parameters are required when exporting public or private key */
+    if ((selection & OSSL_KEYMGMT_SELECT_KEYPAIR) != 0)
+        selection |= OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS;
+
+
+    if ((selection & OSSL_KEYMGMT_SELECT_PUBLIC_KEY) != 0) {
+        /* OSSL_PKEY_PARAM_PUB_KEY */
+        if (key->dh.pub != NULL) {
+            rc = ibmca_param_build_set_bn(key->provctx, bld, NULL,
+                                          OSSL_PKEY_PARAM_PUB_KEY,
+                                          key->dh.pub);
+            if (rc == 0)
+                goto error;
+        }
+    }
+
+    if ((selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) != 0) {
+        /* OSSL_PKEY_PARAM_PRIV_KEY */
+        if (key->dh.priv != NULL) {
+            rc = ibmca_param_build_set_bn(key->provctx, bld, NULL,
+                                          OSSL_PKEY_PARAM_PRIV_KEY,
+                                          key->dh.priv);
+            if (rc == 0)
+                goto error;
+        }
+    }
+
+    if ((selection & OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS) != 0) {
+        /* OSSL_PKEY_PARAM_FFC_P */
+        if (key->dh.ffc_params.p != NULL) {
+            rc = ibmca_param_build_set_bn(key->provctx, bld, NULL,
+                                          OSSL_PKEY_PARAM_FFC_P,
+                                          key->dh.ffc_params.p);
+            if (rc == 0)
+                goto error;
+        }
+
+        /* OSSL_PKEY_PARAM_FFC_Q */
+        if (key->dh.ffc_params.q != NULL) {
+            rc = ibmca_param_build_set_bn(key->provctx, bld, NULL,
+                                          OSSL_PKEY_PARAM_FFC_Q,
+                                          key->dh.ffc_params.q);
+            if (rc == 0)
+                goto error;
+        }
+
+        /* OSSL_PKEY_PARAM_FFC_G */
+        if (key->dh.ffc_params.g != NULL) {
+            rc = ibmca_param_build_set_bn(key->provctx, bld, NULL,
+                                          OSSL_PKEY_PARAM_FFC_G,
+                                          key->dh.ffc_params.g);
+            if (rc == 0)
+                goto error;
+        }
+
+        /* OSSL_PKEY_PARAM_FFC_COFACTOR */
+        if (key->dh.ffc_params.cofactor != NULL) {
+            rc = ibmca_param_build_set_bn(key->provctx, bld, NULL,
+                                          OSSL_PKEY_PARAM_FFC_COFACTOR,
+                                          key->dh.ffc_params.cofactor);
+            if (rc == 0)
+                goto error;
+        }
+
+        /* OSSL_PKEY_PARAM_FFC_GINDEX */
+        rc = ibmca_param_build_set_int(key->provctx, bld, NULL,
+                                       OSSL_PKEY_PARAM_FFC_GINDEX,
+                                       key->dh.ffc_params.gindex);
+        if (rc == 0)
+            goto error;
+
+        /* OSSL_PKEY_PARAM_FFC_PCOUNTER */
+        rc = ibmca_param_build_set_int(key->provctx, bld, NULL,
+                                       OSSL_PKEY_PARAM_FFC_PCOUNTER,
+                                       key->dh.ffc_params.pcounter);
+        if (rc == 0)
+            goto error;
+
+        /* OSSL_PKEY_PARAM_FFC_H */
+        rc = ibmca_param_build_set_int(key->provctx, bld, NULL,
+                                       OSSL_PKEY_PARAM_FFC_H,
+                                       key->dh.ffc_params.hindex);
+        if (rc == 0)
+            goto error;
+
+        /* OSSL_PKEY_PARAM_FFC_SEED */
+        if (key->dh.ffc_params.seed != NULL &&
+            key->dh.ffc_params.seed_len > 0) {
+            rc = ibmca_param_build_set_octet_ptr(key->provctx, bld, NULL,
+                                                 OSSL_PKEY_PARAM_FFC_SEED,
+                                                 key->dh.ffc_params.seed,
+                                                 key->dh.ffc_params.seed_len);
+            if (rc == 0)
+                goto error;
+        }
+
+        /* OSSL_PKEY_PARAM_GROUP_NAME */
+        if (key->dh.ffc_params.group_nid != NID_undef) {
+            name = OBJ_nid2sn(key->dh.ffc_params.group_nid);
+            rc = ibmca_param_build_set_utf8(key->provctx, bld, NULL,
+                                            OSSL_PKEY_PARAM_GROUP_NAME, name);
+            if (rc == 0)
+                goto error;
+        }
+
+        /* OSSL_PKEY_PARAM_FFC_VALIDATE_PQ */
+        rc = ibmca_param_build_set_int(key->provctx, bld, NULL,
+                                       OSSL_PKEY_PARAM_FFC_VALIDATE_PQ,
+                                       key->dh.ffc_params.validate_pq);
+        if (rc == 0)
+            goto error;
+
+        /* OSSL_PKEY_PARAM_FFC_VALIDATE_G */
+        rc = ibmca_param_build_set_int(key->provctx, bld, NULL,
+                                       OSSL_PKEY_PARAM_FFC_VALIDATE_G,
+                                       key->dh.ffc_params.validate_g);
+        if (rc == 0)
+            goto error;
+
+        /* OSSL_PKEY_PARAM_FFC_VALIDATE_LEGACY */
+        rc = ibmca_param_build_set_int(key->provctx, bld, NULL,
+                                       OSSL_PKEY_PARAM_FFC_VALIDATE_LEGACY,
+                                       key->dh.ffc_params.validate_legacy);
+        if (rc == 0)
+            goto error;
+
+        /* OSSL_PKEY_PARAM_FFC_DIGEST */
+        if (key->dh.ffc_params.mdname != NULL) {
+            rc = ibmca_param_build_set_utf8(key->provctx, bld, NULL,
+                                            OSSL_PKEY_PARAM_FFC_DIGEST,
+                                            key->dh.ffc_params.mdname);
+            if (rc == 0)
+                goto error;
+        }
+
+        /* OSSL_PKEY_PARAM_FFC_DIGEST_PROPS */
+        if (key->dh.ffc_params.mdname != NULL) {
+            rc = ibmca_param_build_set_utf8(key->provctx, bld, NULL,
+                                            OSSL_PKEY_PARAM_FFC_DIGEST_PROPS,
+                                            key->dh.ffc_params.mdprops);
+            if (rc == 0)
+                goto error;
+        }
+
+        /* OSSL_PKEY_PARAM_DH_PRIV_LEN */
+        if (key->dh.ffc_params.length > 0) {
+            rc = ibmca_param_build_set_int(key->provctx, bld, NULL,
+                                           OSSL_PKEY_PARAM_DH_PRIV_LEN,
+                                           key->dh.ffc_params.length);
+            if (rc == 0)
+                goto error;
+        }
+    }
+
+    params = OSSL_PARAM_BLD_to_param(bld);
+    if (params == NULL) {
+        put_error_key(key, IBMCA_ERR_INTERNAL_ERROR,
+                      "OSSL_PARAM_BLD_to_param failed");
+        rc = 0;
+        goto error;
+    }
+
+    rc = param_callback(params, cbarg);
+    OSSL_PARAM_free(params);
+
+error:
+    OSSL_PARAM_BLD_free(bld);
+
+    return rc;
+}
+
+static int ibmca_keymgmt_dh_import(void *vkey, int selection,
+                                   const OSSL_PARAM params[])
+{
+    struct ibmca_key *key = vkey;
+    const OSSL_PARAM *parm;
+    const char *name;
+    int rc = 0, val;
+
+    if (key == NULL)
+        return 0;
+
+    ibmca_debug_key(key, "key: %p selection: 0x%x", key, selection);
+    for (parm = params; parm != NULL && parm->key != NULL; parm++)
+        ibmca_debug_key(key, "param: %s", parm->key);
+
+    if ((selection & (OSSL_KEYMGMT_SELECT_KEYPAIR |
+                      OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS)) == 0) {
+        put_error_key(key, IBMCA_ERR_INVALID_PARAM,
+                       "Nothing to import");
+        return 0;
+    }
+
+    /* Clear any already existing key components */
+    ibmca_keymgmt_dh_free_cb(key);
+    ibmca_clean_fallback_pkey_cache(key);
+
+    if ((selection & OSSL_KEYMGMT_SELECT_PUBLIC_KEY) != 0) {
+         /* OSSL_PKEY_PARAM_PUB_KEY */
+        rc = ibmca_param_get_bn(key->provctx, params,
+                                OSSL_PKEY_PARAM_PUB_KEY, &key->dh.pub);
+        if (rc == 0)
+            return 0;
+     }
+
+    if ((selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) != 0) {
+         /* OSSL_PKEY_PARAM_PRIV_KEY */
+        key->dh.priv = BN_secure_new();
+        if (key->dh.priv == NULL) {
+            put_error_key(key, IBMCA_ERR_MALLOC_FAILED, "BN_secure_new failed");
+            return 0;
+        }
+
+        rc = ibmca_param_get_bn(key->provctx, params,
+                                OSSL_PKEY_PARAM_PRIV_KEY, &key->dh.priv);
+        if (rc <= 0) {
+            BN_clear_free(key->dh.priv);
+            key->dh.priv = NULL;
+        }
+        if (rc == 0)
+            return 0;
+     }
+
+    if ((selection & OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS) != 0) {
+        /* OSSL_PKEY_PARAM_FFC_P */
+        rc = ibmca_param_get_bn(key->provctx, params,
+                                OSSL_PKEY_PARAM_FFC_P, &key->dh.ffc_params.p);
+        if (rc == 0)
+            return 0;
+
+        /* OSSL_PKEY_PARAM_FFC_Q */
+        rc = ibmca_param_get_bn(key->provctx, params,
+                                OSSL_PKEY_PARAM_FFC_Q, &key->dh.ffc_params.q);
+        if (rc == 0)
+            return 0;
+
+
+        /* OSSL_PKEY_PARAM_FFC_G */
+        rc = ibmca_param_get_bn(key->provctx, params,
+                                OSSL_PKEY_PARAM_FFC_G, &key->dh.ffc_params.g);
+        if (rc == 0)
+            return 0;
+
+        /* OSSL_PKEY_PARAM_FFC_COFACTOR */
+        rc = ibmca_param_get_bn(key->provctx, params,
+                                OSSL_PKEY_PARAM_FFC_COFACTOR,
+                                &key->dh.ffc_params.cofactor);
+        if (rc == 0)
+            return 0;
+
+        /* OSSL_PKEY_PARAM_FFC_GINDEX */
+        rc = ibmca_param_get_int(key->provctx, params,
+                                 OSSL_PKEY_PARAM_FFC_GINDEX,
+                                 &key->dh.ffc_params.gindex);
+        if (rc == 0)
+            return 0;
+
+        /* OSSL_PKEY_PARAM_FFC_PCOUNTER */
+        rc = ibmca_param_get_int(key->provctx, params,
+                                 OSSL_PKEY_PARAM_FFC_PCOUNTER,
+                                 &key->dh.ffc_params.pcounter);
+        if (rc == 0)
+            return 0;
+
+        /* OSSL_PKEY_PARAM_FFC_H */
+        rc = ibmca_param_get_int(key->provctx, params,
+                                 OSSL_PKEY_PARAM_FFC_H,
+                                 &key->dh.ffc_params.hindex);
+        if (rc == 0)
+            return 0;
+
+        /* OSSL_PKEY_PARAM_FFC_SEED */
+        rc = ibmca_param_get_octet_string(key->provctx, params,
+                                          OSSL_PKEY_PARAM_FFC_SEED,
+                                          (void **)&key->dh.ffc_params.seed,
+                                          &key->dh.ffc_params.seed_len);
+        if (rc == 0)
+            return 0;
+
+        /* OSSL_PKEY_PARAM_GROUP_NAME */
+        rc = ibmca_param_get_utf8(key->provctx, params,
+                                  OSSL_PKEY_PARAM_GROUP_NAME, &name);
+        if (rc == 0)
+            return 0;
+        if (rc > 0) {
+            key->dh.ffc_params.group_nid = OBJ_sn2nid(name);
+            if (key->dh.ffc_params.group_nid == NID_undef) {
+                put_error_key(key, IBMCA_ERR_INVALID_PARAM,
+                              "DH '%s': '%s' is an unsupported group",
+                              OSSL_PKEY_PARAM_GROUP_NAME, name);
+                return 0;
+            }
+            ibmca_debug_key(key, "group_nid: %d", key->dh.ffc_params.group_nid);
+        }
+
+        /* OSSL_PKEY_PARAM_FFC_VALIDATE_PQ */
+        rc = ibmca_param_get_int(key->provctx, params,
+                                 OSSL_PKEY_PARAM_FFC_VALIDATE_PQ, &val);
+        if (rc == 0)
+            return 0;
+        if (rc > 0)
+            key->dh.ffc_params.validate_pq = val;
+
+        /* OSSL_PKEY_PARAM_FFC_VALIDATE_G */
+        rc = ibmca_param_get_int(key->provctx, params,
+                                 OSSL_PKEY_PARAM_FFC_VALIDATE_G, &val);
+        if (rc == 0)
+            return 0;
+        if (rc > 0)
+            key->dh.ffc_params.validate_g = val;
+
+        /* OSSL_PKEY_PARAM_FFC_VALIDATE_LEGACY */
+        rc = ibmca_param_get_int(key->provctx, params,
+                                 OSSL_PKEY_PARAM_FFC_VALIDATE_LEGACY, &val);
+        if (rc == 0)
+            return 0;
+        if (rc > 0)
+            key->dh.ffc_params.validate_legacy = val;
+
+        /* OSSL_PKEY_PARAM_FFC_DIGEST */
+        rc = ibmca_param_get_utf8(key->provctx, params,
+                                  OSSL_PKEY_PARAM_FFC_DIGEST,
+                                  &name);
+        if (rc == 0)
+            return 0;
+        if (rc > 0) {
+            key->dh.ffc_params.mdname = P_STRDUP(key->provctx, name);
+            if (key->dh.ffc_params.mdname == NULL) {
+                put_error_key(key, IBMCA_ERR_MALLOC_FAILED, "P_STRDUP failed");
+                return 0;
+            }
+        }
+
+        /* OSSL_PKEY_PARAM_FFC_DIGEST_PROPS */
+        rc = ibmca_param_get_utf8(key->provctx, params,
+                                  OSSL_PKEY_PARAM_FFC_DIGEST_PROPS,
+                                  &name);
+        if (rc == 0)
+            return 0;
+        if (rc > 0) {
+            key->dh.ffc_params.mdprops = P_STRDUP(key->provctx, name);
+            if (key->dh.ffc_params.mdprops == NULL) {
+                put_error_key(key, IBMCA_ERR_MALLOC_FAILED, "P_STRDUP failed");
+                return 0;
+            }
+        }
+
+        /* OSSL_PKEY_PARAM_DH_PRIV_LEN */
+        rc = ibmca_param_get_int(key->provctx, params,
+                                 OSSL_PKEY_PARAM_DH_PRIV_LEN,
+                                 &key->dh.ffc_params.length);
+        if (rc == 0)
+            return 0;
+    }
+
+    return 1;
+}
+
+static const OSSL_DISPATCH ibmca_dh_keymgmt_functions[] = {
+    /* Constructor, destructor */
+    { OSSL_FUNC_KEYMGMT_NEW, (void (*)(void))ibmca_keymgmt_dh_new },
+    { OSSL_FUNC_KEYMGMT_FREE, (void (*)(void))ibmca_keymgmt_free },
+    { OSSL_FUNC_KEYMGMT_DUP, (void (*)(void))ibmca_keymgmt_dup },
+
+    /* Key generation and loading */
+    { OSSL_FUNC_KEYMGMT_GEN_INIT,
+            (void (*)(void))ibmca_keymgmt_dh_gen_init },
+    { OSSL_FUNC_KEYMGMT_GEN_SET_TEMPLATE,
+            (void (*)(void))ibmca_keymgmt_dh_gen_set_template },
+    { OSSL_FUNC_KEYMGMT_GEN_SET_PARAMS,
+            (void (*)(void))ibmca_keymgmt_dh_gen_set_params },
+    { OSSL_FUNC_KEYMGMT_GEN_SETTABLE_PARAMS,
+            (void (*)(void))ibmca_keymgmt_dh_gen_settable_params },
+    { OSSL_FUNC_KEYMGMT_GEN, (void (*)(void))ibmca_keymgmt_dh_gen },
+    { OSSL_FUNC_KEYMGMT_GEN_CLEANUP,
+              (void (*)(void))ibmca_keymgmt_gen_cleanup },
+    { OSSL_FUNC_KEYMGMT_LOAD, (void (*)(void))ibmca_keymgmt_load },
+
+    /* Key object checking */
+    { OSSL_FUNC_KEYMGMT_HAS, (void (*)(void))ibmca_keymgmt_dh_has },
+    { OSSL_FUNC_KEYMGMT_MATCH, (void (*)(void))ibmca_keymgmt_dh_match },
+    { OSSL_FUNC_KEYMGMT_VALIDATE,
+            (void (*)(void))ibmca_keymgmt_dh_validate },
+    { OSSL_FUNC_KEYMGMT_QUERY_OPERATION_NAME,
+            (void (*)(void))ibmca_keymgmt_dh_query_operation_name },
+
+    /* Key object information */
+    { OSSL_FUNC_KEYMGMT_GET_PARAMS,
+            (void (*) (void))ibmca_keymgmt_dh_get_params },
+    { OSSL_FUNC_KEYMGMT_GETTABLE_PARAMS,
+            (void (*) (void))ibmca_keymgmt_dh_gettable_params },
+    { OSSL_FUNC_KEYMGMT_SET_PARAMS,
+            (void (*) (void))ibmca_keymgmt_dh_set_params },
+    { OSSL_FUNC_KEYMGMT_SETTABLE_PARAMS,
+            (void (*) (void))ibmca_keymgmt_dh_settable_params },
+
+    /* Import and export routines */
+    { OSSL_FUNC_KEYMGMT_EXPORT, (void (*)(void))ibmca_keymgmt_dh_export },
+    { OSSL_FUNC_KEYMGMT_EXPORT_TYPES,
+            (void (*)(void))ibmca_keymgmt_dh_imexport_types },
+    { OSSL_FUNC_KEYMGMT_IMPORT, (void (*)(void))ibmca_keymgmt_dh_import },
+    { OSSL_FUNC_KEYMGMT_IMPORT_TYPES,
+            (void (*)(void))ibmca_keymgmt_dh_imexport_types },
+
+    { 0, NULL }
+};
+
+static const OSSL_DISPATCH ibmca_dhx_keymgmt_functions[] = {
+    /* Constructor, destructor */
+    { OSSL_FUNC_KEYMGMT_NEW, (void (*)(void))ibmca_keymgmt_dhx_new },
+    { OSSL_FUNC_KEYMGMT_FREE, (void (*)(void))ibmca_keymgmt_free },
+    { OSSL_FUNC_KEYMGMT_DUP, (void (*)(void))ibmca_keymgmt_dup },
+
+    /* Key generation and loading */
+    { OSSL_FUNC_KEYMGMT_GEN_INIT,
+            (void (*)(void))ibmca_keymgmt_dhx_gen_init },
+    { OSSL_FUNC_KEYMGMT_GEN_SET_TEMPLATE,
+            (void (*)(void))ibmca_keymgmt_dh_gen_set_template },
+    { OSSL_FUNC_KEYMGMT_GEN_SET_PARAMS,
+            (void (*)(void))ibmca_keymgmt_dh_gen_set_params },
+    { OSSL_FUNC_KEYMGMT_GEN_SETTABLE_PARAMS,
+            (void (*)(void))ibmca_keymgmt_dhx_gen_settable_params },
+    { OSSL_FUNC_KEYMGMT_GEN, (void (*)(void))ibmca_keymgmt_dh_gen },
+    { OSSL_FUNC_KEYMGMT_GEN_CLEANUP,
+              (void (*)(void))ibmca_keymgmt_gen_cleanup },
+    { OSSL_FUNC_KEYMGMT_LOAD, (void (*)(void))ibmca_keymgmt_load },
+
+    /* Key object checking */
+    { OSSL_FUNC_KEYMGMT_HAS, (void (*)(void))ibmca_keymgmt_dh_has },
+    { OSSL_FUNC_KEYMGMT_MATCH, (void (*)(void))ibmca_keymgmt_dh_match },
+    { OSSL_FUNC_KEYMGMT_VALIDATE,
+            (void (*)(void))ibmca_keymgmt_dh_validate },
+    { OSSL_FUNC_KEYMGMT_QUERY_OPERATION_NAME,
+            (void (*)(void))ibmca_keymgmt_dh_query_operation_name },
+
+    /* Key object information */
+    { OSSL_FUNC_KEYMGMT_GET_PARAMS,
+            (void (*) (void))ibmca_keymgmt_dh_get_params },
+    { OSSL_FUNC_KEYMGMT_GETTABLE_PARAMS,
+            (void (*) (void))ibmca_keymgmt_dh_gettable_params },
+    { OSSL_FUNC_KEYMGMT_SET_PARAMS,
+            (void (*) (void))ibmca_keymgmt_dh_set_params },
+    { OSSL_FUNC_KEYMGMT_SETTABLE_PARAMS,
+            (void (*) (void))ibmca_keymgmt_dh_settable_params },
+
+    /* Import and export routines */
+    { OSSL_FUNC_KEYMGMT_EXPORT, (void (*)(void))ibmca_keymgmt_dh_export },
+    { OSSL_FUNC_KEYMGMT_EXPORT_TYPES,
+            (void (*)(void))ibmca_keymgmt_dh_imexport_types },
+    { OSSL_FUNC_KEYMGMT_IMPORT, (void (*)(void))ibmca_keymgmt_dh_import },
+    { OSSL_FUNC_KEYMGMT_IMPORT_TYPES,
+            (void (*)(void))ibmca_keymgmt_dh_imexport_types },
+
+    { 0, NULL }
+};
+
+const OSSL_ALGORITHM ibmca_dh_keymgmt[] = {
+    { "DH:dhKeyAgreement:1.2.840.113549.1.3.1", NULL,
+      ibmca_dh_keymgmt_functions, "IBMCA DH implementation" },
+    { "DHX:X9.42 DH:dhpublicnumber:1.2.840.10046.2.1", NULL,
+      ibmca_dhx_keymgmt_functions, "IBMCA DHX implementation" },
+    { NULL, NULL, NULL, NULL }
+};
+
+struct ibmca_tls_group_constants {
+    unsigned int group_id;
+    unsigned int secbits;
+    int mintls;
+    int maxtls;
+    int mindtls;
+    int maxdtls;
+};
+
+#define IBMCA_TLS_GROUP_ID_ffdhe2048        256
+#define IBMCA_TLS_GROUP_ID_ffdhe3072        257
+#define IBMCA_TLS_GROUP_ID_ffdhe4096        258
+#define IBMCA_TLS_GROUP_ID_ffdhe6144        259
+#define IBMCA_TLS_GROUP_ID_ffdhe8192        260
+
+static const struct ibmca_tls_group_constants ibmca_tls_group_consts[5] = {
+    { IBMCA_TLS_GROUP_ID_ffdhe2048, 112, TLS1_3_VERSION, 0, -1, -1 },
+    { IBMCA_TLS_GROUP_ID_ffdhe3072, 128, TLS1_3_VERSION, 0, -1, -1 },
+    { IBMCA_TLS_GROUP_ID_ffdhe4096, 128, TLS1_3_VERSION, 0, -1, -1 },
+    { IBMCA_TLS_GROUP_ID_ffdhe6144, 128, TLS1_3_VERSION, 0, -1, -1 },
+    { IBMCA_TLS_GROUP_ID_ffdhe8192, 192, TLS1_3_VERSION, 0, -1, -1 },
+};
+
+#define IBMCA_TLS_GROUP_ENTRY(tlsname, realname, algorithm, idx) \
+    { \
+        OSSL_PARAM_utf8_string(OSSL_CAPABILITY_TLS_GROUP_NAME, \
+                               tlsname, sizeof(tlsname)), \
+        OSSL_PARAM_utf8_string(OSSL_CAPABILITY_TLS_GROUP_NAME_INTERNAL, \
+                               realname, sizeof(realname)), \
+        OSSL_PARAM_utf8_string(OSSL_CAPABILITY_TLS_GROUP_ALG, \
+                               algorithm, sizeof(algorithm)), \
+        OSSL_PARAM_uint(OSSL_CAPABILITY_TLS_GROUP_ID, \
+                        (unsigned int *)&ibmca_tls_group_consts[idx].group_id), \
+        OSSL_PARAM_uint(OSSL_CAPABILITY_TLS_GROUP_SECURITY_BITS, \
+                        (unsigned int *)&ibmca_tls_group_consts[idx].secbits), \
+        OSSL_PARAM_int(OSSL_CAPABILITY_TLS_GROUP_MIN_TLS, \
+                       (unsigned int *)&ibmca_tls_group_consts[idx].mintls), \
+        OSSL_PARAM_int(OSSL_CAPABILITY_TLS_GROUP_MAX_TLS, \
+                       (unsigned int *)&ibmca_tls_group_consts[idx].maxtls), \
+        OSSL_PARAM_int(OSSL_CAPABILITY_TLS_GROUP_MIN_DTLS, \
+                       (unsigned int *)&ibmca_tls_group_consts[idx].mindtls), \
+        OSSL_PARAM_int(OSSL_CAPABILITY_TLS_GROUP_MAX_DTLS, \
+                       (unsigned int *)&ibmca_tls_group_consts[idx].maxdtls), \
+        OSSL_PARAM_END \
+    }
+
+static const OSSL_PARAM ibmca_dh_ffdhe2048[] =
+        IBMCA_TLS_GROUP_ENTRY("ffdhe2048", "ffdhe2048", "DH", 0);
+static const OSSL_PARAM ibmca_dh_ffdhe3072[] =
+        IBMCA_TLS_GROUP_ENTRY("ffdhe3072", "ffdhe3072", "DH", 1);
+static const OSSL_PARAM ibmca_dh_ffdhe4096[] =
+        IBMCA_TLS_GROUP_ENTRY("ffdhe4096", "ffdhe4096", "DH", 2);
+static const OSSL_PARAM ibmca_dh_ffdhe6144[] =
+        IBMCA_TLS_GROUP_ENTRY("ffdhe6144", "ffdhe6144", "DH", 3);
+static const OSSL_PARAM ibmca_dh_ffdhe8192[] =
+        IBMCA_TLS_GROUP_ENTRY("ffdhe8192", "ffdhe8192", "DH", 4);
+
+static const OSSL_PARAM *ibmca_dh_tls_group[] = {
+    ibmca_dh_ffdhe2048,
+    ibmca_dh_ffdhe3072,
+    ibmca_dh_ffdhe4096,
+    ibmca_dh_ffdhe6144,
+    ibmca_dh_ffdhe8192,
+    NULL
+};
+
+const struct ibmca_mech_capability ibmca_dh_capabilities[] = {
+    { "TLS-GROUP", ibmca_dh_tls_group },
+    { NULL, NULL }
+};
diff -pruN 1.4.0-1/src/provider/doc/Makefile.am 2.5.0-0ubuntu1/src/provider/doc/Makefile.am
--- 1.4.0-1/src/provider/doc/Makefile.am	1970-01-01 00:00:00.000000000 +0000
+++ 2.5.0-0ubuntu1/src/provider/doc/Makefile.am	2025-04-16 08:09:24.000000000 +0000
@@ -0,0 +1,2 @@
+man5_MANS = ibmca-provider.man
+dist_man5_MANS = $(man5_MANS)
diff -pruN 1.4.0-1/src/provider/doc/ibmca-provider.man 2.5.0-0ubuntu1/src/provider/doc/ibmca-provider.man
--- 1.4.0-1/src/provider/doc/ibmca-provider.man	1970-01-01 00:00:00.000000000 +0000
+++ 2.5.0-0ubuntu1/src/provider/doc/ibmca-provider.man	2025-04-16 08:09:24.000000000 +0000
@@ -0,0 +1,183 @@
+.\"
+.\" Copyright [2021-2022] International Business Machines Corp.
+.\"
+.\" Licensed under the Apache License, Version 2.0 (the "License");
+.\" you may not use this file except in compliance with the License.
+.\" You may obtain a copy of the License at
+.\"
+.\"     http://www.apache.org/licenses/LICENSE-2.0
+.\"
+.\" Unless required by applicable law or agreed to in writing, software
+.\" distributed under the License is distributed on an "AS IS" BASIS,
+.\" WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+.\" See the License for the specific language governing permissions and
+.\" limitations under the License.
+.\"
+.\" Process this file with
+.\" groff -man -Tascii ibmca-provider.5
+.TH IBMCA-PROVIDER 5 February 2022 IBM "IBMCA-PROVIDER user manual"
+.SH NAME
+IBMCA-PROVIDER \- IBMCA-PROVIDER is an OpenSSL provider that uses the libica
+library under s390x to accelerate cryptographic operations.
+
+.SH DESCRIPTION
+IBMCA-PROVIDER accelerates cryptographic operations of applications that use
+OpenSSL. The provider can be configured by the OpenSSL configuration file.
+.P
+The IBMCA-PROVIDER links the \fBlibica-cex\fP library that can accelerate
+RSA, EC, and DH operations. Other (e.g. symmetric or digest) operations are not
+supported by the IBMCA-PROVIDER. Those are provided by the OpenSSL default
+provider (or others). Operations such as the AES cipher, as well as digest
+oprations are accelerated by CPACF by the OpenSSL default provider already.
+There is no need to use another provider for accelerating those.
+
+.SS openssl.cnf
+The OpenSSL configuration file can have a section for the IBMCA provider.
+This section includes the identity (i.e. name) of the provider (i.e. ibmca),
+the module implementing the provider (ibmca-provider.so), provider parameters,
+and if it is to be activated automatically. For a description of the
+provider parameter, see below.
+.P
+Additionally, the OpenSSL configuration file can have a \fBalg_section\fP
+section. There, the default property query can be specified. To prefer using
+the IBMCA provider, but fall back to other providers (i.e. the default provider)
+specify:
+.P
+\fBdefault_properties = ?provider=ibmca\fP.
+
+.SH OPTIONS
+.SS openssl.cnf
+Options for the IBMCA provider section in openssl.cnf:
+.PP
+identity =
+.I ibmca
+.RS
+Set the name of the IBMCA provider. The name of the provider is used in the
+property query string used to fetch the provider for an operation. If no
+identity is specified, the name from the provider section of the OpenSSL config
+file is used, e.g. for an entry like \fBibmca_provider = ibmca_sect\fP in the
+provider section, the name would be \fBibmca_provider\fP.
+.RE
+.PP
+module =
+.I ibmca-provider.so
+.RS
+Set the name and optionally the path to the IBMCA provider shared object file
+allowing OpenSSL to find the file. Usually, providers are loaded from the
+OpenSSL \fBMODULESDIR\fP. Use \fBopenssl version -m\fP to display the
+MODULESDIR used by OpenSSL. Set environment variable \fBOPENSSL_MODULES\fP to
+override the OpenSSL modules directory (ignored in set-user-ID and set-group-ID
+programs).
+.RE
+.IP "activate = 1"
+.RS
+OpenSSL will activate the provider if this option is set to 1.
+.RE
+.PP
+algorithms = ALL |
+.I mechanisms
+.RS
+Redirect all cryptographic operations through the provider or disable types of
+mechanisms that the provider supports.
+If ALL is not used, the algorithms consist of a comma separated list
+of
+.I mechanisms
+: \fBRSA\fP | \fBEC\fP | \fBDH\fP.
+If this option is not specified, \fBALL\fP is the default.
+.PP
+.B Note:
+Depending on the hardware level (IBM z15), EC is already accelerated implicitly
+by OpenSSL for certain curves. Therefore, do not accelerate EC using the IBMCA
+provider if you are on an IBM z15 or later. This would actually make it slower.
+.RE
+.PP
+.IP "debug = yes | no | stderr"
+.RS
+Enables debug output for the IBMCA provider. If this option is not specified,
+no debugging output is produced. If \fBdebug = stderr\fP is specified,
+debugging messages are printed to stderr. Otherwise the debug output is written
+into a trace file in \fB<debug-path>/trace-<provider-name>.<pid>\fP,
+where <debug-path> is the path name of a directory to where the debug files are
+written (default: \fB/tmp\fP), <provider-name> is the name of the IBMCA provider
+from the identity option, and <pid> is the process ID of the current process.
+You can also enable debugging by setting the environment variable
+\fBIBMCA_DEBUG\fP to \fBon\fP or \fBstderr\fP.
+.RE
+.PP
+.IP "debug-path = /dir/to/debug/directory"
+.RS
+Sets the directory path to where debug files are written when debug is enabled
+via \fBdebug = yes\fP or via environment variable \fBIBMCA_DEBUG=on\fP.
+You can also set the debug path by setting the environment variable
+\fBIBMCA_DEBUG_PATH\fP to the directory path. It must be ensured that the user
+under which the application that uses the IBMCA provider runs has write access
+to that directory. If this option is not specified, the default debug path is
+\fB/tmp\fP.
+.RE
+.PP
+.IP "fips = yes | no"
+.RS
+Enables FIPS mode for the IBMCA provider. If FIPS is enabled, it is ensured that
+the \fBlibica-cex\fP library is built with FIPS enabled, and the system is
+running in FIPS mode. If that's not the case, the IBMCA provider initialization
+fails, and a syslog message is issued. If the fips option is not specified, it
+is auto-detected if the \fBlibica-cex\fP library is built with FIPS enabled,
+and the system is running in FIPS mode. If both is true, then the FIPS mode is
+enabled for the IBMCA provider. If FIPS is enabled (either explicitly
+configured or due to auto-detection), the IBMCA provider registers its
+algorithm with \fBfips=yes\fP, thus the IBMCA algorithms may be fetched using
+a property query string containing \fBfips=yes\fP.
+.PP
+.B Note:
+The IBMCA provider is currently not FIPS certified. It does not itself perform
+any FIPS self tests nor an integrity check which would be required to be FIPS
+certifiable. It is only ensured that \fBlibica-cex\fP library has successfully
+performed its self tests and integrity checks when FIPS mode is enabled.
+If you do not want that the IBMCA provider is used for property queries that
+include \fBfips=yes\fP, then disable FIPS mode of the IBMCA provider by setting
+\fBfips = no\fP in the provider configuration.
+.RE
+.PP
+fallback-properties = 
+.I property-query-string
+.RS
+A property query string that is used to fetch algorithms for fallback purposes.
+Fallbacks take place when the \fBlibica-cex\fP library does not support a
+certain key size or EC curve, or if the \fBlibica-cex\fP library fails to
+perform a crypto operation for whatever reason. If this option is not specified,
+a fallback property query string is built automatically as follows:
+\fBprovider!=<ibmca-provider-name>[,fips=yes]\fP. Clause \fBfips=yes\fP is
+included if FIPS mode is enabled for the IBMCA provider (either explicitly
+configured or due to auto-detection).
+Note that the IBMCA provider itself can not be used as fallback provider.
+Thus, the fallback property query string must either select a certain provider
+explicitly (e.g. \fBprovider=default\fP), or must exclude the IBMCA provider
+(\fBprovider!=<ibmca-provider-name>\fP). If FIPS mode is enabled for the
+IBMCA provider, the fallback provider should also be FIPS-enabled. Include
+\fBfips=yes\fP into the fallback property query string, or explicitly select
+the FIPS provider (\fBprovider=fips\fP).
+.RE
+.PP
+.SH ENVIRONMENT
+.TP
+.BR IBMCA_DEBUG
+If
+.B $IBMCA_DEBUG
+is set to \fBstderr\fP debug output to stderr for the IBMCA provider is enabled.
+If it is set to \fBon\fP the debug output is written into a trace file in
+\fB<debug-path>/trace-<provider-name>.<pid>\fP, where <debug-path> is the path
+name of a directory to where the debug files are written (default: \fB/tmp\fP),
+<provider-name> is the name of the IBMCA provider from the identity option,
+and <pid> is the process ID of the current process.
+.PP
+.TP
+.BR IBMCA_DEBUG_PATH
+Sets the directory path to where debug files are written when debug is enabled
+via \fBdebug = yes\fP configuration option or via environment variable
+\fBIBMCA_DEBUG=on\fP. It must be ensured that the user under which the
+application that uses the IBMCA provider runs has write access to that
+directory.
+.PP
+.SH SEE ALSO
+.B provider(1)
+.B config(5)
\ No newline at end of file
diff -pruN 1.4.0-1/src/provider/ec_keyexch.c 2.5.0-0ubuntu1/src/provider/ec_keyexch.c
--- 1.4.0-1/src/provider/ec_keyexch.c	1970-01-01 00:00:00.000000000 +0000
+++ 2.5.0-0ubuntu1/src/provider/ec_keyexch.c	2025-04-16 08:09:24.000000000 +0000
@@ -0,0 +1,752 @@
+/*
+ * Copyright [2021-2022] International Business Machines Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <stddef.h>
+#include <err.h>
+#include <strings.h>
+#include <string.h>
+
+#include <openssl/evp.h>
+#include <openssl/ec.h>
+#include <openssl/kdf.h>
+#include <openssl/core.h>
+#include <openssl/core_dispatch.h>
+#include <openssl/core_names.h>
+#include <openssl/params.h>
+#include <openssl/prov_ssl.h>
+
+#include "p_ibmca.h"
+
+static OSSL_FUNC_keyexch_newctx_fn ibmca_keyexch_ec_newctx;
+static OSSL_FUNC_keyexch_init_fn ibmca_keyexch_ec_init;
+static OSSL_FUNC_keyexch_set_peer_fn ibmca_keyexch_ec_set_peer;
+static OSSL_FUNC_keyexch_derive_fn ibmca_keyexch_ec_derive;
+static OSSL_FUNC_keyexch_set_ctx_params_fn ibmca_keyexch_ec_set_ctx_params;
+static OSSL_FUNC_keyexch_settable_ctx_params_fn
+                                        ibmca_keyexch_ec_settable_ctx_params;
+static OSSL_FUNC_keyexch_get_ctx_params_fn ibmca_keyexch_ec_get_ctx_params;
+static OSSL_FUNC_keyexch_gettable_ctx_params_fn
+                                       ibmca_keyexch_ec_gettable_ctx_params;
+
+static void ibmca_keyexch_ec_free_cb(struct ibmca_op_ctx *ctx);
+static int ibmca_keyexch_ec_dup_cb(const struct ibmca_op_ctx *ctx,
+                                   struct ibmca_op_ctx *new_ctx);
+static int ibmca_keyexch_ec_set_ctx_params(void *vctx,
+                                           const OSSL_PARAM params[]);
+
+static void *ibmca_keyexch_ec_newctx(void *vprovctx)
+{
+    const struct ibmca_prov_ctx *provctx = vprovctx;
+    struct ibmca_op_ctx *opctx;
+
+    if (provctx == NULL)
+        return NULL;
+
+    ibmca_debug_ctx(provctx, "provctx: %p", provctx);
+
+    opctx = ibmca_op_newctx(provctx, NULL, EVP_PKEY_EC,
+                            ibmca_keyexch_ec_free_cb,
+                            ibmca_keyexch_ec_dup_cb);
+    if (opctx == NULL) {
+        ibmca_debug_ctx(provctx, "ERROR: ibmca_op_newctx failed");
+        return NULL;
+    }
+
+    ibmca_debug_ctx(provctx, "opctx: %p", opctx);
+
+    return opctx;
+}
+
+static void ibmca_keyexch_ec_free_cb(struct ibmca_op_ctx *ctx)
+{
+    if (ctx == NULL)
+        return;
+
+    ibmca_debug_op_ctx(ctx, "ctx: %p", ctx);
+
+    if (ctx->ec.derive.peer_key != NULL)
+        ibmca_keymgmt_free(ctx->ec.derive.peer_key);
+    ctx->ec.derive.peer_key = NULL;
+
+    ctx->ec.derive.kdf_type = EVP_PKEY_ECDH_KDF_NONE;
+
+    if (ctx->ec.derive.kdf_md != NULL)
+        EVP_MD_free(ctx->ec.derive.kdf_md);
+    ctx->ec.derive.kdf_md = NULL;
+
+    ctx->ec.derive.kdf_outlen = 0;
+
+    if (ctx->ec.derive.kdf_ukm != NULL)
+        P_CLEAR_FREE(ctx->provctx, ctx->ec.derive.kdf_ukm,
+                     ctx->ec.derive.kdf_ukmlen);
+    ctx->ec.derive.kdf_ukm = NULL;
+    ctx->ec.derive.kdf_ukmlen = 0;
+}
+
+static int ibmca_keyexch_ec_dup_cb(const struct ibmca_op_ctx *ctx,
+                                   struct ibmca_op_ctx *new_ctx)
+{
+    if (ctx == NULL)
+        return 0;
+
+    ibmca_debug_op_ctx(ctx, "ctx: %p new_ctx: %p", ctx, new_ctx);
+
+    if (ctx->ec.derive.peer_key != NULL) {
+        new_ctx->ec.derive.peer_key = ctx->ec.derive.peer_key;
+        ibmca_keymgmt_upref(new_ctx->ec.derive.peer_key);
+    }
+
+    new_ctx->ec.derive.kdf_type = ctx->ec.derive.kdf_type;
+
+    new_ctx->ec.derive.kdf_md = ctx->ec.derive.kdf_md;
+    if (new_ctx->ec.derive.kdf_md != NULL) {
+        if (EVP_MD_up_ref(new_ctx->ec.derive.kdf_md) == 0) {
+            put_error_op_ctx(ctx, IBMCA_ERR_INTERNAL_ERROR,
+                             "EVP_MD_up_ref failed");
+            return 0;
+        }
+    }
+
+    new_ctx->ec.derive.kdf_outlen = ctx->ec.derive.kdf_outlen;
+
+    if (ctx->ec.derive.kdf_ukm != NULL && ctx->ec.derive.kdf_ukmlen > 0) {
+        new_ctx->ec.derive.kdf_ukm = P_MEMDUP(ctx->provctx,
+                                              ctx->ec.derive.kdf_ukm,
+                                              ctx->ec.derive.kdf_ukmlen);
+        if (new_ctx->ec.derive.kdf_ukm == NULL) {
+            put_error_op_ctx(ctx, IBMCA_ERR_MALLOC_FAILED, "P_MEMDUP failed");
+            return 0;
+        }
+        new_ctx->ec.derive.kdf_ukmlen = ctx->ec.derive.kdf_ukmlen;
+    }
+
+    return 1;
+}
+
+static int ibmca_keyexch_ec_init(void *vctx, void *vkey,
+                                 const OSSL_PARAM params[])
+{
+    struct ibmca_op_ctx *ctx = vctx;
+    struct ibmca_key *key = vkey;
+    const OSSL_PARAM *p;
+
+    if (ctx == NULL || key == NULL)
+        return 0;
+
+    ibmca_debug_op_ctx(ctx, "ctx: %p key: %p", ctx, key);
+    for (p = params; p != NULL && p->key != NULL; p++)
+        ibmca_debug_op_ctx(ctx, "param: %s", p->key);
+
+    if (ibmca_op_init(ctx, key, EVP_PKEY_OP_DERIVE) == 0) {
+        ibmca_debug_op_ctx(ctx, "ERROR: ibmca_op_init failed");
+        return 0;
+    }
+
+    /* Setup defaults for this context */
+    ibmca_keyexch_ec_free_cb(ctx);
+
+    if (params != NULL) {
+        if (ibmca_keyexch_ec_set_ctx_params(ctx, params) == 0) {
+            ibmca_debug_op_ctx(ctx,
+                    "ERROR: ibmca_keyexch_ec_set_ctx_params failed");
+            return 0;
+        }
+    }
+
+    return 1;
+}
+
+static int ibmca_keyexch_ec_set_peer(void *vctx, void *vkey)
+{
+    struct ibmca_op_ctx *ctx = vctx;
+    struct ibmca_key *key = vkey;
+
+    if (ctx == NULL || key == NULL)
+        return 0;
+
+    ibmca_debug_op_ctx(ctx, "ctx: %p key: %p", ctx, key);
+
+    if (key->type != EVP_PKEY_EC) {
+        put_error_op_ctx(ctx, IBMCA_ERR_INVALID_PARAM,
+                         "Peer key is not an EC key");
+        return 0;
+    }
+
+    if (ctx->key->match(ctx->key, key,
+                        OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS) != 1) {
+        put_error_op_ctx(ctx, IBMCA_ERR_INVALID_PARAM,
+                         "Peer key uses a different EC curve");
+        return 0;
+    }
+
+    if (key->has(key, OSSL_KEYMGMT_SELECT_PUBLIC_KEY) != 1) {
+        put_error_op_ctx(ctx, IBMCA_ERR_INVALID_PARAM,
+                         "Peer key does not contain a public EC key");
+        return 0;
+    }
+
+    ibmca_keymgmt_upref(key);
+
+    if (ctx->ec.derive.peer_key != NULL)
+        ibmca_keymgmt_free(ctx->ec.derive.peer_key);
+
+    ctx->ec.derive.peer_key = key;
+
+    return 1;
+}
+
+static int ibmca_keyexch_ec_derive_plain_fallback(struct ibmca_op_ctx *ctx,
+                                                  unsigned char *secret,
+                                                  size_t outlen)
+{
+    EVP_PKEY *pkey = NULL;
+    EVP_PKEY *peer = NULL;
+    EVP_PKEY_CTX *pctx = NULL;
+    size_t keylen;
+    int rc = 0;
+
+    ibmca_debug_op_ctx(ctx, "ctx: %p secret: %p outlen: %lu", ctx, secret,
+                       outlen);
+
+    pkey = ibmca_new_fallback_pkey(ctx->key);
+    if (pkey == NULL) {
+        ibmca_debug_op_ctx(ctx,"ERROR: ibmca_new_fallback_pkey failed");
+        goto out;
+    }
+
+    peer = ibmca_new_fallback_pkey(ctx->ec.derive.peer_key);
+    if (peer == NULL) {
+        ibmca_debug_op_ctx(ctx,"ERROR: ibmca_new_fallback_pkey failed");
+        goto out;
+    }
+
+    pctx = ibmca_new_fallback_pkey_ctx(ctx->provctx, pkey, NULL);
+    if (pctx == NULL) {
+        ibmca_debug_op_ctx(ctx,"ERROR: ibmca_new_fallback_pkey_ctx failed");
+        goto out;
+    }
+
+    if (EVP_PKEY_derive_init(pctx) != 1) {
+        put_error_op_ctx(ctx, IBMCA_ERR_INTERNAL_ERROR,
+                         "EVP_PKEY_derive_init failed");
+        goto out;
+    }
+
+    if (ibmca_check_fallback_provider(ctx->provctx, pctx) != 1) {
+        ibmca_debug_op_ctx(ctx, "ERROR: ibmca_check_fallback_provider failed");
+        goto out;
+    }
+
+    if (EVP_PKEY_derive_set_peer(pctx, peer) != 1 ||
+        EVP_PKEY_CTX_set_ecdh_kdf_type(pctx, EVP_PKEY_ECDH_KDF_NONE) != 1) {
+        put_error_op_ctx(ctx, IBMCA_ERR_INTERNAL_ERROR,
+                         "EVP_PKEY_derive_set_peer/EVP_PKEY_CTX_set_ecdh_kdf_type failed");
+        goto out;
+    }
+
+    keylen = outlen;
+    if (EVP_PKEY_derive(pctx, secret, &keylen) != 1 ||
+        keylen != outlen) {
+        put_error_op_ctx(ctx, IBMCA_ERR_INTERNAL_ERROR,
+                         "EVP_PKEY_derive failed");
+        goto out;
+    }
+
+    rc = 1;
+
+out:
+    if (pkey != NULL)
+        EVP_PKEY_free(pkey);
+    if (peer != NULL)
+        EVP_PKEY_free(peer);
+    if (pctx != NULL)
+        EVP_PKEY_CTX_free(pctx);
+
+    return rc;
+}
+
+static int ibmca_keyexch_ec_derive_plain(struct ibmca_op_ctx *ctx,
+                                         unsigned char *secret,
+                                         size_t *secretlen, size_t outlen)
+{
+    int rc = 0;
+    unsigned char *buf;
+    bool use_tbuf = false;
+
+    ibmca_debug_op_ctx(ctx, "ctx: %p secret: %p outlen: %lu", ctx, secret,
+                       outlen);
+
+    if (secret == NULL) {
+        *secretlen = ctx->key->ec.prime_size;
+        rc = 1;
+        goto out;
+    }
+
+    *secretlen = outlen < ctx->key->ec.prime_size ?
+                                            outlen : ctx->key->ec.prime_size;
+
+    if (*secretlen == ctx->key->ec.prime_size) {
+        buf = secret;
+    } else {
+        if (ibmca_op_alloc_tbuf(ctx, ctx->key->ec.prime_size) != 1) {
+            ibmca_debug_op_ctx(ctx, "ERROR: ibmca_op_alloc_tbuf failed");
+            goto out;
+        }
+
+        buf = ctx->tbuf;
+        use_tbuf = true;
+    }
+
+    if (ctx->key->ec.fallback.d != NULL ||
+        (ctx->ec.derive.peer_key->ec.fallback.x != NULL &&
+         ctx->ec.derive.peer_key->ec.fallback.y != NULL)) {
+        rc = ibmca_keyexch_ec_derive_plain_fallback(ctx, buf,
+                                                    ctx->key->ec.prime_size);
+        if (rc != 1) {
+            ibmca_debug_op_ctx(ctx,
+                               "ERROR: ibmca_keyexch_ec_derive_plain_fallback failed");
+            rc = 0;
+            goto out;
+        }
+
+        goto copy;
+    }
+
+    rc = ica_ecdh_derive_secret(ctx->provctx->ica_adapter, ctx->key->ec.key,
+                                ctx->ec.derive.peer_key->ec.key,
+                                buf, ctx->key->ec.prime_size);
+
+    if (rc != 0) {
+        ibmca_debug_op_ctx(ctx, "ica_ecdh_derive_secret failed with: %s",
+                           strerror(rc));
+
+        rc = ibmca_keyexch_ec_derive_plain_fallback(ctx, buf,
+                                                    ctx->key->ec.prime_size);
+        if (rc != 1) {
+            ibmca_debug_op_ctx(ctx,
+                               "ERROR: ibmca_keyexch_ec_derive_plain_fallback failed");
+            rc = 0;
+            goto out;
+        }
+    }
+
+copy:
+    if (use_tbuf)
+        memcpy(secret, ctx->tbuf, *secretlen);
+
+    rc = 1;
+
+out:
+    if (rc != 1)
+        *secretlen = 0;
+    if (use_tbuf && ctx->tbuf != NULL)
+        P_CLEANSE(ctx->provctx, ctx->tbuf, ctx->tbuf_len);
+
+    ibmca_debug_op_ctx(ctx, "secretlen: %lu", *secretlen);
+
+    return rc;
+}
+
+static int ibmca_keyexch_ec_kdf_x963(const struct ibmca_prov_ctx *provctx,
+                                     const unsigned char *z, size_t z_len,
+                                     EVP_MD *md, const unsigned char *ukm,
+                                     size_t ukm_len, unsigned char *out,
+                                     size_t outlen)
+{
+    int rc = 0;
+    OSSL_PARAM params[4];
+    OSSL_PARAM *p;
+    EVP_KDF *kdf = NULL;
+    EVP_KDF_CTX *kctx = NULL;
+
+    kdf = EVP_KDF_fetch(provctx->libctx, OSSL_KDF_NAME_X963KDF, NULL);
+    if (kdf == NULL) {
+        put_error_ctx(provctx, IBMCA_ERR_INTERNAL_ERROR,
+                      "Failed to fetch KDF '%s'", OSSL_KDF_NAME_X963KDF);
+        goto out;
+    }
+
+    kctx = EVP_KDF_CTX_new(kdf);
+    if (kctx == NULL) {
+        put_error_ctx(provctx, IBMCA_ERR_INTERNAL_ERROR,
+                      "EVP_KDF_CTX_new failed");
+        goto out;
+    }
+
+    p = params;
+    *p++ = OSSL_PARAM_construct_utf8_string(OSSL_KDF_PARAM_DIGEST,
+                                            (char *)EVP_MD_get0_name(md), 0);
+    *p++ = OSSL_PARAM_construct_octet_string(OSSL_KDF_PARAM_KEY,
+                                             (void *)z, z_len);
+    *p++ = OSSL_PARAM_construct_octet_string(OSSL_KDF_PARAM_INFO,
+                                             (void *)ukm, ukm_len);
+    *p = OSSL_PARAM_construct_end();
+
+    rc = EVP_KDF_derive(kctx, out, outlen, params);
+    if (rc <= 0) {
+        put_error_ctx(provctx, IBMCA_ERR_INTERNAL_ERROR,
+                      "EVP_KDF_derive failed");
+        goto out;
+    }
+
+    rc = 1;
+
+out:
+    if (kctx != NULL)
+        EVP_KDF_CTX_free(kctx);
+    if (kdf != NULL)
+        EVP_KDF_free(kdf);
+
+    return rc;
+
+}
+
+static int ibmca_keyexch_ec_derive_x963_kdf(struct ibmca_op_ctx *ctx,
+                                            unsigned char *secret,
+                                            size_t *secretlen, size_t outlen)
+{
+    int rc = 0;
+    size_t len;
+
+    ibmca_debug_op_ctx(ctx, "ctx: %p secret: %p outlen: %lu", ctx, secret,
+                       outlen);
+
+    *secretlen = ctx->ec.derive.kdf_outlen;
+
+    if (secret == NULL) {
+        rc = 1;
+        goto out;
+    }
+
+    if (outlen < *secretlen) {
+        put_error_op_ctx(ctx, IBMCA_ERR_INVALID_PARAM,
+                         "Output buffer too small");
+        goto out;
+    }
+
+    if (ibmca_op_alloc_tbuf(ctx, ctx->key->ec.prime_size) != 1) {
+        ibmca_debug_op_ctx(ctx, "ERROR: ibmca_op_alloc_tbuf failed");
+        goto out;
+    }
+
+    rc = ibmca_keyexch_ec_derive_plain(ctx, ctx->tbuf,
+                                       &len, ctx->key->ec.prime_size);
+    if (rc != 1) {
+        ibmca_debug_op_ctx(ctx,
+                           "ERROR: ibmca_keyexch_ec_derive_plain failed");
+        goto out;
+    }
+
+    rc = ibmca_keyexch_ec_kdf_x963(ctx->provctx, ctx->tbuf,
+                                   ctx->key->ec.prime_size,
+                                   ctx->ec.derive.kdf_md,
+                                   ctx->ec.derive.kdf_ukm,
+                                   ctx->ec.derive.kdf_ukmlen,
+                                   secret, ctx->ec.derive.kdf_outlen);
+    if (rc != 1) {
+        ibmca_debug_op_ctx(ctx, "ERROR: ibmca_keyexch_ec_alloc_tbuf failed");
+        goto out;
+    }
+
+    rc = 1;
+
+out:
+    if (rc != 1)
+        *secretlen = 0;
+
+    ibmca_debug_op_ctx(ctx, "secretlen: %lu", *secretlen);
+
+    if (ctx->tbuf != NULL)
+        P_CLEANSE(ctx->provctx, ctx->tbuf, ctx->tbuf_len);
+
+    return rc;
+}
+
+static int ibmca_keyexch_ec_derive(void *vctx,  unsigned char *secret,
+                                   size_t *secretlen, size_t outlen)
+{
+    struct ibmca_op_ctx *ctx = vctx;
+
+    if (ctx == NULL || secretlen == NULL)
+        return 0;
+
+    ibmca_debug_op_ctx(ctx, "ctx: %p secret: %p outlen: %lu", ctx, secret,
+                       outlen);
+
+    switch (ctx->ec.derive.kdf_type) {
+    case EVP_PKEY_ECDH_KDF_X9_63:
+        return ibmca_keyexch_ec_derive_x963_kdf(ctx, secret, secretlen, outlen);
+    case EVP_PKEY_ECDH_KDF_NONE:
+    default:
+        return ibmca_keyexch_ec_derive_plain(ctx, secret, secretlen, outlen);
+    }
+
+    return 0;
+}
+
+static int ibmca_keyexch_ec_get_ctx_params(void *vctx, OSSL_PARAM params[])
+{
+    struct ibmca_op_ctx *ctx = vctx;
+    OSSL_PARAM *p;
+    const char *name;
+    int rc;
+
+    if (ctx == NULL)
+        return 0;
+
+    ibmca_debug_op_ctx(ctx, "ctx: %p", ctx);
+    for (p = params; p != NULL && p->key != NULL; p++)
+        ibmca_debug_op_ctx(ctx, "param: %s", p->key);
+
+    /* OSSL_EXCHANGE_PARAM_EC_ECDH_COFACTOR_MODE */
+    rc = ibmca_param_build_set_int(ctx->provctx, NULL, params,
+                                   OSSL_EXCHANGE_PARAM_EC_ECDH_COFACTOR_MODE,
+                                   0);
+    if (rc == 0)
+       return 0;
+
+    /* OSSL_EXCHANGE_PARAM_KDF_TYPE */
+    switch (ctx->ec.derive.kdf_type) {
+    case EVP_PKEY_ECDH_KDF_X9_63:
+        name = OSSL_KDF_NAME_X963KDF;
+        break;
+    case EVP_PKEY_ECDH_KDF_NONE:
+    default:
+        name = "";
+        break;
+    }
+    rc = ibmca_param_build_set_utf8(ctx->provctx, NULL, params,
+                                    OSSL_EXCHANGE_PARAM_KDF_TYPE, name);
+    if (rc == 0)
+       return 0;
+
+    /* OSSL_EXCHANGE_PARAM_KDF_DIGEST */
+    if (ctx->ec.derive.kdf_md != NULL)
+        name = EVP_MD_get0_name(ctx->ec.derive.kdf_md);
+    else
+        name = "";
+    rc = ibmca_param_build_set_utf8(ctx->provctx, NULL, params,
+                                    OSSL_EXCHANGE_PARAM_KDF_DIGEST, name);
+    if (rc == 0)
+       return 0;
+
+    /* OSSL_EXCHANGE_PARAM_KDF_OUTLEN */
+    rc = ibmca_param_build_set_size_t(ctx->provctx, NULL, params,
+                                      OSSL_EXCHANGE_PARAM_KDF_OUTLEN,
+                                      ctx->ec.derive.kdf_outlen);
+    if (rc == 0)
+       return 0;
+
+    /* OSSL_EXCHANGE_PARAM_KDF_UKM */
+    rc = ibmca_param_build_set_octet_ptr(ctx->provctx, NULL, params,
+                                         OSSL_EXCHANGE_PARAM_KDF_UKM,
+                                         ctx->ec.derive.kdf_ukm,
+                                         ctx->ec.derive.kdf_ukmlen);
+    if (rc == 0)
+       return 0;
+
+    return 1;
+}
+
+static int ibmca_keyexch_ec_set_ctx_params(void *vctx,
+                                           const OSSL_PARAM params[])
+{
+    struct ibmca_op_ctx *ctx = vctx;
+    const OSSL_PARAM *p;
+    const char *name, *props = NULL;
+    int rc, value;
+    void *ukm = NULL;
+    size_t ukmlen;
+    EVP_MD *md;
+
+    if (ctx == NULL)
+        return 0;
+
+    ibmca_debug_op_ctx(ctx, "ctx: %p", ctx);
+    for (p = params; p != NULL && p->key != NULL; p++)
+        ibmca_debug_op_ctx(ctx, "param: %s", p->key);
+
+    /* OSSL_EXCHANGE_PARAM_EC_ECDH_COFACTOR_MODE */
+    rc = ibmca_param_get_int(ctx->provctx, params,
+                             OSSL_EXCHANGE_PARAM_EC_ECDH_COFACTOR_MODE, &value);
+    if (rc == 0)
+        return 0;
+    if (rc > 0) {
+        /* We do not support Cofactor DH (ECC CDH) */
+        if (value != 0) {
+            put_error_op_ctx(ctx, IBMCA_ERR_INVALID_PARAM,
+                             "EC '%s': %d is not supported",
+                             OSSL_EXCHANGE_PARAM_EC_ECDH_COFACTOR_MODE, value);
+            return 0;
+        }
+    }
+
+    /* OSSL_EXCHANGE_PARAM_KDF_TYPE */
+    rc = ibmca_param_get_utf8(ctx->provctx, params,
+                              OSSL_EXCHANGE_PARAM_KDF_TYPE, &name);
+    if (rc == 0)
+        return 0;
+    if (rc > 0) {
+        if (name[0] == '\0') {
+            ctx->ec.derive.kdf_type = EVP_PKEY_ECDH_KDF_NONE;
+        } else if (strcmp(name, OSSL_KDF_NAME_X963KDF) == 0) {
+            ctx->ec.derive.kdf_type = EVP_PKEY_ECDH_KDF_X9_62;
+        } else {
+            put_error_op_ctx(ctx, IBMCA_ERR_INVALID_PARAM,
+                             "EC '%s': '%s' is not supported",
+                             OSSL_EXCHANGE_PARAM_KDF_TYPE, name);
+            return 0;
+        }
+    }
+
+    /* OSSL_EXCHANGE_PARAM_KDF_DIGEST_PROPS */
+    rc = ibmca_param_get_utf8(ctx->provctx, params,
+                              OSSL_EXCHANGE_PARAM_KDF_DIGEST_PROPS, &props);
+    if (rc == 0)
+        return 0;
+
+    /* OSSL_EXCHANGE_PARAM_KDF_DIGEST */
+    rc = ibmca_param_get_utf8(ctx->provctx, params,
+                             OSSL_EXCHANGE_PARAM_KDF_DIGEST, &name);
+    if (rc == 0)
+        return 0;
+    if (rc > 0) {
+        md = EVP_MD_fetch(ctx->provctx->libctx, name,
+                          props != NULL ? props : ctx->propq);
+        if (md == NULL) {
+            put_error_op_ctx(ctx, IBMCA_ERR_INVALID_PARAM,
+                              "EC '%s': '%s' could not be fetched",
+                              OSSL_EXCHANGE_PARAM_KDF_DIGEST, name);
+            return 0;
+        }
+
+        if ((EVP_MD_get_flags(md) & EVP_MD_FLAG_XOF) != 0) {
+            put_error_op_ctx(ctx, IBMCA_ERR_INVALID_PARAM,
+                             "XOF Digest '%s' is not allowed", name);
+            EVP_MD_free(md);
+            return 0;
+        }
+
+        if (ctx->ec.derive.kdf_md != NULL)
+            EVP_MD_free(ctx->ec.derive.kdf_md);
+        ctx->ec.derive.kdf_md = md;
+    }
+
+    /* OSSL_EXCHANGE_PARAM_KDF_OUTLEN */
+    rc = ibmca_param_get_size_t(ctx->provctx, params,
+                                OSSL_EXCHANGE_PARAM_KDF_OUTLEN,
+                                &ctx->ec.derive.kdf_outlen);
+    if (rc == 0)
+        return 0;
+
+    /* OSSL_EXCHANGE_PARAM_KDF_UKM */
+    rc = ibmca_param_get_octet_string(ctx->provctx, params,
+                                      OSSL_EXCHANGE_PARAM_KDF_UKM,
+                                      &ukm, &ukmlen);
+    if (rc == 0)
+        return 0;
+    if (rc > 0) {
+        if (ctx->ec.derive.kdf_ukm != NULL)
+            P_CLEAR_FREE(ctx->provctx, ctx->ec.derive.kdf_ukm,
+                         ctx->ec.derive.kdf_ukmlen);
+        ctx->ec.derive.kdf_ukm = ukm;
+        ctx->ec.derive.kdf_ukmlen = ukmlen;
+    }
+
+    return 1;
+}
+
+static const OSSL_PARAM ibmca_keyexch_ec_gettable_params[] = {
+    OSSL_PARAM_int(OSSL_EXCHANGE_PARAM_EC_ECDH_COFACTOR_MODE, NULL),
+    OSSL_PARAM_utf8_string(OSSL_EXCHANGE_PARAM_KDF_TYPE, NULL, 0),
+    OSSL_PARAM_utf8_string(OSSL_EXCHANGE_PARAM_KDF_DIGEST, NULL, 0),
+    OSSL_PARAM_size_t(OSSL_EXCHANGE_PARAM_KDF_OUTLEN, NULL),
+    OSSL_PARAM_DEFN(OSSL_EXCHANGE_PARAM_KDF_UKM, OSSL_PARAM_OCTET_PTR, NULL, 0),
+    OSSL_PARAM_END
+};
+
+static const OSSL_PARAM *ibmca_keyexch_ec_gettable_ctx_params(
+                                                void *vctx, void *vprovctx)
+{
+    const struct ibmca_op_ctx *ctx = vctx;
+    const struct ibmca_prov_ctx *provctx = vprovctx;
+    const OSSL_PARAM *p;
+
+    ibmca_debug_ctx(provctx, "ctx: %p", ctx);
+
+    for (p = ibmca_keyexch_ec_gettable_params;
+                                    p != NULL && p->key != NULL; p++)
+        ibmca_debug_ctx(provctx, "param: %s", p->key);
+
+    return ibmca_keyexch_ec_gettable_params;
+}
+
+static const OSSL_PARAM ibmca_keyexch_ec_settable_params[] = {
+    OSSL_PARAM_int(OSSL_EXCHANGE_PARAM_EC_ECDH_COFACTOR_MODE, NULL),
+    OSSL_PARAM_utf8_string(OSSL_EXCHANGE_PARAM_KDF_TYPE, NULL, 0),
+    OSSL_PARAM_utf8_string(OSSL_EXCHANGE_PARAM_KDF_DIGEST, NULL, 0),
+    OSSL_PARAM_utf8_string(OSSL_EXCHANGE_PARAM_KDF_DIGEST_PROPS, NULL, 0),
+    OSSL_PARAM_size_t(OSSL_EXCHANGE_PARAM_KDF_OUTLEN, NULL),
+    OSSL_PARAM_octet_string(OSSL_EXCHANGE_PARAM_KDF_UKM, NULL, 0),
+    OSSL_PARAM_END
+};
+
+static const OSSL_PARAM *ibmca_keyexch_ec_settable_ctx_params(
+                                                void *vctx, void *vprovctx)
+{
+    const struct ibmca_op_ctx *ctx = vctx;
+    const struct ibmca_prov_ctx *provctx = vprovctx;
+    const OSSL_PARAM *p;
+
+    ibmca_debug_ctx(provctx, "ctx: %p", ctx);
+
+    for (p = ibmca_keyexch_ec_settable_params; p != NULL && p->key != NULL; p++)
+        ibmca_debug_ctx(provctx, "param: %s", p->key);
+
+    return ibmca_keyexch_ec_settable_params;
+}
+
+static const OSSL_DISPATCH ibmca_ec_keyexch_functions[] = {
+    /* Context management */
+    { OSSL_FUNC_KEYEXCH_NEWCTX, (void (*)(void))ibmca_keyexch_ec_newctx },
+    { OSSL_FUNC_KEYEXCH_FREECTX, (void (*)(void))ibmca_op_freectx },
+    { OSSL_FUNC_KEYEXCH_DUPCTX, (void (*)(void))ibmca_op_dupctx },
+
+    /* Shared secret derivation */
+    { OSSL_FUNC_KEYEXCH_INIT, (void (*)(void))ibmca_keyexch_ec_init },
+    { OSSL_FUNC_KEYEXCH_SET_PEER, (void (*)(void))ibmca_keyexch_ec_set_peer },
+    { OSSL_FUNC_KEYEXCH_DERIVE, (void (*)(void))ibmca_keyexch_ec_derive },
+
+    /* Key Exchange parameters */
+    { OSSL_FUNC_KEYEXCH_SET_CTX_PARAMS,
+        (void (*)(void))ibmca_keyexch_ec_set_ctx_params },
+    { OSSL_FUNC_KEYEXCH_SETTABLE_CTX_PARAMS,
+        (void (*)(void))ibmca_keyexch_ec_settable_ctx_params },
+    { OSSL_FUNC_KEYEXCH_GET_CTX_PARAMS,
+            (void (*)(void))ibmca_keyexch_ec_get_ctx_params },
+    { OSSL_FUNC_KEYEXCH_GETTABLE_CTX_PARAMS,
+        (void (*)(void))ibmca_keyexch_ec_gettable_ctx_params },
+
+    { 0, NULL }
+};
+
+const OSSL_ALGORITHM ibmca_ec_keyexch[] = {
+    { "ECDH", NULL, ibmca_ec_keyexch_functions, "IBMCA ECDH implementation" },
+    { NULL, NULL, NULL, NULL }
+};
diff -pruN 1.4.0-1/src/provider/ec_keymgmt.c 2.5.0-0ubuntu1/src/provider/ec_keymgmt.c
--- 1.4.0-1/src/provider/ec_keymgmt.c	1970-01-01 00:00:00.000000000 +0000
+++ 2.5.0-0ubuntu1/src/provider/ec_keymgmt.c	2025-04-16 08:09:24.000000000 +0000
@@ -0,0 +1,2525 @@
+/*
+ * Copyright [2021-2022] International Business Machines Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <stddef.h>
+#include <err.h>
+#include <strings.h>
+#include <string.h>
+#include <errno.h>
+
+#include <openssl/evp.h>
+#include <openssl/bn.h>
+#include <openssl/ec.h>
+#include <openssl/core.h>
+#include <openssl/core_dispatch.h>
+#include <openssl/core_names.h>
+#include <openssl/params.h>
+#include <openssl/param_build.h>
+#include <openssl/prov_ssl.h>
+
+#include "p_ibmca.h"
+
+static OSSL_FUNC_keymgmt_new_fn ibmca_keymgmt_ec_new;
+static OSSL_FUNC_keymgmt_gen_init_fn ibmca_keymgmt_ec_gen_init;
+static OSSL_FUNC_keymgmt_gen_set_template_fn ibmca_keymgmt_ec_gen_set_template;
+static OSSL_FUNC_keymgmt_gen_set_params_fn ibmca_keymgmt_ec_gen_set_params;
+static OSSL_FUNC_keymgmt_gen_settable_params_fn
+                                        ibmca_keymgmt_ec_gen_settable_params;
+static OSSL_FUNC_keymgmt_gen_fn ibmca_keymgmt_ec_gen;
+static OSSL_FUNC_keymgmt_has_fn ibmca_keymgmt_ec_has;
+static OSSL_FUNC_keymgmt_match_fn ibmca_keymgmt_ec_match;
+static OSSL_FUNC_keymgmt_validate_fn ibmca_keymgmt_ec_validate;
+static OSSL_FUNC_keymgmt_query_operation_name_fn
+                                        ibmca_keymgmt_ec_query_operation_name;
+static OSSL_FUNC_keymgmt_get_params_fn ibmca_keymgmt_ec_get_params;
+static OSSL_FUNC_keymgmt_gettable_params_fn ibmca_keymgmt_ec_gettable_params;
+static OSSL_FUNC_keymgmt_set_params_fn ibmca_keymgmt_ec_set_params;
+static OSSL_FUNC_keymgmt_settable_params_fn ibmca_keymgmt_ec_settable_params;
+static OSSL_FUNC_keymgmt_export_fn ibmca_keymgmt_ec_export;
+static OSSL_FUNC_keymgmt_export_types_fn ibmca_keymgmt_ec_imexport_types;
+static OSSL_FUNC_keymgmt_import_fn ibmca_keymgmt_ec_import;
+
+static void ibmca_keymgmt_ec_free_cb(struct ibmca_key *key);
+static int ibmca_keymgmt_ec_dup_cb(const struct ibmca_key *key,
+                                   struct ibmca_key *new_key);
+
+static size_t ibmca_keymgmt_ec_get_prime_size(const struct ibmca_key *key);
+static size_t ibmca_keymgmt_ec_get_max_param_size(const struct ibmca_key *key);
+
+static int ibmca_keymgmt_ec_pub_key_as_buf(const struct ibmca_key *key,
+                                           unsigned char **x,
+                                           unsigned char **y)
+{
+    unsigned char *q = NULL;
+    unsigned int len, i;
+    bool all_zero;
+    int rc = 0;
+
+    *x = NULL;
+    *y = NULL;
+
+    if (key->ec.key == NULL || key->ec.prime_size == 0 ||
+        key->ec.curve_nid == NID_undef)
+        return -1;
+
+    q = P_ZALLOC(key->provctx, key->ec.prime_size * 2);
+    if (q == NULL) {
+        put_error_key(key, IBMCA_ERR_MALLOC_FAILED,
+                      "Failed to allocate public EC key part");
+        goto out;
+    }
+
+    rc = ica_ec_key_get_public_key(key->ec.key, q, &len);
+    if ((rc != 0 && rc != EINVAL) || len != key->ec.prime_size * 2) {
+        put_error_key(key, IBMCA_ERR_LIBICA_FAILED,
+                      "Failed to get public EC key from libica key: %s",
+                      strerror(rc));
+        rc = 0;
+        goto out;
+    }
+    if (rc == EINVAL) { /* No public key */
+        rc = -1;
+        goto out;
+    }
+
+    for (i = 0, all_zero = true; i < len && all_zero; i++) {
+        if (q[i] != 0) {
+            all_zero = false;
+            break;
+        }
+    }
+    if (all_zero) { /* No public key */
+        rc = -1;
+        goto out;
+    }
+
+    *x = P_ZALLOC(key->provctx, key->ec.prime_size);
+    *y = P_ZALLOC(key->provctx, key->ec.prime_size);
+    if (*x == NULL || *y == NULL) {
+        put_error_key(key, IBMCA_ERR_MALLOC_FAILED,
+                      "Failed to allocate public EC key parts");
+        P_CLEAR_FREE(key->provctx, *x, key->ec.prime_size);
+        *x = NULL;
+        P_CLEAR_FREE(key->provctx, *y, key->ec.prime_size);
+        *y = NULL;
+        goto out;
+    }
+
+    memcpy(*x, q, key->ec.prime_size);
+    memcpy(*y, q + key->ec.prime_size, key->ec.prime_size);
+
+    rc = 1;
+
+out:
+    if (q != NULL)
+        P_CLEAR_FREE(key->provctx, q, key->ec.prime_size * 2);
+
+    return rc;
+}
+
+static int ibmca_keymgmt_ec_priv_key_as_buf(const struct ibmca_key *key,
+                                            unsigned char **d)
+{
+
+    unsigned int len, i;
+    int all_zero;
+    int rc = 0;
+
+    if (key->ec.key == NULL || key->ec.prime_size == 0 ||
+        key->ec.curve_nid == NID_undef)
+        return -1;
+
+
+    *d = P_SECURE_ZALLOC(key->provctx, key->ec.prime_size);
+    if (*d == NULL) {
+        put_error_key(key, IBMCA_ERR_MALLOC_FAILED,
+                      "Failed to allocate private EC key part");
+        goto out;
+    }
+
+    rc = ica_ec_key_get_private_key(key->ec.key, *d, &len);
+    if ((rc != 0 && rc != EINVAL) || len != key->ec.prime_size) {
+        put_error_key(key, IBMCA_ERR_LIBICA_FAILED,
+                      "Failed to get private EC key from libica key: %s",
+                      strerror(rc));
+        rc = 0;
+        goto out;
+    }
+
+    if (rc == EINVAL) { /* No private key */
+        rc = -1;
+        goto out;
+    }
+
+    for (i = 0, all_zero = 1; i < len && all_zero; i++)
+        all_zero &= ((*d)[i] == 0);
+    if (all_zero) { /* No private key */
+        rc = -1;
+        goto out;
+    }
+
+    rc = 1;
+
+out:
+    if (rc != 1 && *d != NULL) {
+        P_SECURE_CLEAR_FREE(key->provctx, *d, key->ec.prime_size);
+        *d = NULL;
+    }
+
+    return rc;
+}
+
+static int ibmca_keymgmt_ec_pub_key_as_bn(const struct ibmca_key *key,
+                                          BIGNUM **x, BIGNUM **y)
+{
+    unsigned char *buf_x = NULL, *buf_y = NULL;
+    int rc;
+
+    if (key->ec.fallback.x != NULL && key->ec.fallback.y != NULL) {
+        *x = BN_dup(key->ec.fallback.x);
+        *y = BN_dup(key->ec.fallback.y);
+
+        if (*x == NULL || *y == NULL) {
+            put_error_key(key, IBMCA_ERR_INTERNAL_ERROR, "BN_dup failed");
+            goto error;
+        }
+
+        return 1;
+    }
+
+    rc = ibmca_keymgmt_ec_pub_key_as_buf(key, &buf_x, &buf_y);
+    if (rc != 1)
+        return rc;
+
+    *x = BN_bin2bn(buf_x, key->ec.prime_size, NULL);
+    *y = BN_bin2bn(buf_y, key->ec.prime_size, NULL);
+    if (*x == NULL || *y == NULL) {
+        put_error_key(key, IBMCA_ERR_INTERNAL_ERROR, "BN_bin2bn failed");
+       goto error;
+    }
+
+    P_CLEAR_FREE(key->provctx, buf_x, key->ec.prime_size);
+    P_CLEAR_FREE(key->provctx, buf_y, key->ec.prime_size);
+
+    return 1;
+
+error:
+    if (*x != NULL)
+        BN_free(*x);
+    *x = NULL;
+    if (*y != NULL)
+        BN_free(*y);
+    *y = NULL;
+
+    if (buf_x != NULL)
+        P_CLEAR_FREE(key->provctx, buf_x, key->ec.prime_size);
+    if (buf_y != NULL)
+        P_CLEAR_FREE(key->provctx, buf_y, key->ec.prime_size);
+
+    return 0;
+}
+
+static int ibmca_keymgmt_ec_priv_key_as_bn(const struct ibmca_key *key,
+                                           BIGNUM **d)
+{
+    unsigned char *buf_d = NULL;
+    int rc;
+
+    if (key->ec.fallback.d != NULL) {
+        *d = BN_dup(key->ec.fallback.d);
+
+        if (*d == NULL) {
+            put_error_key(key, IBMCA_ERR_INTERNAL_ERROR, "BN_dup failed");
+            goto error;
+        }
+
+        return 1;
+    }
+    rc = ibmca_keymgmt_ec_priv_key_as_buf(key, &buf_d);
+    if (rc != 1)
+        return rc;
+
+    *d = BN_secure_new();
+    if (*d == NULL) {
+        put_error_key(key, IBMCA_ERR_MALLOC_FAILED, "BN_secure_new failed");
+        goto error;
+    }
+
+    *d = BN_bin2bn(buf_d, key->ec.prime_size, *d);
+    if (*d == NULL) {
+        put_error_key(key, IBMCA_ERR_INTERNAL_ERROR, "BN_bin2bn failed");
+       goto error;
+    }
+
+    P_SECURE_CLEAR_FREE(key->provctx, buf_d, key->ec.prime_size);
+
+    return 1;
+
+error:
+    if (*d != NULL)
+        BN_clear_free(*d);
+    *d = NULL;
+
+    if (buf_d != NULL)
+        P_SECURE_CLEAR_FREE(key->provctx, buf_d, key->ec.prime_size);
+
+    return 0;
+}
+
+static int ibmca_keymgmt_ec_pub_key_from_bn(struct ibmca_key *key,
+                                            BIGNUM *x, BIGNUM *y)
+{
+    unsigned int privlen;
+    unsigned char *x_buf = NULL, *y_buf = NULL;
+    int rc = 0;
+
+    if (x == NULL || y == NULL) {
+        put_error_key(key, IBMCA_ERR_INVALID_PARAM, "Need both, x and y");
+        return 0;
+    }
+
+    if (key->ec.curve_nid == NID_undef) {
+        put_error_key(key, IBMCA_ERR_INVALID_PARAM, "Curve nid is not known");
+        return 0;
+    }
+
+    if (key->ec.key == NULL) {
+        key->ec.prime_size = ibmca_keymgmt_ec_get_prime_size(key);
+        if (key->ec.prime_size == 0) {
+            put_error_key(key, IBMCA_ERR_EC_CURVE_NOT_SUPPORTED,
+                          "Unsupported curve nid: %d", key->ec.curve_nid);
+            return 0;
+        }
+
+        ibmca_debug_key(key, "prime_size: %lu", key->ec.prime_size);
+
+        key->ec.key = ica_ec_key_new(key->ec.curve_nid, &privlen);
+        if (key->ec.key == NULL || key->ec.prime_size != privlen) {
+            ibmca_debug_key(key,  "ica_ec_key_new failed");
+            goto fallback;
+        }
+    }
+
+    x_buf = P_ZALLOC(key->provctx, key->ec.prime_size);
+    y_buf = P_ZALLOC(key->provctx, key->ec.prime_size);
+    if (x_buf == NULL || y_buf == NULL) {
+        put_error_key(key, IBMCA_ERR_MALLOC_FAILED,
+                      "Failed to allocate public EC key parts");
+        goto out;
+    }
+
+    if (BN_bn2binpad(x, x_buf, key->ec.prime_size) <= 0 ||
+        BN_bn2binpad(y, y_buf, key->ec.prime_size) <= 0) {
+        put_error_key(key, IBMCA_ERR_INTERNAL_ERROR, "BN_bn2binpad failed");
+        goto out;
+    }
+
+    rc = ica_ec_key_init(x_buf, y_buf, NULL, key->ec.key);
+    if (rc != 0) {
+        ibmca_debug_key(key, "ica_ec_key_init failed: %s",
+                        strerror(rc));
+        rc = 0;
+        goto fallback;
+    }
+
+    rc = 1;
+    goto out;
+
+fallback:
+    ibmca_debug_key(key, "using fallback");
+
+    if (key->ec.key != NULL)
+        ica_ec_key_free(key->ec.key);
+    key->ec.key = NULL;
+
+    if (key->ec.fallback.x != NULL)
+        BN_free(key->ec.fallback.x);
+    if (key->ec.fallback.y != NULL)
+        BN_free(key->ec.fallback.y);
+
+    key->ec.fallback.x = BN_dup(x);
+    key->ec.fallback.y = BN_dup(y);
+    if (key->ec.fallback.x == NULL || key->ec.fallback.y == NULL) {
+        put_error_key(key, IBMCA_ERR_INTERNAL_ERROR, "BN_dup failed");
+        goto out;
+    }
+    rc = 1;
+
+out:
+    if (x_buf != NULL)
+        P_CLEAR_FREE(key->provctx, x_buf, key->ec.prime_size);
+    if (y_buf != NULL)
+        P_CLEAR_FREE(key->provctx, y_buf, key->ec.prime_size);
+
+    return rc;
+}
+
+static int ibmca_keymgmt_ec_priv_key_from_bn(struct ibmca_key *key, BIGNUM *d)
+{
+    unsigned int privlen;
+    unsigned char *d_buf = NULL;
+    int rc = 0;
+
+    if (d == NULL) {
+        put_error_key(key, IBMCA_ERR_INVALID_PARAM, "Need private part d");
+        return 0;
+    }
+
+    if (key->ec.curve_nid == NID_undef) {
+        put_error_key(key, IBMCA_ERR_INVALID_PARAM, "Curve nid is not known");
+        return 0;
+    }
+
+    if (key->ec.key == NULL) {
+        key->ec.prime_size = ibmca_keymgmt_ec_get_prime_size(key);
+        if (key->ec.prime_size == 0) {
+            put_error_key(key, IBMCA_ERR_EC_CURVE_NOT_SUPPORTED,
+                          "Unsupported curve nid: %d", key->ec.curve_nid);
+            return 0;
+        }
+
+        ibmca_debug_key(key, "prime_size: %lu", key->ec.prime_size);
+
+        key->ec.key = ica_ec_key_new(key->ec.curve_nid, &privlen);
+        if (key->ec.key == NULL || key->ec.prime_size != privlen) {
+            ibmca_debug_key(key, "ica_ec_key_new failed");
+            goto fallback;
+        }
+    }
+
+    d_buf = P_SECURE_ZALLOC(key->provctx, key->ec.prime_size);
+    if (d_buf == NULL) {
+        put_error_key(key, IBMCA_ERR_MALLOC_FAILED,
+                      "Failed to allocate private EC key part");
+        goto out;
+    }
+
+    if (BN_bn2binpad(d, d_buf, key->ec.prime_size) <= 0) {
+        put_error_key(key, IBMCA_ERR_INTERNAL_ERROR, "BN_bn2binpad failed");
+        goto out;
+    }
+
+    rc = ica_ec_key_init(NULL, NULL, d_buf, key->ec.key);
+    if (rc != 0) {
+        ibmca_debug_key(key, "ica_ec_key_init failed: %s",
+                        strerror(rc));
+        rc = 0;
+        goto fallback;
+    }
+
+    rc = 1;
+    goto out;
+
+fallback:
+    ibmca_debug_key(key, "using fallback");
+
+    if (key->ec.key != NULL)
+        ica_ec_key_free(key->ec.key);
+    key->ec.key = NULL;
+
+    if (key->ec.fallback.d != NULL)
+        BN_free(key->ec.fallback.d);
+
+    key->ec.fallback.d = BN_dup(d);
+    if (key->ec.fallback.d == NULL) {
+        put_error_key(key, IBMCA_ERR_INTERNAL_ERROR, "BN_dup failed");
+        goto out;
+    }
+    rc = 1;
+
+out:
+    if (d_buf != NULL)
+        P_SECURE_CLEAR_FREE(key->provctx, d_buf, key->ec.prime_size);
+
+    return rc;
+}
+
+static int ibmca_keymgmt_ec_pub_key_to_data(struct ibmca_key *key,
+                                            BIGNUM *x, BIGNUM *y,
+                                            OSSL_PARAM_BLD *bld,
+                                            OSSL_PARAM params[])
+{
+    EC_GROUP *group = NULL;
+    EC_POINT *point = NULL;
+    unsigned char *enc = NULL;
+    size_t enc_len = 0;
+    int rc = 0;
+
+    /* OSSL_PKEY_PARAM_EC_PUB_X */
+    rc = ibmca_param_build_set_bn(key->provctx, bld, params,
+                                  OSSL_PKEY_PARAM_EC_PUB_X, x);
+    if (rc == 0)
+        return 0;
+
+    /* OSSL_PKEY_PARAM_EC_PUB_Y */
+    rc = ibmca_param_build_set_bn(key->provctx, bld, params,
+                                  OSSL_PKEY_PARAM_EC_PUB_Y, y);
+    if (rc == 0)
+        return 0;
+
+    group = EC_GROUP_new_by_curve_name(key->ec.curve_nid);
+    if (group == NULL) {
+        put_error_key(key, IBMCA_ERR_INTERNAL_ERROR,
+                      "EC_GROUP_new_by_curve_name failed");
+        goto out;
+    }
+
+    point = EC_POINT_new(group);
+    if (point == NULL) {
+        put_error_key(key, IBMCA_ERR_INTERNAL_ERROR,
+                      "EC_POINT_new failed");
+        goto out;
+    }
+
+    if (EC_POINT_set_affine_coordinates(group, point, x, y, NULL) == 0) {
+        put_error_key(key, IBMCA_ERR_INTERNAL_ERROR,
+                      "EC_POINT_set_affine_coordinates failed");
+        goto out;
+    }
+
+    /* OSSL_PKEY_PARAM_PUB_KEY */
+    if (OSSL_PARAM_locate(params, OSSL_PKEY_PARAM_PUB_KEY) != NULL) {
+        enc_len = EC_POINT_point2buf(group, point, POINT_CONVERSION_COMPRESSED,
+                                     &enc, NULL);
+        if (enc_len == 0) {
+            put_error_key(key, IBMCA_ERR_INTERNAL_ERROR,
+                          "EC_POINT_point2buf failed");
+            goto out;
+        }
+
+        rc = ibmca_param_build_set_octet_ptr(key->provctx, bld, params,
+                                             OSSL_PKEY_PARAM_PUB_KEY,
+                                             enc, enc_len);
+        if (rc == 0)
+            goto out;
+
+        P_FREE(key->provctx, enc);
+        enc = NULL;
+    }
+
+    /* OSSL_PKEY_PARAM_ENCODED_PUBLIC_KEY */
+    if (OSSL_PARAM_locate(params, OSSL_PKEY_PARAM_ENCODED_PUBLIC_KEY) != NULL) {
+        enc_len = EC_POINT_point2buf(group, point, POINT_CONVERSION_UNCOMPRESSED,
+                                     &enc, NULL);
+        if (enc_len == 0) {
+            put_error_key(key, IBMCA_ERR_INTERNAL_ERROR,
+                          "EC_POINT_point2buf failed");
+            goto out;
+        }
+
+        rc = ibmca_param_build_set_octet_ptr(key->provctx, bld, params,
+                                             OSSL_PKEY_PARAM_ENCODED_PUBLIC_KEY,
+                                             enc, enc_len);
+        if (rc == 0)
+            goto out;
+
+        P_FREE(key->provctx, enc);
+        enc = NULL;
+    }
+
+    rc = 1;
+
+out:
+    EC_GROUP_free(group);
+    EC_POINT_free(point);
+    if (enc != NULL)
+        P_FREE(key->provctx, enc);
+
+    return rc;
+}
+
+static int ibmca_keymgmt_ec_priv_key_to_data(struct ibmca_key *key,
+                                             BIGNUM *d,
+                                             OSSL_PARAM_BLD *bld,
+                                             OSSL_PARAM params[])
+{
+    int rc = 0;
+
+    /* OSSL_PKEY_PARAM_PRIV_KEY */
+    rc = ibmca_param_build_set_bn(key->provctx, bld, params,
+                                  OSSL_PKEY_PARAM_PRIV_KEY, d);
+    if (rc == 0)
+        return 0;
+
+    return 1;
+}
+
+static void *ibmca_keymgmt_ec_new(void *vprovctx)
+{
+    const struct ibmca_prov_ctx *provctx = vprovctx;
+    struct ibmca_key *key;
+
+    if (provctx == NULL)
+        return NULL;
+
+    ibmca_debug_ctx(provctx, "provctx: %p", provctx);
+
+    key = ibmca_keymgmt_new(provctx, EVP_PKEY_EC, "EC",
+                            ibmca_keymgmt_ec_free_cb,
+                            ibmca_keymgmt_ec_dup_cb,
+                            ibmca_keymgmt_ec_get_max_param_size,
+                            ibmca_keymgmt_ec_export,
+                            ibmca_keymgmt_ec_import,
+                            ibmca_keymgmt_ec_has,
+                            ibmca_keymgmt_ec_match);
+    if (key == NULL) {
+        ibmca_debug_ctx(provctx, "ERROR: ibmca_keymgmt_new failed");
+        return NULL;
+    }
+
+    key->ec.curve_nid = NID_undef;
+    key->ec.format = POINT_CONVERSION_UNCOMPRESSED;
+    key->ec.prime_size = 0;
+    key->ec.include_pub = true;
+
+    return key;
+}
+
+static void ibmca_keymgmt_ec_free_cb(struct ibmca_key *key)
+{
+    if (key == NULL)
+        return;
+
+    ibmca_debug_key(key, "key: %p", key);
+
+    if (key->ec.key != NULL)
+        ica_ec_key_free(key->ec.key);
+    key->ec.key = NULL;
+
+    if (key->ec.fallback.x != NULL)
+        BN_free(key->ec.fallback.x);
+    key->ec.fallback.x = NULL;
+    if (key->ec.fallback.y != NULL)
+        BN_free(key->ec.fallback.y);
+    key->ec.fallback.y = NULL;
+    if (key->ec.fallback.d != NULL)
+        BN_free(key->ec.fallback.d);
+    key->ec.fallback.d = NULL;
+
+    key->ec.curve_nid = NID_undef;
+    key->ec.format = POINT_CONVERSION_UNCOMPRESSED;
+    key->ec.prime_size = 0;
+    key->ec.include_pub = true;
+}
+
+static int ibmca_keymgmt_ec_dup_cb(const struct ibmca_key *key,
+                                   struct ibmca_key *new_key)
+{
+    unsigned int privlen;
+    unsigned char *x = NULL, *y = NULL, *d = NULL;
+    bool has_pub = false, has_priv = false;
+    int rc = 0;
+
+    if (key == NULL || new_key == NULL)
+        return 0;
+
+    ibmca_debug_key(key, "key: %p new_key: %p", key, new_key);
+
+    new_key->ec.curve_nid = key->ec.curve_nid;
+    new_key->ec.format = key->ec.format;
+    new_key->ec.include_pub = key->ec.include_pub;
+    new_key->ec.prime_size = key->ec.prime_size;
+
+    if (key->ec.key != NULL) {
+        new_key->ec.key = ica_ec_key_new(new_key->ec.curve_nid, &privlen);
+        if (new_key->ec.key == NULL) {
+            put_error_key(key, IBMCA_ERR_LIBICA_FAILED,
+                          "Failed to allocate libica EC key");
+            goto out;
+        }
+
+        if (privlen != new_key->ec.prime_size) {
+            put_error_key(key, IBMCA_ERR_LIBICA_FAILED,
+                          "Newly allocated libica EC key has a different size");
+            goto out;
+        }
+
+        rc = ibmca_keymgmt_ec_pub_key_as_buf(key, &x, &y);
+        if (rc == 0)
+            goto out;
+        has_pub = (rc == 1);
+
+        rc = ibmca_keymgmt_ec_priv_key_as_buf(key, &d);
+        if (rc == 0)
+            goto out;
+        has_priv = (rc == 1);
+
+        rc = ica_ec_key_init(has_pub ? x : NULL, has_pub ? y : NULL,
+                             has_priv ? d : NULL, new_key->ec.key);
+        if (rc != 0) {
+            put_error_key(key, IBMCA_ERR_LIBICA_FAILED,
+                          "Failed to initialize libica EC key: %s",
+                          strerror(rc));
+            rc = 0;
+            goto out;
+        }
+    }
+
+    if (key->ec.fallback.x != NULL && key->ec.fallback.y != NULL) {
+        new_key->ec.fallback.x = BN_dup(key->ec.fallback.x);
+        new_key->ec.fallback.y = BN_dup(key->ec.fallback.y);
+        if (new_key->ec.fallback.x == NULL || new_key->ec.fallback.y == NULL) {
+            put_error_key(key, IBMCA_ERR_INTERNAL_ERROR,
+                          "BN_dup failed");
+            goto out;
+        }
+    }
+
+    if (key->ec.fallback.d != NULL) {
+        new_key->ec.fallback.d = BN_dup(key->ec.fallback.d);
+        if (new_key->ec.fallback.d == NULL) {
+            put_error_key(key, IBMCA_ERR_INTERNAL_ERROR,
+                          "BN_dup failed");
+            goto out;
+        }
+    }
+
+    rc = 1;
+
+out:
+    if (x != NULL)
+        P_CLEAR_FREE(key->provctx, x, privlen);
+    if (y != NULL)
+        P_CLEAR_FREE(key->provctx, y, privlen);
+    if (d != NULL)
+        P_SECURE_CLEAR_FREE(key->provctx, d, privlen);
+
+    return rc;
+}
+
+static int ibmca_keymgmt_ec_has(const void *vkey, int selection)
+{
+    const struct ibmca_key *key = vkey;
+    BIGNUM *x = NULL, *y = NULL, *d = NULL;
+    int rc, ok = 1;
+
+    if (key == NULL)
+        return 0;
+
+    ibmca_debug_key(key, "key: %p selection: 0x%x", key, selection);
+
+    if ((selection & OSSL_KEYMGMT_SELECT_PUBLIC_KEY) != 0) {
+        rc = ibmca_keymgmt_ec_pub_key_as_bn(key, &x, &y);
+        if (rc == 0)
+            goto out;
+        ok = ok & (rc == 1);
+    }
+
+    if ((selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) != 0) {
+        rc = ibmca_keymgmt_ec_priv_key_as_bn(key, &d);
+        if (rc == 0)
+            goto out;
+        ok = ok & (rc == 1);
+    }
+
+    if ((selection & OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS) != 0)
+        ok = ok & (key->ec.curve_nid != NID_undef);
+
+out:
+    if (x != NULL)
+        BN_free(x);
+    if (y != NULL)
+        BN_free(y);
+    if (d != NULL)
+        BN_free(d);
+
+    ibmca_debug_key(key, "ok: %d", ok);
+
+    return ok;
+}
+
+static int ibmca_keymgmt_ec_match(const void *vkey1, const void *vkey2,
+                                  int selection)
+{
+    const struct ibmca_key *key1 = vkey1;
+    const struct ibmca_key *key2 = vkey2;
+    BIGNUM *x1 = NULL, *y1 = NULL, *d1 = NULL;
+    BIGNUM *x2 = NULL, *y2 = NULL, *d2 = NULL;
+    int ok = 1, rc1, rc2, checked = 0;
+
+    if (key1 == NULL || key2 == NULL)
+        return 0;
+
+    ibmca_debug_key(key1, "key1: %p key2: %p selection: 0x%x", key1, key2,
+                    selection);
+
+    if (ibmca_keymgmt_match(key1, key2) == 0)
+        return 0;
+
+    if ((selection & OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS) != 0)
+        ok = (key1->ec.curve_nid == key2->ec.curve_nid &&
+              key1->ec.prime_size == key2->ec.prime_size);
+
+    if ((selection & OSSL_KEYMGMT_SELECT_PUBLIC_KEY) != 0) {
+        rc1 = ibmca_keymgmt_ec_pub_key_as_bn(key1, &x1, &y1);
+        if (rc1 == 0) {
+            ok = 0;
+            goto out;
+        }
+
+        rc2 = ibmca_keymgmt_ec_pub_key_as_bn(key2, &x2, &y2);
+        if (rc2 == 0) {
+             ok = 0;
+             goto out;
+        }
+
+        ok = ok && (rc1 == rc2 && (rc1 == -1 ||
+                    (BN_cmp(x1, x2) == 0 && BN_cmp(y1, y2) == 0)));
+        checked = 1;
+    }
+
+    if (!checked && (selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) != 0) {
+        rc1 = ibmca_keymgmt_ec_priv_key_as_bn(key1, &d1);
+        if (rc1 == 0) {
+            ok = 0;
+            goto out;
+        }
+
+        rc2 = ibmca_keymgmt_ec_priv_key_as_bn(key2, &d2);
+        if (rc2 == 0) {
+             ok = 0;
+             goto out;
+        }
+
+        ok = ok && (rc1 == rc2 && (rc1 == -1 ||
+                    (BN_cmp(d1, d2) == 0)));
+    }
+
+out:
+    if (x1 != NULL)
+        BN_free(x1);
+    if (x2 != NULL)
+        BN_free(x2);
+    if (y1 != NULL)
+        BN_free(y1);
+    if (y2 != NULL)
+        BN_free(y2);
+    if (d1 != NULL)
+        BN_free(d1);
+    if (d2 != NULL)
+        BN_free(d2);
+
+    ibmca_debug_key(key1, "ok: %d", ok);
+
+    return ok;
+}
+
+static int ibmca_keymgmt_ec_validate(const void *vkey, int selection,
+                                     int checktype)
+{
+    struct ibmca_key *key = (struct ibmca_key *)vkey;
+    EVP_PKEY *pkey = NULL;
+    EVP_PKEY_CTX *pctx = NULL;
+    int rc = 0;
+
+    if (key == NULL)
+        return 0;
+
+    ibmca_debug_key(key, "key: %p selection: 0x%x checktype: 0x%x", key,
+                    selection, checktype);
+
+    if ((key->ec.fallback.x != NULL && key->ec.fallback.y != NULL) ||
+        key->ec.fallback.d != NULL) {
+        /* Check fallback key using OpenSSL */
+        pkey = ibmca_new_fallback_pkey(key);
+        if (pkey == NULL)
+            goto out;
+
+        pctx = ibmca_new_fallback_pkey_ctx(key->provctx, pkey, NULL);
+        if (pctx == NULL)
+            goto out;
+
+        if ((selection & OSSL_KEYMGMT_SELECT_KEYPAIR)
+                                == OSSL_KEYMGMT_SELECT_KEYPAIR) {
+            rc = EVP_PKEY_check(pctx);
+        } else if ((selection & OSSL_KEYMGMT_SELECT_PUBLIC_KEY) != 0) {
+            rc = EVP_PKEY_public_check(pctx);
+        }
+        goto out;
+    }
+
+    /*
+     * If the selected key parts are present, they are valid:
+     * Either the EC key has been generated by libica, then the EC is valid per
+     * definition, or the EC key has been imported, then the validity has
+     * already been checked during ica_ec_key_init().
+     */
+    if (ibmca_keymgmt_ec_has(key, selection) == 0)
+        goto out;
+
+    rc = 1;
+
+out:
+    ibmca_debug_key(key, "valid: %d", rc);
+
+    if (pctx != NULL)
+        EVP_PKEY_CTX_free(pctx);
+    if (pkey != NULL)
+        EVP_PKEY_free(pkey);
+
+    return rc;
+}
+
+
+static const char *ibmca_keymgmt_ec_query_operation_name(int operation_id)
+{
+    switch (operation_id) {
+    case OSSL_OP_KEYEXCH:
+        return "ECDH";
+    case OSSL_OP_SIGNATURE:
+        return "ECDSA";
+    }
+
+    return NULL;
+}
+
+static void ibmca_keymgmt_ec_gen_free_cb(struct ibmca_op_ctx *ctx)
+{
+    if (ctx == NULL)
+        return;
+
+    ibmca_debug_op_ctx(ctx, "ctx: %p", ctx);
+
+    ctx->ec.gen.curve_nid = NID_undef;
+    ctx->ec.gen.format = POINT_CONVERSION_UNCOMPRESSED;
+
+    if (ctx->ec.gen.dhkem_ikm != NULL)
+        P_CLEAR_FREE(ctx->provctx, ctx->ec.gen.dhkem_ikm,
+                    ctx->ec.gen.dhkem_ikmlen);
+    ctx->ec.gen.dhkem_ikm = NULL;
+    ctx->ec.gen.dhkem_ikmlen = 0;
+}
+
+static int ibmca_keymgmt_ec_gen_dup_cb(const struct ibmca_op_ctx *ctx,
+                                       struct ibmca_op_ctx *new_ctx)
+{
+    if (ctx == NULL)
+        return 0;
+
+    ibmca_debug_op_ctx(ctx, "ctx: %p new_ctx: %p", ctx, new_ctx);
+
+    new_ctx->ec.gen.curve_nid = ctx->ec.gen.curve_nid;
+    new_ctx->ec.gen.format = ctx->ec.gen.format;
+
+    if (ctx->ec.gen.dhkem_ikm != NULL)
+        P_CLEAR_FREE(ctx->provctx, ctx->ec.gen.dhkem_ikm,
+                    ctx->ec.gen.dhkem_ikmlen);
+    new_ctx->ec.gen.dhkem_ikm = NULL;
+    new_ctx->ec.gen.dhkem_ikmlen = 0;
+    if (ctx->ec.gen.dhkem_ikm != NULL) {
+        new_ctx->ec.gen.dhkem_ikm = P_MEMDUP(ctx->provctx,
+                                             ctx->ec.gen.dhkem_ikm,
+                                             ctx->ec.gen.dhkem_ikmlen);
+        if (new_ctx->ec.gen.dhkem_ikm == NULL) {
+            put_error_op_ctx(ctx, IBMCA_ERR_MALLOC_FAILED,
+                          "Failed to duplicate DHKEM-IKM buffer");
+            return 0;
+        }
+        new_ctx->ec.gen.dhkem_ikmlen = ctx->ec.gen.dhkem_ikmlen;
+    }
+
+    return 1;
+}
+
+static void *ibmca_keymgmt_ec_gen_init(void *vprovctx, int selection,
+                                       const OSSL_PARAM params[])
+{
+    const struct ibmca_prov_ctx *provctx = vprovctx;
+    struct ibmca_op_ctx *ctx;
+    const OSSL_PARAM *p;
+
+    if (provctx == NULL)
+        return NULL;
+
+    ibmca_debug_ctx(provctx, "provctx: %p selection: 0x%x", provctx, selection);
+    for (p = params; p != NULL && p->key != NULL; p++)
+        ibmca_debug_ctx(provctx, "param: %s", p->key);
+
+    if ((selection & (OSSL_KEYMGMT_SELECT_KEYPAIR |
+                      OSSL_KEYMGMT_SELECT_ALL_PARAMETERS)) == 0) {
+        put_error_ctx(provctx, IBMCA_ERR_INVALID_PARAM,
+                      "selection is not KEYPAIR and/or parameters");
+        return NULL;
+    }
+
+    ctx = ibmca_keymgmt_gen_init(provctx, EVP_PKEY_EC,
+                                 ibmca_keymgmt_ec_gen_free_cb,
+                                 ibmca_keymgmt_ec_gen_dup_cb);
+    if (ctx == NULL) {
+        ibmca_debug_ctx(provctx, "ERROR: ibmca_keymgmt_gen_init failed");
+        return NULL;
+    }
+
+    /* set defaults */
+    ctx->ec.gen.selection = selection;
+    ctx->ec.gen.curve_nid = NID_undef;
+    ctx->ec.gen.format = POINT_CONVERSION_UNCOMPRESSED;
+
+    if (params != NULL) {
+        if (ibmca_keymgmt_ec_gen_set_params(ctx, params) == 0) {
+            ibmca_debug_ctx(provctx,
+                            "ERROR: ibmca_keymgmt_ec_gen_set_params failed");
+            ibmca_op_freectx(ctx);
+            return NULL;
+        }
+    }
+
+    return ctx;
+}
+
+static int ibmca_keymgmt_ec_gen_set_template(void *vgenctx, void *vtempl)
+{
+    struct ibmca_op_ctx *genctx = vgenctx;
+    struct ibmca_key *templ = vtempl;
+
+    if (genctx == NULL || templ == NULL)
+        return 0;
+
+    ibmca_debug_op_ctx(genctx, "genctx: %p templ: %p", genctx, templ);
+
+    if (genctx->type != templ->type) {
+        put_error_op_ctx(genctx, IBMCA_ERR_INVALID_PARAM,
+                         "invalid template key type");
+        return 0;
+    }
+
+    ibmca_keymgmt_ec_gen_free_cb(genctx);
+
+    genctx->ec.gen.curve_nid = templ->ec.curve_nid;
+    genctx->ec.gen.format = templ->ec.format;
+
+    ibmca_debug_op_ctx(genctx, "curve_nid: %d", genctx->ec.gen.curve_nid);
+    ibmca_debug_op_ctx(genctx, "format: %d", genctx->ec.gen.format);
+
+    return 1;
+}
+
+static int ibmca_keymgmt_ec_gen_set_params(void *vgenctx,
+                                           const OSSL_PARAM params[])
+{
+    struct ibmca_op_ctx *genctx = vgenctx;
+    OSSL_PARAM grp_params[] = { OSSL_PARAM_END, OSSL_PARAM_END };
+    const OSSL_PARAM *p;
+    const char *name;
+    EC_GROUP *group;
+    int rc, value;
+#ifdef OSSL_PKEY_PARAM_DHKEM_IKM
+    unsigned char *ptr = NULL;
+    size_t len = 0;
+#endif
+
+    if (genctx == NULL)
+        return 0;
+
+    ibmca_debug_op_ctx(genctx, "genctx: %p", genctx);
+    for (p = params; p != NULL && p->key != NULL; p++)
+        ibmca_debug_op_ctx(genctx, "param: %s", p->key);
+
+    /* OSSL_PKEY_PARAM_GROUP_NAME */
+    rc = ibmca_param_get_utf8(genctx->provctx, params,
+                              OSSL_PKEY_PARAM_GROUP_NAME, &name);
+    if (rc == 0)
+        return 0;
+    if (rc > 0) {
+        grp_params[0] = OSSL_PARAM_construct_utf8_ptr(
+                                    OSSL_PKEY_PARAM_GROUP_NAME,
+                                    (char **)&name, 0);
+        group = EC_GROUP_new_from_params(grp_params, genctx->provctx->libctx,
+                                         NULL);
+        if (group == NULL) {
+            put_error_op_ctx(genctx, IBMCA_ERR_EC_CURVE_NOT_SUPPORTED,
+                             "EC '%s': '%s' is an unsupported curve",
+                             OSSL_PKEY_PARAM_GROUP_NAME, name);
+            return 0;
+        }
+
+        genctx->ec.gen.curve_nid = EC_GROUP_get_curve_name(group);
+        EC_GROUP_free(group);
+    }
+
+    /* OSSL_PKEY_PARAM_EC_POINT_CONVERSION_FORMAT */
+    rc = ibmca_param_get_utf8(genctx->provctx, params,
+                              OSSL_PKEY_PARAM_EC_POINT_CONVERSION_FORMAT,
+                              &name);
+    if (rc == 0)
+        return 0;
+    if (rc > 0) {
+        if (strcasecmp(name,
+                OSSL_PKEY_EC_POINT_CONVERSION_FORMAT_UNCOMPRESSED) != 0) {
+            genctx->ec.gen.format = POINT_CONVERSION_UNCOMPRESSED;
+        } else if (strcasecmp(name,
+                OSSL_PKEY_EC_POINT_CONVERSION_FORMAT_COMPRESSED) != 0) {
+            genctx->ec.gen.format = POINT_CONVERSION_COMPRESSED;
+        } else if (strcasecmp(name,
+                OSSL_PKEY_EC_POINT_CONVERSION_FORMAT_HYBRID) != 0) {
+            genctx->ec.gen.format = POINT_CONVERSION_HYBRID;
+        } else {
+            put_error_op_ctx(genctx, IBMCA_ERR_INVALID_PARAM,
+                             "EC '%s': '%s' is an unsupported format",
+                             OSSL_PKEY_PARAM_EC_POINT_CONVERSION_FORMAT, name);
+            return 0;
+        }
+    }
+
+    /* OSSL_PKEY_PARAM_EC_ENCODING */
+    rc = ibmca_param_get_utf8(genctx->provctx, params,
+                              OSSL_PKEY_PARAM_EC_ENCODING, &name);
+    if (rc == 0)
+        return 0;
+    if (rc > 0) {
+        /* We only support named curves */
+        if (strcasecmp(name, OSSL_PKEY_EC_ENCODING_GROUP) != 0) {
+            put_error_op_ctx(genctx, IBMCA_ERR_INVALID_PARAM,
+                             "EC '%s': '%s' is an unsupported encoding",
+                             OSSL_PKEY_PARAM_EC_ENCODING, name);
+            return 0;
+        }
+    }
+
+    /*  OSSL_PKEY_PARAM_USE_COFACTOR_ECDH */
+    rc = ibmca_param_get_int(genctx->provctx, params,
+                             OSSL_PKEY_PARAM_USE_COFACTOR_ECDH, &value);
+    if (rc == 0)
+        return 0;
+    if (rc > 0) {
+        /* We do not support Cofactor DH (ECC CDH) */
+        if (value != 0) {
+            put_error_op_ctx(genctx, IBMCA_ERR_INVALID_PARAM,
+                             "EC '%s': %d is not supported",
+                             OSSL_PKEY_PARAM_USE_COFACTOR_ECDH, value);
+            return 0;
+        }
+    }
+
+#ifdef OSSL_PKEY_PARAM_DHKEM_IKM
+    rc = ibmca_param_get_octet_string(genctx->provctx, params,
+                                      OSSL_PKEY_PARAM_DHKEM_IKM,
+                                      (void **)&ptr, &len);
+    if (rc == 0)
+        return 0;
+    if (rc > 0) {
+        if (genctx->ec.gen.dhkem_ikm != NULL)
+            P_CLEAR_FREE(genctx->provctx, genctx->ec.gen.dhkem_ikm,
+                         genctx->ec.gen.dhkem_ikmlen);
+        genctx->ec.gen.dhkem_ikm = ptr;
+        genctx->ec.gen.dhkem_ikmlen = len;
+    }
+#endif
+
+    return 1;
+}
+
+static const OSSL_PARAM ibmca_ec_op_ctx_settable_params[] = {
+    OSSL_PARAM_utf8_string(OSSL_PKEY_PARAM_GROUP_NAME, NULL, 0),
+    OSSL_PARAM_utf8_string(OSSL_PKEY_PARAM_EC_POINT_CONVERSION_FORMAT, NULL, 0),
+    OSSL_PARAM_utf8_string(OSSL_PKEY_PARAM_EC_ENCODING, NULL, 0),
+    OSSL_PARAM_int(OSSL_PKEY_PARAM_USE_COFACTOR_ECDH, NULL),
+    OSSL_PARAM_END
+};
+
+static const OSSL_PARAM *ibmca_keymgmt_ec_gen_settable_params(void *vgenctx,
+                                                              void *vprovctx)
+{
+    const struct ibmca_op_ctx *genctx = vgenctx;
+    const struct ibmca_prov_ctx *provctx = vprovctx;
+    const OSSL_PARAM *p;
+
+    UNUSED(genctx);
+
+    if (provctx == NULL)
+        return NULL;
+
+    for (p = ibmca_ec_op_ctx_settable_params; p != NULL && p->key != NULL; p++)
+        ibmca_debug_ctx(provctx, "param: %s", p->key);
+
+    return ibmca_ec_op_ctx_settable_params;
+}
+
+static int ibmca_keymgmt_ec_gen_fallback(struct ibmca_op_ctx *genctx,
+                                         struct ibmca_key *key,
+                                         OSSL_CALLBACK *osslcb, void *cbarg)
+{
+    struct ibmca_keygen_cb_data cbdata;
+    EVP_PKEY_CTX *pctx = NULL;
+    EVP_PKEY *pkey = NULL;
+#ifdef OSSL_PKEY_PARAM_DHKEM_IKM
+    OSSL_PARAM params[2];
+#endif
+    int rc = 0;
+
+    ibmca_debug_op_ctx(genctx, "genctx: %p", genctx);
+
+    pctx = ibmca_new_fallback_pkey_ctx(genctx->provctx, NULL, "EC");
+    if (pctx == NULL) {
+        ibmca_debug_op_ctx(genctx, "ERROR: ibmca_new_fallback_pkey_ctx failed");
+        goto out;
+    }
+
+    if (EVP_PKEY_keygen_init(pctx) != 1) {
+        put_error_key(key, IBMCA_ERR_INTERNAL_ERROR,
+                     "EVP_PKEY_keygen_init failed");
+        goto out;
+    }
+
+    if (ibmca_check_fallback_provider(genctx->provctx, pctx) != 1) {
+        ibmca_debug_op_ctx(genctx,
+                           "ERROR: ibmca_check_fallback_provider failed");
+        goto out;
+    }
+
+    if (EVP_PKEY_CTX_set_ec_paramgen_curve_nid(pctx,
+                                               genctx->ec.gen.curve_nid) != 1) {
+        put_error_key(key, IBMCA_ERR_INTERNAL_ERROR,
+                     "EVP_PKEY_CTX_set_ec_paramgen_curve_nid failed");
+        goto out;
+    }
+
+#ifdef OSSL_PKEY_PARAM_DHKEM_IKM
+    if (genctx->ec.gen.dhkem_ikm != NULL && genctx->ec.gen.dhkem_ikmlen > 0) {
+        params[0] = OSSL_PARAM_construct_octet_string(
+                                                OSSL_PKEY_PARAM_DHKEM_IKM,
+                                                genctx->ec.gen.dhkem_ikm,
+                                                genctx->ec.gen.dhkem_ikmlen);
+        params[1] = OSSL_PARAM_construct_end();
+
+        if (EVP_PKEY_CTX_set_params(pctx, params) != 1) {
+            put_error_op_ctx(genctx, IBMCA_ERR_INTERNAL_ERROR,
+                             "EVP_PKEY_CTX_set_params failed");
+            goto out;
+        }
+    }
+#endif
+
+    if (osslcb != NULL) {
+        cbdata.osslcb = osslcb;
+        cbdata.cbarg = cbarg;
+        EVP_PKEY_CTX_set_cb(pctx, ibmca_keygen_cb);
+        EVP_PKEY_CTX_set_app_data(pctx, &cbdata);
+    }
+
+    if (EVP_PKEY_generate(pctx, &pkey) != 1) {
+        put_error_key(key, IBMCA_ERR_INTERNAL_ERROR,
+                      "EVP_PKEY_generate failed");
+        goto out;
+    }
+
+    rc = ibmca_import_from_fallback_pkey(key, pkey, OSSL_KEYMGMT_SELECT_ALL);
+    if (rc != 1) {
+        ibmca_debug_op_ctx(genctx,
+                           "ERROR: ibmca_import_from_fallback_pkey failed");
+        goto out;
+    }
+
+    rc = 1;
+
+out:
+    if (pctx != NULL)
+        EVP_PKEY_CTX_free(pctx);
+    if (pkey != NULL)
+        EVP_PKEY_free(pkey);
+
+    return rc;
+}
+
+static void *ibmca_keymgmt_ec_gen(void *vgenctx, OSSL_CALLBACK *osslcb,
+                                  void *cbarg)
+{
+    struct ibmca_op_ctx *genctx = vgenctx;
+    OSSL_PARAM cb_params[] = { OSSL_PARAM_END, OSSL_PARAM_END, OSSL_PARAM_END };
+    struct ibmca_key *key = NULL;
+    unsigned int privlen;
+    int rc, p, n;
+    bool fallback = false;
+
+    if (genctx == NULL)
+        return NULL;
+
+    ibmca_debug_op_ctx(genctx, "genctx: %p", genctx);
+
+    cb_params[0] = OSSL_PARAM_construct_int(OSSL_GEN_PARAM_POTENTIAL, &p);
+    cb_params[1] = OSSL_PARAM_construct_int(OSSL_GEN_PARAM_ITERATION, &n);
+
+    key = ibmca_keymgmt_new(genctx->provctx, genctx->type, "EC",
+                            ibmca_keymgmt_ec_free_cb,
+                            ibmca_keymgmt_ec_dup_cb,
+                            ibmca_keymgmt_ec_get_max_param_size,
+                            ibmca_keymgmt_ec_export,
+                            ibmca_keymgmt_ec_import,
+                            ibmca_keymgmt_ec_has,
+                            ibmca_keymgmt_ec_match);
+    if (key == NULL) {
+        ibmca_debug_op_ctx(genctx, "ERROR: ibmca_keymgmt_new failed");
+        return NULL;
+    }
+
+    key->ec.curve_nid = genctx->ec.gen.curve_nid;
+    key->ec.format = genctx->ec.gen.format;
+    key->ec.include_pub = true;
+
+    ibmca_debug_op_ctx(genctx, "curve_nid: %d", key->ec.curve_nid);
+    ibmca_debug_op_ctx(genctx, "format: %d", key->ec.format);
+    ibmca_debug_op_ctx(genctx, "include_pub: %d", key->ec.include_pub);
+
+    key->ec.prime_size = ibmca_keymgmt_ec_get_prime_size(key);
+    if (key->ec.prime_size == 0) {
+        put_error_op_ctx(genctx, IBMCA_ERR_EC_CURVE_NOT_SUPPORTED,
+                         "Unsupported curve nid: %d", key->ec.curve_nid);
+        ibmca_keymgmt_free(key);
+        return NULL;
+    }
+
+    ibmca_debug_op_ctx(genctx, "prime_size: %lu", key->ec.prime_size);
+
+    if ((genctx->ec.gen.selection & OSSL_KEYMGMT_SELECT_KEYPAIR) == 0)
+        goto out;
+
+    ibmca_debug_op_ctx(genctx, "dhkem_ikm: %p", genctx->ec.gen.dhkem_ikm);
+    ibmca_debug_op_ctx(genctx, "dhkem_ikmlen: %u", genctx->ec.gen.dhkem_ikmlen);
+
+    if (genctx->ec.gen.dhkem_ikm != NULL && genctx->ec.gen.dhkem_ikmlen > 0) {
+        ibmca_debug_op_ctx(genctx, "DHKEM-IKM is set, force use of fallback");
+        fallback = true;
+    }
+
+    key->ec.key = ica_ec_key_new(key->ec.curve_nid, &privlen);
+    if (key->ec.key == NULL || key->ec.prime_size != privlen) {
+        ibmca_debug_op_ctx(genctx, "ica_ec_key_new failed");
+        fallback = true;
+    }
+
+    p = 0;
+    n = 0;
+    if (osslcb != NULL && osslcb(cb_params, cbarg) == 0) {
+        put_error_op_ctx(genctx, IBMCA_ERR_INTERNAL_ERROR, "osslcb failed");
+        ibmca_keymgmt_free(key);
+        return NULL;
+    }
+
+    if (!fallback)
+        rc = ica_ec_key_generate(genctx->provctx->ica_adapter, key->ec.key);
+    else
+        rc = ENODEV;
+    if (rc != 0 || fallback) {
+        if (!fallback)
+            ibmca_debug_op_ctx(genctx, "ica_ec_key_generate failed with: %s",
+                               strerror(rc));
+
+        if (key->ec.key != NULL)
+            ica_ec_key_free(key->ec.key);
+        key->ec.key = NULL;
+
+        rc = ibmca_keymgmt_ec_gen_fallback(genctx, key, osslcb, cbarg);
+        if (rc != 1) {
+            ibmca_debug_op_ctx(genctx,
+                               "ERROR: ibmca_keymgmt_ec_gen_fallback failed");
+            ibmca_keymgmt_free(key);
+            return NULL;
+        }
+    }
+
+    p = 3;
+    n = 0;
+    if (osslcb != NULL && osslcb(cb_params, cbarg) == 0) {
+        put_error_op_ctx(genctx, IBMCA_ERR_INTERNAL_ERROR, "osslcb failed");
+        ibmca_keymgmt_free(key);
+        return NULL;
+    }
+
+out:
+    ibmca_debug_op_ctx(genctx, "key: %p", key);
+
+    return key;
+}
+
+static int ibmca_keymgmt_ec_pub_key_from_data(const struct ibmca_key *key,
+                                              const OSSL_PARAM params[],
+                                              BIGNUM **x, BIGNUM **y,
+                                              point_conversion_form_t *format)
+{
+    int rc = 0;
+    unsigned char *enc = NULL;
+    size_t enc_len = 0;
+    EC_GROUP *group = NULL;
+    EC_POINT *point = NULL;
+
+    *format = POINT_CONVERSION_UNCOMPRESSED;
+
+    /* OSSL_PKEY_PARAM_ENCODED_PUBLIC_KEY */
+    rc = ibmca_param_get_octet_string(key->provctx, params,
+                                      OSSL_PKEY_PARAM_ENCODED_PUBLIC_KEY,
+                                      (void **)&enc, &enc_len);
+    if (rc == 0)
+        return 0;
+    if (rc < 0) {
+        /* OSSL_PKEY_PARAM_PUB_KEY */
+        rc = ibmca_param_get_octet_string(key->provctx, params,
+                                          OSSL_PKEY_PARAM_PUB_KEY,
+                                          (void **)&enc, &enc_len);
+        if (rc == 0)
+            return 0;
+    }
+    if (rc > 0) {
+        group = EC_GROUP_new_by_curve_name(key->ec.curve_nid);
+        if (group == NULL) {
+            put_error_key(key, IBMCA_ERR_INTERNAL_ERROR,
+                          "EC_GROUP_new_by_curve_name failed");
+            goto out;
+        }
+
+        point = EC_POINT_new(group);
+        if (point == NULL) {
+            put_error_key(key, IBMCA_ERR_INTERNAL_ERROR,
+                          "EC_POINT_new failed");
+            goto out;
+        }
+
+        if (EC_POINT_oct2point(group, point, enc, enc_len, NULL) == 0) {
+            put_error_key(key, IBMCA_ERR_INTERNAL_ERROR,
+                          "EC_POINT_oct2point failed");
+            goto out;
+        }
+
+        *x = BN_new();
+        *y = BN_new();
+        if (*x == NULL || *y == NULL) {
+            put_error_key(key, IBMCA_ERR_MALLOC_FAILED, "BN_new failed");
+            goto out;
+        }
+
+        if (EC_POINT_get_affine_coordinates(group, point, *x, *y, NULL) == 0) {
+            put_error_key(key, IBMCA_ERR_INTERNAL_ERROR,
+                          "EC_POINT_get_affine_coordinates failed");
+            goto out;
+        }
+
+        *format = (point_conversion_form_t)(enc[0] & ~0x01);
+
+        rc = 1;
+
+out:
+        if (group != NULL)
+            EC_GROUP_free(group);
+        if (point != NULL)
+            EC_POINT_free(point);
+        if (enc != NULL)
+            P_FREE(key->provctx, enc);
+
+        return rc; /* do not check for X and Y params anymore */
+    }
+
+    /* OSSL_PKEY_PARAM_EC_PUB_X */
+    rc = ibmca_param_get_bn(key->provctx, params, OSSL_PKEY_PARAM_EC_PUB_X, x);
+    if (rc == 0)
+        return 0;
+
+    /* OSSL_PKEY_PARAM_EC_PUB_Y */
+    rc = ibmca_param_get_bn(key->provctx, params, OSSL_PKEY_PARAM_EC_PUB_Y, y);
+    if (rc == 0)
+        return 0;
+
+   return 1;
+}
+
+static int ibmca_keymgmt_ec_priv_key_from_data(const struct ibmca_key *key,
+                                               const OSSL_PARAM params[],
+                                               BIGNUM **d)
+{
+    int rc = 0;
+
+    /* OSSL_PKEY_PARAM_PRIV_KEY */
+    *d = BN_secure_new();
+    if (*d == NULL) {
+        put_error_key(key, IBMCA_ERR_MALLOC_FAILED, "BN_secure_new failed");
+        return 0;
+    }
+
+    rc = ibmca_param_get_bn(key->provctx, params, OSSL_PKEY_PARAM_PRIV_KEY, d);
+    if (rc <= 0) {
+        BN_clear_free(*d);
+        *d = NULL;
+        return rc;
+    }
+
+   return 1;
+}
+
+static const OSSL_PARAM ibmca_ec_gettable_params[] = {
+    OSSL_PARAM_int(OSSL_PKEY_PARAM_BITS, NULL),
+    OSSL_PARAM_int(OSSL_PKEY_PARAM_SECURITY_BITS, NULL),
+    OSSL_PARAM_int(OSSL_PKEY_PARAM_MAX_SIZE, NULL),
+    OSSL_PARAM_utf8_string(OSSL_PKEY_PARAM_DEFAULT_DIGEST, NULL, 0),
+    OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_ENCODED_PUBLIC_KEY, NULL, 0),
+    OSSL_PARAM_int(OSSL_PKEY_PARAM_EC_DECODED_FROM_EXPLICIT_PARAMS, NULL),
+    OSSL_PARAM_utf8_string(OSSL_PKEY_PARAM_GROUP_NAME, NULL, 0),
+    OSSL_PARAM_utf8_string(OSSL_PKEY_PARAM_EC_ENCODING, NULL, 0),
+    OSSL_PARAM_utf8_string(OSSL_PKEY_PARAM_EC_POINT_CONVERSION_FORMAT, NULL, 0),
+    OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_PUB_KEY, NULL, 0),
+    OSSL_PARAM_BN(OSSL_PKEY_PARAM_EC_PUB_X, NULL, 0),
+    OSSL_PARAM_BN(OSSL_PKEY_PARAM_EC_PUB_Y, NULL, 0),
+    OSSL_PARAM_BN(OSSL_PKEY_PARAM_PRIV_KEY, NULL, 0),
+    OSSL_PARAM_int(OSSL_PKEY_PARAM_USE_COFACTOR_ECDH, NULL),
+    OSSL_PARAM_int(OSSL_PKEY_PARAM_EC_INCLUDE_PUBLIC, NULL),
+    OSSL_PARAM_END
+};
+
+static const OSSL_PARAM *ibmca_keymgmt_ec_gettable_params(void *vprovctx)
+{
+    const struct ibmca_prov_ctx *provctx = vprovctx;
+    const OSSL_PARAM *p;
+
+    if (provctx == NULL)
+        return NULL;
+
+    for (p = ibmca_ec_gettable_params; p != NULL && p->key != NULL; p++)
+        ibmca_debug_ctx(provctx, "param: %s", p->key);
+
+    return ibmca_ec_gettable_params;
+}
+
+static size_t ibmca_keymgmt_ec_get_max_param_size(const struct ibmca_key *key)
+{
+    int rc = 0;
+    ECDSA_SIG *sig = NULL;
+    EC_GROUP *group = NULL;
+    const BIGNUM *bn;
+    BIGNUM *r = NULL, *s = NULL;
+
+    group = EC_GROUP_new_by_curve_name(key->ec.curve_nid);
+    if (group == NULL) {
+        put_error_key(key, IBMCA_ERR_INTERNAL_ERROR,
+                      "EC_GROUP_new_by_curve_name failed");
+        goto out;
+    }
+
+    bn = EC_GROUP_get0_order(group);
+    if (bn == NULL) {
+        put_error_key(key, IBMCA_ERR_INTERNAL_ERROR,
+                      "EC_GROUP_get0_order failed");
+        goto out;
+    }
+
+    sig = ECDSA_SIG_new();
+    if (sig == NULL) {
+        put_error_key(key, IBMCA_ERR_MALLOC_FAILED, "ECDSA_SIG_new failed");
+        goto out;
+    }
+
+    r = BN_dup(bn);
+    s = BN_dup(bn);
+    if (ECDSA_SIG_set0(sig, r, s) == 0) {
+        put_error_key(key, IBMCA_ERR_INTERNAL_ERROR, "ECDSA_SIG_set0 failed");
+        goto out;
+    }
+
+    rc = i2d_ECDSA_SIG(sig, NULL);
+    if (rc <= 0) {
+        put_error_key(key, IBMCA_ERR_INTERNAL_ERROR, "i2d_ECDSA_SIG failed");
+        rc = 0;
+        goto out;
+    }
+
+out:
+    if (group != NULL)
+        EC_GROUP_free(group);
+    if (sig != NULL)
+        ECDSA_SIG_free(sig);
+
+    return rc;
+}
+
+static size_t ibmca_keymgmt_ec_get_prime_bits(const struct ibmca_key *key)
+{
+    int rc = 0;
+    EC_GROUP *group = NULL;
+
+    group = EC_GROUP_new_by_curve_name(key->ec.curve_nid);
+    if (group == NULL) {
+        put_error_key(key, IBMCA_ERR_INTERNAL_ERROR,
+                      "EC_GROUP_new_by_curve_name failed");
+        goto out;
+    }
+
+    rc = EC_GROUP_order_bits(group);
+    if (rc <= 0) {
+        put_error_key(key, IBMCA_ERR_INTERNAL_ERROR,
+                      "EC_GROUP_order_bits failed");
+        rc = 0;
+        goto out;
+    }
+
+out:
+    if (group != NULL)
+        EC_GROUP_free(group);
+
+    return rc;
+}
+
+static size_t ibmca_keymgmt_ec_get_prime_size(const struct ibmca_key *key)
+{
+    return (ibmca_keymgmt_ec_get_prime_bits(key) + 7) / 8;
+}
+
+static int ibmca_keymgmt_ec_get_security_bits(const struct ibmca_key *key)
+{
+    int bits;
+
+    bits = ibmca_keymgmt_ec_get_prime_bits(key);
+    if (bits == 0)
+        return 0;
+
+    /*
+     * The following estimates are based on the values published
+     * in Table 2 of "NIST Special Publication 800-57 Part 1 Revision 4"
+     * at http://dx.doi.org/10.6028/NIST.SP.800-57pt1r4 .
+     */
+    if (bits >= 512)
+        bits = 256;
+    else if (bits >= 384)
+        bits = 192;
+    else if (bits >= 256)
+        bits = 128;
+    else if (bits >= 224)
+        bits = 112;
+    else if (bits >= 160)
+        bits = 80;
+    else
+        bits = bits / 2;
+
+    return bits;
+}
+
+static int ibmca_keymgmt_ec_get_params(void *vkey, OSSL_PARAM params[])
+{
+    struct ibmca_key *key = vkey;
+    OSSL_PARAM *parm;
+    const char *name;
+    BIGNUM *x = NULL, *y = NULL, *d = NULL;
+    int rc, size;
+
+    if (key == NULL)
+        return 0;
+
+    ibmca_debug_key(key, "key: %p", key);
+    for (parm = params; parm != NULL && parm->key != NULL; parm++)
+        ibmca_debug_key(key, "param: %s", parm->key);
+
+    /* OSSL_PKEY_PARAM_BITS */
+    if (OSSL_PARAM_locate(params, OSSL_PKEY_PARAM_BITS) != NULL) {
+        size = ibmca_keymgmt_ec_get_prime_bits(key);
+        if (size == 0)
+            return 0;
+
+        rc = ibmca_param_build_set_int(key->provctx, NULL, params,
+                                       OSSL_PKEY_PARAM_BITS, size);
+        if (rc == 0)
+            return 0;
+    }
+
+    /* OSSL_PKEY_PARAM_SECURITY_BITS */
+    if (OSSL_PARAM_locate(params, OSSL_PKEY_PARAM_SECURITY_BITS) != NULL) {
+        size = ibmca_keymgmt_ec_get_security_bits(key);
+        if (size == 0)
+            return 0;
+
+        rc = ibmca_param_build_set_int(key->provctx, NULL, params,
+                                       OSSL_PKEY_PARAM_SECURITY_BITS, size);
+        if (rc == 0)
+            return 0;
+    }
+
+    /* OSSL_PKEY_PARAM_MAX_SIZE */
+    if (OSSL_PARAM_locate(params, OSSL_PKEY_PARAM_MAX_SIZE) != NULL) {
+        size = ibmca_keymgmt_ec_get_max_param_size(key);
+        if (size == 0)
+            return 0;
+
+        rc = ibmca_param_build_set_int(key->provctx, NULL, params,
+                                       OSSL_PKEY_PARAM_MAX_SIZE, size);
+        if (rc == 0)
+            return 0;
+    }
+
+    /* OSSL_PKEY_PARAM_DEFAULT_DIGEST */
+    rc = ibmca_param_build_set_utf8(key->provctx, NULL, params,
+                                    OSSL_PKEY_PARAM_DEFAULT_DIGEST,
+                                    OBJ_nid2sn(IBMCA_EC_DEFAULT_DIGEST));
+    if (rc == 0)
+        return 0;
+
+    /* OSSL_PKEY_PARAM_EC_DECODED_FROM_EXPLICIT_PARAMS */
+    rc = ibmca_param_build_set_int(key->provctx, NULL, params,
+                    OSSL_PKEY_PARAM_EC_DECODED_FROM_EXPLICIT_PARAMS, 0);
+    if (rc == 0)
+        return 0;
+
+    /* OSSL_PKEY_PARAM_USE_COFACTOR_ECDH */
+    rc = ibmca_param_build_set_int(key->provctx, NULL, params,
+                                   OSSL_PKEY_PARAM_USE_COFACTOR_ECDH, 0);
+    if (rc == 0)
+        return 0;
+
+    /* OSSL_PKEY_PARAM_EC_ENCODING */
+    rc = ibmca_param_build_set_utf8(key->provctx, NULL, params,
+                                    OSSL_PKEY_PARAM_EC_ENCODING,
+                                    OSSL_PKEY_EC_ENCODING_GROUP);
+    if (rc == 0)
+        return 0;
+
+    /* OSSL_PKEY_PARAM_EC_POINT_CONVERSION_FORMAT */
+    switch (key->ec.format) {
+    case POINT_CONVERSION_COMPRESSED:
+        name = OSSL_PKEY_EC_POINT_CONVERSION_FORMAT_COMPRESSED;
+        break;
+    case POINT_CONVERSION_UNCOMPRESSED:
+        name = OSSL_PKEY_EC_POINT_CONVERSION_FORMAT_UNCOMPRESSED;
+        break;
+    case POINT_CONVERSION_HYBRID:
+        name = OSSL_PKEY_EC_POINT_CONVERSION_FORMAT_HYBRID;
+        break;
+    default:
+        name = "";
+        break;
+    }
+    rc = ibmca_param_build_set_utf8(key->provctx, NULL, params,
+                                    OSSL_PKEY_PARAM_EC_POINT_CONVERSION_FORMAT,
+                                    name);
+    if (rc == 0)
+        return 0;
+
+    /* OSSL_PKEY_PARAM_EC_INCLUDE_PUBLIC */
+    rc = ibmca_param_build_set_int(key->provctx, NULL, params,
+                                   OSSL_PKEY_PARAM_EC_INCLUDE_PUBLIC,
+                                   key->ec.include_pub ? 1 : 0);
+    if (rc == 0)
+        return 0;
+
+    rc = ibmca_param_build_set_utf8(key->provctx, NULL, params,
+                                    OSSL_PKEY_PARAM_GROUP_NAME,
+                                    OBJ_nid2sn(key->ec.curve_nid));
+    if (rc == 0)
+        return 0;
+
+    /*
+     * OSSL_PKEY_PARAM_ENCODED_PUBLIC_KEY
+     * OSSL_PKEY_PARAM_PUB_KEY
+     * OSSL_PKEY_PARAM_EC_PUB_X
+     * OSSL_PKEY_PARAM_EC_PUB_Y
+     */
+    rc = ibmca_keymgmt_ec_pub_key_as_bn(key, &x, &y);
+    if (rc == 0)
+        goto out;
+    if (rc > 0) {
+        rc = ibmca_keymgmt_ec_pub_key_to_data(key, x, y, NULL, params);
+        if (rc == 0)
+            goto out;
+    }
+
+    /* OSSL_PKEY_PARAM_PRIV_KEY */
+    rc = ibmca_keymgmt_ec_priv_key_as_bn(key, &d);
+    if (rc == 0)
+        goto out;
+    if (rc > 0) {
+        rc = ibmca_keymgmt_ec_priv_key_to_data(key, d, NULL, params);
+        if (rc == 0)
+            goto out;
+    }
+
+    rc = 1;
+out:
+    if (x != NULL)
+        BN_free(x);
+    if (y != NULL)
+        BN_free(y);
+    if (d != NULL)
+        BN_free(d);
+
+    return rc;
+}
+
+static const OSSL_PARAM ibmca_ec_settable_params[] = {
+    OSSL_PARAM_utf8_string(OSSL_PKEY_PARAM_EC_POINT_CONVERSION_FORMAT, NULL, 0),
+    OSSL_PARAM_utf8_string(OSSL_PKEY_PARAM_EC_ENCODING, NULL, 0),
+    OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_ENCODED_PUBLIC_KEY, NULL, 0),
+    OSSL_PARAM_int(OSSL_PKEY_PARAM_EC_INCLUDE_PUBLIC, NULL),
+    OSSL_PARAM_END
+};
+
+static const OSSL_PARAM *ibmca_keymgmt_ec_settable_params(void *vprovctx)
+{
+    const struct ibmca_prov_ctx *provctx = vprovctx;
+    const OSSL_PARAM *p;
+
+    if (provctx == NULL)
+        return NULL;
+
+    for (p = ibmca_ec_settable_params; p != NULL && p->key != NULL; p++)
+        ibmca_debug_ctx(provctx, "param: %s", p->key);
+
+    return ibmca_ec_settable_params;
+}
+
+static int ibmca_keymgmt_ec_set_params(void *vkey, const OSSL_PARAM params[])
+{
+    struct ibmca_key *key = vkey;
+    const OSSL_PARAM *parm;
+    BIGNUM *x = NULL, *y = NULL;
+    point_conversion_form_t format;
+    const char *name;
+    int rc, value;
+
+    if (key == NULL)
+        return 0;
+
+    ibmca_debug_key(key, "key: %p", key);
+    for (parm = params; parm != NULL && parm->key != NULL; parm++)
+        ibmca_debug_key(key, "param: %s", parm->key);
+
+    /* OSSL_PKEY_PARAM_EC_POINT_CONVERSION_FORMAT */
+    rc = ibmca_param_get_utf8(key->provctx, params,
+                              OSSL_PKEY_PARAM_EC_POINT_CONVERSION_FORMAT,
+                              &name);
+    if (rc == 0)
+        return 0;
+    if (rc > 0) {
+        if (strcasecmp(name,
+                OSSL_PKEY_EC_POINT_CONVERSION_FORMAT_UNCOMPRESSED) != 0) {
+            key->ec.format = POINT_CONVERSION_UNCOMPRESSED;
+        } else if (strcasecmp(name,
+                OSSL_PKEY_EC_POINT_CONVERSION_FORMAT_COMPRESSED) != 0) {
+            key->ec.format = POINT_CONVERSION_COMPRESSED;
+        } else if (strcasecmp(name,
+                OSSL_PKEY_EC_POINT_CONVERSION_FORMAT_HYBRID) != 0) {
+            key->ec.format = POINT_CONVERSION_HYBRID;
+        } else {
+            put_error_key(key, IBMCA_ERR_INVALID_PARAM,
+                          "EC '%s': '%s' is an unsupported format",
+                          OSSL_PKEY_PARAM_EC_POINT_CONVERSION_FORMAT, name);
+            rc = 0;
+            goto out;
+        }
+
+        ibmca_clean_fallback_pkey_cache(key);
+    }
+
+    /* OSSL_PKEY_PARAM_EC_ENCODING */
+    rc = ibmca_param_get_utf8(key->provctx, params,
+                              OSSL_PKEY_PARAM_EC_ENCODING, &name);
+    if (rc == 0)
+        return 0;
+    if (rc > 0) {
+        /* We only support named curves */
+        if (strcasecmp(name, OSSL_PKEY_EC_ENCODING_GROUP) != 0) {
+            put_error_key(key, IBMCA_ERR_INVALID_PARAM,
+                          "EC '%s': '%s' is an unsupported encoding",
+                          OSSL_PKEY_PARAM_EC_ENCODING, name);
+            rc = 0;
+            goto out;
+        }
+
+        ibmca_clean_fallback_pkey_cache(key);
+    }
+
+    /*  OSSL_PKEY_PARAM_USE_COFACTOR_ECDH */
+    rc = ibmca_param_get_int(key->provctx, params,
+                             OSSL_PKEY_PARAM_USE_COFACTOR_ECDH, &value);
+    if (rc == 0)
+        return 0;
+    if (rc > 0) {
+        /* We do not support Cofactor DH (ECC CDH) */
+        if (value != 0) {
+            put_error_key(key, IBMCA_ERR_INVALID_PARAM,
+                          "EC '%s': %d is not supported",
+                          OSSL_PKEY_PARAM_USE_COFACTOR_ECDH, value);
+            rc = 0;
+            goto out;
+        }
+
+        ibmca_clean_fallback_pkey_cache(key);
+    }
+
+    /* OSSL_PKEY_PARAM_ENCODED_PUBLIC_KEY */
+    rc = ibmca_keymgmt_ec_pub_key_from_data(key, params, &x, &y, &format);
+    if (rc == 0)
+        goto out;
+    if (rc > 0 && x != NULL && y != NULL) {
+        rc = ibmca_keymgmt_ec_pub_key_from_bn(key, x, y);
+        if (rc == 0)
+            goto out;
+
+        key->ec.format = format;
+        ibmca_clean_fallback_pkey_cache(key);
+    }
+
+    /* OSSL_PKEY_PARAM_EC_INCLUDE_PUBLIC */
+    rc = ibmca_param_get_int(key->provctx, params,
+                             OSSL_PKEY_PARAM_EC_INCLUDE_PUBLIC, &value);
+    if (rc == 0)
+        return 0;
+    if (rc > 0) {
+        key->ec.include_pub = (value != 0);
+        ibmca_clean_fallback_pkey_cache(key);
+    }
+
+    rc = 1;
+
+out:
+    BN_free(x);
+    BN_free(y);
+
+    return rc;
+}
+
+static const OSSL_PARAM ibmca_keymgmt_ec_imexport_priv_key[] = {
+    OSSL_PARAM_BN(OSSL_PKEY_PARAM_PRIV_KEY, NULL, 0),
+    OSSL_PARAM_END
+};
+
+static const OSSL_PARAM ibmca_keymgmt_ec_imexport_pub_key[] = {
+    OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_PUB_KEY, NULL, 0),
+    OSSL_PARAM_END
+};
+
+static const OSSL_PARAM ibmca_keymgmt_ec_imexport_key_pair[] = {
+    OSSL_PARAM_BN(OSSL_PKEY_PARAM_PRIV_KEY, NULL, 0),
+    OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_PUB_KEY, NULL, 0),
+    OSSL_PARAM_END
+};
+
+static const OSSL_PARAM ibmca_keymgmt_ec_imexport_dom_params[] = {
+    OSSL_PARAM_utf8_string(OSSL_PKEY_PARAM_GROUP_NAME, NULL, 0),
+    OSSL_PARAM_utf8_string(OSSL_PKEY_PARAM_EC_ENCODING, NULL, 0),
+    OSSL_PARAM_utf8_string(OSSL_PKEY_PARAM_EC_POINT_CONVERSION_FORMAT, NULL, 0),
+    OSSL_PARAM_END
+};
+
+static const OSSL_PARAM ibmca_keymgmt_ec_imexport_priv_key_dom_params[] = {
+    OSSL_PARAM_BN(OSSL_PKEY_PARAM_PRIV_KEY, NULL, 0),
+    OSSL_PARAM_utf8_string(OSSL_PKEY_PARAM_GROUP_NAME, NULL, 0),
+    OSSL_PARAM_utf8_string(OSSL_PKEY_PARAM_EC_ENCODING, NULL, 0),
+    OSSL_PARAM_utf8_string(OSSL_PKEY_PARAM_EC_POINT_CONVERSION_FORMAT, NULL, 0),
+    OSSL_PARAM_END
+};
+
+static const OSSL_PARAM ibmca_keymgmt_ec_imexport_pub_key_dom_params[] = {
+    OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_PUB_KEY, NULL, 0),
+    OSSL_PARAM_utf8_string(OSSL_PKEY_PARAM_GROUP_NAME, NULL, 0),
+    OSSL_PARAM_utf8_string(OSSL_PKEY_PARAM_EC_ENCODING, NULL, 0),
+    OSSL_PARAM_utf8_string(OSSL_PKEY_PARAM_EC_POINT_CONVERSION_FORMAT, NULL, 0),
+    OSSL_PARAM_END
+};
+
+static const OSSL_PARAM ibmca_keymgmt_ec_imexport_key_pair_dom_params[] = {
+    OSSL_PARAM_BN(OSSL_PKEY_PARAM_PRIV_KEY, NULL, 0),
+    OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_PUB_KEY, NULL, 0),
+    OSSL_PARAM_utf8_string(OSSL_PKEY_PARAM_GROUP_NAME, NULL, 0),
+    OSSL_PARAM_utf8_string(OSSL_PKEY_PARAM_EC_ENCODING, NULL, 0),
+    OSSL_PARAM_utf8_string(OSSL_PKEY_PARAM_EC_POINT_CONVERSION_FORMAT, NULL, 0),
+    OSSL_PARAM_END
+};
+
+static const OSSL_PARAM ibmca_keymgmt_ec_imexport_other_params[] = {
+    OSSL_PARAM_int(OSSL_PKEY_PARAM_USE_COFACTOR_ECDH, NULL),
+    OSSL_PARAM_int(OSSL_PKEY_PARAM_EC_INCLUDE_PUBLIC, NULL),
+    OSSL_PARAM_END
+};
+
+static const OSSL_PARAM ibmca_keymgmt_ec_imexport_priv_key_other_params[] = {
+    OSSL_PARAM_BN(OSSL_PKEY_PARAM_PRIV_KEY, NULL, 0),
+    OSSL_PARAM_int(OSSL_PKEY_PARAM_USE_COFACTOR_ECDH, NULL),
+    OSSL_PARAM_int(OSSL_PKEY_PARAM_EC_INCLUDE_PUBLIC, NULL),
+    OSSL_PARAM_END
+};
+
+static const OSSL_PARAM ibmca_keymgmt_ec_imexport_pub_key_other_params[] = {
+    OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_PUB_KEY, NULL, 0),
+    OSSL_PARAM_int(OSSL_PKEY_PARAM_USE_COFACTOR_ECDH, NULL),
+    OSSL_PARAM_int(OSSL_PKEY_PARAM_EC_INCLUDE_PUBLIC, NULL),
+    OSSL_PARAM_END
+};
+
+static const OSSL_PARAM ibmca_keymgmt_ec_imexport_key_pair_other_params[] = {
+    OSSL_PARAM_BN(OSSL_PKEY_PARAM_PRIV_KEY, NULL, 0),
+    OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_PUB_KEY, NULL, 0),
+    OSSL_PARAM_int(OSSL_PKEY_PARAM_USE_COFACTOR_ECDH, NULL),
+    OSSL_PARAM_int(OSSL_PKEY_PARAM_EC_INCLUDE_PUBLIC, NULL),
+    OSSL_PARAM_END
+};
+
+static const OSSL_PARAM ibmca_keymgmt_ec_imexport_all_params[] = {
+    OSSL_PARAM_utf8_string(OSSL_PKEY_PARAM_GROUP_NAME, NULL, 0),
+    OSSL_PARAM_utf8_string(OSSL_PKEY_PARAM_EC_ENCODING, NULL, 0),
+    OSSL_PARAM_utf8_string(OSSL_PKEY_PARAM_EC_POINT_CONVERSION_FORMAT, NULL, 0),
+    OSSL_PARAM_int(OSSL_PKEY_PARAM_USE_COFACTOR_ECDH, NULL),
+    OSSL_PARAM_int(OSSL_PKEY_PARAM_EC_INCLUDE_PUBLIC, NULL),
+    OSSL_PARAM_END
+};
+
+static const OSSL_PARAM ibmca_keymgmt_ec_imexport_priv_key_all_params[] = {
+    OSSL_PARAM_BN(OSSL_PKEY_PARAM_PRIV_KEY, NULL, 0),
+    OSSL_PARAM_utf8_string(OSSL_PKEY_PARAM_GROUP_NAME, NULL, 0),
+    OSSL_PARAM_utf8_string(OSSL_PKEY_PARAM_EC_ENCODING, NULL, 0),
+    OSSL_PARAM_utf8_string(OSSL_PKEY_PARAM_EC_POINT_CONVERSION_FORMAT, NULL, 0),
+    OSSL_PARAM_int(OSSL_PKEY_PARAM_USE_COFACTOR_ECDH, NULL),
+    OSSL_PARAM_int(OSSL_PKEY_PARAM_EC_INCLUDE_PUBLIC, NULL),
+    OSSL_PARAM_END
+};
+
+static const OSSL_PARAM ibmca_keymgmt_ec_imexport_pub_key_all_params[] = {
+    OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_PUB_KEY, NULL, 0),
+    OSSL_PARAM_utf8_string(OSSL_PKEY_PARAM_GROUP_NAME, NULL, 0),
+    OSSL_PARAM_utf8_string(OSSL_PKEY_PARAM_EC_ENCODING, NULL, 0),
+    OSSL_PARAM_utf8_string(OSSL_PKEY_PARAM_EC_POINT_CONVERSION_FORMAT, NULL, 0),
+    OSSL_PARAM_int(OSSL_PKEY_PARAM_USE_COFACTOR_ECDH, NULL),
+    OSSL_PARAM_int(OSSL_PKEY_PARAM_EC_INCLUDE_PUBLIC, NULL),
+    OSSL_PARAM_END
+};
+
+static const OSSL_PARAM ibmca_keymgmt_ec_imexport_key_pair_all_params[] = {
+    OSSL_PARAM_BN(OSSL_PKEY_PARAM_PRIV_KEY, NULL, 0),
+    OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_PUB_KEY, NULL, 0),
+    OSSL_PARAM_utf8_string(OSSL_PKEY_PARAM_GROUP_NAME, NULL, 0),
+    OSSL_PARAM_utf8_string(OSSL_PKEY_PARAM_EC_ENCODING, NULL, 0),
+    OSSL_PARAM_utf8_string(OSSL_PKEY_PARAM_EC_POINT_CONVERSION_FORMAT, NULL, 0),
+    OSSL_PARAM_int(OSSL_PKEY_PARAM_USE_COFACTOR_ECDH, NULL),
+    OSSL_PARAM_int(OSSL_PKEY_PARAM_EC_INCLUDE_PUBLIC, NULL),
+    OSSL_PARAM_END
+};
+
+static const OSSL_PARAM *ibmca_keymgmt_ec_imexport_types(int selection)
+{
+    selection &= OSSL_KEYMGMT_SELECT_ALL;
+
+    switch (selection) {
+    case OSSL_KEYMGMT_SELECT_PRIVATE_KEY:
+        return ibmca_keymgmt_ec_imexport_priv_key;
+    case OSSL_KEYMGMT_SELECT_PUBLIC_KEY:
+        return ibmca_keymgmt_ec_imexport_pub_key;
+    case OSSL_KEYMGMT_SELECT_KEYPAIR:
+        return ibmca_keymgmt_ec_imexport_key_pair;
+    case OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS:
+        return ibmca_keymgmt_ec_imexport_dom_params;
+    case OSSL_KEYMGMT_SELECT_PRIVATE_KEY | OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS:
+        return ibmca_keymgmt_ec_imexport_priv_key_dom_params;
+    case OSSL_KEYMGMT_SELECT_PUBLIC_KEY | OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS:
+        return ibmca_keymgmt_ec_imexport_pub_key_dom_params;
+    case OSSL_KEYMGMT_SELECT_KEYPAIR | OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS:
+        return ibmca_keymgmt_ec_imexport_key_pair_dom_params;
+    case OSSL_KEYMGMT_SELECT_OTHER_PARAMETERS:
+        return ibmca_keymgmt_ec_imexport_other_params;
+    case OSSL_KEYMGMT_SELECT_PRIVATE_KEY | OSSL_KEYMGMT_SELECT_OTHER_PARAMETERS:
+        return ibmca_keymgmt_ec_imexport_priv_key_other_params;
+    case OSSL_KEYMGMT_SELECT_PUBLIC_KEY | OSSL_KEYMGMT_SELECT_OTHER_PARAMETERS:
+        return ibmca_keymgmt_ec_imexport_pub_key_other_params;
+    case OSSL_KEYMGMT_SELECT_KEYPAIR | OSSL_KEYMGMT_SELECT_OTHER_PARAMETERS:
+        return ibmca_keymgmt_ec_imexport_key_pair_other_params;
+    case OSSL_KEYMGMT_SELECT_ALL_PARAMETERS:
+        return ibmca_keymgmt_ec_imexport_all_params;
+    case OSSL_KEYMGMT_SELECT_PRIVATE_KEY | OSSL_KEYMGMT_SELECT_ALL_PARAMETERS:
+        return ibmca_keymgmt_ec_imexport_priv_key_all_params;
+    case OSSL_KEYMGMT_SELECT_PUBLIC_KEY | OSSL_KEYMGMT_SELECT_ALL_PARAMETERS:
+        return ibmca_keymgmt_ec_imexport_pub_key_all_params;
+    case OSSL_KEYMGMT_SELECT_KEYPAIR | OSSL_KEYMGMT_SELECT_ALL_PARAMETERS:
+        return ibmca_keymgmt_ec_imexport_key_pair_all_params;
+    }
+
+    return NULL;
+}
+
+static int ibmca_keymgmt_ec_export(void *vkey, int selection,
+                                   OSSL_CALLBACK *param_callback, void *cbarg)
+{
+    struct ibmca_key *key = vkey;
+    OSSL_PARAM_BLD *bld;
+    OSSL_PARAM *params = NULL;
+    EC_GROUP *group = NULL;
+    EC_POINT *point = NULL;
+    unsigned char *enc = NULL;
+    size_t enc_len = 0;
+    BIGNUM *x = NULL, *y = NULL, *d = NULL;
+    char *name;
+    int rc = 1, format;
+
+    if (key == NULL || param_callback == NULL)
+        return 0;
+
+    ibmca_debug_key(key, "key: %p selection: 0x%x", key, selection);
+
+    bld = OSSL_PARAM_BLD_new();
+    if (bld == NULL) {
+        put_error_key(key, IBMCA_ERR_MALLOC_FAILED,
+                      "OSSL_PARAM_BLD_new failed");
+        return 0;
+    }
+
+    /* Public key is required when exporting private key */
+    if ((selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) != 0)
+        selection |= OSSL_KEYMGMT_SELECT_PUBLIC_KEY;
+    /* Domain parameters are required when exporting public or private key */
+    if ((selection & OSSL_KEYMGMT_SELECT_KEYPAIR) != 0)
+        selection |= OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS;
+
+    if ((selection & OSSL_KEYMGMT_SELECT_PUBLIC_KEY) != 0) {
+        /* Public key parts */
+        rc = ibmca_keymgmt_ec_pub_key_as_bn(key, &x, &y);
+        if (rc == 0)
+            goto error;
+
+        group = EC_GROUP_new_by_curve_name(key->ec.curve_nid);
+        if (group == NULL) {
+            put_error_key(key, IBMCA_ERR_INTERNAL_ERROR,
+                          "EC_GROUP_new_by_curve_name failed");
+            rc = 0;
+            goto error;
+        }
+
+        point = EC_POINT_new(group);
+        if (point == NULL) {
+            put_error_key(key, IBMCA_ERR_INTERNAL_ERROR,
+                          "EC_POINT_new failed");
+            rc = 0;
+            goto error;
+        }
+
+        if (EC_POINT_set_affine_coordinates(group, point, x, y, NULL) == 0) {
+            put_error_key(key, IBMCA_ERR_INTERNAL_ERROR,
+                          "EC_POINT_set_affine_coordinates failed");
+            rc = 0;
+            goto error;
+        }
+
+        /* OSSL_PKEY_PARAM_PUB_KEY */
+#if OPENSSL_VERSION_PREREQ(3, 1)
+        /*
+         * Since OpenSSL 3.1: Export OSSL_PKEY_PARAM_PUB_KEY in the format
+         * selected by OSSL_PKEY_PARAM_EC_POINT_CONVERSION_FORMAT.
+         */
+        format = key->ec.format;
+#else
+        format = POINT_CONVERSION_COMPRESSED;
+#endif
+        enc_len = EC_POINT_point2buf(group, point, format, &enc, NULL);
+        if (enc_len == 0) {
+            put_error_key(key, IBMCA_ERR_INTERNAL_ERROR,
+                          "EC_POINT_point2buf failed");
+            rc = 0;
+            goto error;
+        }
+
+        rc = ibmca_param_build_set_octet_ptr(key->provctx, bld, params,
+                                             OSSL_PKEY_PARAM_PUB_KEY,
+                                             enc, enc_len);
+        if (rc == 0)
+            goto error;
+    }
+
+    if ((selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) != 0) {
+        /* Private key parts */
+        rc = ibmca_keymgmt_ec_priv_key_as_bn(key, &d);
+        if (rc == 0)
+            goto error;
+        if (rc > 0) {
+            rc = ibmca_keymgmt_ec_priv_key_to_data(key, d, bld, NULL);
+            if (rc == 0)
+                goto error;
+        }
+    }
+
+    if ((selection & OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS) != 0) {
+        /* OSSL_PKEY_PARAM_GROUP_NAME */
+        rc = ibmca_param_build_set_utf8(key->provctx, bld, NULL,
+                                        OSSL_PKEY_PARAM_GROUP_NAME,
+                                        OBJ_nid2sn(key->ec.curve_nid));
+        if (rc == 0)
+            goto error;
+
+        /* OSSL_PKEY_PARAM_EC_ENCODING */
+        rc = ibmca_param_build_set_utf8(key->provctx, bld, NULL,
+                                        OSSL_PKEY_PARAM_EC_ENCODING,
+                                        OSSL_PKEY_EC_ENCODING_GROUP);
+        if (rc == 0)
+            goto error;
+
+        /* OSSL_PKEY_PARAM_EC_POINT_CONVERSION_FORMAT */
+        switch (key->ec.format) {
+        case POINT_CONVERSION_COMPRESSED:
+            name = OSSL_PKEY_EC_POINT_CONVERSION_FORMAT_COMPRESSED;
+            break;
+        case POINT_CONVERSION_UNCOMPRESSED:
+            name = OSSL_PKEY_EC_POINT_CONVERSION_FORMAT_UNCOMPRESSED;
+            break;
+        case POINT_CONVERSION_HYBRID:
+            name = OSSL_PKEY_EC_POINT_CONVERSION_FORMAT_HYBRID;
+            break;
+        default:
+            name = "";
+            break;
+        }
+        rc = ibmca_param_build_set_utf8(key->provctx, bld, NULL,
+                                    OSSL_PKEY_PARAM_EC_POINT_CONVERSION_FORMAT,
+                                    name);
+        if (rc == 0)
+            goto error;
+    }
+
+    if ((selection & OSSL_KEYMGMT_SELECT_OTHER_PARAMETERS) != 0) {
+        /* OSSL_PKEY_PARAM_USE_COFACTOR_ECDH */
+        rc = ibmca_param_build_set_int(key->provctx, bld, NULL,
+                                       OSSL_PKEY_PARAM_USE_COFACTOR_ECDH, 0);
+        if (rc == 0)
+            goto error;
+
+        /* OSSL_PKEY_PARAM_EC_INCLUDE_PUBLIC */
+        rc = ibmca_param_build_set_int(key->provctx, bld, NULL,
+                                       OSSL_PKEY_PARAM_EC_INCLUDE_PUBLIC,
+                                       key->ec.include_pub ? 1 : 0);
+        if (rc == 0)
+            goto error;
+    }
+
+    params = OSSL_PARAM_BLD_to_param(bld);
+    if (params == NULL) {
+        put_error_key(key, IBMCA_ERR_INTERNAL_ERROR,
+                      "OSSL_PARAM_BLD_to_param failed");
+        rc = 0;
+        goto error;
+    }
+
+    rc = param_callback(params, cbarg);
+    OSSL_PARAM_free(params);
+
+error:
+    OSSL_PARAM_BLD_free(bld);
+    if (x != NULL)
+        BN_free(x);
+    if (y != NULL)
+        BN_free(y);
+    if (d != NULL)
+        BN_free(d);
+    if (group != NULL)
+        EC_GROUP_free(group);
+    if (point != NULL)
+        EC_POINT_free(point);
+    if (enc != NULL)
+        P_FREE(key->provctx, enc);
+
+    return rc;
+}
+
+static int ibmca_keymgmt_ec_import(void *vkey, int selection,
+                                   const OSSL_PARAM params[])
+{
+    struct ibmca_key *key = vkey;
+    const OSSL_PARAM *parm;
+    point_conversion_form_t format;
+    BIGNUM *x = NULL, *y = NULL, *d = NULL;
+    OSSL_PARAM grp_params[] = { OSSL_PARAM_END, OSSL_PARAM_END };
+    EC_GROUP *group;
+    const char *name;
+    int value, rc = 0;
+
+    if (key == NULL)
+        return 0;
+
+    ibmca_debug_key(key, "key: %p selection: 0x%x", key, selection);
+    for (parm = params; parm != NULL && parm->key != NULL; parm++)
+        ibmca_debug_key(key, "param: %s", parm->key);
+
+    /* Clear any already existing key components */
+    ibmca_keymgmt_ec_free_cb(key);
+    ibmca_clean_fallback_pkey_cache(key);
+
+    if ((selection & OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS) == 0) {
+        put_error_key(key, IBMCA_ERR_INVALID_PARAM,
+                       "EC domain parameters are mandatory");
+        return 0;
+    }
+
+    if ((selection & OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS) != 0) {
+        /* OSSL_PKEY_PARAM_GROUP_NAME */
+        rc = ibmca_param_get_utf8(key->provctx, params,
+                                  OSSL_PKEY_PARAM_GROUP_NAME, &name);
+        if (rc == 0)
+            return 0;
+        if (rc > 0) {
+            grp_params[0] = OSSL_PARAM_construct_utf8_ptr(
+                                        OSSL_PKEY_PARAM_GROUP_NAME,
+                                        (char **)&name, 0);
+            group = EC_GROUP_new_from_params(grp_params, key->provctx->libctx,
+                                             NULL);
+            if (group == NULL) {
+                put_error_key(key, IBMCA_ERR_INVALID_PARAM,
+                              "EC '%s': '%s' is an unsupported curve",
+                              OSSL_PKEY_PARAM_GROUP_NAME, name);
+                return 0;
+            }
+
+            key->ec.curve_nid = EC_GROUP_get_curve_name(group);
+            EC_GROUP_free(group);
+
+            ibmca_debug_key(key, "curve_nid: %d", key->ec.curve_nid);
+        }
+
+        /* OSSL_PKEY_PARAM_EC_ENCODING */
+        rc = ibmca_param_get_utf8(key->provctx, params,
+                                  OSSL_PKEY_PARAM_EC_ENCODING, &name);
+        if (rc == 0)
+            return 0;
+        if (rc > 0) {
+            /* We only support named curves */
+            if (strcasecmp(name, OSSL_PKEY_EC_ENCODING_GROUP) != 0) {
+                put_error_key(key, IBMCA_ERR_INVALID_PARAM,
+                              "EC '%s': '%s' is an unsupported encoding",
+                              OSSL_PKEY_PARAM_EC_ENCODING, name);
+                return 0;
+            }
+        }
+    }
+
+    if ((selection & OSSL_KEYMGMT_SELECT_OTHER_PARAMETERS) != 0) {
+        /* OSSL_PKEY_PARAM_USE_COFACTOR_ECDH */
+        rc = ibmca_param_get_int(key->provctx, params,
+                                 OSSL_PKEY_PARAM_USE_COFACTOR_ECDH, &value);
+        if (rc == 0)
+            return 0;
+        if (rc > 0) {
+            /* We do not support Cofactor DH (ECC CDH) */
+            if (value != 0) {
+                put_error_key(key, IBMCA_ERR_INVALID_PARAM,
+                              "EC '%s': %d is not supported",
+                              OSSL_PKEY_PARAM_USE_COFACTOR_ECDH, value);
+                return 0;
+            }
+        }
+
+        /* OSSL_PKEY_PARAM_EC_INCLUDE_PUBLIC */
+        rc = ibmca_param_get_int(key->provctx, params,
+                                 OSSL_PKEY_PARAM_EC_INCLUDE_PUBLIC, &value);
+        if (rc == 0)
+            return 0;
+        if (rc > 0) {
+            key->ec.include_pub = (value != 0);
+            ibmca_debug_key(key, "include_pub: %d", key->ec.include_pub);
+        }
+    }
+
+    if ((selection & OSSL_KEYMGMT_SELECT_PUBLIC_KEY) != 0) {
+        /* Public key parts */
+
+        /* OSSL_PKEY_PARAM_PUB_KEY */
+        rc = ibmca_keymgmt_ec_pub_key_from_data(key, params, &x, &y, &format);
+        if (rc == 0)
+            goto out;
+        if (rc > 0 && x != NULL && y != NULL) {
+            rc = ibmca_keymgmt_ec_pub_key_from_bn(key, x, y);
+            if (rc == 0)
+                goto out;
+
+            key->ec.format = format;
+            ibmca_debug_key(key, "format: %d", key->ec.format);
+        }
+    }
+
+    if ((selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) != 0) {
+        /* OSSL_PKEY_PARAM_PRIV_KEY */
+        rc = ibmca_keymgmt_ec_priv_key_from_data(key, params, &d);
+        if (rc == 0)
+            goto out;
+        if (rc > 0 && d != NULL) {
+            rc = ibmca_keymgmt_ec_priv_key_from_bn(key, d);
+            if (rc == 0)
+                goto out;
+
+            key->ec.format = format;
+            ibmca_debug_key(key, "format: %d", key->ec.format);
+        }
+    }
+
+    rc = 1;
+
+out:
+    if (x != NULL)
+        BN_free(x);
+    if (y != NULL)
+        BN_free(y);
+    if (d != NULL)
+        BN_free(d);
+
+    return rc;
+}
+
+static const OSSL_DISPATCH ibmca_ec_keymgmt_functions[] = {
+    /* Constructor, destructor */
+    { OSSL_FUNC_KEYMGMT_NEW, (void (*)(void))ibmca_keymgmt_ec_new },
+    { OSSL_FUNC_KEYMGMT_FREE, (void (*)(void))ibmca_keymgmt_free },
+    { OSSL_FUNC_KEYMGMT_DUP, (void (*)(void))ibmca_keymgmt_dup },
+
+    /* Key generation and loading */
+    { OSSL_FUNC_KEYMGMT_GEN_INIT,
+            (void (*)(void))ibmca_keymgmt_ec_gen_init },
+    { OSSL_FUNC_KEYMGMT_GEN_SET_TEMPLATE,
+            (void (*)(void))ibmca_keymgmt_ec_gen_set_template },
+    { OSSL_FUNC_KEYMGMT_GEN_SET_PARAMS,
+            (void (*)(void))ibmca_keymgmt_ec_gen_set_params },
+    { OSSL_FUNC_KEYMGMT_GEN_SETTABLE_PARAMS,
+        (void (*)(void))ibmca_keymgmt_ec_gen_settable_params },
+    { OSSL_FUNC_KEYMGMT_GEN, (void (*)(void))ibmca_keymgmt_ec_gen },
+    { OSSL_FUNC_KEYMGMT_GEN_CLEANUP,
+            (void (*)(void))ibmca_keymgmt_gen_cleanup },
+    { OSSL_FUNC_KEYMGMT_LOAD, (void (*)(void))ibmca_keymgmt_load },
+
+    /* Key object checking */
+    { OSSL_FUNC_KEYMGMT_HAS, (void (*)(void))ibmca_keymgmt_ec_has },
+    { OSSL_FUNC_KEYMGMT_MATCH, (void (*)(void))ibmca_keymgmt_ec_match },
+    { OSSL_FUNC_KEYMGMT_VALIDATE,
+            (void (*)(void))ibmca_keymgmt_ec_validate },
+    { OSSL_FUNC_KEYMGMT_QUERY_OPERATION_NAME,
+        (void (*)(void))ibmca_keymgmt_ec_query_operation_name },
+
+    /* Key object information */
+    { OSSL_FUNC_KEYMGMT_GET_PARAMS,
+            (void (*) (void))ibmca_keymgmt_ec_get_params },
+    { OSSL_FUNC_KEYMGMT_GETTABLE_PARAMS,
+            (void (*) (void))ibmca_keymgmt_ec_gettable_params },
+    { OSSL_FUNC_KEYMGMT_SET_PARAMS,
+            (void (*) (void))ibmca_keymgmt_ec_set_params },
+    { OSSL_FUNC_KEYMGMT_SETTABLE_PARAMS,
+            (void (*) (void))ibmca_keymgmt_ec_settable_params },
+
+    /* Import and export routines */
+    { OSSL_FUNC_KEYMGMT_EXPORT, (void (*)(void))ibmca_keymgmt_ec_export },
+    { OSSL_FUNC_KEYMGMT_EXPORT_TYPES,
+            (void (*)(void))ibmca_keymgmt_ec_imexport_types },
+    { OSSL_FUNC_KEYMGMT_IMPORT, (void (*)(void))ibmca_keymgmt_ec_import },
+    { OSSL_FUNC_KEYMGMT_IMPORT_TYPES,
+            (void (*)(void))ibmca_keymgmt_ec_imexport_types },
+
+    { 0, NULL }
+};
+
+const OSSL_ALGORITHM ibmca_ec_keymgmt[] = {
+    { "EC:id-ecPublicKey:1.2.840.10045.2.1", NULL,
+      ibmca_ec_keymgmt_functions, "IBMCA EC implementation" },
+    { NULL, NULL, NULL, NULL }
+};
+
+struct ibmca_tls_group_constants {
+    unsigned int group_id;
+    unsigned int secbits;
+    int mintls;
+    int maxtls;
+    int mindtls;
+    int maxdtls;
+};
+
+#define IBMCA_TLS_GROUP_ID_secp192r1        19
+#define IBMCA_TLS_GROUP_ID_secp224r1        21
+#define IBMCA_TLS_GROUP_ID_secp256r1        23
+#define IBMCA_TLS_GROUP_ID_secp384r1        24
+#define IBMCA_TLS_GROUP_ID_secp521r1        25
+#define IBMCA_TLS_GROUP_ID_brainpoolP256r1  28
+#define IBMCA_TLS_GROUP_ID_brainpoolP384r1  27
+#define IBMCA_TLS_GROUP_ID_brainpoolP512r1  28
+
+static const struct ibmca_tls_group_constants ibmca_tls_group_consts[8] = {
+    { IBMCA_TLS_GROUP_ID_secp192r1, 80, TLS1_VERSION, TLS1_2_VERSION,
+      DTLS1_VERSION, DTLS1_2_VERSION },
+    { IBMCA_TLS_GROUP_ID_secp224r1, 112, TLS1_VERSION, TLS1_2_VERSION,
+      DTLS1_VERSION, DTLS1_2_VERSION },
+    { IBMCA_TLS_GROUP_ID_secp256r1, 128, TLS1_VERSION, 0, DTLS1_VERSION, 0 },
+    { IBMCA_TLS_GROUP_ID_secp384r1, 192, TLS1_VERSION, 0, DTLS1_VERSION, 0 },
+    { IBMCA_TLS_GROUP_ID_secp521r1, 256, TLS1_VERSION, 0, DTLS1_VERSION, 0 },
+    { IBMCA_TLS_GROUP_ID_brainpoolP256r1, 128, TLS1_VERSION, TLS1_2_VERSION,
+      DTLS1_VERSION, DTLS1_2_VERSION },
+    { IBMCA_TLS_GROUP_ID_brainpoolP384r1, 192, TLS1_VERSION, TLS1_2_VERSION,
+      DTLS1_VERSION, DTLS1_2_VERSION },
+    { IBMCA_TLS_GROUP_ID_brainpoolP512r1, 256, TLS1_VERSION, TLS1_2_VERSION,
+      DTLS1_VERSION, DTLS1_2_VERSION },
+};
+
+#define IBMCA_TLS_GROUP_ENTRY(tlsname, realname, algorithm, idx) \
+    { \
+        OSSL_PARAM_utf8_string(OSSL_CAPABILITY_TLS_GROUP_NAME, \
+                               tlsname, sizeof(tlsname)), \
+        OSSL_PARAM_utf8_string(OSSL_CAPABILITY_TLS_GROUP_NAME_INTERNAL, \
+                               realname, sizeof(realname)), \
+        OSSL_PARAM_utf8_string(OSSL_CAPABILITY_TLS_GROUP_ALG, \
+                               algorithm, sizeof(algorithm)), \
+        OSSL_PARAM_uint(OSSL_CAPABILITY_TLS_GROUP_ID, \
+                        (unsigned int *)&ibmca_tls_group_consts[idx].group_id), \
+        OSSL_PARAM_uint(OSSL_CAPABILITY_TLS_GROUP_SECURITY_BITS, \
+                        (unsigned int *)&ibmca_tls_group_consts[idx].secbits), \
+        OSSL_PARAM_int(OSSL_CAPABILITY_TLS_GROUP_MIN_TLS, \
+                       (unsigned int *)&ibmca_tls_group_consts[idx].mintls), \
+        OSSL_PARAM_int(OSSL_CAPABILITY_TLS_GROUP_MAX_TLS, \
+                       (unsigned int *)&ibmca_tls_group_consts[idx].maxtls), \
+        OSSL_PARAM_int(OSSL_CAPABILITY_TLS_GROUP_MIN_DTLS, \
+                       (unsigned int *)&ibmca_tls_group_consts[idx].mindtls), \
+        OSSL_PARAM_int(OSSL_CAPABILITY_TLS_GROUP_MAX_DTLS, \
+                       (unsigned int *)&ibmca_tls_group_consts[idx].maxdtls), \
+        OSSL_PARAM_END \
+    }
+
+static const OSSL_PARAM ibmca_ec_secp192r1[] =
+        IBMCA_TLS_GROUP_ENTRY("secp192r1", "prime192v1", "EC", 0);
+static const OSSL_PARAM ibmca_ec_p192[] =
+        IBMCA_TLS_GROUP_ENTRY("P-192", "prime192v1", "EC", 0);
+static const OSSL_PARAM ibmca_ec_secp224r1[] =
+        IBMCA_TLS_GROUP_ENTRY("secp224r1", "secp224r1", "EC", 1);
+static const OSSL_PARAM ibmca_ec_p224[] =
+        IBMCA_TLS_GROUP_ENTRY("P-224", "secp224r1", "EC", 1);
+static const OSSL_PARAM ibmca_ec_secp256r1[] =
+        IBMCA_TLS_GROUP_ENTRY("secp256r1", "prime256v1", "EC", 2);
+static const OSSL_PARAM ibmca_ec_p256[] =
+        IBMCA_TLS_GROUP_ENTRY("P-256", "prime256v1", "EC", 2);
+static const OSSL_PARAM ibmca_ec_secp384r1[] =
+        IBMCA_TLS_GROUP_ENTRY("secp384r1", "secp384r1", "EC", 3);
+static const OSSL_PARAM ibmca_ec_p384[] =
+        IBMCA_TLS_GROUP_ENTRY("P-384", "secp384r1", "EC", 3);
+static const OSSL_PARAM ibmca_ec_secp521r1[] =
+        IBMCA_TLS_GROUP_ENTRY("secp521r1", "secp521r1", "EC", 4);
+static const OSSL_PARAM ibmca_ec_p521[] =
+        IBMCA_TLS_GROUP_ENTRY("P-521", "secp521r1", "EC", 4);
+static const OSSL_PARAM ibmca_ec_brainpoolP256r1[] =
+        IBMCA_TLS_GROUP_ENTRY("brainpoolP256r1", "brainpoolP256r1", "EC", 5);
+static const OSSL_PARAM ibmca_ec_brainpoolP384r1[] =
+        IBMCA_TLS_GROUP_ENTRY("brainpoolP384r1", "brainpoolP384r1", "EC", 6);
+static const OSSL_PARAM ibmca_ec_brainpoolP512r1[] =
+        IBMCA_TLS_GROUP_ENTRY("brainpoolP512r1", "brainpoolP512r1", "EC", 7);
+
+static const OSSL_PARAM *ibmca_ec_tls_group[] = {
+    ibmca_ec_secp192r1,
+    ibmca_ec_p192,
+    ibmca_ec_secp224r1,
+    ibmca_ec_p224,
+    ibmca_ec_secp256r1,
+    ibmca_ec_p256,
+    ibmca_ec_secp384r1,
+    ibmca_ec_p384,
+    ibmca_ec_secp521r1,
+    ibmca_ec_p521,
+    ibmca_ec_brainpoolP256r1,
+    ibmca_ec_brainpoolP384r1,
+    ibmca_ec_brainpoolP512r1,
+    NULL
+};
+
+const struct ibmca_mech_capability ibmca_ec_capabilities[] = {
+    { "TLS-GROUP", ibmca_ec_tls_group },
+    { NULL, NULL }
+};
diff -pruN 1.4.0-1/src/provider/ec_signature.c 2.5.0-0ubuntu1/src/provider/ec_signature.c
--- 1.4.0-1/src/provider/ec_signature.c	1970-01-01 00:00:00.000000000 +0000
+++ 2.5.0-0ubuntu1/src/provider/ec_signature.c	2025-04-16 08:09:24.000000000 +0000
@@ -0,0 +1,1332 @@
+/*
+ * Copyright [2021-2022] International Business Machines Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <stddef.h>
+#include <err.h>
+#include <strings.h>
+#include <string.h>
+#include <errno.h>
+
+#include <openssl/evp.h>
+#include <openssl/bn.h>
+#include <openssl/ec.h>
+#include <openssl/x509.h>
+#include <openssl/core.h>
+#include <openssl/core_dispatch.h>
+#include <openssl/core_names.h>
+#include <openssl/params.h>
+#include <openssl/prov_ssl.h>
+
+#include "p_ibmca.h"
+
+static OSSL_FUNC_signature_newctx_fn ibmca_signature_ec_newctx;
+static OSSL_FUNC_signature_sign_init_fn ibmca_signature_ec_sign_init;
+static OSSL_FUNC_signature_sign_fn ibmca_signature_ec_sign;
+static OSSL_FUNC_signature_verify_init_fn ibmca_signature_ec_verify_init;
+static OSSL_FUNC_signature_verify_fn ibmca_signature_ec_verify;
+static OSSL_FUNC_signature_digest_sign_init_fn
+                                ibmca_signature_ec_digest_sign_init;
+static OSSL_FUNC_signature_digest_sign_update_fn
+                                ibmca_signature_ec_digest_signverify_update;
+static OSSL_FUNC_signature_digest_sign_final_fn
+                                ibmca_signature_ec_digest_sign_final;
+static OSSL_FUNC_signature_digest_verify_init_fn
+                                ibmca_signature_ec_digest_verify_init;
+static OSSL_FUNC_signature_digest_verify_final_fn
+                                ibmca_signature_ec_digest_verify_final;
+static OSSL_FUNC_signature_get_ctx_params_fn ibmca_signature_ec_get_ctx_params;
+static OSSL_FUNC_signature_gettable_ctx_params_fn
+                                ibmca_signature_ec_gettable_ctx_params;
+static OSSL_FUNC_signature_set_ctx_params_fn ibmca_signature_ec_set_ctx_params;
+static OSSL_FUNC_signature_settable_ctx_params_fn
+                                ibmca_signature_ec_settable_ctx_params;
+static OSSL_FUNC_signature_get_ctx_md_params_fn
+                                ibmca_signature_ec_get_ctx_md_params;
+static OSSL_FUNC_signature_gettable_ctx_md_params_fn
+                                ibmca_signature_ec_gettable_ctx_md_params;
+static OSSL_FUNC_signature_set_ctx_md_params_fn
+                               ibmca_signature_ec_set_ctx_md_params;
+static OSSL_FUNC_signature_settable_ctx_md_params_fn
+                                ibmca_signature_ec_settable_ctx_md_params;
+#ifdef EVP_PKEY_OP_SIGNMSG
+static OSSL_FUNC_signature_sign_message_update_fn
+                                ibmca_signature_ec_signverify_message_update;
+static OSSL_FUNC_signature_sign_message_final_fn
+                                ibmca_signature_ec_sign_message_final;
+static OSSL_FUNC_signature_verify_message_final_fn
+                                ibmca_signature_ec_verify_message_final;
+static OSSL_FUNC_signature_query_key_types_fn
+                                ibmca_signature_ec_query_key_types;
+#endif
+
+static void ibmca_signature_ec_free_cb(struct ibmca_op_ctx *ctx);
+static int ibmca_signature_ec_dup_cb(const struct ibmca_op_ctx *ctx,
+                                     struct ibmca_op_ctx *new_ctx);
+
+#ifdef EVP_PKEY_OP_SIGNMSG
+static const char *ibmca_signature_ec_keytypes[] = { "EC", NULL };
+
+static const char **ibmca_signature_ec_query_key_types(void)
+{
+    return ibmca_signature_ec_keytypes;
+}
+#endif
+
+static void*ibmca_signature_ec_newctx(void *vprovctx, const char *propq)
+{
+    const struct ibmca_prov_ctx *provctx = vprovctx;
+    struct ibmca_op_ctx *opctx;
+
+    if (provctx == NULL)
+        return NULL;
+
+    ibmca_debug_ctx(provctx, "provctx: %p", provctx);
+
+    opctx = ibmca_op_newctx(provctx, propq, EVP_PKEY_EC,
+                            ibmca_signature_ec_free_cb,
+                            ibmca_signature_ec_dup_cb);
+    if (opctx == NULL) {
+        ibmca_debug_ctx(provctx, "ERROR: ibmca_op_newctx failed");
+        return NULL;
+    }
+
+    ibmca_debug_ctx(provctx, "opctx: %p", opctx);
+
+    return opctx;
+}
+
+static void ibmca_signature_ec_free_cb(struct ibmca_op_ctx *ctx)
+{
+    if (ctx == NULL)
+        return;
+
+    ibmca_debug_op_ctx(ctx, "ctx: %p", ctx);
+
+    if (ctx->ec.signature.md != NULL)
+        EVP_MD_free(ctx->ec.signature.md);
+    ctx->ec.signature.md = NULL;
+    ctx->ec.signature.set_md_allowed = true;
+
+    ctx->ec.signature.md_size = 0;
+
+    if (ctx->ec.signature.md_ctx != NULL)
+        EVP_MD_CTX_free(ctx->ec.signature.md_ctx);
+    ctx->ec.signature.md_ctx = NULL;
+
+    ctx->ec.signature.nonce_type = 0;
+
+    if (ctx->ec.signature.signature != NULL)
+        P_FREE(ctx->provctx, ctx->ec.signature.signature);
+    ctx->ec.signature.signature = NULL;
+    ctx->ec.signature.signature_len = 0;
+}
+
+static int ibmca_signature_ec_dup_cb(const struct ibmca_op_ctx *ctx,
+                                     struct ibmca_op_ctx *new_ctx)
+{
+    if (ctx == NULL)
+        return 0;
+
+    ibmca_debug_op_ctx(ctx, "ctx: %p new_ctx: %p", ctx, new_ctx);
+
+    new_ctx->ec.signature.md = ctx->ec.signature.md;
+    if (new_ctx->ec.signature.md != NULL) {
+        if (EVP_MD_up_ref(new_ctx->ec.signature.md) == 0) {
+            put_error_op_ctx(ctx, IBMCA_ERR_INTERNAL_ERROR,
+                             "EVP_MD_up_ref failed");
+            return 0;
+        }
+    }
+
+    new_ctx->ec.signature.set_md_allowed = ctx->ec.signature.set_md_allowed;
+    new_ctx->ec.signature.md_size = ctx->ec.signature.md_size;
+
+    if (ctx->ec.signature.md_ctx != NULL) {
+        new_ctx->ec.signature.md_ctx = EVP_MD_CTX_new();
+        if (new_ctx->ec.signature.md_ctx == NULL ||
+            EVP_MD_CTX_copy(new_ctx->ec.signature.md_ctx,
+                            ctx->ec.signature.md_ctx) == 0) {
+            put_error_op_ctx(ctx, IBMCA_ERR_INTERNAL_ERROR,
+                             "EVP_MD_CTX_copy failed");
+            return 0;
+        }
+    }
+
+    new_ctx->ec.signature.nonce_type = ctx->ec.signature.nonce_type;
+
+    new_ctx->ec.signature.signature_len = 0;
+    new_ctx->ec.signature.signature = NULL;
+    if (ctx->ec.signature.signature != NULL) {
+        new_ctx->ec.signature.signature = P_MEMDUP(ctx->provctx,
+                                               ctx->ec.signature.signature,
+                                               ctx->ec.signature.signature_len);
+        if (new_ctx->ec.signature.signature == NULL) {
+            put_error_op_ctx(ctx, IBMCA_ERR_MALLOC_FAILED,
+                             "P_MEMDUP failed");
+            return 0;
+        }
+        new_ctx->ec.signature.signature_len = ctx->ec.signature.signature_len;
+    }
+
+    return 1;
+}
+
+static int ibmca_signature_ec_set_md(struct ibmca_op_ctx *ctx,
+                                     const char *mdname, int md_nid,
+                                     const char *props)
+{
+    EVP_MD *md;
+
+    if (mdname == NULL)
+        mdname = OBJ_nid2sn(md_nid);
+
+    ibmca_debug_op_ctx(ctx, "ctx: %p mdname: '%s'", ctx, mdname);
+
+    if (!ctx->ec.signature.set_md_allowed) {
+        put_error_op_ctx(ctx, IBMCA_ERR_INVALID_PARAM,
+                         "Digest not allowed to be set in the current state");
+        return 0;
+    }
+
+    md = EVP_MD_fetch(ctx->provctx->libctx, mdname,
+                      props != NULL ? props : ctx->propq);
+    if (md == NULL) {
+        put_error_op_ctx(ctx, IBMCA_ERR_INVALID_PARAM,
+                         "Digest '%s' could not be fetched", mdname);
+        return 0;
+    }
+
+    if ((EVP_MD_get_flags(md) & EVP_MD_FLAG_XOF) != 0) {
+        put_error_op_ctx(ctx, IBMCA_ERR_INVALID_PARAM,
+                         "XOF Digest '%s' is not allowed", mdname);
+        EVP_MD_free(md);
+        return 0;
+    }
+
+    if (ctx->ec.signature.md != NULL)
+        EVP_MD_free(ctx->ec.signature.md);
+
+    ctx->ec.signature.md = md;
+    ctx->ec.signature.md_size = EVP_MD_get_size(md);
+
+    return 1;
+}
+
+static int ibmca_signature_ec_op_init(struct ibmca_op_ctx *ctx,
+                                      struct ibmca_key *key,
+                                      const OSSL_PARAM params[],
+                                      int operation, const char *mdname)
+{
+    const OSSL_PARAM *p;
+
+    if (ctx == NULL || key == NULL)
+        return 0;
+
+    ibmca_debug_op_ctx(ctx, "ctx: %p key: %p operation: %d mdname: %s", ctx,
+                       key, operation, mdname != NULL ? mdname : "(null)");
+    for (p = params; p != NULL && p->key != NULL; p++)
+        ibmca_debug_op_ctx(ctx, "param: %s", p->key);
+
+    if (ibmca_op_init(ctx, key, operation) == 0) {
+        ibmca_debug_op_ctx(ctx, "ERROR: ibmca_op_init failed");
+        return 0;
+    }
+
+    /* Setup defaults for this context */
+    ibmca_signature_ec_free_cb(ctx);
+
+    ctx->ec.signature.set_md_allowed = true;
+
+    if (mdname != NULL) {
+        if (ibmca_signature_ec_set_md(ctx, mdname, 0, NULL) == 0)
+            return 0;
+    }
+
+    if (params != NULL) {
+        if (ibmca_signature_ec_set_ctx_params(ctx, params) == 0) {
+            ibmca_debug_op_ctx(ctx,
+                    "ERROR: ibmca_signature_ec_set_ctx_params failed");
+            return 0;
+        }
+    }
+
+    switch (operation) {
+    case EVP_PKEY_OP_SIGNCTX:
+    case EVP_PKEY_OP_VERIFYCTX:
+#ifdef EVP_PKEY_OP_SIGNMSG
+    case EVP_PKEY_OP_SIGNMSG:
+    case EVP_PKEY_OP_VERIFYMSG:
+#endif
+        ctx->ec.signature.md_ctx = EVP_MD_CTX_new();
+        if (ctx->ec.signature.md_ctx == NULL) {
+            put_error_op_ctx(ctx, IBMCA_ERR_INTERNAL_ERROR,
+                             "EVP_MD_CTX_new failed");
+            return 0;
+        }
+
+        if (EVP_DigestInit_ex2(ctx->ec.signature.md_ctx,
+                               ctx->ec.signature.md, params) == 0) {
+            put_error_op_ctx(ctx, IBMCA_ERR_INTERNAL_ERROR,
+                             "EVP_DigestInit_ex2 failed");
+            return 0;
+        }
+
+        ctx->ec.signature.set_md_allowed = false;
+        break;
+    }
+
+    return 1;
+}
+
+static int ibmca_signature_ec_sign_init(void *vctx, void *vkey,
+                                        const OSSL_PARAM params[])
+{
+    struct ibmca_op_ctx *ctx = vctx;
+    struct ibmca_key *key = vkey;
+
+    return ibmca_signature_ec_op_init(ctx, key, params,
+                                      EVP_PKEY_OP_SIGN, NULL);
+}
+
+static int ibmca_signature_ec_verify_init(void *vctx, void *vkey,
+                                          const OSSL_PARAM params[])
+{
+    struct ibmca_op_ctx *ctx = vctx;
+    struct ibmca_key *key = vkey;
+
+    return ibmca_signature_ec_op_init(ctx, key, params,
+                                      EVP_PKEY_OP_VERIFY, NULL);
+}
+
+static int ibmca_signature_ec_sign_fallback(struct ibmca_op_ctx *ctx,
+                                            unsigned char *sig, size_t *siglen,
+                                            const unsigned char *tbs,
+                                            size_t tbslen)
+{
+    EVP_PKEY *pkey = NULL;
+    EVP_PKEY_CTX *pctx = NULL;
+#ifdef OSSL_SIGNATURE_PARAM_NONCE_TYPE
+    OSSL_PARAM params[3];
+    const char *md_name;
+#endif
+    int rc = 0;
+
+    ibmca_debug_op_ctx(ctx, "ctx: %p key: %p tbslen: %lu sig: %p siglen: %lu",
+                       ctx, ctx->key, tbslen, sig, *siglen);
+
+    pkey = ibmca_new_fallback_pkey(ctx->key);
+    if (pkey == NULL) {
+        ibmca_debug_op_ctx(ctx,"ERROR: ibmca_new_fallback_pkey failed");
+        goto out;
+    }
+
+    pctx = ibmca_new_fallback_pkey_ctx(ctx->provctx, pkey, NULL);
+    if (pctx == NULL) {
+        ibmca_debug_op_ctx(ctx,"ERROR: ibmca_new_fallback_pkey_ctx failed");
+        goto out;
+    }
+
+    if (EVP_PKEY_sign_init(pctx) != 1) {
+        put_error_op_ctx(ctx, IBMCA_ERR_INTERNAL_ERROR,
+                         "EVP_PKEY_sign_init failed");
+        goto out;
+    }
+
+    if (ibmca_check_fallback_provider(ctx->provctx, pctx) != 1) {
+        ibmca_debug_op_ctx(ctx, "ERROR: ibmca_check_fallback_provider failed");
+        goto out;
+    }
+
+#ifdef OSSL_SIGNATURE_PARAM_NONCE_TYPE
+    ibmca_debug_op_ctx(ctx, "nonce_type: %u", ctx->ec.signature.nonce_type);
+
+    if (ctx->ec.signature.nonce_type != 0) {
+        md_name = EVP_MD_get0_name(ctx->ec.signature.md);
+        if (md_name == NULL) {
+            put_error_op_ctx(ctx, IBMCA_ERR_INVALID_PARAM,
+                             "Digest must be set when using deterministic "
+                             "signatures");
+            goto out;
+        }
+
+        ibmca_debug_op_ctx(ctx, "md_name: %s", md_name);
+
+        params[0] = OSSL_PARAM_construct_utf8_string(
+                                              OSSL_SIGNATURE_PARAM_DIGEST,
+                                              (char *)md_name, strlen(md_name));
+        params[1] = OSSL_PARAM_construct_uint(OSSL_SIGNATURE_PARAM_NONCE_TYPE,
+                                              &ctx->ec.signature.nonce_type);
+        params[2] = OSSL_PARAM_construct_end();
+
+        if (EVP_PKEY_CTX_set_params(pctx, params) != 1) {
+            put_error_op_ctx(ctx, IBMCA_ERR_INTERNAL_ERROR,
+                             "EVP_PKEY_CTX_set_params failed");
+            goto out;
+        }
+    }
+#endif
+
+    if (EVP_PKEY_sign(pctx, sig, siglen, tbs, tbslen) != 1) {
+        put_error_op_ctx(ctx, IBMCA_ERR_INTERNAL_ERROR,
+                         "EVP_PKEY_sign failed");
+        goto out;
+    }
+
+    rc = 1;
+
+out:
+    if (pkey != NULL)
+        EVP_PKEY_free(pkey);
+    if (pctx != NULL)
+        EVP_PKEY_CTX_free(pctx);
+
+    return rc;
+}
+
+static int ibmca_signature_ec_sign(void *vctx,
+                                   unsigned char *sig, size_t *siglen,
+                                   size_t sigsize, const unsigned char *tbs,
+                                   size_t tbslen)
+{
+    struct ibmca_op_ctx *ctx = vctx;
+    ECDSA_SIG *ecdsa_sig = NULL;
+    BIGNUM *r = NULL, *s = NULL;
+    unsigned char *p;
+    int rc = 0;
+
+    if (ctx == NULL || siglen == NULL || tbs == NULL)
+        return 0;
+
+    ibmca_debug_op_ctx(ctx, "ctx: %p key: %p tbslen: %lu sigsize: %lu",
+                       ctx, ctx->key, tbslen, sigsize);
+
+    if (ctx->key == NULL ||
+        (ctx->operation != EVP_PKEY_OP_SIGN &&
+#ifdef EVP_PKEY_OP_SIGNMSG
+         ctx->operation != EVP_PKEY_OP_SIGNCTX &&
+         ctx->operation != EVP_PKEY_OP_SIGNMSG)) {
+#else
+         ctx->operation != EVP_PKEY_OP_SIGNCTX)) {
+#endif
+        put_error_op_ctx(ctx, IBMCA_ERR_INTERNAL_ERROR,
+                         "sign operation not initialized");
+        return 0;
+    }
+
+#ifdef EVP_PKEY_OP_SIGNMSG
+     if (ctx->operation == EVP_PKEY_OP_SIGNMSG) {
+         rc = ibmca_signature_ec_signverify_message_update(ctx, tbs, tbslen);
+         if (rc != 1)
+             goto out;
+
+         rc = ibmca_signature_ec_sign_message_final(ctx, sig, siglen, sigsize);
+         goto out;
+     }
+#endif
+
+    *siglen = ctx->key->get_max_param_size(ctx->key);
+
+    if (sig == NULL) { /* size query */
+        rc = 1;
+        goto out;
+    }
+
+    if (sigsize < *siglen) {
+        put_error_op_ctx(ctx, IBMCA_ERR_INVALID_PARAM,
+                         "Output buffer too small");
+        goto out;
+    }
+
+    if (ctx->ec.signature.md_size != 0) {
+        if (tbslen != ctx->ec.signature.md_size) {
+            put_error_op_ctx(ctx, IBMCA_ERR_INVALID_PARAM,
+                             "Invalid input data size: %lu expected: %d",
+                             tbslen, ctx->ec.signature.md_size);
+            goto out;
+        }
+    }
+
+    if (ibmca_op_alloc_tbuf(ctx, ctx->key->ec.prime_size * 2) == 0) {
+        ibmca_debug_op_ctx(ctx, "ERROR: ibmca_op_alloc_tbuf failed");
+        goto out;
+    }
+
+    if (ctx->key->ec.fallback.d != NULL || ctx->ec.signature.nonce_type != 0) {
+        rc = ibmca_signature_ec_sign_fallback(ctx, sig, siglen, tbs, tbslen);
+        if (rc != 1) {
+            ibmca_debug_op_ctx(ctx,
+                               "ERROR: ibmca_signature_ec_sign_fallback failed");
+            rc = 0;
+        }
+        goto out;
+    }
+
+    rc = ica_ecdsa_sign(ctx->provctx->ica_adapter, ctx->key->ec.key,
+                        tbs, tbslen, ctx->tbuf, ctx->tbuf_len);
+    if (rc != 0) {
+        ibmca_debug_op_ctx(ctx, "ica_ecdsa_sign failed with: %s", strerror(rc));
+
+        rc = ibmca_signature_ec_sign_fallback(ctx, sig, siglen, tbs, tbslen);
+        if (rc != 1) {
+            ibmca_debug_op_ctx(ctx,
+                               "ERROR: ibmca_signature_ec_sign_fallback failed");
+            rc = 0;
+        }
+        goto out;
+    }
+
+    r = BN_bin2bn(ctx->tbuf, ctx->key->ec.prime_size, NULL);
+    s = BN_bin2bn(ctx->tbuf + ctx->key->ec.prime_size,
+                  ctx->key->ec.prime_size, NULL);
+    if (r == NULL || s == NULL) {
+        put_error_op_ctx(ctx, IBMCA_ERR_INTERNAL_ERROR, "BN_bin2bn failed");
+        goto out;
+    }
+
+    ecdsa_sig = ECDSA_SIG_new();
+    if (ecdsa_sig == NULL ||
+        ECDSA_SIG_set0(ecdsa_sig, r, s) == 0) {
+        put_error_op_ctx(ctx, IBMCA_ERR_INTERNAL_ERROR,
+                         "ECDSA_SIG_new/ECDSA_SIG_set0 failed");
+        goto out;
+    }
+    r = NULL;
+    s = NULL;
+
+    p = sig;
+    *siglen = i2d_ECDSA_SIG(ecdsa_sig, &p);
+    if (*siglen <= 0) {
+        put_error_op_ctx(ctx, IBMCA_ERR_INTERNAL_ERROR, "i2d_ECDSA_SIG failed");
+        goto out;
+    }
+
+    rc = 1;
+
+ out:
+     if (ecdsa_sig != NULL)
+         ECDSA_SIG_free(ecdsa_sig);
+     if (r != NULL)
+         BN_free(r);
+     if (s != NULL)
+         BN_free(s);
+
+    ibmca_debug_op_ctx(ctx, "siglen: %lu rc: %d", *siglen, rc);
+
+    return rc;
+}
+
+static int ibmca_signature_ec_verify_fallback(struct ibmca_op_ctx *ctx,
+                                              const unsigned char *sig,
+                                              size_t siglen,
+                                              const unsigned char *tbs,
+                                              size_t tbslen)
+{
+    EVP_PKEY *pkey = NULL;
+    EVP_PKEY_CTX *pctx = NULL;
+    int rc = 0;
+
+    ibmca_debug_op_ctx(ctx, "ctx: %p key: %p tbslen: %lu sig: %p siglen: %lu",
+                       ctx, ctx->key, tbslen, sig, siglen);
+
+    pkey = ibmca_new_fallback_pkey(ctx->key);
+    if (pkey == NULL) {
+        ibmca_debug_op_ctx(ctx,"ERROR: ibmca_new_fallback_pkey failed");
+        goto out;
+    }
+
+    pctx = ibmca_new_fallback_pkey_ctx(ctx->provctx, pkey, NULL);
+    if (pctx == NULL) {
+        ibmca_debug_op_ctx(ctx,"ERROR: ibmca_new_fallback_pkey_ctx failed");
+        goto out;
+    }
+
+    if (EVP_PKEY_verify_init(pctx) != 1) {
+        put_error_op_ctx(ctx, IBMCA_ERR_INTERNAL_ERROR,
+                         "EVP_PKEY_verify_init failed");
+        goto out;
+    }
+
+    if (ibmca_check_fallback_provider(ctx->provctx, pctx) != 1) {
+        ibmca_debug_op_ctx(ctx, "ERROR: ibmca_check_fallback_provider failed");
+        goto out;
+    }
+
+    rc = EVP_PKEY_verify(pctx, sig, siglen, tbs, tbslen);
+    if (rc < 0) {
+        put_error_op_ctx(ctx, IBMCA_ERR_INTERNAL_ERROR,
+                         "EVP_PKEY_verify failed with %d", rc);
+        goto out;
+    }
+    if (rc == 0) {
+        put_error_op_ctx(ctx, IBMCA_ERR_SIGNATURE_BAD, "Signature bad");
+        goto out;
+    }
+
+    rc = 1;
+
+out:
+    if (pkey != NULL)
+        EVP_PKEY_free(pkey);
+    if (pctx != NULL)
+        EVP_PKEY_CTX_free(pctx);
+
+    return rc;
+}
+
+static int ibmca_signature_ec_verify(void *vctx,
+                                     const unsigned char *sig, size_t siglen,
+                                     const unsigned char *tbs, size_t tbslen)
+{
+    struct ibmca_op_ctx *ctx = vctx;
+    ECDSA_SIG *ecdsa_sig = NULL;
+    const BIGNUM *r, *s;
+    const unsigned char *p;
+    unsigned char *der = NULL;
+    int derlen = -1;
+#ifdef EVP_PKEY_OP_SIGNMSG
+    OSSL_PARAM params[2];
+#endif
+    int rc = -1;
+
+    if (ctx == NULL || sig == NULL || tbs == NULL)
+        return -1;
+
+    ibmca_debug_op_ctx(ctx, "ctx: %p key: %p tbslen: %lu siglen: %lu",
+                       ctx, ctx->key, tbslen, siglen);
+
+    if (ctx->key == NULL ||
+        (ctx->operation != EVP_PKEY_OP_VERIFY &&
+#ifdef EVP_PKEY_OP_SIGNMSG
+         ctx->operation != EVP_PKEY_OP_VERIFYCTX &&
+         ctx->operation != EVP_PKEY_OP_VERIFYMSG)) {
+#else
+         ctx->operation != EVP_PKEY_OP_VERIFYCTX)) {
+#endif
+        put_error_op_ctx(ctx, IBMCA_ERR_INTERNAL_ERROR,
+                         "verify operation not initialized");
+        return -1;
+    }
+
+#ifdef EVP_PKEY_OP_SIGNMSG
+    if (ctx->operation == EVP_PKEY_OP_VERIFYMSG) {
+        params[0] = OSSL_PARAM_construct_octet_string(
+                                          OSSL_SIGNATURE_PARAM_SIGNATURE,
+                                          (unsigned char *)sig, siglen);
+        params[1] = OSSL_PARAM_construct_end();
+
+        rc = ibmca_signature_ec_set_ctx_params(ctx, params);
+        if (rc != 1)
+            goto out;
+
+        rc = ibmca_signature_ec_signverify_message_update(ctx, tbs, tbslen);
+        if (rc != 1)
+            goto out;
+
+        rc = ibmca_signature_ec_verify_message_final(ctx);
+        goto out;
+    }
+#endif
+
+    if (ctx->ec.signature.md_size != 0) {
+        if (tbslen != ctx->ec.signature.md_size) {
+            put_error_op_ctx(ctx, IBMCA_ERR_INVALID_PARAM,
+                             "Invalid input data size: %lu expected: %d",
+                             tbslen, ctx->ec.signature.md_size);
+            goto out;
+        }
+    }
+
+    if (ibmca_op_alloc_tbuf(ctx, ctx->key->ec.prime_size * 2) == 0) {
+        ibmca_debug_op_ctx(ctx, "ERROR: ibmca_op_alloc_tbuf failed");
+        goto out;
+    }
+
+    if (ctx->key->ec.fallback.x != NULL && ctx->key->ec.fallback.y) {
+        rc = ibmca_signature_ec_verify_fallback(ctx, sig, siglen, tbs, tbslen);
+        goto out;
+    }
+
+    p = sig;
+    if (d2i_ECDSA_SIG(&ecdsa_sig, &p, siglen) == NULL ||
+        ecdsa_sig == NULL) {
+        put_error_op_ctx(ctx, IBMCA_ERR_SIGNATURE_BAD, "d2i_ECDSA_SIG failed");
+        goto out;
+    }
+
+    /* Ensure signature uses DER and doesn't have trailing garbage */
+    derlen = i2d_ECDSA_SIG(ecdsa_sig, &der);
+    if ((size_t)derlen != siglen || memcmp(sig, der, derlen) != 0) {
+        put_error_op_ctx(ctx, IBMCA_ERR_SIGNATURE_BAD,
+                         "Signature encoding wrong");
+        goto out;
+    }
+
+    r = ECDSA_SIG_get0_r(ecdsa_sig);
+    s = ECDSA_SIG_get0_s(ecdsa_sig);
+    if (r == NULL || s == NULL ||
+        BN_bn2binpad(r, ctx->tbuf, ctx->key->ec.prime_size) <= 0 ||
+        BN_bn2binpad(s, ctx->tbuf + ctx->key->ec.prime_size,
+                     ctx->key->ec.prime_size) <= 0) {
+        put_error_op_ctx(ctx, IBMCA_ERR_INTERNAL_ERROR,
+                         "BN_bn2binpad failed");
+        goto out;
+    }
+
+    rc = ica_ecdsa_verify(ctx->provctx->ica_adapter, ctx->key->ec.key,
+                          tbs, tbslen, ctx->tbuf, ctx->tbuf_len);
+    if (rc == EFAULT) {
+        put_error_op_ctx(ctx, IBMCA_ERR_SIGNATURE_BAD, "Bad signature");
+        rc = 0;
+        goto out;
+    }
+    if (rc != 0) {
+        ibmca_debug_op_ctx(ctx, "ica_ecdsa_verify failed with: %s",
+                           strerror(rc));
+
+        rc = ibmca_signature_ec_verify_fallback(ctx, sig, siglen, tbs, tbslen);
+        goto out;
+    }
+
+    rc = 1;
+
+out:
+    if (ecdsa_sig != NULL)
+        ECDSA_SIG_free(ecdsa_sig);
+    if (der != NULL)
+        P_FREE(ctx->provctx, der);
+
+    ibmca_debug_op_ctx(ctx, "rc: %d", rc);
+
+    return rc;
+}
+
+static int ibmca_signature_ec_get_algid(struct ibmca_op_ctx *ctx,
+                                        OSSL_PARAM *p)
+{
+    ASN1_OBJECT *oid = NULL;
+    X509_ALGOR * algid = NULL;
+    unsigned char *aid_buf = NULL;
+    size_t aid_len;
+
+    if (ctx->ec.signature.md == NULL) {
+        put_error_op_ctx(ctx, IBMCA_ERR_INVALID_PARAM, "No digest is set");
+        return 0;
+    }
+
+    switch (EVP_MD_get_type(ctx->ec.signature.md)) {
+    case NID_sha1:
+        oid = OBJ_nid2obj(NID_ecdsa_with_SHA1);
+        break;
+    case NID_sha224:
+         oid = OBJ_nid2obj(NID_ecdsa_with_SHA224);
+         break;
+    case NID_sha256:
+         oid = OBJ_nid2obj(NID_ecdsa_with_SHA256);
+         break;
+    case NID_sha384:
+         oid = OBJ_nid2obj(NID_ecdsa_with_SHA384);
+         break;
+    case NID_sha512:
+         oid = OBJ_nid2obj(NID_ecdsa_with_SHA512);
+         break;
+    case NID_sha3_224:
+         oid = OBJ_nid2obj(NID_ecdsa_with_SHA3_224);
+         break;
+    case NID_sha3_256:
+         oid = OBJ_nid2obj(NID_ecdsa_with_SHA3_256);
+         break;
+    case NID_sha3_384:
+         oid = OBJ_nid2obj(NID_ecdsa_with_SHA3_384);
+         break;
+    case NID_sha3_512:
+         oid = OBJ_nid2obj(NID_ecdsa_with_SHA3_512);
+         break;
+    default:
+        put_error_op_ctx(ctx, IBMCA_ERR_INVALID_PARAM,
+                         "AlgorithmID not supported for digest '%s'",
+                         EVP_MD_get0_name(ctx->ec.signature.md));
+        return 0;
+    }
+
+    algid = X509_ALGOR_new();
+    if (algid == NULL ||
+        X509_ALGOR_set0(algid, oid, V_ASN1_UNDEF, NULL) == 0 ||
+        (aid_len = i2d_X509_ALGOR(algid, &aid_buf)) <= 0) {
+        put_error_op_ctx(ctx, IBMCA_ERR_INTERNAL_ERROR,
+                         "X509_ALGOR_new/X509_ALGOR_set0/i2d_X509_ALGOR failed");
+        X509_ALGOR_free(algid);
+        return 0;
+    }
+
+    if (OSSL_PARAM_set_octet_string(p, aid_buf, aid_len) == 0) {
+        put_error_op_ctx(ctx, IBMCA_ERR_INTERNAL_ERROR,
+                         "Failed to return param '%s'",
+                         OSSL_SIGNATURE_PARAM_ALGORITHM_ID);
+        P_FREE(ctx->provctx, aid_buf);
+        X509_ALGOR_free(algid);
+        return 0;
+    }
+    P_FREE(ctx->provctx, aid_buf);
+    X509_ALGOR_free(algid);
+
+    ibmca_debug_op_ctx(ctx, "param '%s': [octet string] (%lu bytes)",
+                       OSSL_SIGNATURE_PARAM_ALGORITHM_ID, aid_len);
+
+    return 1;
+}
+
+static int ibmca_signature_ec_get_ctx_params(void *vctx,
+                                             OSSL_PARAM params[])
+{
+    struct ibmca_op_ctx *ctx = vctx;
+    OSSL_PARAM *p;
+    const char *name = NULL;
+    int rc;
+
+    if (ctx == NULL)
+        return 0;
+
+    ibmca_debug_op_ctx(ctx, "ctx: %p", ctx);
+    for (p = params; p != NULL && p->key != NULL; p++)
+        ibmca_debug_op_ctx(ctx, "param: %s", p->key);
+
+    /* OSSL_SIGNATURE_PARAM_ALGORITHM_ID */
+    p = OSSL_PARAM_locate(params, OSSL_SIGNATURE_PARAM_ALGORITHM_ID);
+    if (p != NULL && ibmca_signature_ec_get_algid(ctx, p) == 0)
+        return 0;
+
+    /* OSSL_SIGNATURE_PARAM_DIGEST */
+    if (ctx->ec.signature.md != NULL)
+        name = EVP_MD_get0_name(ctx->ec.signature.md);
+    else
+        name = "";
+    rc = ibmca_param_build_set_utf8(ctx->provctx, NULL, params,
+                                    OSSL_SIGNATURE_PARAM_DIGEST, name);
+    if (rc == 0)
+       return 0;
+
+    /* OSSL_SIGNATURE_PARAM_DIGEST_SIZE */
+    rc = ibmca_param_build_set_size_t(ctx->provctx, NULL, params,
+                                      OSSL_SIGNATURE_PARAM_DIGEST,
+                                      ctx->ec.signature.md_size);
+    if (rc == 0)
+       return 0;
+
+#ifdef OSSL_SIGNATURE_PARAM_NONCE_TYPE
+    /* OSSL_SIGNATURE_PARAM_NONCE_TYPE */
+    rc = ibmca_param_build_set_uint(ctx->provctx, NULL, params,
+                                    OSSL_SIGNATURE_PARAM_NONCE_TYPE,
+                                    ctx->ec.signature.nonce_type);
+    if (rc == 0)
+       return 0;
+#endif
+
+    return 1;
+}
+
+static int ibmca_signature_ec_set_ctx_params(void *vctx,
+                                             const OSSL_PARAM params[])
+{
+    struct ibmca_op_ctx *ctx = vctx;
+    const OSSL_PARAM *p;
+    const char *name, *props = NULL;
+    size_t md_size;
+#ifdef EVP_PKEY_OP_SIGNMSG
+    size_t len;
+    unsigned char *ptr = NULL;
+#endif
+    int rc;
+
+    if (ctx == NULL)
+        return 0;
+
+    ibmca_debug_op_ctx(ctx, "ctx: %p", ctx);
+    for (p = params; p != NULL && p->key != NULL; p++)
+        ibmca_debug_op_ctx(ctx, "param: %s", p->key);
+
+    switch (ctx->operation) {
+    case EVP_PKEY_OP_SIGN:
+    case EVP_PKEY_OP_VERIFY:
+    case EVP_PKEY_OP_VERIFYRECOVER:
+    case EVP_PKEY_OP_SIGNCTX:
+    case EVP_PKEY_OP_VERIFYCTX:
+        /* OSSL_SIGNATURE_PARAM_PROPERTIES */
+        rc = ibmca_param_get_utf8(ctx->provctx, params,
+                                  OSSL_SIGNATURE_PARAM_PROPERTIES, &props);
+        if (rc == 0)
+            return 0;
+
+        /* OSSL_SIGNATURE_PARAM_DIGEST */
+        rc = ibmca_param_get_utf8(ctx->provctx, params,
+                                  OSSL_SIGNATURE_PARAM_DIGEST, &name);
+        if (rc == 0)
+            return 0;
+        if (rc > 0 &&
+            ibmca_signature_ec_set_md(ctx, name, 0, props) == 0)
+            return 0;
+
+        /* OSSL_SIGNATURE_PARAM_DIGEST_SIZE */
+        rc = ibmca_param_get_size_t(ctx->provctx, params,
+                                    OSSL_SIGNATURE_PARAM_DIGEST_SIZE, &md_size);
+        if (rc == 0)
+            return 0;
+        if (rc > 0) {
+            if (!ctx->ec.signature.set_md_allowed) {
+                put_error_op_ctx(ctx, IBMCA_ERR_INVALID_PARAM,
+                                 "Digest size not allowed to be set in the "
+                                 "current state");
+                return 0;
+            }
+            ctx->ec.signature.md_size = md_size;
+        }
+        break;
+
+#ifdef EVP_PKEY_OP_SIGNMSG
+    case EVP_PKEY_OP_SIGNMSG:
+    case EVP_PKEY_OP_VERIFYMSG:
+        /* OSSL_SIGNATURE_PARAM_SIGNATURE */
+        rc = ibmca_param_get_octet_string(ctx->provctx, params,
+                                          OSSL_SIGNATURE_PARAM_SIGNATURE,
+                                          (void **)&ptr, &len);
+        if (rc == 0)
+            return 0;
+        if (rc > 0) {
+            if (ctx->ec.signature.signature != NULL)
+                P_CLEAR_FREE(ctx->provctx, ctx->ec.signature.signature,
+                        ctx->ec.signature.signature_len);
+            ctx->ec.signature.signature = ptr;
+            ctx->ec.signature.signature_len = len;
+        }
+        break;
+#endif
+    }
+
+#ifdef OSSL_SIGNATURE_PARAM_NONCE_TYPE
+    /* OSSL_SIGNATURE_PARAM_NONCE_TYPE */
+    rc = ibmca_param_get_uint(ctx->provctx, params,
+                              OSSL_SIGNATURE_PARAM_NONCE_TYPE,
+                              &ctx->ec.signature.nonce_type);
+    if (rc == 0)
+        return 0;
+#endif
+
+    return 1;
+}
+
+static const OSSL_PARAM ibmca_signature_ec_gettable_params[] = {
+    OSSL_PARAM_octet_string(OSSL_SIGNATURE_PARAM_ALGORITHM_ID, NULL, 0),
+    OSSL_PARAM_utf8_string(OSSL_SIGNATURE_PARAM_DIGEST, NULL, 0),
+    OSSL_PARAM_size_t(OSSL_SIGNATURE_PARAM_DIGEST_SIZE, NULL),
+#ifdef OSSL_SIGNATURE_PARAM_NONCE_TYPE
+    OSSL_PARAM_uint(OSSL_SIGNATURE_PARAM_NONCE_TYPE, NULL),
+#endif
+    OSSL_PARAM_END
+};
+
+static const OSSL_PARAM *ibmca_signature_ec_gettable_ctx_params(
+                                                void *vctx, void *vprovctx)
+{
+    const struct ibmca_op_ctx *ctx = vctx;
+    const struct ibmca_prov_ctx *provctx = vprovctx;
+    const OSSL_PARAM *p;
+
+    ibmca_debug_ctx(provctx, "ctx: %p", ctx);
+
+    for (p = ibmca_signature_ec_gettable_params;
+                                    p != NULL && p->key != NULL; p++)
+        ibmca_debug_ctx(provctx, "param: %s", p->key);
+
+    return ibmca_signature_ec_gettable_params;
+}
+
+static const OSSL_PARAM ibmca_signature_ec_settable_params[] = {
+    OSSL_PARAM_utf8_string(OSSL_SIGNATURE_PARAM_DIGEST, NULL, 0),
+    OSSL_PARAM_size_t(OSSL_SIGNATURE_PARAM_DIGEST_SIZE, NULL),
+    OSSL_PARAM_utf8_string(OSSL_SIGNATURE_PARAM_PROPERTIES, NULL, 0),
+#ifdef OSSL_SIGNATURE_PARAM_NONCE_TYPE
+    OSSL_PARAM_uint(OSSL_SIGNATURE_PARAM_NONCE_TYPE, NULL),
+#endif
+    OSSL_PARAM_END
+};
+
+static const OSSL_PARAM ibmca_signature_ec_settable_params_no_digest[] = {
+    OSSL_PARAM_END
+};
+
+#ifdef EVP_PKEY_OP_SIGNMSG
+static const OSSL_PARAM ibmca_signature_ec_sigalg_settable_params[] = {
+    OSSL_PARAM_octet_string(OSSL_SIGNATURE_PARAM_SIGNATURE, NULL, 0),
+#ifdef OSSL_SIGNATURE_PARAM_NONCE_TYPE
+    OSSL_PARAM_uint(OSSL_SIGNATURE_PARAM_NONCE_TYPE, NULL),
+#endif
+    OSSL_PARAM_END
+};
+#endif
+
+static const OSSL_PARAM *ibmca_signature_ec_settable_ctx_params(
+                                                void *vctx, void *vprovctx)
+{
+    const struct ibmca_op_ctx *ctx = vctx;
+    const struct ibmca_prov_ctx *provctx = vprovctx;
+    const OSSL_PARAM *p, *params;
+
+    ibmca_debug_ctx(provctx, "ctx: %p", ctx);
+
+#ifdef EVP_PKEY_OP_SIGNMSG
+    if (ctx != NULL && ctx->operation == EVP_PKEY_OP_VERIFYMSG)
+        params = ibmca_signature_ec_sigalg_settable_params;
+    else if (ctx != NULL && ctx->operation == EVP_PKEY_OP_SIGNMSG)
+        params = NULL;
+    else if (ctx == NULL || ctx->ec.signature.set_md_allowed)
+#else
+    if (ctx == NULL || ctx->ec.signature.set_md_allowed)
+#endif
+        params = ibmca_signature_ec_settable_params;
+    else
+        params = ibmca_signature_ec_settable_params_no_digest;
+
+    for (p = params; p != NULL && p->key != NULL; p++)
+        ibmca_debug_ctx(provctx, "param: %s", p->key);
+
+    return params;
+}
+
+#ifdef EVP_PKEY_OP_SIGNMSG
+static int ibmca_signature_ec_signverify_message_update(void *vctx,
+                                            const unsigned char *data,
+                                            size_t datalen)
+{
+    struct ibmca_op_ctx *ctx = vctx;
+
+    return ibmca_digest_signverify_update(ctx, ctx->ec.signature.md_ctx,
+                                          data, datalen);
+}
+
+static int ibmca_signature_ec_sign_message_final(void *vctx,
+                                                 unsigned char *sig,
+                                                 size_t *siglen,
+                                                 size_t sigsize)
+{
+    struct ibmca_op_ctx *ctx = vctx;
+    int rc;
+
+    ctx->operation = EVP_PKEY_OP_SIGNCTX;
+    rc = ibmca_digest_sign_final(ctx, ctx->ec.signature.md_ctx,
+                                 ibmca_signature_ec_sign,
+                                sig, siglen, sigsize);
+    ctx->operation = EVP_PKEY_OP_SIGNMSG;
+
+    return rc;
+}
+
+static int ibmca_signature_ec_verify_message_final(void *vctx)
+{
+    struct ibmca_op_ctx *ctx = vctx;
+    int rc;
+
+    ctx->operation = EVP_PKEY_OP_VERIFYCTX;
+    rc = ibmca_digest_verify_final(ctx, ctx->ec.signature.md_ctx,
+                                   ibmca_signature_ec_verify,
+                                   ctx->ec.signature.signature,
+                                   ctx->ec.signature.signature_len);
+    ctx->operation = EVP_PKEY_OP_VERIFYMSG;
+
+    return rc;
+}
+#endif
+
+static int ibmca_signature_ec_digest_sign_init(void *vctx, const char *mdname,
+                                               void *vkey,
+                                               const OSSL_PARAM params[])
+{
+    struct ibmca_op_ctx *ctx = vctx;
+    struct ibmca_key *key = vkey;
+
+    return ibmca_signature_ec_op_init(ctx, key, params,
+                                      EVP_PKEY_OP_SIGNCTX, mdname);
+}
+
+static int ibmca_signature_ec_digest_verify_init(void *vctx, const char *mdname,
+                                                 void *vkey,
+                                                 const OSSL_PARAM params[])
+{
+    struct ibmca_op_ctx *ctx = vctx;
+    struct ibmca_key *key = vkey;
+
+    return ibmca_signature_ec_op_init(ctx, key, params,
+                                      EVP_PKEY_OP_VERIFYCTX, mdname);
+}
+
+static int ibmca_signature_ec_digest_signverify_update(void *vctx,
+                                            const unsigned char *data,
+                                            size_t datalen)
+{
+    struct ibmca_op_ctx *ctx = vctx;
+
+    return ibmca_digest_signverify_update(ctx, ctx->ec.signature.md_ctx,
+                                          data, datalen);
+}
+
+static int ibmca_signature_ec_digest_sign_final(void *vctx,
+                                                unsigned char *sig,
+                                                size_t *siglen,
+                                                size_t sigsize)
+{
+    struct ibmca_op_ctx *ctx = vctx;
+
+    return ibmca_digest_sign_final(ctx, ctx->ec.signature.md_ctx,
+                                   ibmca_signature_ec_sign,
+                                   sig, siglen, sigsize);
+}
+
+static int ibmca_signature_ec_digest_verify_final(void *vctx,
+                                                  const unsigned char *sig,
+                                                  size_t siglen)
+{
+    struct ibmca_op_ctx *ctx = vctx;
+
+    return ibmca_digest_verify_final(ctx, ctx->ec.signature.md_ctx,
+                                     ibmca_signature_ec_verify,
+                                     sig, siglen);
+}
+
+static int ibmca_signature_ec_get_ctx_md_params(void *vctx, OSSL_PARAM *params)
+{
+    struct ibmca_op_ctx *ctx = vctx;
+
+    return ibmca_get_ctx_md_params(ctx, ctx->ec.signature.md_ctx, params);
+}
+
+static int ibmca_signature_ec_set_ctx_md_params(void *vctx,
+                                                const OSSL_PARAM params[])
+{
+    struct ibmca_op_ctx *ctx = vctx;
+
+    return ibmca_set_ctx_md_params(ctx, ctx->ec.signature.md_ctx, params);
+}
+
+static const OSSL_PARAM *ibmca_signature_ec_gettable_ctx_md_params(void *vctx)
+{
+    struct ibmca_op_ctx *ctx = vctx;
+
+    return ibmca_gettable_ctx_md_params(ctx, ctx->ec.signature.md);
+}
+
+static const OSSL_PARAM *ibmca_signature_ec_settable_ctx_md_params(void *vctx)
+{
+    struct ibmca_op_ctx *ctx = vctx;
+
+    return ibmca_settable_ctx_md_params(ctx, ctx->ec.signature.md);
+}
+
+static const OSSL_DISPATCH ibmca_ecdsa_signature_functions[] = {
+    /* Signature context constructor, destructor */
+    { OSSL_FUNC_SIGNATURE_NEWCTX, (void (*)(void))ibmca_signature_ec_newctx },
+    { OSSL_FUNC_SIGNATURE_FREECTX, (void (*)(void))ibmca_op_freectx },
+    { OSSL_FUNC_SIGNATURE_DUPCTX, (void (*)(void))ibmca_op_dupctx },
+    /* Signing */
+    { OSSL_FUNC_SIGNATURE_SIGN_INIT,
+            (void (*)(void))ibmca_signature_ec_sign_init },
+    { OSSL_FUNC_SIGNATURE_SIGN, (void (*)(void))ibmca_signature_ec_sign },
+    /* Verifying */
+    { OSSL_FUNC_SIGNATURE_VERIFY_INIT,
+            (void (*)(void))ibmca_signature_ec_verify_init },
+    { OSSL_FUNC_SIGNATURE_VERIFY, (void (*)(void))ibmca_signature_ec_verify },
+    /* Digest Sign */
+    { OSSL_FUNC_SIGNATURE_DIGEST_SIGN_INIT,
+            (void (*)(void))ibmca_signature_ec_digest_sign_init },
+    { OSSL_FUNC_SIGNATURE_DIGEST_SIGN_UPDATE,
+            (void (*)(void))ibmca_signature_ec_digest_signverify_update },
+    { OSSL_FUNC_SIGNATURE_DIGEST_SIGN_FINAL,
+            (void (*)(void))ibmca_signature_ec_digest_sign_final },
+    /* Digest Verify */
+    { OSSL_FUNC_SIGNATURE_DIGEST_VERIFY_INIT,
+            (void (*)(void))ibmca_signature_ec_digest_verify_init },
+    { OSSL_FUNC_SIGNATURE_DIGEST_VERIFY_UPDATE,
+            (void (*)(void))ibmca_signature_ec_digest_signverify_update },
+    { OSSL_FUNC_SIGNATURE_DIGEST_VERIFY_FINAL,
+            (void (*)(void))ibmca_signature_ec_digest_verify_final },
+    /* Signature parameters */
+    { OSSL_FUNC_SIGNATURE_GET_CTX_PARAMS,
+            (void (*)(void))ibmca_signature_ec_get_ctx_params },
+    { OSSL_FUNC_SIGNATURE_GETTABLE_CTX_PARAMS,
+            (void (*)(void))ibmca_signature_ec_gettable_ctx_params },
+    { OSSL_FUNC_SIGNATURE_SET_CTX_PARAMS, (void
+            (*)(void))ibmca_signature_ec_set_ctx_params },
+    { OSSL_FUNC_SIGNATURE_SETTABLE_CTX_PARAMS,
+            (void (*)(void))ibmca_signature_ec_settable_ctx_params },
+    /* MD parameters */
+    { OSSL_FUNC_SIGNATURE_GET_CTX_MD_PARAMS,
+            (void (*)(void))ibmca_signature_ec_get_ctx_md_params },
+    { OSSL_FUNC_SIGNATURE_GETTABLE_CTX_MD_PARAMS,
+        (void (*)(void))ibmca_signature_ec_gettable_ctx_md_params },
+    { OSSL_FUNC_SIGNATURE_SET_CTX_MD_PARAMS,
+            (void (*)(void))ibmca_signature_ec_set_ctx_md_params },
+    { OSSL_FUNC_SIGNATURE_SETTABLE_CTX_MD_PARAMS,
+        (void (*)(void))ibmca_signature_ec_settable_ctx_md_params },
+    { 0, NULL }
+};
+
+#ifdef EVP_PKEY_OP_SIGNMSG
+#define IBMCA_IMPL_ECDSA_SIGALG(md, MD)                                        \
+    static OSSL_FUNC_signature_sign_init_fn                                    \
+                     ibmca_signature_ec_##md##_sign_init;                      \
+    static OSSL_FUNC_signature_verify_init_fn                                  \
+                     ibmca_signature_ec_##md##_verify_init;                    \
+    static OSSL_FUNC_signature_sign_message_init_fn                            \
+                     ibmca_signature_ec_##md##_sign_message_init;              \
+    static OSSL_FUNC_signature_verify_message_init_fn                          \
+                     ibmca_signature_ec_##md##_verify_message_init;            \
+                                                                               \
+    static int ibmca_signature_ec_##md##_sign_init(void *vctx, void *vkey,     \
+                                                   const OSSL_PARAM params[])  \
+    {                                                                          \
+        struct ibmca_op_ctx *ctx = vctx;                                       \
+        struct ibmca_key *key = vkey;                                          \
+                                                                               \
+        return ibmca_signature_ec_op_init(ctx, key, params,                    \
+                                          EVP_PKEY_OP_SIGN, #MD);              \
+    }                                                                          \
+                                                                               \
+    static int ibmca_signature_ec_##md##_verify_init(void *vctx, void *vkey,   \
+                                                     const OSSL_PARAM params[])\
+    {                                                                          \
+        struct ibmca_op_ctx *ctx = vctx;                                       \
+        struct ibmca_key *key = vkey;                                          \
+                                                                               \
+        return ibmca_signature_ec_op_init(ctx, key, params,                    \
+                                          EVP_PKEY_OP_VERIFY, #MD);            \
+    }                                                                          \
+                                                                               \
+    static int ibmca_signature_ec_##md##_sign_message_init(                    \
+                                                    void *vctx, void *vkey,    \
+                                                    const OSSL_PARAM params[]) \
+    {                                                                          \
+        struct ibmca_op_ctx *ctx = vctx;                                       \
+        struct ibmca_key *key = vkey;                                          \
+                                                                               \
+        return ibmca_signature_ec_op_init(ctx, key, params,                    \
+                                          EVP_PKEY_OP_SIGNMSG, #MD);           \
+    }                                                                          \
+                                                                               \
+       static int ibmca_signature_ec_##md##_verify_message_init(               \
+                                                    void *vctx, void *vkey,    \
+                                                    const OSSL_PARAM params[]) \
+       {                                                                       \
+           struct ibmca_op_ctx *ctx = vctx;                                    \
+           struct ibmca_key *key = vkey;                                       \
+                                                                               \
+           return ibmca_signature_ec_op_init(ctx, key, params,                 \
+                                             EVP_PKEY_OP_VERIFYMSG, #MD);      \
+       }                                                                       \
+                                                                               \
+    static const OSSL_DISPATCH ibmca_ecdsa_##md##_signature_functions[] = {    \
+        /* Signature context constructor, destructor */                        \
+        { OSSL_FUNC_SIGNATURE_NEWCTX,                                          \
+                (void (*)(void))ibmca_signature_ec_newctx },                   \
+        { OSSL_FUNC_SIGNATURE_FREECTX, (void (*)(void))ibmca_op_freectx },     \
+        { OSSL_FUNC_SIGNATURE_DUPCTX, (void (*)(void))ibmca_op_dupctx },       \
+        { OSSL_FUNC_SIGNATURE_QUERY_KEY_TYPES,                                 \
+                (void (*)(void))ibmca_signature_ec_query_key_types },          \
+        /* Signing */                                                          \
+        { OSSL_FUNC_SIGNATURE_SIGN_INIT,                                       \
+                (void (*)(void))ibmca_signature_ec_##md##_sign_init },         \
+        { OSSL_FUNC_SIGNATURE_SIGN,                                            \
+                (void (*)(void))ibmca_signature_ec_sign },                     \
+        /* Verifying */                                                        \
+        { OSSL_FUNC_SIGNATURE_VERIFY_INIT,                                     \
+                (void (*)(void))ibmca_signature_ec_##md##_verify_init },       \
+        { OSSL_FUNC_SIGNATURE_VERIFY,                                          \
+                (void (*)(void))ibmca_signature_ec_verify },                   \
+        /* Sign Message */                                                     \
+        { OSSL_FUNC_SIGNATURE_SIGN_MESSAGE_INIT,                               \
+                (void (*)(void))ibmca_signature_ec_##md##_sign_message_init }, \
+        { OSSL_FUNC_SIGNATURE_SIGN_MESSAGE_UPDATE,                             \
+                (void (*)(void))ibmca_signature_ec_signverify_message_update },\
+        { OSSL_FUNC_SIGNATURE_SIGN_MESSAGE_FINAL,                              \
+                (void (*)(void))ibmca_signature_ec_sign_message_final },       \
+        /* Verify Message */                                                   \
+        { OSSL_FUNC_SIGNATURE_VERIFY_MESSAGE_INIT,                             \
+               (void (*)(void))ibmca_signature_ec_##md##_verify_message_init },\
+        { OSSL_FUNC_SIGNATURE_VERIFY_MESSAGE_UPDATE,                           \
+                (void (*)(void))ibmca_signature_ec_signverify_message_update },\
+        { OSSL_FUNC_SIGNATURE_VERIFY_MESSAGE_FINAL,                            \
+                (void (*)(void))ibmca_signature_ec_verify_message_final },     \
+        /* Signature parameters */                                             \
+        { OSSL_FUNC_SIGNATURE_GET_CTX_PARAMS,                                  \
+                (void (*)(void))ibmca_signature_ec_get_ctx_params },           \
+        { OSSL_FUNC_SIGNATURE_GETTABLE_CTX_PARAMS,                             \
+                (void (*)(void))ibmca_signature_ec_gettable_ctx_params },      \
+        { OSSL_FUNC_SIGNATURE_SET_CTX_PARAMS,                                  \
+                (void (*)(void))ibmca_signature_ec_set_ctx_params },           \
+        { OSSL_FUNC_SIGNATURE_SETTABLE_CTX_PARAMS,                             \
+               (void (*)(void))ibmca_signature_ec_settable_ctx_params },       \
+        { 0, NULL }                                                            \
+    }
+
+#define IBMCA_DEF_ECDSA_SIGALG(md, MD, names)                                  \
+    { names, NULL, ibmca_ecdsa_##md##_signature_functions,                     \
+      "IBMCA ECDSA " #MD " implementation" }
+
+IBMCA_IMPL_ECDSA_SIGALG(sha1, SHA1);
+IBMCA_IMPL_ECDSA_SIGALG(sha224, SHA2-224);
+IBMCA_IMPL_ECDSA_SIGALG(sha256, SHA2-256);
+IBMCA_IMPL_ECDSA_SIGALG(sha384, SHA2-384);
+IBMCA_IMPL_ECDSA_SIGALG(sha512, SHA2-512);
+IBMCA_IMPL_ECDSA_SIGALG(sha3_224, SHA3-224);
+IBMCA_IMPL_ECDSA_SIGALG(sha3_256, SHA3-256);
+IBMCA_IMPL_ECDSA_SIGALG(sha3_384, SHA3-384);
+IBMCA_IMPL_ECDSA_SIGALG(sha3_512, SHA3-512);
+
+#endif
+
+const OSSL_ALGORITHM ibmca_ec_signature[] = {
+    { "ECDSA", NULL, ibmca_ecdsa_signature_functions,
+      "IBMCA ECDSA implementation" },
+#ifdef EVP_PKEY_OP_SIGNMSG
+    IBMCA_DEF_ECDSA_SIGALG(sha1, SHA1,
+                           "ECDSA-SHA1:ECDSA-SHA-1:ecdsa-with-SHA1:"
+                           "1.2.840.10045.4.1"),
+    IBMCA_DEF_ECDSA_SIGALG(sha224, SHA2-224,
+                           "ECDSA-SHA2-224:ECDSA-SHA224:ecdsa-with-SHA224:"
+                           "1.2.840.10045.4.3.1"),
+    IBMCA_DEF_ECDSA_SIGALG(sha256, SHA2-256,
+                           "ECDSA-SHA2-256:ECDSA-SHA256:ecdsa-with-SHA256:"
+                           "1.2.840.10045.4.3.2"),
+    IBMCA_DEF_ECDSA_SIGALG(sha384, SHA2-384,
+                           "ECDSA-SHA2-384:ECDSA-SHA384:ecdsa-with-SHA384:"
+                           "1.2.840.10045.4.3.3"),
+    IBMCA_DEF_ECDSA_SIGALG(sha512, SHA2-512,
+                           "ECDSA-SHA2-512:ECDSA-SHA512:ecdsa-with-SHA512:"
+                           "1.2.840.10045.4.3.4"),
+    IBMCA_DEF_ECDSA_SIGALG(sha3_224, SHA3-224,
+                           "ECDSA-SHA3-224:ecdsa_with_SHA3-224:"
+                           "id-ecdsa-with-sha3-224:2.16.840.1.101.3.4.3.9"),
+    IBMCA_DEF_ECDSA_SIGALG(sha3_256, SHA3-256,
+                           "ECDSA-SHA3-256:ecdsa_with_SHA3-256:"
+                           "id-ecdsa-with-sha3-256:2.16.840.1.101.3.4.3.10"),
+    IBMCA_DEF_ECDSA_SIGALG(sha3_384, SHA3-384,
+                           "ECDSA-SHA3-384:ecdsa_with_SHA3-384:"
+                           "id-ecdsa-with-sha3-384:2.16.840.1.101.3.4.3.11"),
+    IBMCA_DEF_ECDSA_SIGALG(sha3_512, SHA3-512,
+                           "ECDSA-SHA3-512:ecdsa_with_SHA3-512:"
+                           "id-ecdsa-with-sha3-512:2.16.840.1.101.3.4.3.12"),
+#endif
+    { NULL, NULL, NULL, NULL }
+};
diff -pruN 1.4.0-1/src/provider/ibmca-provider-opensslconfig 2.5.0-0ubuntu1/src/provider/ibmca-provider-opensslconfig
--- 1.4.0-1/src/provider/ibmca-provider-opensslconfig	1970-01-01 00:00:00.000000000 +0000
+++ 2.5.0-0ubuntu1/src/provider/ibmca-provider-opensslconfig	2025-04-16 08:09:24.000000000 +0000
@@ -0,0 +1,155 @@
+#!/usr/bin/perl
+
+#
+# Copyright [2021-2022] International Business Machines Corp.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+#
+# Generate openssl.cnf from the system config file with added provider section
+# for ibmca provider.
+#
+# USE WITH CARE: No automation can replace human knowledge and understanding
+#                of the desired configuration!
+#
+
+use strict;
+use warnings;
+
+sub generate()
+{
+    my ($osslconfpath);
+    my ($ih, $line, $oh, $defaultcnfsect, $indefaultsect, $providersect, $inprovidersect);
+    my ($inalgsect, $algsection);
+
+    $osslconfpath = `openssl version -d` || die "Please install openssl binary";
+    $osslconfpath =~ s/OPENSSLDIR: \"([^\"]*)\"$/$1/ || die "Failed to extract OpenSSL configuration directory";
+    chomp $osslconfpath;
+
+    open($ih, "<", "$osslconfpath/openssl.cnf") or die "Cannot open $osslconfpath/openssl.cnf";
+    open($oh, ">", "openssl.cnf.ibmca-provider") or die "Cannot open openssl.cnf.ibmca-provider";
+
+    $defaultcnfsect = undef;
+    $indefaultsect = 0;
+    $providersect = undef;
+    $inprovidersect = 0;
+    while ($line = <$ih>) {
+        if ($line =~ /openssl_conf\s*=\s*(.*)/) {
+            $defaultcnfsect = $1;
+            chomp $defaultcnfsect;
+        }
+        if ($indefaultsect) {
+            if ($line =~ /\[\s*\w+\s*\]/) {
+                if (!$providersect) {
+                    print $oh "providers = provider_section\n";
+                }
+                if (!$algsection) {
+                    print $oh "alg_section = evp_properties\n";
+                }
+                $indefaultsect = 0;
+            } elsif ($line =~ /^\s*providers\s*=\s*(\w+)\s*/) {
+                $providersect = $1;
+                chomp $providersect;
+            } elsif ($line =~ /^\s*alg_section\s*=\s*(\w+)\s*/) {
+                $algsection = $1;
+                chomp $algsection;
+            }
+        } elsif ($inalgsect) {
+            if ($line =~ /\[\s*\w+\s*\]/) {
+                print $oh "default_properties = ?provider=ibmca\n";
+                $inalgsect = 0;
+            } elsif ($line =~ /^\s*default_properties\s*=\s*(\w+)\s*/) {
+                print $oh "default_properties = ?provider=ibmca\n";
+                print $oh "# The following was commented out by ibmca-provider-opensslconfig script\n";
+                print "WARNING: The default_properties in $algsection was modified by this script.\n";
+                $line = "# $line";
+            }
+        } elsif ($inprovidersect) {
+            if ($line =~ /\[\s*\w+\s*\]/) {
+                $inprovidersect = 0;
+                print $oh "ibmca_provider = ibmca_provider_section\n";
+                print $oh "# Make sure that you have configured and activated at least one other provider!\n";
+                print "WARNING: The IBMCA provider was added to section [$providersect].\n"; 
+                print "Make sure that you have configured and activated at least one other provider, e.g. the default provider!\n";
+            }
+        }
+        print $oh "$line";
+        if ($defaultcnfsect && $line =~ /\[\s*$defaultcnfsect\s*\]/) {
+            $indefaultsect = 1;
+        }
+        if ($algsection && $line =~ /\[\s*$algsection\s*\]/) {
+            $inalgsect = 1;
+        }
+        if ($providersect && $line =~ /\[\s*$providersect\s*\]/) {
+            $inprovidersect = 1;
+        }
+    }
+
+    if (!$defaultcnfsect) {
+        print $oh, qq|
+openssl_conf = openssl_init
+[openssl_init]
+providers = provider_section
+|;
+    }
+
+    if (!$providersect) {
+        print $oh qq|
+[provider_section]
+default = default_sect
+ibmca_provider = ibmca_provider_section
+
+[default_sect]
+activate = 1
+|;
+    }
+
+    print $oh qq|
+[ibmca_provider_section]
+identity = ibmca
+module = ibmca-provider.so
+activate = 1
+# Note: Disable the FIPS mode of the IBMCA provider by setting fips=no in the 
+# provider configuration. The IBMCA provider is currently not FIPS-certified.
+# It does not perform any FIPS self-tests itself nor an integrity check which
+# would be required to be FIPS-certified. It is only checked whether libica
+# library has successfully performed its self-tests and integrity checks when
+# FIPS mode is enabled.
+fips = no
+# Note: Depending on the hardware level (IBM z15), EC is already accelerated
+# implicitly by OpenSSL for certain curves. Therefore, do not accelerate EC 
+# using the IBMCA provider if you are on an IBM z15 or later. This would 
+# actually make it slower.
+algorithms = RSA,EC,DH
+#fallback-properties = provider=default
+|;
+
+    if (!$algsection) {
+        print $oh qq|
+[evp_properties]
+default_properties = ?provider=ibmca
+|;
+    }
+
+    close($ih);
+    close($oh);
+    
+    print qq|
+Successfully generated openssl.cnf.ibmca-provider file.  Please review this configuration
+and, if you are happy with the changes, replace $osslconfpath/openssl.cnf with
+this file.
+|;
+}
+
+generate();
diff -pruN 1.4.0-1/src/provider/ibmca-provider.map 2.5.0-0ubuntu1/src/provider/ibmca-provider.map
--- 1.4.0-1/src/provider/ibmca-provider.map	1970-01-01 00:00:00.000000000 +0000
+++ 2.5.0-0ubuntu1/src/provider/ibmca-provider.map	2025-04-16 08:09:24.000000000 +0000
@@ -0,0 +1,7 @@
+IBMCA_PROVIDER_1.1.0 {
+    global:
+        OSSL_provider_init;
+
+    local:
+        *;
+};
diff -pruN 1.4.0-1/src/provider/openssl.cnf.provider.sample 2.5.0-0ubuntu1/src/provider/openssl.cnf.provider.sample
--- 1.4.0-1/src/provider/openssl.cnf.provider.sample	1970-01-01 00:00:00.000000000 +0000
+++ 2.5.0-0ubuntu1/src/provider/openssl.cnf.provider.sample	2025-04-16 08:09:24.000000000 +0000
@@ -0,0 +1,42 @@
+#
+# OpenSSL example configuration file. This file will load the IBMCA provider
+# for all apps that have OpenSSL config support compiled into them.
+#
+# Adding OpenSSL config support is as simple as adding the following line to
+# the app:
+#
+# #define OPENSSL_LOAD_CONF	1
+#
+openssl_conf = openssl_def
+
+[openssl_def]
+providers = provider_sect
+alg_section = evp_properties
+
+[provider_sect]
+default = default_sect
+ibmca_provider = ibmca_sect
+
+[default_sect]
+activate = 1
+
+[ibmca_sect]
+identity = ibmca
+module = ibmca-provider.so
+activate = 1
+# Note: Disable the FIPS mode of the IBMCA provider by setting fips=no in the 
+# provider configuration. The IBMCA provider is currently not FIPS-certified.
+# It does not perform any FIPS self-tests itself nor an integrity check which
+# would be required to be FIPS-certified. It is only checked whether libica
+# library has successfully performed its self-tests and integrity checks when
+# FIPS mode is enabled.
+fips = no
+# Note: Depending on the hardware level (IBM z15), EC is already accelerated
+# implicitly by OpenSSL for certain curves. Therefore, do not accelerate EC 
+# using the IBMCA provider if you are on an IBM z15 or later. This would 
+# actually make it slower.
+algorithms = RSA,EC,DH
+#fallback-properties = provider=default
+
+[evp_properties]
+default_properties = ?provider=ibmca
diff -pruN 1.4.0-1/src/provider/p_context.c 2.5.0-0ubuntu1/src/provider/p_context.c
--- 1.4.0-1/src/provider/p_context.c	1970-01-01 00:00:00.000000000 +0000
+++ 2.5.0-0ubuntu1/src/provider/p_context.c	2025-04-16 08:09:24.000000000 +0000
@@ -0,0 +1,445 @@
+/*
+ * Copyright [2021-2022] International Business Machines Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <stddef.h>
+#include <err.h>
+#include <strings.h>
+#include <string.h>
+
+#include <openssl/evp.h>
+#include <openssl/core.h>
+#include <openssl/core_dispatch.h>
+#include <openssl/core_names.h>
+#include <openssl/params.h>
+
+#include "p_ibmca.h"
+
+struct ibmca_op_ctx *ibmca_op_newctx(const struct ibmca_prov_ctx *provctx,
+                                     const char *propq, int type,
+                                     void (*free_cb)(struct ibmca_op_ctx *ctx),
+                                     int (*dup_cb)
+                                                (const struct ibmca_op_ctx *ctx,
+                                                 struct ibmca_op_ctx *new_ctx))
+{
+    struct ibmca_op_ctx *ctx;
+
+    if (provctx == NULL)
+        return NULL;
+
+    ibmca_debug_ctx(provctx, "propq: %s type: %d", propq != NULL ? propq : "",
+                 type);
+
+    ctx = P_ZALLOC(provctx, sizeof(struct ibmca_op_ctx));
+    if (ctx == NULL) {
+        put_error_ctx(provctx, IBMCA_ERR_MALLOC_FAILED,
+                      "Failed to allocate operation context");
+        return NULL;
+    }
+
+    ctx->provctx = provctx;
+    ctx->type = type;
+    ctx->free_cb = free_cb;
+    ctx->dup_cb = dup_cb;
+
+    if (propq != NULL) {
+        ctx->propq = P_STRDUP(provctx, propq);
+        if (ctx->propq == NULL) {
+            put_error_ctx(provctx, IBMCA_ERR_MALLOC_FAILED, "strdup failed");
+            P_FREE(provctx, ctx);
+            return NULL;
+        }
+    }
+
+    ibmca_debug_ctx(provctx, "ctx: %p", ctx);
+    return ctx;
+}
+
+void ibmca_op_freectx(void *vctx)
+{
+    struct ibmca_op_ctx *ctx = vctx;
+
+    if (ctx == NULL)
+        return;
+
+    ibmca_debug_op_ctx(ctx, "ctx: %p", ctx);
+
+    if (ctx->key != NULL)
+        ibmca_keymgmt_free(ctx->key);
+    ctx->key = NULL;
+
+    if (ctx->propq != NULL)
+        P_FREE(ctx->provctx, (void *)ctx->propq);
+    ctx->propq = NULL;
+
+    if (ctx->free_cb != NULL)
+        ctx->free_cb(ctx);
+
+    if (ctx->tbuf != NULL)
+        P_SECURE_CLEAR_FREE(ctx->provctx, ctx->tbuf, ctx->tbuf_len);
+    ctx->tbuf = NULL;
+    ctx->tbuf_len = 0;
+
+    P_FREE(ctx->provctx, ctx);
+}
+
+void *ibmca_op_dupctx(void *vctx)
+{
+    struct ibmca_op_ctx *ctx = vctx;
+    struct ibmca_op_ctx *new_ctx;
+
+    if (ctx == NULL)
+        return NULL;
+
+    ibmca_debug_op_ctx(ctx, "ctx: %p", ctx);
+
+    new_ctx = ibmca_op_newctx(ctx->provctx, ctx->propq, ctx->type,
+                              ctx->free_cb, ctx->dup_cb);
+    if (new_ctx == NULL) {
+        ibmca_debug_op_ctx(ctx, "ERROR: ibmca_op_newctx failed");
+        return NULL;
+    }
+
+    new_ctx->operation = ctx->operation;
+
+    if (ctx->key != NULL) {
+        new_ctx->key = ctx->key;
+        ibmca_keymgmt_upref(ctx->key);
+    }
+
+    if (ctx->dup_cb != NULL) {
+        if (ctx->dup_cb(ctx, new_ctx) == 0) {
+            ibmca_debug_op_ctx(ctx, "ERROR: dup_cb failed");
+            ibmca_op_freectx(new_ctx);
+            return NULL;
+        }
+    }
+
+    ibmca_debug_op_ctx(ctx, "new_ctx: %p", new_ctx);
+    return new_ctx;
+}
+
+int ibmca_op_init(struct ibmca_op_ctx *ctx, struct ibmca_key *key,
+                  int operation)
+{
+    if (ctx == NULL)
+        return 0;
+
+    ibmca_debug_op_ctx(ctx, "ctx: %p key: %p operation: %d", ctx, key,
+                       operation);
+
+    if (key != NULL) {
+        switch (ctx->type) {
+        case EVP_PKEY_RSA:
+        case EVP_PKEY_RSA_PSS:
+            if (key->type != EVP_PKEY_RSA &&
+                key->type != EVP_PKEY_RSA_PSS) {
+                put_error_op_ctx(ctx, IBMCA_ERR_INTERNAL_ERROR,
+                                 "key type mismatch: ctx type: %d key type: %d",
+                                 ctx->type, key->type);
+                return 0;
+            }
+            break;
+        case EVP_PKEY_EC:
+            if (key->type != EVP_PKEY_EC) {
+                put_error_op_ctx(ctx, IBMCA_ERR_INTERNAL_ERROR,
+                                 "key type mismatch: ctx type: %d key type: %d",
+                                 ctx->type, key->type);
+                return 0;
+            }
+            break;
+        case EVP_PKEY_DH:
+        case EVP_PKEY_DHX:
+            if (key->type != EVP_PKEY_DH &&
+                key->type != EVP_PKEY_DHX) {
+                put_error_op_ctx(ctx, IBMCA_ERR_INTERNAL_ERROR,
+                                 "key type mismatch: ctx type: %d key type: %d",
+                                 ctx->type, key->type);
+                return 0;
+            }
+            break;
+        default:
+            put_error_op_ctx(ctx, IBMCA_ERR_INTERNAL_ERROR,
+                             "key type unknown: ctx type: %d key type: %d",
+                             ctx->type, key->type);
+            return 0;
+        }
+
+        switch (operation) {
+        case EVP_PKEY_OP_DECRYPT:
+        case EVP_PKEY_OP_DERIVE:
+        case EVP_PKEY_OP_SIGN:
+        case EVP_PKEY_OP_SIGNCTX:
+            if (key->has(key, OSSL_KEYMGMT_SELECT_PRIVATE_KEY |
+                              OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS) != 1) {
+                put_error_op_ctx(ctx, IBMCA_ERR_INVALID_PARAM,
+                                 "operation %d not possible without a private key",
+                                 operation);
+                return 0;
+            }
+            break;
+
+        case EVP_PKEY_OP_ENCRYPT:
+        case EVP_PKEY_OP_VERIFY:
+        case EVP_PKEY_OP_VERIFYCTX:
+            if (key->has(key, OSSL_KEYMGMT_SELECT_PUBLIC_KEY |
+                              OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS) != 1) {
+                put_error_op_ctx(ctx, IBMCA_ERR_INVALID_PARAM,
+                                 "operation %d not possible without a public key",
+                                 operation);
+                return 0;
+            }
+            break;
+        }
+
+        ibmca_keymgmt_upref(key);
+    }
+
+    if (ctx->key != NULL)
+        ibmca_keymgmt_free(ctx->key);
+
+    ctx->key = key;
+    ctx->operation = operation;
+
+    return 1;
+}
+
+int ibmca_op_alloc_tbuf(struct ibmca_op_ctx *ctx, size_t tbuf_len)
+{
+    ibmca_debug_op_ctx(ctx, "ctx: %p", ctx);
+
+    if (ctx->tbuf != NULL) {
+        if (ctx->tbuf_len >= tbuf_len)
+            return 1;
+
+        P_FREE(ctx->provctx, ctx->tbuf);
+        ctx->tbuf_len = 0;
+    }
+
+    ctx->tbuf = P_SECURE_ZALLOC(ctx->provctx, tbuf_len);
+    if (ctx->tbuf == NULL) {
+        put_error_op_ctx(ctx, IBMCA_ERR_MALLOC_FAILED,
+                         "Failed to allocate temporary buffer");
+        return 0;
+    }
+
+    ctx->tbuf_len = tbuf_len;
+
+    return 1;
+}
+
+int ibmca_digest_signverify_update(struct ibmca_op_ctx *ctx, EVP_MD_CTX *md_ctx,
+                                   const unsigned char *data, size_t datalen)
+{
+    if (ctx == NULL || data == NULL)
+        return 0;
+
+    ibmca_debug_op_ctx(ctx, "ctx: %p key: %p datalen: %lu", ctx, ctx->key,
+                       datalen);
+
+    if (ctx->key == NULL || md_ctx == NULL ||
+        (ctx->operation != EVP_PKEY_OP_SIGNCTX &&
+#ifdef EVP_PKEY_OP_SIGNMSG
+         ctx->operation != EVP_PKEY_OP_VERIFYCTX &&
+         ctx->operation != EVP_PKEY_OP_SIGNMSG &&
+         ctx->operation != EVP_PKEY_OP_VERIFYMSG)) {
+#else
+         ctx->operation != EVP_PKEY_OP_VERIFYCTX)) {
+#endif
+        put_error_op_ctx(ctx, IBMCA_ERR_INTERNAL_ERROR,
+                         "sign operation not initialized");
+        return 0;
+    }
+
+    if (EVP_DigestUpdate(md_ctx, data, datalen) == 0) {
+        put_error_op_ctx(ctx, IBMCA_ERR_INTERNAL_ERROR,
+                         "EVP_DigestUpdate failed");
+        return 0;
+    }
+
+    return 1;
+}
+
+int ibmca_digest_sign_final(struct ibmca_op_ctx *ctx, EVP_MD_CTX *md_ctx,
+                            OSSL_FUNC_signature_sign_fn *sign_func,
+                            unsigned char *sig, size_t *siglen, size_t sigsize)
+{
+    unsigned char tbs[EVP_MAX_MD_SIZE];
+    unsigned int tbslen = 0;
+
+    if (ctx == NULL || siglen == NULL || sign_func == NULL)
+        return 0;
+
+    ibmca_debug_op_ctx(ctx, "ctx: %p key: %p sigsize: %lu",
+                       ctx, ctx->key, sigsize);
+
+    if (ctx->key == NULL || md_ctx == NULL ||
+#ifdef EVP_PKEY_OP_SIGNMSG
+        (ctx->operation != EVP_PKEY_OP_SIGNCTX &&
+         ctx->operation != EVP_PKEY_OP_SIGNMSG)) {
+#else
+        ctx->operation != EVP_PKEY_OP_SIGNCTX) {
+#endif
+        put_error_op_ctx(ctx, IBMCA_ERR_INTERNAL_ERROR,
+                         "sign operation not initialized");
+        return 0;
+    }
+
+    if (sig != NULL) {
+       if (EVP_DigestFinal_ex(md_ctx, tbs, &tbslen) == 0) {
+           put_error_op_ctx(ctx, IBMCA_ERR_INTERNAL_ERROR,
+                            "EVP_DigestFinal_ex failed");
+           return 0;
+       }
+    }
+
+    if (sign_func(ctx, sig, siglen, sigsize, tbs, (size_t)tbslen) == 0) {
+        ibmca_debug_op_ctx(ctx, "ERROR: sign_func failed");
+        return 0;
+    }
+
+    ibmca_debug_op_ctx(ctx, "siglen: %lu", *siglen);
+
+    return 1;
+}
+
+int ibmca_digest_verify_final(struct ibmca_op_ctx *ctx, EVP_MD_CTX *md_ctx,
+                              OSSL_FUNC_signature_verify_fn *verify_func,
+                              const unsigned char *sig, size_t siglen)
+{
+    unsigned char tbs[EVP_MAX_MD_SIZE];
+    unsigned int tbslen = 0;
+
+    if (ctx == NULL || sig == NULL || verify_func == NULL)
+        return 0;
+
+    ibmca_debug_op_ctx(ctx, "ctx: %p key: %p siglen: %lu",
+                       ctx, ctx->key, siglen);
+
+    if (ctx->key == NULL || md_ctx == NULL ||
+#ifdef EVP_PKEY_OP_SIGNMSG
+        (ctx->operation != EVP_PKEY_OP_VERIFYCTX &&
+         ctx->operation != EVP_PKEY_OP_VERIFYMSG)) {
+#else
+        ctx->operation != EVP_PKEY_OP_VERIFYCTX) {
+#endif
+        put_error_op_ctx(ctx, IBMCA_ERR_INTERNAL_ERROR,
+                         "verify operation not initialized");
+        return 0;
+    }
+
+    if (EVP_DigestFinal_ex(md_ctx, tbs, &tbslen) == 0) {
+        put_error_op_ctx(ctx, IBMCA_ERR_INTERNAL_ERROR,
+                         "EVP_DigestFinal_ex failed");
+        return 0;
+    }
+
+    return verify_func(ctx, sig, siglen, tbs, (size_t)tbslen);
+}
+
+int ibmca_get_ctx_md_params(struct ibmca_op_ctx *ctx, EVP_MD_CTX *md_ctx,
+                            OSSL_PARAM *params)
+{
+    const OSSL_PARAM *p;
+
+    ibmca_debug_op_ctx(ctx, "ctx: %p", ctx);
+    for (p = params; p != NULL && p->key != NULL; p++)
+         ibmca_debug_op_ctx(ctx, "param: %s", p->key);
+
+    if (md_ctx == NULL) {
+        put_error_op_ctx(ctx, IBMCA_ERR_INVALID_PARAM,
+                         "Digest sign/verify context not initialized");
+        return 0;
+    }
+
+    if (EVP_MD_CTX_get_params(md_ctx, params) == 0) {
+        put_error_op_ctx(ctx, IBMCA_ERR_INTERNAL_ERROR,
+                         "EVP_MD_CTX_get_params failed");
+        return 0;
+    }
+
+    return 1;
+}
+
+int ibmca_set_ctx_md_params(struct ibmca_op_ctx *ctx, EVP_MD_CTX *md_ctx,
+                            const OSSL_PARAM params[])
+{
+    const OSSL_PARAM *p;
+
+    ibmca_debug_op_ctx(ctx, "ctx: %p", ctx);
+    for (p = params; p != NULL && p->key != NULL; p++)
+         ibmca_debug_op_ctx(ctx, "param: %s", p->key);
+
+    if (md_ctx == NULL) {
+        put_error_op_ctx(ctx, IBMCA_ERR_INVALID_PARAM,
+                         "Digest sign/verify context not initialized");
+        return 0;
+    }
+
+    if (EVP_MD_CTX_set_params(md_ctx, params) == 0) {
+        put_error_op_ctx(ctx, IBMCA_ERR_INTERNAL_ERROR,
+                         "EVP_MD_CTX_set_params failed");
+        return 0;
+    }
+
+    return 1;
+}
+
+const OSSL_PARAM *ibmca_gettable_ctx_md_params(const struct ibmca_op_ctx *ctx,
+                                               const EVP_MD *md)
+{
+    const OSSL_PARAM *p, *params;
+
+    ibmca_debug_op_ctx(ctx, "ctx: %p", ctx);
+
+    if (md == NULL) {
+        if (ctx != NULL)
+            put_error_op_ctx(ctx, IBMCA_ERR_INVALID_PARAM,
+                             "Digest sign/verify context not initialized");
+        return NULL;
+    }
+
+    params = EVP_MD_gettable_ctx_params(md);
+
+    for (p = params; p != NULL && p->key != NULL; p++)
+         ibmca_debug_op_ctx(ctx, "param: %s", p->key);
+
+    return params;
+}
+
+const OSSL_PARAM *ibmca_settable_ctx_md_params(const struct ibmca_op_ctx *ctx,
+                                               const EVP_MD *md)
+{
+    const OSSL_PARAM *p, *params;
+
+    ibmca_debug_op_ctx(ctx, "ctx: %p", ctx);
+
+    if (md == NULL) {
+        if (ctx != NULL)
+            put_error_op_ctx(ctx, IBMCA_ERR_INVALID_PARAM,
+                            "Digest sign/verify context not initialized");
+        return NULL;
+    }
+
+    params = EVP_MD_settable_ctx_params(md);
+
+    for (p = params; p != NULL && p->key != NULL; p++)
+         ibmca_debug_op_ctx(ctx, "param: %s", p->key);
+
+    return params;
+}
diff -pruN 1.4.0-1/src/provider/p_ibmca.c 2.5.0-0ubuntu1/src/provider/p_ibmca.c
--- 1.4.0-1/src/provider/p_ibmca.c	1970-01-01 00:00:00.000000000 +0000
+++ 2.5.0-0ubuntu1/src/provider/p_ibmca.c	2025-04-16 08:09:24.000000000 +0000
@@ -0,0 +1,1418 @@
+/*
+ * Copyright [2021-2022] International Business Machines Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+#define _GNU_SOURCE
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <err.h>
+#include <strings.h>
+#include <string.h>
+#include <syslog.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include <openssl/evp.h>
+#include <openssl/core.h>
+#include <openssl/core_dispatch.h>
+#include <openssl/core_names.h>
+#include <openssl/params.h>
+#include <openssl/param_build.h>
+
+#include "p_ibmca.h"
+
+#ifndef IBMCA_LOGDIR
+#error "IBMCA_LOGDIR must be defined"
+#endif
+#ifndef IBMCA_VERSION
+#error "IBMCA_VERSION must be defined"
+#endif
+
+static OSSL_FUNC_provider_teardown_fn ibmca_teardown;
+static OSSL_FUNC_provider_gettable_params_fn ibmca_gettable_params;
+static OSSL_FUNC_provider_get_params_fn ibmca_get_params;
+static OSSL_FUNC_provider_query_operation_fn ibmca_query;
+static OSSL_FUNC_provider_get_reason_strings_fn ibmca_get_reason_strings;
+static OSSL_FUNC_provider_get_capabilities_fn ibmca_prov_get_capabilities;
+static OSSL_FUNC_provider_self_test_fn ibmca_prov_self_tests;
+
+struct ibmca_config_item {
+    const char *key;
+    int (*func)(struct ibmca_prov_ctx *provctx, const char *key,
+                const char *value);
+};
+
+static int ibmca_config_debug(struct ibmca_prov_ctx *provctx,
+                              const char *key, const char *value);
+static int ibmca_config_debug_path(struct ibmca_prov_ctx *provctx,
+                                   const char *key, const char *value);
+static int ibmca_config_fips(struct ibmca_prov_ctx *provctx,
+                             const char *key, const char *value);
+static int ibmca_config_algorithms(struct ibmca_prov_ctx *provctx,
+                                   const char *key, const char *value);
+static int ibmca_config_fallback_props(struct ibmca_prov_ctx *provctx,
+                                       const char *key, const char *value);
+static int ibmca_config_module_filename(struct ibmca_prov_ctx *provctx,
+                                        const char *key, const char *value);
+static int ibmca_config_openssl_version(struct ibmca_prov_ctx *provctx,
+                                        const char *key, const char *value);
+
+static const struct ibmca_config_item config_items[] = {
+    { IBMCA_CONF_DEBUG_PATH, ibmca_config_debug_path },
+    { IBMCA_CONF_DEBUG, ibmca_config_debug },
+    { IBMCA_CONF_FIPS, ibmca_config_fips },
+    { IBMCA_CONF_ALGORITHMS, ibmca_config_algorithms },
+    { IBMCA_CONF_FALLBACK_PROPS, ibmca_config_fallback_props },
+    { OSSL_PROV_PARAM_CORE_MODULE_FILENAME, ibmca_config_module_filename },
+    { OSSL_PROV_PARAM_CORE_VERSION, ibmca_config_openssl_version },
+};
+
+#define NUM_CONFIG_ITEMS    \
+    (sizeof(config_items) / sizeof(struct ibmca_config_item))
+
+struct ibmca_mech_algorithm {
+    int operation;
+    const OSSL_ALGORITHM *algorithms;
+};
+
+struct ibmca_ica_mech_info {
+    const char *algo;
+    const unsigned int *ica_mechs;
+    const struct ibmca_mech_algorithm *algos;
+    const struct ibmca_mech_capability *capabilities;
+};
+
+static const unsigned int ica_rsa_mech[] = {
+    RSA_ME,
+    RSA_CRT,
+    /* RSA_KEY_GEN_CRT is always supported, but only in SW */
+    0
+};
+
+static const struct ibmca_mech_algorithm ibmca_rsa_algorithms[] = {
+    { OSSL_OP_KEYMGMT, ibmca_rsa_keymgmt },
+    { OSSL_OP_ASYM_CIPHER, ibmca_rsa_asym_cipher },
+    { OSSL_OP_SIGNATURE, ibmca_rsa_signature },
+    { 0, NULL }
+};
+
+static const unsigned int ica_ec_mech[] = {
+    EC_DH,
+    EC_DSA_SIGN,
+    EC_DSA_VERIFY,
+    EC_KGEN,
+    0
+};
+
+static const  struct ibmca_mech_algorithm ibmca_ec_algorithms[] = {
+    { OSSL_OP_KEYMGMT, ibmca_ec_keymgmt },
+    { OSSL_OP_SIGNATURE, ibmca_ec_signature },
+    { OSSL_OP_KEYEXCH, ibmca_ec_keyexch },
+    { 0, NULL }
+};
+
+static const  struct ibmca_mech_algorithm ibmca_dh_algorithms[] = {
+    { OSSL_OP_KEYMGMT, ibmca_dh_keymgmt },
+    { OSSL_OP_KEYEXCH, ibmca_dh_keyexch },
+    { 0, NULL }
+};
+
+static const unsigned int ica_dh_mech[] = {
+    RSA_ME, /* DH uses RSA mod-expo for derive */
+    0
+};
+
+static const struct ibmca_ica_mech_info ica_mech_infos[] = {
+    { IBMCA_CONFIG_ALGO_RSA, ica_rsa_mech, ibmca_rsa_algorithms, NULL },
+    { IBMCA_CONFIG_ALGO_EC, ica_ec_mech, ibmca_ec_algorithms,
+                                                    ibmca_ec_capabilities },
+    { IBMCA_CONFIG_ALGO_DH, ica_dh_mech, ibmca_dh_algorithms,
+                                                    ibmca_dh_capabilities },
+    { NULL, NULL, NULL, NULL }
+};
+
+static const OSSL_ITEM ibmca_reason_strings[] = {
+    { IBMCA_ERR_INTERNAL_ERROR, "Internal error" },
+    { IBMCA_ERR_MALLOC_FAILED,  "Memory allocation failed" },
+    { IBMCA_ERR_INVALID_PARAM,  "Invalid parameter encountered" },
+    { IBMCA_ERR_CONFIGURATION,  "Provider configuration error" },
+    { IBMCA_ERR_LIBICA_FAILED,  "A libica function returned an error" },
+    { IBMCA_ERR_SIGNATURE_BAD,  "Signature bad" },
+    { IBMCA_ERR_EC_CURVE_NOT_SUPPORTED, "EC curve not supported" },
+    { 0, NULL }
+};
+
+void ibmca_debug_print(struct ibmca_prov_ctx *provctx, const char *func,
+                       const char *fmt, ...)
+{
+    char tmp_fmt[500];
+    char time_buf[200] = "";
+    va_list ap;
+    time_t t;
+    struct tm *tm;
+    pid_t old_pid;
+
+    t = time(NULL);
+    tm = localtime(&t);
+    if (strftime(time_buf, sizeof(time_buf), "%m/%d/%Y %H:%M:%S", tm) == 0)
+        return;
+
+    if (snprintf(tmp_fmt, sizeof(tmp_fmt), "DBG: %s %u %s: %s",
+                 time_buf, (unsigned int)gettid(), func, fmt) >
+                                                    (int)sizeof(tmp_fmt))
+        return;
+
+    (void)pthread_mutex_lock(&provctx->debug_mutex);
+    /* no error checking here: if lock fails print trace msg anyway */
+
+    if (provctx->debug_file != NULL && getpid() != provctx->debug_pid) {
+        /* process was forked off from parent process: open new trace file */
+        old_pid = provctx->debug_pid;
+        fclose(provctx->debug_file);
+        provctx->debug_file = NULL;
+
+        /* avoid recursive call from ibmca_config_debug/ibmca_config_bool */
+        provctx->debug = false;
+        if (ibmca_config_debug(provctx, IBMCA_CONF_DEBUG, "on") == 0)
+            goto out;
+
+        fprintf(provctx->debug_file,
+                "*** Forked off from parent process %u ***\n", old_pid);
+    }
+
+    va_start(ap, fmt);
+    if (provctx->debug_file != NULL) {
+        vfprintf(provctx->debug_file, tmp_fmt, ap);
+        fputc('\n', provctx->debug_file);
+        fflush(provctx->debug_file);
+    } else {
+        vwarnx(tmp_fmt, ap);
+    }
+    va_end(ap);
+
+out:
+    pthread_mutex_unlock(&provctx->debug_mutex);
+}
+
+void ibmca_put_error(const struct ibmca_prov_ctx *provctx, int err,
+                     const char *file, int line, const char *func,
+                     char *fmt, ...)
+{
+    va_list ap;
+
+    if (provctx == NULL)
+        return;
+
+    va_start(ap, fmt);
+    provctx->c_new_error(provctx->handle);
+    provctx->c_set_error_debug(provctx->handle, file, line, func);
+    provctx->c_vset_error(provctx->handle, err, fmt, ap);
+    va_end(ap);
+}
+
+char *ibmca_strdup(const struct ibmca_prov_ctx *provctx, const char *str,
+                   const char* file, int line)
+{
+    char *ret;
+
+    if (str == NULL || provctx == NULL)
+        return NULL;
+
+    ret = provctx->c_malloc(strlen(str) + 1, file, line);
+    if (ret != NULL)
+        strcpy(ret, str);
+
+    return ret;
+}
+
+void *ibmca_memdup(const struct ibmca_prov_ctx *provctx, const void *data,
+                   size_t size, const char* file, int line)
+{
+    void *ret;
+
+    if (data == NULL || size >= INT_MAX)
+        return NULL;
+
+    ret = provctx->c_malloc(size, file, line);
+    if (ret != NULL)
+        memcpy(ret, data, size);
+
+    return ret;
+}
+
+void *ibmca_secure_memdup(const struct ibmca_prov_ctx *provctx,
+                          const void *data, size_t size,
+                          const char* file, int line)
+{
+    void *ret;
+
+    if (data == NULL || size >= INT_MAX)
+        return NULL;
+
+    ret = provctx->c_secure_malloc(size, file, line);
+    if (ret != NULL)
+        memcpy(ret, data, size);
+
+    return ret;
+}
+
+int ibmca_param_build_set_bn(const struct ibmca_prov_ctx *provctx,
+                             OSSL_PARAM_BLD *bld, OSSL_PARAM *p,
+                             const char *key, const BIGNUM *bn)
+{
+    char *str;
+
+    if (bn == NULL)
+        return 0;
+
+    if (bld != NULL) {
+        if (OSSL_PARAM_BLD_push_BN(bld, key, bn) == 0) {
+            put_error_ctx(provctx, IBMCA_ERR_INTERNAL_ERROR,
+                          "Failed to return param '%s'", key);
+            return 0;
+        }
+        goto out;
+    }
+
+    p = OSSL_PARAM_locate(p, key);
+    if (p == NULL)
+        return 1;
+
+    if (OSSL_PARAM_set_BN(p, bn) == 0) {
+        put_error_ctx(provctx, IBMCA_ERR_INTERNAL_ERROR,
+                      "Failed to return param '%s'", key);
+        return 0;
+    }
+
+out:
+    if (provctx->debug) {
+        if (BN_get_flags(bn, BN_FLG_SECURE)) {
+            ibmca_debug_ctx(provctx,
+                            "param '%s': [sensitive value omitted] (%d bits)",
+                            key, BN_num_bits(bn));
+        } else {
+            str = BN_bn2hex(bn);
+            ibmca_debug_ctx(provctx, "param '%s': 0x%s (%d bits)", key, str,
+                            BN_num_bits(bn));
+            P_FREE(provctx, str);
+        }
+    }
+
+    return 1;
+}
+
+int ibmca_param_build_set_int(const struct ibmca_prov_ctx *provctx,
+                              OSSL_PARAM_BLD *bld, OSSL_PARAM *p,
+                              const char *key, int val)
+{
+    if (bld != NULL) {
+        if (OSSL_PARAM_BLD_push_int(bld, key, val) == 0) {
+            put_error_ctx(provctx, IBMCA_ERR_INTERNAL_ERROR,
+                          "Failed to return param '%s'", key);
+            return 0;
+        }
+        goto out;
+    }
+
+    p = OSSL_PARAM_locate(p, key);
+    if (p == NULL)
+        return 1;
+
+    if (OSSL_PARAM_set_int(p, val) == 0) {
+        put_error_ctx(provctx, IBMCA_ERR_INTERNAL_ERROR,
+                      "Failed to return param '%s'", key);
+        return 0;
+    }
+
+out:
+    ibmca_debug_ctx(provctx, "param '%s': %d", key, val);
+    return 1;
+}
+
+int ibmca_param_build_set_uint(const struct ibmca_prov_ctx *provctx,
+                               OSSL_PARAM_BLD *bld, OSSL_PARAM *p,
+                               const char *key, unsigned int val)
+{
+    if (bld != NULL) {
+        if (OSSL_PARAM_BLD_push_uint(bld, key, val) == 0) {
+            put_error_ctx(provctx, IBMCA_ERR_INTERNAL_ERROR,
+                          "Failed to return param '%s'", key);
+            return 0;
+        }
+        goto out;
+    }
+
+    p = OSSL_PARAM_locate(p, key);
+    if (p == NULL)
+        return 1;
+
+    if (OSSL_PARAM_set_uint(p, val) == 0) {
+        put_error_ctx(provctx, IBMCA_ERR_INTERNAL_ERROR,
+                      "Failed to return param '%s'", key);
+        return 0;
+    }
+
+out:
+    ibmca_debug_ctx(provctx, "param '%s': %u", key, val);
+    return 1;
+}
+
+int ibmca_param_build_set_utf8(const struct ibmca_prov_ctx *provctx,
+                               OSSL_PARAM_BLD *bld, OSSL_PARAM *p,
+                               const char *key, const char *str)
+{
+    if (str == NULL)
+        return 0;
+
+    if (bld != NULL) {
+        if (OSSL_PARAM_BLD_push_utf8_string(bld, key, str, 0) == 0) {
+            put_error_ctx(provctx, IBMCA_ERR_INTERNAL_ERROR,
+                          "Failed to return param '%s'", key);
+            return 0;
+        }
+        goto out;
+    }
+
+    p = OSSL_PARAM_locate(p, key);
+    if (p == NULL)
+        return 1;
+
+    if (OSSL_PARAM_set_utf8_string(p, str) == 0) {
+        put_error_ctx(provctx, IBMCA_ERR_INTERNAL_ERROR,
+                      "Failed to return param '%s'", key);
+        return 0;
+    }
+
+out:
+    ibmca_debug_ctx(provctx, "param '%s': '%s'", key, str);
+    return 1;
+}
+
+int ibmca_param_build_set_octet_ptr(const struct ibmca_prov_ctx *provctx,
+                                    OSSL_PARAM_BLD *bld, OSSL_PARAM *p,
+                                    const char *key, const void *val,
+                                    size_t len)
+{
+    if (val == NULL)
+        return 0;
+
+    if (bld != NULL) {
+        if (OSSL_PARAM_BLD_push_octet_string(bld, key, val, len) == 0) {
+            put_error_ctx(provctx, IBMCA_ERR_INTERNAL_ERROR,
+                          "Failed to return param '%s'", key);
+            return 0;
+        }
+        goto out;
+    }
+
+    p = OSSL_PARAM_locate(p, key);
+    if (p == NULL)
+        return 1;
+
+    if (OSSL_PARAM_set_octet_string(p, val, len) == 0) {
+        put_error_ctx(provctx, IBMCA_ERR_INTERNAL_ERROR,
+                      "Failed to return param '%s'", key);
+        return 0;
+    }
+
+out:
+    ibmca_debug_ctx(provctx, "param '%s': [octet string] (%lu bytes)", key,
+                    len);
+    return 1;
+}
+
+int ibmca_param_build_set_size_t(const struct ibmca_prov_ctx *provctx,
+                                 OSSL_PARAM_BLD *bld, OSSL_PARAM *p,
+                                 const char *key, size_t val)
+{
+    if (bld != NULL) {
+        if (OSSL_PARAM_BLD_push_size_t(bld, key, val) == 0) {
+            put_error_ctx(provctx, IBMCA_ERR_INTERNAL_ERROR,
+                          "Failed to return param '%s'", key);
+            return 0;
+        }
+        goto out;
+    }
+
+    p = OSSL_PARAM_locate(p, key);
+    if (p == NULL)
+        return 1;
+
+    if (OSSL_PARAM_set_size_t(p, val) == 0) {
+        put_error_ctx(provctx, IBMCA_ERR_INTERNAL_ERROR,
+                      "Failed to return param '%s'", key);
+        return 0;
+    }
+
+out:
+    ibmca_debug_ctx(provctx, "param '%s': %lu", key, val);
+    return 1;
+}
+
+int ibmca_param_get_bn(const struct ibmca_prov_ctx *provctx,
+                       const OSSL_PARAM params[], const char *key, BIGNUM **bn)
+{
+    const OSSL_PARAM *p;
+
+    if (bn == NULL)
+        return 0;
+
+    p = OSSL_PARAM_locate_const(params, key);
+    if (p == NULL)
+        return -1;
+
+    if (OSSL_PARAM_get_BN(p, bn) == 0 || *bn == NULL) {
+        put_error_ctx(provctx, IBMCA_ERR_INVALID_PARAM,
+                      "Failed to get param '%s'", key);
+        return 0;
+    }
+
+    if (provctx->debug) {
+        if (BN_get_flags(*bn, BN_FLG_SECURE)) {
+            ibmca_debug_ctx(provctx,
+                            "param '%s': [sensitive value omitted] (%d bits)",
+                            key, BN_num_bits(*bn));
+        } else {
+            char *str = BN_bn2hex(*bn);
+            ibmca_debug_ctx(provctx, "param '%s': 0x%s (%d bits)", key,
+                            str != NULL ? str : "", BN_num_bits(*bn));
+            P_FREE(provctx, str);
+        }
+    }
+
+    return 1;
+}
+
+int ibmca_param_get_int(const struct ibmca_prov_ctx *provctx,
+                        const OSSL_PARAM params[], const char *key, int *val)
+{
+    const OSSL_PARAM *p;
+
+    p = OSSL_PARAM_locate_const(params, key);
+    if (p == NULL)
+        return -1;
+
+    if (OSSL_PARAM_get_int(p, val) == 0) {
+        put_error_ctx(provctx, IBMCA_ERR_INVALID_PARAM,
+                      "Failed to get param '%s'", key);
+        return 0;
+    }
+
+    ibmca_debug_ctx(provctx, "param '%s': %d", key, *val);
+    return 1;
+}
+
+int ibmca_param_get_uint(const struct ibmca_prov_ctx *provctx,
+                         const OSSL_PARAM params[], const char *key,
+                         unsigned int *val)
+{
+    const OSSL_PARAM *p;
+
+    p = OSSL_PARAM_locate_const(params, key);
+    if (p == NULL)
+        return -1;
+
+    if (OSSL_PARAM_get_uint(p, val) == 0) {
+        put_error_ctx(provctx, IBMCA_ERR_INVALID_PARAM,
+                      "Failed to get param '%s'", key);
+        return 0;
+    }
+
+    ibmca_debug_ctx(provctx, "param '%s': %u", key, *val);
+    return 1;
+}
+
+int ibmca_param_get_size_t(const struct ibmca_prov_ctx *provctx,
+                           const OSSL_PARAM params[], const char *key,
+                           size_t *val)
+{
+    const OSSL_PARAM *p;
+
+    p = OSSL_PARAM_locate_const(params, key);
+    if (p == NULL)
+        return -1;
+
+    if (OSSL_PARAM_get_size_t(p, val) == 0) {
+        put_error_ctx(provctx, IBMCA_ERR_INVALID_PARAM,
+                      "Failed to get param '%s'", key);
+        return 0;
+    }
+
+    ibmca_debug_ctx(provctx, "param '%s': %lu", key, *val);
+    return 1;
+}
+
+int ibmca_param_get_utf8(const struct ibmca_prov_ctx *provctx,
+                         const OSSL_PARAM params[], const char *key,
+                         const char **str)
+{
+    const OSSL_PARAM *p;
+
+    p = OSSL_PARAM_locate_const(params, key);
+    if (p == NULL)
+        return -1;
+
+    if (p->data_type == OSSL_PARAM_UTF8_STRING) {
+        *str = p->data;
+    } else if (OSSL_PARAM_get_utf8_ptr(p, str) == 0) {
+        put_error_ctx(provctx, IBMCA_ERR_INVALID_PARAM,
+                      "Failed to get param '%s'", key);
+        return 0;
+    }
+
+    ibmca_debug_ctx(provctx, "param '%s': '%s'", key, *str);
+    return 1;
+}
+
+int ibmca_param_get_octet_string(const struct ibmca_prov_ctx *provctx,
+                                 const OSSL_PARAM params[], const char *key,
+                                 void **val, size_t *len)
+{
+    const OSSL_PARAM *p;
+
+    p = OSSL_PARAM_locate_const(params, key);
+    if (p == NULL)
+        return -1;
+
+    if (OSSL_PARAM_get_octet_string(p, val, 0, len) == 0) {
+        put_error_ctx(provctx, IBMCA_ERR_INVALID_PARAM,
+                      "Failed to get param '%s'", key);
+        return 0;
+    }
+
+    ibmca_debug_ctx(provctx, "param '%s': [octet string] (%lu bytes)",
+                    key, *len);
+    return 1;
+}
+
+static void ibmca_teardown(void *vprovctx)
+{
+    struct ibmca_prov_ctx *provctx = vprovctx;
+    int i;
+
+    if (provctx == NULL)
+        return;
+
+    ibmca_debug_ctx(provctx, "provctx: %p", provctx);
+
+    if (provctx->ica_adapter > DRIVER_NOT_LOADED)
+        ica_close_adapter(provctx->ica_adapter);
+
+    for (i = 0; i < OSSL_OP__HIGHEST; i++) {
+        if (provctx->algorithms[i] != NULL)
+            P_FREE(provctx, provctx->algorithms[i]);
+    }
+
+    if (provctx->algo_enabled != NULL)
+        P_FREE(provctx, provctx->algo_enabled);
+
+    if (provctx->property_def != NULL)
+        P_FREE(provctx, provctx->property_def);
+
+    if (provctx->fallback_property_query != NULL &&
+        provctx->fallback_property_query != provctx->fallback_props_conf)
+        P_FREE(provctx, provctx->fallback_property_query);
+
+    if (provctx->libctx != NULL)
+        OSSL_LIB_CTX_free(provctx->libctx);
+
+    if (provctx->debug_file != NULL)
+        fclose(provctx->debug_file);
+
+    pthread_mutex_destroy(&provctx->debug_mutex);
+
+    P_FREE(provctx, provctx);
+#if HAVE_DECL_ICA_CLEANUP == 1
+    ica_cleanup();
+#endif
+}
+
+static const OSSL_PARAM ibmca_param_types[] = {
+    OSSL_PARAM_DEFN(OSSL_PROV_PARAM_NAME, OSSL_PARAM_UTF8_PTR, NULL, 0),
+    OSSL_PARAM_DEFN(OSSL_PROV_PARAM_VERSION, OSSL_PARAM_UTF8_PTR, NULL, 0),
+    OSSL_PARAM_DEFN(OSSL_PROV_PARAM_BUILDINFO, OSSL_PARAM_UTF8_PTR, NULL, 0),
+    OSSL_PARAM_DEFN(OSSL_PROV_PARAM_STATUS, OSSL_PARAM_INTEGER, NULL, 0),
+    OSSL_PARAM_END
+};
+
+static const OSSL_PARAM *ibmca_gettable_params(void *vprovctx)
+{
+    const struct ibmca_prov_ctx *provctx = vprovctx;
+
+    if (provctx == NULL)
+        return NULL;
+
+    ibmca_debug_ctx(provctx, "provctx: %p", provctx);
+    return ibmca_param_types;
+}
+
+static int ibmca_get_params(void *vprovctx, OSSL_PARAM params[])
+{
+    const struct ibmca_prov_ctx *provctx = vprovctx;
+    OSSL_PARAM *p;
+
+    if (provctx == NULL)
+        return 0;
+
+    ibmca_debug_ctx(provctx, "provctx: %p", provctx);
+
+    p = OSSL_PARAM_locate(params, OSSL_PROV_PARAM_NAME);
+    if (p != NULL && !OSSL_PARAM_set_utf8_ptr(p, provctx->name)) {
+        put_error_ctx(provctx, IBMCA_ERR_INTERNAL_ERROR,
+                      "OSSL_PARAM_set_utf8_ptr failed");
+        return 0;
+    }
+    p = OSSL_PARAM_locate(params, OSSL_PROV_PARAM_VERSION);
+    if (p != NULL && !OSSL_PARAM_set_utf8_ptr(p, IBMCA_VERSION)) {
+        put_error_ctx(provctx, IBMCA_ERR_INTERNAL_ERROR,
+                      "OSSL_PARAM_set_utf8_ptr failed");
+    return 0;
+    }
+    p = OSSL_PARAM_locate(params, OSSL_PROV_PARAM_BUILDINFO);
+    if (p != NULL && !OSSL_PARAM_set_utf8_ptr(p, IBMCA_VERSION)) {
+        put_error_ctx(provctx, IBMCA_ERR_INTERNAL_ERROR,
+                      "OSSL_PARAM_set_utf8_ptr failed");
+        return 0;
+    }
+    p = OSSL_PARAM_locate(params, OSSL_PROV_PARAM_STATUS);
+    if (p != NULL && !OSSL_PARAM_set_int(p, 1)) {
+        put_error_ctx(provctx, IBMCA_ERR_INTERNAL_ERROR,
+                      "OSSL_PARAM_set_int failed");
+        return 0;
+    }
+
+    return 1;
+}
+
+static const OSSL_ALGORITHM *ibmca_query(void *vprovctx, int operation_id,
+                                         int *no_cache)
+{
+    const struct ibmca_prov_ctx *provctx = vprovctx;
+
+    if (provctx == NULL)
+        return NULL;
+
+    *no_cache = 0;
+
+    ibmca_debug_ctx(provctx, "provctx: %p operation_id: %d", provctx,
+                    operation_id);
+
+    if (operation_id < 0 || operation_id > OSSL_OP__HIGHEST)
+        return NULL;
+
+    ibmca_debug_ctx(provctx, "algorithms: %p",
+                    provctx->algorithms[operation_id]);
+
+    return provctx->algorithms[operation_id];
+}
+
+static const OSSL_ITEM *ibmca_get_reason_strings(void *vprovctx)
+{
+    const struct ibmca_prov_ctx *provctx = vprovctx;
+
+    ibmca_debug_ctx(provctx, "provctx: %p", provctx);
+    return ibmca_reason_strings;
+}
+
+static int ibmca_prov_get_capabilities(void *vprovctx, const char *capability,
+                                       OSSL_CALLBACK *cb, void *arg)
+{
+    const struct ibmca_prov_ctx *provctx = vprovctx;
+    int i, k, rc = 0;
+    const struct ibmca_mech_capability *cap;
+
+    if (capability == NULL || cb == NULL)
+        return 0;
+
+    ibmca_debug_ctx(provctx, "provctx: %p capability: %s", provctx,
+                    capability);
+
+    for (i = 0; ica_mech_infos[i].algo != NULL; i++) {
+        for (cap = ica_mech_infos[i].capabilities;
+             cap != NULL && cap->capability != NULL; cap++) {
+            if (strcasecmp(capability, cap->capability) != 0)
+                continue;
+
+            ibmca_debug_ctx(provctx, "algorithm '%s' supports this capability",
+                            ica_mech_infos[i].algo);
+
+            for (k = 0; cap->details[k] != NULL; k++) {
+                rc = cb(cap->details[k], arg);
+                if (rc == 0)
+                    break;
+            }
+
+            if (rc == 0)
+                break;
+        }
+    }
+
+    return rc;
+}
+
+static int ibmca_prov_self_tests(void *vprovctx)
+{
+    const struct ibmca_prov_ctx *provctx = vprovctx;
+
+    ibmca_debug_ctx(provctx, "provctx: %p", provctx);
+
+    if (provctx->fips == 0)
+        return 1;
+
+    /*
+     * libica performs FIPS self-tests if running in FIPS mode. If any of
+     * the fips tests failed, additional flags are on.
+     */
+    return (provctx->ica_fips_status == ICA_FIPS_MODE) ? 1 : 0;
+}
+
+static const OSSL_DISPATCH ibmca_dispatch_table[] = {
+    { OSSL_FUNC_PROVIDER_TEARDOWN, (void (*)(void))ibmca_teardown },
+    { OSSL_FUNC_PROVIDER_GETTABLE_PARAMS,
+                (void (*)(void))ibmca_gettable_params },
+    { OSSL_FUNC_PROVIDER_GET_PARAMS, (void (*)(void))ibmca_get_params },
+    { OSSL_FUNC_PROVIDER_QUERY_OPERATION, (void (*)(void))ibmca_query },
+    { OSSL_FUNC_PROVIDER_GET_REASON_STRINGS,
+                (void (*)(void))ibmca_get_reason_strings },
+    { OSSL_FUNC_PROVIDER_GET_CAPABILITIES,
+                (void (*)(void))ibmca_prov_get_capabilities },
+    { OSSL_FUNC_PROVIDER_SELF_TEST,
+                (void (*)(void))ibmca_prov_self_tests },
+    { 0, NULL }
+};
+
+static int ibmca_config_const_string(struct ibmca_prov_ctx *provctx,
+                                     const char *key, const char *value,
+                                     const char **string)
+{
+    ibmca_debug_ctx(provctx, "provctx: %p key: '%s' value: '%s'",
+                    provctx, key, value);
+
+    *string = value;
+
+    return 1;
+}
+
+static int ibmca_config_module_filename(struct ibmca_prov_ctx *provctx,
+                                        const char *key, const char *value)
+{
+    return ibmca_config_const_string(provctx, key, value,
+                                     &provctx->module_filename);
+}
+
+static int ibmca_config_openssl_version(struct ibmca_prov_ctx *provctx,
+                                        const char *key, const char *value)
+{
+    return ibmca_config_const_string(provctx, key, value,
+                                     &provctx->openssl_version);
+}
+
+static int ibmca_config_fallback_props(struct ibmca_prov_ctx *provctx,
+                                       const char *key, const char *value)
+{
+    return ibmca_config_const_string(provctx, key, value,
+                                     &provctx->fallback_props_conf);
+}
+
+static int ibmca_config_bool(struct ibmca_prov_ctx *provctx,
+                             const char *key, const char *value, bool *bval)
+{
+    ibmca_debug_ctx(provctx, "provctx: %p key: '%s' value: '%s'",
+                    provctx, key, value);
+
+    if (strcasecmp(value, "on") == 0 ||
+        strcasecmp(value, "true") == 0 ||
+        strcasecmp(value, "yes") == 0 ||
+        strcasecmp(value, "1") == 0) {
+        *bval = true;
+    } else if (strcasecmp(value, "off") == 0 ||
+        strcasecmp(value, "false") == 0 ||
+        strcasecmp(value, "no") == 0 ||
+        strcasecmp(value, "0") == 0) {
+        *bval = false;
+    } else {
+        put_error_ctx(provctx, IBMCA_ERR_INVALID_PARAM,
+                      "Failed to parse config value: '%s' = '%s'", key, value);
+        return 0;
+    }
+
+    return 1;
+}
+
+static int ibmca_config_debug(struct ibmca_prov_ctx *provctx,
+                              const char *key, const char *value)
+{
+    char debug_file[PATH_MAX];
+    char prov_name[PATH_MAX];
+    char *p;
+
+    /*
+     * If debug is already on (e.g. due to IBMCA_DEBUG environment variable)
+     * do not override the setting.
+     */
+    if (provctx->debug == true)
+        return 1;
+
+    if (strcasecmp(value, "stderr") == 0) {
+        provctx->debug = true;
+        return 1;
+    }
+
+    if (ibmca_config_bool(provctx, key, value, &provctx->debug) == 0)
+        return 0;
+
+    if (provctx->debug == true) {
+        provctx->debug_pid = getpid();
+
+        strncpy(prov_name, provctx->name, sizeof(prov_name) - 1);
+        prov_name[sizeof(prov_name) - 1] = '\0';
+        while ((p = strchr(prov_name, '/')) != NULL)
+            *p = '_';
+
+        if (snprintf(debug_file, sizeof(debug_file), "%s/trace-%s.%d",
+                     provctx->debug_path != NULL ? provctx->debug_path :
+                                                   IBMCA_LOGDIR,
+                     prov_name, provctx->debug_pid)
+                                        >= (int)sizeof(debug_file)) {
+            put_error_ctx(provctx, IBMCA_ERR_INTERNAL_ERROR,
+                          "IBMCA_LOGDIR too long: '%s'", IBMCA_LOGDIR);
+            return 0;
+        }
+
+        provctx->debug_file = fopen(debug_file, "a");
+        if (provctx->debug_file == NULL) {
+            put_error_ctx(provctx, IBMCA_ERR_INTERNAL_ERROR,
+                          "Failed to open trace file: '%s': %s - tracing to stderr instead",
+                          debug_file, strerror(errno));
+            return 1;
+        }
+
+        fprintf(provctx->debug_file,
+                "*** Trace activated for IBMCA version %s ***\n",
+                IBMCA_VERSION);
+    }
+
+    return 1;
+}
+
+static int ibmca_config_debug_path(struct ibmca_prov_ctx *provctx,
+                                   const char *key, const char *value)
+{
+    /*
+     * If the debug path is already set (e.g. due to IBMCA_DEBUG_PATH
+     * environment variable) do not override the setting.
+     */
+    if (provctx->debug_path != NULL)
+        return 1;
+
+    return ibmca_config_const_string(provctx, key, value,
+                                     &provctx->debug_path);
+}
+
+static int ibmca_config_fips(struct ibmca_prov_ctx *provctx,
+                             const char *key, const char *value)
+{
+    if (ibmca_config_bool(provctx, key, value, &provctx->fips) == 0)
+        return 0;
+
+    provctx->fips_configured = true;
+    return 1;
+}
+
+static int ibmca_config_algorithms(struct ibmca_prov_ctx *provctx,
+                                   const char *key, const char *value)
+{
+    char *val, *tok;
+    int i, rc = 1;
+    bool found;
+
+    ibmca_debug_ctx(provctx, "provctx: %p key: '%s' value: '%s'",
+                    provctx, key, value);
+
+    val = P_STRDUP(provctx, value);
+    if (val == NULL) {
+        put_error_ctx(provctx, IBMCA_ERR_MALLOC_FAILED,
+                      "Failed to strdup config value");
+        return 0;
+    }
+
+    for (i = 0; ica_mech_infos[i].algo != NULL; i++)
+        provctx->algo_enabled[i] = false;
+
+    for (tok = strtok(val, ","); tok != NULL; tok = strtok(NULL, ",")) {
+        ibmca_debug_ctx(provctx, "algorithm: '%s'", tok);
+
+        if (strcasecmp(tok, IBMCA_CONFIG_ALGO_ALL) == 0) {
+            for (i = 0; ica_mech_infos[i].algo != NULL; i++)
+                provctx->algo_enabled[i] = true;
+
+            continue;
+        }
+
+        for (i = 0, found = false; ica_mech_infos[i].algo != NULL; i++) {
+            if (strcasecmp(tok, ica_mech_infos[i].algo) == 0) {
+                provctx->algo_enabled[i] = true;
+                found = true;
+                break;
+            }
+        }
+        if (found)
+            continue;
+
+        put_error_ctx(provctx, IBMCA_ERR_CONFIGURATION,
+                      "Unknown algorithm name '%s' in configuration for provider '%s'",
+                      tok, provctx->name);
+        rc = 0;
+        break;
+    }
+
+    P_FREE(provctx, val);
+    return rc;
+}
+
+static int ibmca_get_configuration(struct ibmca_prov_ctx *provctx)
+{
+    OSSL_PARAM params[NUM_CONFIG_ITEMS + 1];
+    char *param_ptrs[NUM_CONFIG_ITEMS];
+    const char *value;
+    int i, num;
+
+    ibmca_debug_ctx(provctx, "provctx: %p", provctx);
+
+    for (num = 0; ica_mech_infos[num].algo != NULL; num++)
+        ;
+    provctx->algo_enabled = P_ZALLOC(provctx, num * sizeof(bool));
+    if (provctx->algo_enabled == NULL) {
+        put_error_ctx(provctx, IBMCA_ERR_MALLOC_FAILED,
+                      "Failed to allocate memory for algorithm list");
+        return 0;
+    }
+    for (i = 0; ica_mech_infos[i].algo != NULL; i++)
+        provctx->algo_enabled[i] = true;
+
+    for (i = 0; i < (int)NUM_CONFIG_ITEMS; i++)
+        params[i] = OSSL_PARAM_construct_utf8_ptr(config_items[i].key,
+                                                  &param_ptrs[i],
+                                                  sizeof(param_ptrs[i]));
+    params[NUM_CONFIG_ITEMS] = OSSL_PARAM_construct_end();
+
+    if (provctx->c_get_params(provctx->handle, params) != 1) {
+        put_error_ctx(provctx, IBMCA_ERR_INTERNAL_ERROR,
+                      "Failed to get configuration parameters for provider '%s'",
+                      provctx->name);
+        return 0;
+    }
+
+    for (i = 0; i < (int)NUM_CONFIG_ITEMS; i++) {
+        if (!OSSL_PARAM_modified(&params[i]))
+            continue;
+
+        if (OSSL_PARAM_get_utf8_ptr(&params[i], &value) != 1) {
+            put_error_ctx(provctx, IBMCA_ERR_INTERNAL_ERROR,
+                          "Failed to get configuration parameter '%s' for provider '%s'",
+                          params[i].key, provctx->name);
+            return 0;
+        }
+
+        if (config_items[i].func(provctx, params[i].key, value) != 1)
+            return 0;
+    }
+
+    return 1;
+}
+
+static bool ibmca_libica_mechs_supported(const unsigned int *mechs,
+                                         libica_func_list_element *mech_list,
+                                         unsigned int mech_len)
+{
+    unsigned int i, k;
+    bool found;
+
+    for (i = 0; mechs[i] != 0; i++) {
+        for (k = 0, found = false; k < mech_len; k++) {
+            if (mech_list[k].mech_mode_id != mechs[i])
+                continue;
+
+            if (mech_list[k].flags &
+                (ICA_FLAG_SW | ICA_FLAG_SHW | ICA_FLAG_DHW))
+                found = true;
+
+            break;
+        }
+
+        if (!found)
+            return false;
+    }
+
+    return true;
+}
+
+static int ibmca_libica_init(struct ibmca_prov_ctx *provctx)
+{
+    unsigned int mech_len, i;
+    libica_func_list_element *mech_list = NULL;
+    int rc, ret = 1;
+
+    ibmca_debug_ctx(provctx, "provctx: %p", provctx);
+
+    ica_set_fallback_mode(0);
+
+    provctx->ica_fips_status = ica_fips_status();
+    ibmca_debug_ctx(provctx, "ica_fips_status: %d", provctx->ica_fips_status);
+
+    if (provctx->fips_configured == false) {
+        /* FIPS not configured, auto-detect from libica */
+        provctx->fips = (provctx->ica_fips_status == ICA_FIPS_MODE);
+    } else if (provctx->fips == true) {
+        /* Ensure libica is also running in fips mode */
+        if (provctx->ica_fips_status != ICA_FIPS_MODE) {
+            put_error_ctx(provctx, IBMCA_ERR_CONFIGURATION,
+                          "'fips=yes' is configured, but libica is not running in FIPS mode");
+            syslog(LOG_ERR, "IBMCA provider: 'fips=yes' is configured, but libica is not running in FIPS mode");
+            return 0;
+        }
+    }
+    ibmca_debug_ctx(provctx, "fips: %d", provctx->fips);
+
+    rc = ica_open_adapter(&provctx->ica_adapter);
+    if (rc != 0) {
+        put_error_ctx(provctx, IBMCA_ERR_LIBICA_FAILED,
+                      "ica_open_adapter failed: %s", strerror(rc));
+        syslog(LOG_ERR, "IBMCA provider: ica_open_adapter failed: %s", strerror(rc));
+        provctx->ica_adapter = DRIVER_NOT_LOADED;
+        return 0;
+    }
+
+    ibmca_debug_ctx(provctx, "ica_adapter: %p", provctx->ica_adapter);
+
+    rc = ica_get_functionlist(NULL, &mech_len);
+    if (rc != 0) {
+        put_error_ctx(provctx, IBMCA_ERR_LIBICA_FAILED,
+                      "ica_get_functionlist failed: %s", strerror(rc));
+        return 0;
+    }
+
+    ibmca_debug_ctx(provctx, "mech_len: %u", mech_len);
+
+    mech_list = P_ZALLOC(provctx,
+                  sizeof(libica_func_list_element) * mech_len);
+    if (mech_list == NULL) {
+        put_error_ctx(provctx, IBMCA_ERR_MALLOC_FAILED,
+                      "Failed to allocate memory for mechanism list");
+        return 0;
+    }
+
+    rc = ica_get_functionlist(mech_list, &mech_len);
+    if (rc != 0) {
+        put_error_ctx(provctx, IBMCA_ERR_LIBICA_FAILED,
+                      "ica_get_functionlist failed: %s", strerror(rc));
+        ret = 0;
+        goto done;
+    }
+
+    for (i = 0; ica_mech_infos[i].algo != NULL; i++) {
+        if (!ibmca_libica_mechs_supported(ica_mech_infos[i].ica_mechs,
+                                          mech_list, mech_len)) {
+            ibmca_debug_ctx(provctx, "algo '%s' not supported by libica",
+                            ica_mech_infos[i].algo);
+            provctx->algo_enabled[i] = false;
+        }
+    }
+
+done:
+    P_FREE(provctx, mech_list);
+    return ret;
+}
+
+static int ibmca_add_algorithm(struct ibmca_prov_ctx *provctx,
+                               int operation,
+                               const char *names,
+                               const OSSL_DISPATCH *implementation,
+                               const char *description)
+{
+    unsigned int num;
+    OSSL_ALGORITHM *alg, *new_algs;
+
+    ibmca_debug_ctx(provctx, "provctx: %p operation: %d names: '%s'",
+                    provctx, operation, names);
+
+    for (alg = provctx->algorithms[operation], num = 0;
+         alg != NULL && alg->algorithm_names != NULL; alg++, num++)
+        ;
+
+    new_algs = P_REALLOC(provctx, provctx->algorithms[operation],
+                         (num + 2) * sizeof(OSSL_ALGORITHM));
+    if (new_algs == NULL) {
+        put_error_ctx(provctx, IBMCA_ERR_MALLOC_FAILED,
+                      "Failed to allocate memory for algorithm array");
+        return 0;
+    }
+
+    new_algs[num].algorithm_names = names;
+    new_algs[num].property_definition = provctx->property_def;
+    new_algs[num].implementation = implementation;
+    new_algs[num].algorithm_description = description;
+
+    memset(&new_algs[num + 1], 0, sizeof(OSSL_ALGORITHM));
+
+    provctx->algorithms[operation] = new_algs;
+
+    return 1;
+}
+
+static int ibmca_setup_algorithms(struct ibmca_prov_ctx *provctx)
+{
+    unsigned int i;
+    size_t len;
+    const struct ibmca_mech_algorithm *alg;
+    const OSSL_ALGORITHM *ossl_alg;
+
+    ibmca_debug_ctx(provctx, "provctx: %p", provctx);
+
+    len = strlen("provider=") + strlen(provctx->name) + 1;
+    if (provctx->fips)
+        len += strlen(",fips=yes");
+    provctx->property_def = P_ZALLOC(provctx, len);
+    if (provctx->property_def == NULL) {
+        put_error_ctx(provctx, IBMCA_ERR_MALLOC_FAILED,
+                      "Failed to allocate memory for property definition");
+        return 0;
+    }
+    sprintf(provctx->property_def, "provider=%s%s", provctx->name,
+            provctx->fips ? ",fips=yes" : "");
+
+    ibmca_debug_ctx(provctx, "property_def: '%s'", provctx->property_def);
+
+    if (provctx->fallback_props_conf != NULL) {
+        provctx->fallback_property_query =
+                                    (char *)provctx->fallback_props_conf;
+    } else {
+        /*
+         * Build a property query string for fall-back operations that excludes
+         * the IBMCA provider, since this would produce an endless loop.
+         */
+        len = strlen("provider!=") + strlen(provctx->name) + 1;
+        if (provctx->fips)
+            len += strlen(",fips=yes");
+        provctx->fallback_property_query = P_ZALLOC(provctx, len);
+        if (provctx->fallback_property_query == NULL) {
+            put_error_ctx(provctx, IBMCA_ERR_MALLOC_FAILED,
+                          "Failed to allocate property fallback query string");
+            return 0;
+        }
+        sprintf(provctx->fallback_property_query, "provider!=%s%s",
+                provctx->name, provctx->fips ? ",fips=yes" : "");
+    }
+
+    ibmca_debug_ctx(provctx, "fallback_property_query: '%s'",
+                    provctx->fallback_property_query);
+
+    for (i = 0; ica_mech_infos[i].algo != NULL; i++) {
+        ibmca_debug_ctx(provctx, "algorithm '%s' enabled: %d",
+                        ica_mech_infos[i].algo, provctx->algo_enabled[i]);
+
+        if (provctx->algo_enabled[i] == false)
+            continue;
+
+        for (alg = ica_mech_infos[i].algos; alg->operation != 0; alg++) {
+            for (ossl_alg = alg->algorithms; ossl_alg != NULL &&
+                    ossl_alg->algorithm_names != NULL; ossl_alg++) {
+                if (ibmca_add_algorithm(provctx, alg->operation,
+                    ossl_alg->algorithm_names,
+                    ossl_alg->implementation,
+                    ossl_alg->algorithm_description) != 1) {
+                    put_error_ctx(provctx, IBMCA_ERR_MALLOC_FAILED,
+                                  "Failed to algorithm '%s' for operation %d",
+                                  ossl_alg->algorithm_names, alg->operation);
+                    return 0;
+                }
+            }
+        }
+
+    }
+
+    return 1;
+}
+
+int OSSL_provider_init(const OSSL_CORE_HANDLE *handle,
+                       const OSSL_DISPATCH *in,
+                       const OSSL_DISPATCH **out,
+                       void **provctx)
+{
+    const OSSL_DISPATCH *dptc;
+    OSSL_FUNC_CRYPTO_zalloc_fn *c_zalloc = NULL;
+    OSSL_FUNC_CRYPTO_free_fn *c_free = NULL;
+    OSSL_FUNC_core_set_error_debug_fn *c_set_error_debug = NULL;
+    OSSL_FUNC_core_get_libctx_fn *c_get_libctx = NULL;
+    OSSL_FUNC_core_vset_error_fn *c_vset_error = NULL;
+    OSSL_FUNC_core_new_error_fn *c_new_error = NULL;
+    OSSL_FUNC_provider_name_fn *c_provider_name = NULL;
+    struct ibmca_prov_ctx *ctx;
+    char *val;
+    *provctx = NULL;
+
+    if (handle == NULL || in == NULL || out == NULL || provctx == NULL)
+        return 0;
+
+    for (dptc = in; dptc->function_id != 0; dptc++) {
+        switch (dptc->function_id) {
+        case OSSL_FUNC_CORE_GET_LIBCTX:
+            c_get_libctx = OSSL_FUNC_core_get_libctx(dptc);
+            break;
+        case OSSL_FUNC_CORE_NEW_ERROR:
+            c_new_error = OSSL_FUNC_core_new_error(dptc);
+            break;
+        case OSSL_FUNC_CORE_SET_ERROR_DEBUG:
+            c_set_error_debug = OSSL_FUNC_core_set_error_debug(dptc);
+            break;
+        case OSSL_FUNC_CORE_VSET_ERROR:
+            c_vset_error = OSSL_FUNC_core_vset_error(dptc);
+            break;
+        case OSSL_FUNC_CRYPTO_ZALLOC:
+            c_zalloc = OSSL_FUNC_CRYPTO_zalloc(dptc);
+            break;
+        case OSSL_FUNC_CRYPTO_FREE:
+            c_free = OSSL_FUNC_CRYPTO_free(dptc);
+            break;
+        case OSSL_FUNC_PROVIDER_NAME:
+            c_provider_name = OSSL_FUNC_provider_name(dptc);
+            break;
+        default:
+            /* Just ignore anything we don't understand for now */
+            break;
+        }
+    }
+
+    if (c_get_libctx == NULL || c_zalloc == NULL || c_free == NULL ||
+        c_new_error == NULL || c_set_error_debug == NULL ||
+        c_vset_error == NULL || c_provider_name == NULL)
+        return 0;
+
+    ctx = c_zalloc(sizeof(struct ibmca_prov_ctx), __FILE__, __LINE__);
+    if (ctx == NULL) {
+        c_new_error(handle);
+        c_set_error_debug(handle, __FILE__, __LINE__, __func__);
+        c_vset_error(handle, IBMCA_ERR_MALLOC_FAILED,
+                     "Failed to allocate provider context", NULL);
+        return 0;
+    }
+
+    ctx->handle = handle;
+    ctx->name = c_provider_name(handle);
+    ctx->c_get_libctx = c_get_libctx;
+    ctx->c_new_error = c_new_error;
+    ctx->c_set_error_debug = c_set_error_debug;
+    ctx->c_vset_error = c_vset_error;
+    ctx->c_zalloc = c_zalloc;
+    ctx->c_free = c_free;
+    ctx->ica_adapter = DRIVER_NOT_LOADED;
+
+    val = secure_getenv(IBMCA_DEBUG_PATH_ENVVAR);
+    if (val != NULL)
+        ibmca_config_debug_path(ctx, IBMCA_CONF_DEBUG_PATH, val);
+    val = getenv(IBMCA_DEBUG_ENVVAR);
+    if (val != NULL)
+        ibmca_config_debug(ctx, IBMCA_CONF_DEBUG, val);
+    if (pthread_mutex_init(&ctx->debug_mutex, NULL) != 0) {
+        put_error_ctx(ctx, IBMCA_ERR_INTERNAL_ERROR,
+                      "pthread_mutex_init failed for provider '%s': %s",
+                      ctx->name, strerror(errno));
+        c_free(ctx, __FILE__, __LINE__);
+        return 0;
+    }
+
+    ctx->libctx = OSSL_LIB_CTX_new_child(handle, in);
+    if (ctx->libctx == NULL) {
+        put_error_ctx(ctx, IBMCA_ERR_INTERNAL_ERROR,
+                      "OSSL_LIB_CTX_new_child failed for provider '%s'",
+                      ctx->name);
+        ibmca_teardown(ctx);
+        return 0;
+    }
+
+    for (dptc = in; dptc->function_id != 0; dptc++) {
+        switch (dptc->function_id) {
+        case OSSL_FUNC_CORE_GETTABLE_PARAMS:
+            ctx->c_gettable_params = OSSL_FUNC_core_gettable_params(dptc);
+            break;
+        case OSSL_FUNC_CORE_GET_PARAMS:
+            ctx->c_get_params = OSSL_FUNC_core_get_params(dptc);
+            break;
+        case OSSL_FUNC_CRYPTO_MALLOC:
+            ctx->c_malloc = OSSL_FUNC_CRYPTO_malloc(dptc);
+            break;
+        case OSSL_FUNC_CRYPTO_REALLOC:
+            ctx->c_realloc = OSSL_FUNC_CRYPTO_realloc(dptc);
+            break;
+        case OSSL_FUNC_CRYPTO_CLEAR_FREE:
+            ctx->c_clear_free = OSSL_FUNC_CRYPTO_clear_free(dptc);
+            break;
+        case OSSL_FUNC_CRYPTO_SECURE_MALLOC:
+            ctx->c_secure_malloc = OSSL_FUNC_CRYPTO_secure_malloc(dptc);
+            break;
+        case OSSL_FUNC_CRYPTO_SECURE_ZALLOC:
+            ctx->c_secure_zalloc = OSSL_FUNC_CRYPTO_secure_zalloc(dptc);
+            break;
+        case OSSL_FUNC_CRYPTO_SECURE_FREE:
+            ctx->c_secure_free = OSSL_FUNC_CRYPTO_secure_free(dptc);
+            break;
+        case OSSL_FUNC_CRYPTO_SECURE_CLEAR_FREE:
+            ctx->c_secure_clear_free = OSSL_FUNC_CRYPTO_secure_clear_free(dptc);
+            break;
+        case OSSL_FUNC_OPENSSL_CLEANSE:
+            ctx->c_cleanse = OSSL_FUNC_OPENSSL_cleanse(dptc);
+            break;
+        default:
+            /* Just ignore anything we don't understand  */
+            break;
+        }
+    }
+
+    if (ctx->c_gettable_params == NULL || ctx->c_get_params == NULL ||
+        ctx->c_malloc == NULL || ctx->c_realloc == NULL ||
+        ctx->c_clear_free == NULL || ctx->c_secure_malloc == NULL ||
+        ctx->c_secure_zalloc == NULL || ctx->c_secure_free == NULL ||
+        ctx->c_secure_clear_free == NULL || ctx->c_cleanse == NULL) {
+        put_error_ctx(ctx, IBMCA_ERR_INVALID_PARAM,
+                      "Not all required core functions are available for provider '%s'",
+                      ctx->name);
+        ibmca_teardown(ctx);
+        return 0;
+    }
+
+    if (ibmca_get_configuration(ctx) != 1) {
+        ibmca_teardown(ctx);
+        return 0;
+    }
+
+    if (ibmca_libica_init(ctx) != 1) {
+        ibmca_teardown(ctx);
+        return 0;
+    }
+
+    ibmca_debug_ctx(ctx, "provctx: %p name: '%s'", ctx, ctx->name);
+
+    if (ibmca_setup_algorithms(ctx) != 1) {
+        ibmca_teardown(ctx);
+        return 0;
+    }
+
+    *provctx = ctx;
+    *out = ibmca_dispatch_table;
+    return 1;
+}
diff -pruN 1.4.0-1/src/provider/p_ibmca.h 2.5.0-0ubuntu1/src/provider/p_ibmca.h
--- 1.4.0-1/src/provider/p_ibmca.h	1970-01-01 00:00:00.000000000 +0000
+++ 2.5.0-0ubuntu1/src/provider/p_ibmca.h	2025-04-16 08:09:24.000000000 +0000
@@ -0,0 +1,555 @@
+/*
+ * Copyright [2021-2022] International Business Machines Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+#include <stdint.h>
+#include <stdio.h>
+#include <stdbool.h>
+#include <unistd.h>
+#include <pthread.h>
+
+#include <ica_api.h>
+
+#include <openssl/provider.h>
+#include <openssl/ec.h>
+
+/* Environment variable name to enable debug */
+#define IBMCA_DEBUG_ENVVAR          "IBMCA_DEBUG"
+#define IBMCA_DEBUG_PATH_ENVVAR     "IBMCA_DEBUG_PATH"
+
+/* IBMCA provider configuration key words */
+#define IBMCA_CONF_DEBUG            "debug"
+#define IBMCA_CONF_DEBUG_PATH       "debug-path"
+#define IBMCA_CONF_ALGORITHMS       "algorithms"
+#define IBMCA_CONF_FIPS             "fips"
+#define IBMCA_CONF_FALLBACK_PROPS   "fallback-properties"
+
+/* IBMCA provider configuration key words for algorithms */
+#define IBMCA_CONFIG_ALGO_ALL       "ALL"
+#define IBMCA_CONFIG_ALGO_RSA       "RSA"
+#define IBMCA_CONFIG_ALGO_EC        "EC"
+#define IBMCA_CONFIG_ALGO_DH        "DH"
+
+/* IBMCA provider context */
+struct ibmca_prov_ctx {
+    const OSSL_CORE_HANDLE *handle;
+    OSSL_LIB_CTX *libctx;
+    const char *name;
+    const char *openssl_version;
+    const char *module_filename;
+    OSSL_FUNC_core_get_libctx_fn *c_get_libctx;
+    OSSL_FUNC_core_new_error_fn *c_new_error;
+    OSSL_FUNC_core_set_error_debug_fn *c_set_error_debug;
+    OSSL_FUNC_core_vset_error_fn *c_vset_error;
+    OSSL_FUNC_core_gettable_params_fn *c_gettable_params;
+    OSSL_FUNC_core_get_params_fn *c_get_params;
+    OSSL_FUNC_CRYPTO_zalloc_fn *c_zalloc;
+    OSSL_FUNC_CRYPTO_malloc_fn *c_malloc;
+    OSSL_FUNC_CRYPTO_realloc_fn *c_realloc;
+    OSSL_FUNC_CRYPTO_free_fn *c_free;
+    OSSL_FUNC_CRYPTO_clear_free_fn *c_clear_free;
+    OSSL_FUNC_CRYPTO_secure_malloc_fn *c_secure_malloc;
+    OSSL_FUNC_CRYPTO_secure_zalloc_fn *c_secure_zalloc;
+    OSSL_FUNC_CRYPTO_secure_free_fn *c_secure_free;
+    OSSL_FUNC_CRYPTO_secure_clear_free_fn *c_secure_clear_free;
+    OSSL_FUNC_OPENSSL_cleanse_fn *c_cleanse;
+    bool debug;
+    const char *debug_path;
+    FILE *debug_file;
+    pid_t debug_pid;
+    pthread_mutex_t debug_mutex;
+    bool fips;
+    bool fips_configured;
+    bool *algo_enabled;
+    char *property_def;
+    const char *fallback_props_conf;
+    char *fallback_property_query;
+    ica_adapter_handle_t ica_adapter;
+    int ica_fips_status;
+    OSSL_ALGORITHM *algorithms[OSSL_OP__HIGHEST + 1];
+};
+
+/* IBMCA provider key */
+
+struct ibmca_pss_params {
+     int digest_nid;
+     int mgf_nid;
+     int mgf_digest_nid;
+     int saltlen;
+     bool restricted;
+};
+
+struct ibmca_ffc_params {
+    int group_nid;
+    BIGNUM *p;
+    BIGNUM *q;
+    BIGNUM *g; /* DHX only */
+    BIGNUM *cofactor; /* DHX only */
+    int length;
+    unsigned char *seed;
+    size_t seed_len;
+    int gindex;
+    int pcounter;
+    int hindex;
+    unsigned int validate_pq : 1;
+    unsigned int validate_g : 1;
+    unsigned int validate_legacy : 1;
+    const char *mdname;
+    const char *mdprops;
+};
+
+struct ibmca_key {
+    const struct ibmca_prov_ctx *provctx;
+    unsigned int ref_count;
+    int type; /* EVP_PKEY_xxx types */
+    void (*free_cb)(struct ibmca_key *key);
+    int (*dup_cb)(const struct ibmca_key *key, struct ibmca_key *new_key);
+    size_t (*get_max_param_size)(const struct ibmca_key *key);
+    OSSL_FUNC_keymgmt_export_fn *export;
+    OSSL_FUNC_keymgmt_import_fn *import;
+    OSSL_FUNC_keymgmt_has_fn *has;
+    OSSL_FUNC_keymgmt_match_fn *match;
+    const char *algorithm;
+    EVP_PKEY *fallback_pkey_cache;
+    pthread_rwlock_t fallback_pkey_cache_lock;
+    union {
+        struct {
+            size_t bits;
+            size_t keylength;
+            ica_rsa_key_crt_t private_crt;
+            ica_rsa_key_mod_expo_t private_me;
+            ica_rsa_key_mod_expo_t public;
+            struct ibmca_pss_params pss; /* For type EVP_PKEY_RSA_PSS only */
+            BN_BLINDING *blinding;
+            BN_BLINDING *mt_blinding;
+            pthread_rwlock_t blinding_lock;
+            BN_MONT_CTX *blinding_mont_ctx;
+            BN_ULONG blinding_mont_ctx_n0;
+        } rsa; /* For type EVP_PKEY_RSA and EVP_PKEY_RSA_PSS */
+        struct {
+            int curve_nid;
+            point_conversion_form_t format;
+            bool include_pub;
+            size_t prime_size;
+            ICA_EC_KEY *key;
+            struct {
+                BIGNUM *x;
+                BIGNUM *y;
+                BIGNUM *d;
+            } fallback;
+        } ec; /* For type EVP_PKEY_EC */
+        struct {
+            BIGNUM *pub;
+            BIGNUM *priv;
+            struct ibmca_ffc_params ffc_params;
+        } dh; /* For type EVP_PKEY_DH and EVP_PKEY_DHX */
+    };
+};
+
+void ibmca_keymgmt_upref(struct ibmca_key *key);
+unsigned int ibmca_keymgmt_downref(struct ibmca_key *key);
+struct ibmca_key *ibmca_keymgmt_new(const struct ibmca_prov_ctx *provctx,
+                                    int type, const char *algorithm,
+                                    void (*free_cb)(struct ibmca_key *key),
+                                    int (*dup_cb)(const struct ibmca_key *key,
+                                                  struct ibmca_key *new_key),
+                                    size_t (*get_max_param_size)(
+                                                const struct ibmca_key *key),
+                                    OSSL_FUNC_keymgmt_export_fn *export,
+                                    OSSL_FUNC_keymgmt_import_fn *import,
+                                    OSSL_FUNC_keymgmt_has_fn *has,
+                                    OSSL_FUNC_keymgmt_match_fn *match);
+int ibmca_keymgmt_match(const struct ibmca_key *key1,
+                        const struct ibmca_key *key2);
+struct ibmca_op_ctx *ibmca_keymgmt_gen_init(
+                                    const struct ibmca_prov_ctx *provctx,
+                                    int type,
+                                    void (*free_cb)
+                                        (struct ibmca_op_ctx *ctx),
+                                    int (*dup_cb)
+                                        (const struct ibmca_op_ctx *ctx,
+                                         struct ibmca_op_ctx *new_ctx));
+bool ibmca_keymgmt_rsa_pub_valid(const ica_rsa_key_mod_expo_t *public);
+bool ibmca_keymgmt_rsa_priv_crt_valid(const ica_rsa_key_crt_t *private_crt);
+bool ibmca_keymgmt_rsa_priv_me_valid(const ica_rsa_key_mod_expo_t *private_me);
+
+OSSL_FUNC_keymgmt_free_fn ibmca_keymgmt_free;
+OSSL_FUNC_keymgmt_dup_fn ibmca_keymgmt_dup;
+OSSL_FUNC_keymgmt_gen_cleanup_fn ibmca_keymgmt_gen_cleanup;
+OSSL_FUNC_keymgmt_load_fn ibmca_keymgmt_load;
+
+/* IBMCA provider operation context */
+struct ibmca_op_ctx {
+    const struct ibmca_prov_ctx *provctx;
+    int type; /* EVP_PKEY_xxx types */
+    const char *propq;
+    struct ibmca_key *key;
+    int operation;
+    void (*free_cb)(struct ibmca_op_ctx *ctx);
+    int (*dup_cb)(const struct ibmca_op_ctx *ctx, struct ibmca_op_ctx *new_ctx);
+    unsigned char *tbuf;
+    size_t tbuf_len;
+    union {
+        union {
+            struct {
+                size_t bits;
+                BIGNUM *pub_exp;
+                struct ibmca_pss_params pss; /* For EVP_PKEY_RSA_PSS only */
+            } gen; /* For operation EVP_PKEY_OP_KEYGEN */
+            struct {
+                int pad_mode;
+                EVP_MD *mgf1_md;
+                EVP_MD *oaep_md;
+                unsigned char *oaep_label;
+                size_t oaep_labellen;
+                unsigned int tls_clnt_version;
+                unsigned int tls_alt_version;
+                unsigned int implicit_rejection;
+            } cipher; /* For operation EVP_PKEY_OP_ENCRYPT/DECRYPT */
+            struct {
+                int pad_mode;
+                EVP_MD *md;
+                bool set_md_allowed;
+                EVP_MD *mgf1_md;
+                int saltlen;
+                struct ibmca_pss_params pss;
+                EVP_MD_CTX *md_ctx;
+                unsigned char *signature;
+                size_t signature_len;
+            } signature; /* For operation EVP_PKEY_OP_SIGN/VERIFY */
+        } rsa; /* For type EVP_PKEY_RSA and EVP_PKEY_RSA_PSS */
+        union {
+            struct {
+                int selection;
+                int curve_nid;
+                point_conversion_form_t format;
+                unsigned char *dhkem_ikm;
+                size_t dhkem_ikmlen;
+            } gen; /* For operation EVP_PKEY_OP_KEYGEN */
+            struct {
+                EVP_MD *md;
+                bool set_md_allowed;
+                size_t md_size;
+                EVP_MD_CTX *md_ctx;
+                unsigned int nonce_type;
+                unsigned char *signature;
+                size_t signature_len;
+            } signature; /* For operation EVP_PKEY_OP_SIGN/VERIFY */
+            struct {
+                struct ibmca_key *peer_key;
+                int kdf_type; /* EVP_PKEY_ECDH_KDF_xxx */
+                EVP_MD *kdf_md;
+                size_t kdf_outlen;
+                unsigned char *kdf_ukm;
+                size_t kdf_ukmlen;
+            } derive;/* For operation EVP_PKEY_OP_DERIVE */
+        } ec; /* For type EVP_PKEY_EC */
+        union {
+            struct {
+                EVP_PKEY_CTX *pctx;
+                int selection;
+                int priv_len;
+            } gen; /* For operation EVP_PKEY_OP_KEYGEN */
+            struct {
+                struct ibmca_key *peer_key;
+                bool pad;
+                int kdf_type; /* EVP_PKEY_DH_KDF_xxx */
+                EVP_MD *kdf_md;
+                size_t kdf_outlen;
+                unsigned char *kdf_ukm;
+                size_t kdf_ukmlen;
+                char *kdf_cekalg;
+            } derive;/* For operation EVP_PKEY_OP_DERIVE */
+        } dh; /* For type EVP_PKEY_DH and EVP_PKEY_DHX */
+    };
+};
+
+struct ibmca_op_ctx *ibmca_op_newctx(const struct ibmca_prov_ctx *provctx,
+                                     const char *propq, int type,
+                                     void (*free_cb)(struct ibmca_op_ctx *ctx),
+                                     int (*dup_cb)
+                                               (const struct ibmca_op_ctx *ctx,
+                                                struct ibmca_op_ctx *new_ctx));
+int ibmca_op_init(struct ibmca_op_ctx *ctx, struct ibmca_key *key,
+                  int operation);
+int ibmca_op_alloc_tbuf(struct ibmca_op_ctx *ctx, size_t tbuf_len);
+
+int ibmca_digest_signverify_update(struct ibmca_op_ctx *ctx, EVP_MD_CTX *md_ctx,
+                                   const unsigned char *data, size_t datalen);
+int ibmca_digest_sign_final(struct ibmca_op_ctx *ctx, EVP_MD_CTX *md_ctx,
+                            OSSL_FUNC_signature_sign_fn *sign_func,
+                            unsigned char *sig, size_t *siglen, size_t sigsize);
+int ibmca_digest_verify_final(struct ibmca_op_ctx *ctx, EVP_MD_CTX *md_ctx,
+                              OSSL_FUNC_signature_verify_fn *verify_func,
+                              const unsigned char *sig, size_t siglen);
+int ibmca_get_ctx_md_params(struct ibmca_op_ctx *ctx, EVP_MD_CTX *md_ctx,
+                            OSSL_PARAM *params);
+int ibmca_set_ctx_md_params(struct ibmca_op_ctx *ctx, EVP_MD_CTX *md_ctx,
+                            const OSSL_PARAM params[]);
+const OSSL_PARAM *ibmca_gettable_ctx_md_params(const struct ibmca_op_ctx *ctx,
+                                               const EVP_MD *md);
+const OSSL_PARAM *ibmca_settable_ctx_md_params(const struct ibmca_op_ctx *ctx,
+                                               const EVP_MD *md);
+
+OSSL_FUNC_asym_cipher_freectx_fn ibmca_op_freectx;
+OSSL_FUNC_asym_cipher_dupctx_fn ibmca_op_dupctx;
+
+/* Macros for calling core functions */
+#define P_ZALLOC(prov_ctx, size)                    \
+        (prov_ctx)->c_zalloc((size), __FILE__ , __LINE__)
+#define P_MALLOC(prov_ctx, size)                    \
+        (prov_ctx)->c_malloc((size), __FILE__ , __LINE__)
+#define P_REALLOC(prov_ctx, ptr, size)              \
+        (prov_ctx)->c_realloc((ptr), (size), __FILE__ , __LINE__)
+#define P_FREE(prov_ctx, ptr)                       \
+        (prov_ctx)->c_free((ptr), __FILE__ , __LINE__)
+#define P_CLEAR_FREE(prov_ctx, ptr, size)           \
+        (prov_ctx)->c_clear_free((ptr), (size), __FILE__ , __LINE__)
+#define P_SECURE_MALLOC(prov_ctx, size)             \
+        (prov_ctx)->c_secure_malloc((size), __FILE__ , __LINE__)
+#define P_SECURE_ZALLOC(prov_ctx, size)             \
+        (prov_ctx)->c_secure_zalloc((size), __FILE__ , __LINE__)
+#define P_SECURE_FREE(prov_ctx, ptr)                \
+        (prov_ctx)->c_secure_free((ptr), __FILE__ , __LINE__)
+#define P_SECURE_CLEAR_FREE(prov_ctx, ptr, size)    \
+        (prov_ctx)->c_secure_clear_free((ptr), (size), __FILE__ , __LINE__)
+#define P_CLEANSE(prov_ctx, ptr, size)              \
+        (prov_ctx)->c_cleanse((ptr), (size))
+#define P_STRDUP(prov_ctx, str)                     \
+        ibmca_strdup((prov_ctx), (str), __FILE__ , __LINE__)
+#define P_MEMDUP(prov_ctx, data, size)              \
+        ibmca_memdup((prov_ctx), (data), (size), __FILE__ , __LINE__)
+#define P_SECURE_MEMDUP(prov_ctx, data, size)       \
+        ibmca_secure_memdup((prov_ctx), (data), (size), __FILE__ , __LINE__)
+
+char *ibmca_strdup(const struct ibmca_prov_ctx *provctx, const char *str,
+                   const char* file, int line);
+void *ibmca_memdup(const struct ibmca_prov_ctx *provctx, const void *data,
+                   size_t size, const char* file, int line);
+void *ibmca_secure_memdup(const struct ibmca_prov_ctx *provctx,
+                          const void *data, size_t size,
+                          const char* file, int line);
+
+EVP_PKEY_CTX *ibmca_new_fallback_pkey_ctx(const struct ibmca_prov_ctx *provctx,
+                                          EVP_PKEY *pkey,
+                                          const char *algorithm);
+EVP_PKEY *ibmca_new_fallback_pkey(struct ibmca_key *key);
+void ibmca_clean_fallback_pkey_cache(struct ibmca_key *key);
+int ibmca_import_from_fallback_pkey(struct ibmca_key *key, const EVP_PKEY *pkey,
+                                    int selection);
+int ibmca_check_fallback_provider(const struct ibmca_prov_ctx *provctx,
+                                  EVP_PKEY_CTX *pctx);
+
+struct ibmca_keygen_cb_data {
+    OSSL_CALLBACK *osslcb;
+    void *cbarg;
+};
+
+int ibmca_keygen_cb(EVP_PKEY_CTX *ctx);
+
+int ibmca_param_build_set_bn(const struct ibmca_prov_ctx *provctx,
+                             OSSL_PARAM_BLD *bld, OSSL_PARAM *p,
+                             const char *key, const BIGNUM *bn);
+int ibmca_param_build_set_int(const struct ibmca_prov_ctx *provctx,
+                              OSSL_PARAM_BLD *bld, OSSL_PARAM *p,
+                              const char *key, int val);
+int ibmca_param_build_set_uint(const struct ibmca_prov_ctx *provctx,
+                               OSSL_PARAM_BLD *bld, OSSL_PARAM *p,
+                               const char *key, unsigned int val);
+int ibmca_param_build_set_utf8(const struct ibmca_prov_ctx *provctx,
+                               OSSL_PARAM_BLD *bld, OSSL_PARAM *p,
+                               const char *key, const char *str);
+int ibmca_param_build_set_octet_ptr(const struct ibmca_prov_ctx *provctx,
+                                    OSSL_PARAM_BLD *bld, OSSL_PARAM *p,
+                                    const char *key, const void *val,
+                                    size_t len);
+int ibmca_param_build_set_size_t(const struct ibmca_prov_ctx *provctx,
+                                 OSSL_PARAM_BLD *bld, OSSL_PARAM *p,
+                                 const char *key, size_t val);
+int ibmca_param_get_bn(const struct ibmca_prov_ctx *provctx,
+                       const OSSL_PARAM params[], const char *key, BIGNUM **bn);
+int ibmca_param_get_int(const struct ibmca_prov_ctx *provctx,
+                        const OSSL_PARAM params[], const char *key, int *val);
+int ibmca_param_get_uint(const struct ibmca_prov_ctx *provctx,
+                         const OSSL_PARAM params[], const char *key,
+                         unsigned int *val);
+int ibmca_param_get_size_t(const struct ibmca_prov_ctx *provctx,
+                           const OSSL_PARAM params[], const char *key,
+                           size_t *val);
+int ibmca_param_get_utf8(const struct ibmca_prov_ctx *provctx,
+                         const OSSL_PARAM params[], const char *key,
+                         const char **str);
+int ibmca_param_get_octet_string(const struct ibmca_prov_ctx *provctx,
+                                 const OSSL_PARAM params[], const char *key,
+                                 void **val, size_t *len);
+
+/* Debug and error handling functions and macros */
+void ibmca_debug_print(struct ibmca_prov_ctx *provctx, const char *func,
+                       const char *fmt, ...);
+
+#define ibmca_debug(ctx, fmt...)                                       \
+        do {                                                           \
+            if ((ctx)->debug)                                          \
+                ibmca_debug_print((struct ibmca_prov_ctx*)(ctx),       \
+                                  __func__, fmt);                      \
+        } while (0)
+
+#define ibmca_debug_ctx(ctx, fmt...)    ibmca_debug((ctx), fmt)
+#define ibmca_debug_key(key, fmt...)    ibmca_debug((key)->provctx, fmt)
+#define ibmca_debug_op_ctx(ctx, fmt...) ibmca_debug((ctx)->provctx, fmt)
+
+void ibmca_put_error(const struct ibmca_prov_ctx *provctx, int err,
+                     const char *file, int line, const char *func,
+                     char *fmt, ...);
+
+#define put_error_ctx(ctx, err, fmt...)             \
+        do {                                        \
+            ibmca_debug_ctx((ctx), "ERROR: "fmt);   \
+            ibmca_put_error((ctx), (err), __FILE__, \
+                __LINE__, __func__, fmt);           \
+        } while (0)
+#define put_error_key(key, err, fmt...)             \
+        put_error_ctx((key)->provctx, (err), fmt)
+#define put_error_op_ctx(ctx, err, fmt...)          \
+        put_error_ctx((ctx)->provctx, (err), fmt)
+
+#define IBMCA_ERR_INTERNAL_ERROR                1
+#define IBMCA_ERR_MALLOC_FAILED                 2
+#define IBMCA_ERR_INVALID_PARAM                 3
+#define IBMCA_ERR_CONFIGURATION                 4
+#define IBMCA_ERR_LIBICA_FAILED                 5
+#define IBMCA_ERR_SIGNATURE_BAD                 6
+#define IBMCA_ERR_EC_CURVE_NOT_SUPPORTED        7
+
+#define UNUSED(var)                             ((void)(var))
+
+/* Algorithm support definitions */
+
+struct ibmca_mech_capability {
+    const char *capability;
+    const OSSL_PARAM **details;
+};
+
+extern const OSSL_ALGORITHM ibmca_rsa_keymgmt[];
+extern const OSSL_ALGORITHM ibmca_rsa_asym_cipher[];
+extern const OSSL_ALGORITHM ibmca_rsa_signature[];
+
+#define IBMCA_RSA_DEFAULT_BITS              2048
+#define IBMCA_RSA_DEFAULT_PUB_EXP           65537L
+#define IBMCA_RSA_DEFAULT_DIGEST            NID_sha256
+#define IBMCA_RSA_PSS_DEFAULT_DIGEST        NID_sha1
+#define IBMCA_RSA_PSS_DEFAULT_MGF           NID_mgf1
+#define IBMCA_RSA_PSS_DEFAULT_MGF_DIGEST    NID_sha1
+#define IBMCA_RSA_PSS_DEFAULT_SALTLEN       20
+#define IBMCA_RSA_OAEP_DEFAULT_DIGEST       NID_sha1
+#define IBMCA_RSA_DEFAULT_PADDING           RSA_PKCS1_PADDING
+#define IBMCA_RSA_MIN_MODULUS_BITS          512
+#define IBMCA_SSL_MAX_MASTER_KEY_LENGTH     48
+
+#define IBMCA_RSA_PSS_DEFAULTS   { IBMCA_RSA_PSS_DEFAULT_DIGEST,        \
+                                   IBMCA_RSA_PSS_DEFAULT_MGF,           \
+                                   IBMCA_RSA_PSS_DEFAULT_MGF_DIGEST,    \
+                                   IBMCA_RSA_PSS_DEFAULT_SALTLEN,       \
+                                   false                                \
+                                 }
+
+#define MAX_DIGINFO_SIZE                    128
+
+extern const OSSL_ITEM ibmca_rsa_padding_table[];
+
+int ibmca_rsa_build_digest_info(const struct ibmca_prov_ctx *provctx,
+                                const EVP_MD *md, const unsigned char *data,
+                                size_t data_len, unsigned char *out,
+                                size_t outsize, size_t *outlen);
+int ibmca_rsa_add_pkcs1_padding(const struct ibmca_prov_ctx *provctx, int type,
+                                const unsigned char *in, size_t inlen,
+                                unsigned char *out, size_t outlen);
+int ibmca_rsa_check_pkcs1_padding_type1(const struct ibmca_prov_ctx *provctx,
+                                        const unsigned char *in, size_t inlen,
+                                        unsigned char *out, size_t outsize,
+                                        unsigned char **outptr, size_t *outlen);
+int ibmca_rsa_check_pkcs1_padding_type2(const struct ibmca_prov_ctx *provctx,
+                                        const unsigned char *in, size_t inlen,
+                                        unsigned char *out, size_t outsize,
+                                        size_t *outlen,
+                                        const unsigned char *kdk,
+                                        size_t kdklen);
+int ibmca_rsa_add_oaep_mgf1_padding(const struct ibmca_prov_ctx *provctx,
+                                    const unsigned char *in, size_t inlen,
+                                    unsigned char *out, size_t outlen,
+                                    const EVP_MD *oaep_md,
+                                    const EVP_MD *mgf1_md,
+                                    const unsigned char *label,
+                                    size_t label_len);
+int ibmca_rsa_check_oaep_mgf1_padding(const struct ibmca_prov_ctx *provctx,
+                                      const unsigned char *in, size_t inlen,
+                                      unsigned char *out, size_t outsize,
+                                      size_t *outlen,
+                                      const EVP_MD *oaep_md,
+                                      const EVP_MD *mgf1_md,
+                                      const unsigned char *label,
+                                      size_t label_len);
+int ibmca_rsa_check_pkcs1_tls_padding(const struct ibmca_prov_ctx *provctx,
+                                      unsigned int client_version,
+                                      unsigned int alt_version,
+                                      const unsigned char *in, size_t inlen,
+                                      unsigned char *out, size_t outsize,
+                                      size_t *outlen);
+int ibmca_rsa_add_x931_padding(const struct ibmca_prov_ctx *provctx,
+                               const unsigned char *in, size_t inlen,
+                               unsigned char *out, size_t outlen,
+                               int digest_nid);
+int ibmca_rsa_check_X931_padding(const struct ibmca_prov_ctx *provctx,
+                                 const unsigned char *in, int inlen,
+                                 unsigned char *out, size_t outsize,
+                                 unsigned char **outptr, size_t *outlen,
+                                 int digest_nid);
+int ibmca_rsa_add_pss_mgf1_padding(const struct ibmca_prov_ctx *provctx,
+                                   const unsigned char *in, size_t inlen,
+                                   unsigned char *out, size_t outlen,
+                                   const EVP_MD *pss_md, const EVP_MD *mgf1_md,
+                                   int saltlen);
+int ibmca_rsa_check_pss_mgf1_padding(const struct ibmca_prov_ctx *provctx,
+                                     const unsigned char *in, size_t inlen,
+                                     const unsigned char *data, size_t datalen,
+                                     const EVP_MD *pss_md,
+                                     const EVP_MD *mgf1_md,
+                                     int saltlen);
+
+int ibmca_keymgmt_rsa_derive_kdk(struct ibmca_key *key,
+                                 const unsigned char *in, size_t inlen,
+                                 unsigned char *kdk, size_t kdklen);
+
+int ibmca_keymgmt_rsa_pub_as_bn(struct ibmca_key *key, BIGNUM **n, BIGNUM **e);
+
+int ibmca_rsa_priv_with_blinding(struct ibmca_key *key, const unsigned char *in,
+                                 unsigned char *out, size_t rsa_size);
+
+int ossl_bn_rsa_do_unblind(const unsigned char *intermediate,
+                           const BIGNUM *unblind,
+                           const unsigned char *to_mod,
+                           unsigned char *buf, int num,
+                           BN_MONT_CTX *m_ctx, BN_ULONG n0);
+
+extern const OSSL_ALGORITHM ibmca_ec_keymgmt[];
+extern const OSSL_ALGORITHM ibmca_ec_signature[];
+extern const OSSL_ALGORITHM ibmca_ec_keyexch[];
+extern const struct ibmca_mech_capability ibmca_ec_capabilities[];
+
+#define IBMCA_EC_DEFAULT_DIGEST             NID_sha256
+
+extern const OSSL_ALGORITHM ibmca_dh_keymgmt[];
+extern const OSSL_ALGORITHM ibmca_dh_keyexch[];
+extern const struct ibmca_mech_capability ibmca_dh_capabilities[];
diff -pruN 1.4.0-1/src/provider/p_key.c 2.5.0-0ubuntu1/src/provider/p_key.c
--- 1.4.0-1/src/provider/p_key.c	1970-01-01 00:00:00.000000000 +0000
+++ 2.5.0-0ubuntu1/src/provider/p_key.c	2025-04-16 08:09:24.000000000 +0000
@@ -0,0 +1,498 @@
+/*
+ * Copyright [2021-2022] International Business Machines Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <stddef.h>
+#include <err.h>
+#include <strings.h>
+#include <string.h>
+#include <errno.h>
+#include <assert.h>
+
+#include <openssl/evp.h>
+#include <openssl/core.h>
+#include <openssl/core_dispatch.h>
+#include <openssl/core_names.h>
+#include <openssl/params.h>
+
+#include "p_ibmca.h"
+
+void ibmca_keymgmt_upref(struct ibmca_key *key)
+{
+    ibmca_debug_key(key, "key: %p", key);
+
+    __sync_add_and_fetch(&key->ref_count, 1);
+
+    ibmca_debug_key(key, "ref_count: %u", key->ref_count);
+}
+
+unsigned int ibmca_keymgmt_downref(struct ibmca_key *key)
+{
+    const struct ibmca_prov_ctx *provctx;
+    unsigned int cnt;
+
+    assert(key->ref_count > 0);
+
+    provctx = key->provctx;
+    ibmca_debug_ctx(provctx, "key: %p ", key);
+
+    cnt = __sync_sub_and_fetch(&key->ref_count, 1);
+    ibmca_debug_ctx(provctx, "ref_count: %u", cnt);
+
+    return cnt;
+}
+
+struct ibmca_key *ibmca_keymgmt_new(const struct ibmca_prov_ctx *provctx,
+                                    int type, const char *algorithm,
+                                    void (*free_cb)(struct ibmca_key *key),
+                                    int (*dup_cb)(const struct ibmca_key *key,
+                                                  struct ibmca_key *new_key),
+                                    size_t (*get_max_param_size)(
+                                                  const struct ibmca_key *key),
+                                    OSSL_FUNC_keymgmt_export_fn *export,
+                                    OSSL_FUNC_keymgmt_import_fn *import,
+                                    OSSL_FUNC_keymgmt_has_fn *has,
+                                    OSSL_FUNC_keymgmt_match_fn *match)
+{
+    struct ibmca_key *key;
+
+    if (provctx == NULL)
+        return NULL;
+
+    ibmca_debug_ctx(provctx, "provctx: %p type: %d algorithm: '%s'", provctx,
+                    type, algorithm);
+
+    key = P_ZALLOC(provctx, sizeof(struct ibmca_key));
+    if (key == NULL) {
+        put_error_ctx(provctx, IBMCA_ERR_MALLOC_FAILED,
+                      "Failed to allocate a key");
+        return NULL;
+    }
+
+    key->provctx = provctx;
+    key->type = type;
+    key->algorithm = algorithm;
+    key->free_cb = free_cb;
+    key->dup_cb = dup_cb;
+    key->get_max_param_size = get_max_param_size;
+    key->export = export;
+    key->import = import;
+    key->has = has;
+    key->match = match;
+
+    if (pthread_rwlock_init(&key->fallback_pkey_cache_lock, NULL) != 0) {
+        put_error_ctx(provctx, IBMCA_ERR_INTERNAL_ERROR,
+                      "pthread_rwlock_init failed: %s", strerror(errno));
+        P_FREE(provctx, key);
+        return NULL;
+    }
+
+    ibmca_keymgmt_upref(key);
+
+    ibmca_debug_ctx(provctx, "key: %p", key);
+
+    return key;
+}
+
+void ibmca_keymgmt_free(void *vkey)
+{
+    struct ibmca_key *key = vkey;
+
+    if (key == NULL)
+        return;
+
+    ibmca_debug_key(key, "key: %p", key);
+
+    if (ibmca_keymgmt_downref(key) > 0)
+        return;
+
+    ibmca_debug_key(key, "free key: %p", key);
+
+    if (key->free_cb != NULL)
+        key->free_cb(key);
+
+    ibmca_clean_fallback_pkey_cache(key);
+
+    pthread_rwlock_destroy(&key->fallback_pkey_cache_lock);
+
+    P_FREE(key->provctx, key);
+}
+
+void *ibmca_keymgmt_dup(const void *vfrom, int selection)
+{
+    const struct ibmca_key *from = vfrom;
+    struct ibmca_key *key;
+
+    if (from == NULL)
+        return NULL;
+
+    ibmca_debug_key(from, "from: %p selection: 0x%x", from, selection);
+
+    if ((selection & OSSL_KEYMGMT_SELECT_ALL) == 0)
+        return NULL;
+
+    key = ibmca_keymgmt_new(from->provctx, from->type, from->algorithm,
+                            from->free_cb, from->dup_cb,
+                            from->get_max_param_size,
+                            from->export, from->import, from->has, from->match);
+    if (key == NULL) {
+        ibmca_debug_key(from, "ERROR: ibmca_keymgmt_new failed");
+        return NULL;
+    }
+
+    if (from->dup_cb != NULL) {
+        if (from->dup_cb(from, key) == 0) {
+            ibmca_debug_key(from, "ERROR: dup_cb failed");
+            ibmca_keymgmt_free(key);
+            return NULL;
+        }
+    }
+
+    return key;
+}
+
+int ibmca_keymgmt_match(const struct ibmca_key *key1,
+                        const struct ibmca_key *key2)
+{
+    if (key1 == NULL || key2 == NULL)
+        return 0;
+
+    ibmca_debug_key(key1, "key1: %p key2: %p", key1, key2);
+
+    if (key1->provctx != key2->provctx)
+        return 0;
+    if (key1->type != key2->type)
+        return 0;
+
+    return 1;
+}
+
+struct ibmca_op_ctx *ibmca_keymgmt_gen_init(
+                                        const struct ibmca_prov_ctx *provctx,
+                                        int type,
+                                        void (*free_cb)
+                                            (struct ibmca_op_ctx *ctx),
+                                        int (*dup_cb)
+                                            (const struct ibmca_op_ctx *ctx,
+                                             struct ibmca_op_ctx *new_ctx))
+{
+    struct ibmca_op_ctx *genctx;
+
+    if (provctx == NULL)
+        return NULL;
+
+    ibmca_debug_ctx(provctx, "type: %d", type);
+
+    genctx = ibmca_op_newctx(provctx, NULL, type, free_cb, dup_cb);
+    if (genctx == NULL) {
+        ibmca_debug_ctx(provctx, "ERROR: ibmca_op_newctx failed");
+        return NULL;
+    }
+
+    if (ibmca_op_init(genctx, NULL, EVP_PKEY_OP_KEYGEN) == 0) {
+        ibmca_debug_ctx(provctx, "ERROR: ibmca_op_init failed");
+        ibmca_op_freectx(genctx);
+        return NULL;
+    }
+
+    ibmca_debug_ctx(provctx, "genctx: %p", genctx);
+    return genctx;
+}
+
+void ibmca_keymgmt_gen_cleanup(void *vgenctx)
+{
+    struct ibmca_op_ctx *genctx = vgenctx;
+
+    if (genctx == NULL)
+        return;
+
+    ibmca_debug_op_ctx(genctx, "genctx: %p", genctx);
+    ibmca_op_freectx(genctx);
+}
+
+void *ibmca_keymgmt_load(const void *reference, size_t reference_sz)
+{
+    struct ibmca_key *key;
+
+    if (reference == NULL)
+        return NULL;
+
+    if (reference_sz == sizeof(struct ibmca_key)) {
+        /* The contents of the reference is the address to our object */
+        key = *(struct ibmca_key **)reference;
+
+        /* We grabbed, so we detach it */
+        *(struct ibmca_key **)reference = NULL;
+        return key;
+    }
+
+    return NULL;
+}
+
+EVP_PKEY_CTX *ibmca_new_fallback_pkey_ctx(const struct ibmca_prov_ctx *provctx,
+                                          EVP_PKEY *pkey,
+                                          const char *algorithm)
+{
+    EVP_PKEY_CTX *pkey_ctx = NULL;
+
+    ibmca_debug_ctx(provctx, "pkey: %p algorithm: '%s'", pkey,
+                    algorithm != NULL ? algorithm : "(null)");
+
+    if (pkey != NULL) {
+        pkey_ctx = EVP_PKEY_CTX_new_from_pkey(provctx->libctx, pkey,
+                                              provctx->fallback_property_query);
+        if (pkey_ctx == NULL) {
+            put_error_ctx(provctx, IBMCA_ERR_INTERNAL_ERROR,
+                          "EVP_PKEY_CTX_new_from_name for pkey %p failed",
+                          pkey);
+            return NULL;
+        }
+    } else if (algorithm != NULL) {
+        pkey_ctx = EVP_PKEY_CTX_new_from_name(provctx->libctx, algorithm,
+                                              provctx->fallback_property_query);
+        if (pkey_ctx == NULL) {
+            put_error_ctx(provctx, IBMCA_ERR_INTERNAL_ERROR,
+                          "EVP_PKEY_CTX_new_from_name for '%s' failed",
+                          algorithm);
+            return NULL;
+        }
+    } else {
+        put_error_ctx(provctx, IBMCA_ERR_INVALID_PARAM,
+                      "Neither pkey nor algorithm specified");
+    }
+
+    return pkey_ctx;
+}
+
+struct ibmca_fallback_pkey_cb_data {
+    const struct ibmca_key *key;
+    EVP_PKEY_CTX *pctx;
+    EVP_PKEY *pkey;
+};
+
+static int ibmca_fallback_pkey_cb(const OSSL_PARAM params[], void *arg)
+{
+    struct ibmca_fallback_pkey_cb_data *data = arg;
+    const OSSL_PARAM *p;
+    int rc;
+
+    ibmca_debug_key(data->key, "key: %p", data->key);
+    for (p = params; p != NULL && p->key != NULL; p++)
+        ibmca_debug_key(data->key, "param: %s", p->key);
+
+    rc = EVP_PKEY_fromdata(data->pctx, &data->pkey,
+                           OSSL_KEYMGMT_SELECT_KEYPAIR |
+                                       OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS,
+                           (OSSL_PARAM *)params);
+    if (rc == 0) {
+        put_error_key(data->key, IBMCA_ERR_INTERNAL_ERROR,
+                      "EVP_PKEY_fromdata failed");
+        return 0;
+    }
+
+    return 1;
+}
+
+EVP_PKEY *ibmca_new_fallback_pkey(struct ibmca_key *key)
+{
+    struct ibmca_fallback_pkey_cb_data data;
+    EVP_PKEY *pkey;
+    int rc = 0;
+
+    if (key == NULL)
+        return NULL;
+
+    ibmca_debug_key(key, "key: %p", key);
+
+    /* Get from cache if one exists */
+    if (pthread_rwlock_rdlock(&key->fallback_pkey_cache_lock) != 0) {
+        ibmca_debug_key(key, "ERROR: pthread_rwlock_rdlock failed: %s",
+                        strerror(errno));
+        return NULL;
+    }
+
+retry:
+    if (key->fallback_pkey_cache != NULL) {
+        pkey = key->fallback_pkey_cache;
+        if (EVP_PKEY_up_ref(pkey) != 1) {
+            put_error_key(key, IBMCA_ERR_INTERNAL_ERROR,
+                          "EVP_PKEY_up_ref failed");
+            pkey = NULL;
+        }
+        pthread_rwlock_unlock(&key->fallback_pkey_cache_lock);
+        ibmca_debug_key(key, "got from cache: %p", pkey);
+        return pkey;
+    }
+    pthread_rwlock_unlock(&key->fallback_pkey_cache_lock);
+
+    /*
+     * WR-lock the cache while creating a fallback PKEY to avoid other threads
+     * from creating one on parallel. They will have to wait to get the RD-lock
+     * above and will then get out fallback PKEY from the cache once we have
+     * finished creating it.
+     */
+    if (pthread_rwlock_wrlock(&key->fallback_pkey_cache_lock) != 0) {
+        ibmca_debug_key(key, "ERROR: pthread_rwlock_wrlock failed: %s",
+                        strerror(errno));
+        return NULL;
+    }
+
+    /*
+     * Another thread might have put its pkey to the cache in the meantime.
+     * There is a small time window when we gave up the RD-lock until we got
+     * the WR-lock where another thread could have put its fallback pkey to
+     * the cache.
+     * So we need to check now while we are holding the WR-lock if there is
+     * one in the cache and use that one if so.
+     */
+    if (key->fallback_pkey_cache != NULL)
+        goto retry;
+
+    data.key = key;
+    data.pkey = NULL;
+    data.pctx = ibmca_new_fallback_pkey_ctx(
+                                    (struct ibmca_prov_ctx *)key->provctx,
+                                    NULL, key->algorithm);
+    if (data.pctx == NULL)
+        goto out;
+
+    if (EVP_PKEY_fromdata_init(data.pctx) == 0) {
+        put_error_key(key, IBMCA_ERR_INTERNAL_ERROR,
+                      "EVP_PKEY_fromdata_init failed");
+        goto out;
+    }
+
+    rc = key->export((struct ibmca_key *)key,
+                     OSSL_KEYMGMT_SELECT_KEYPAIR |
+                                     OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS,
+                     ibmca_fallback_pkey_cb, &data);
+    if (rc == 0 || data.pkey == NULL)
+        goto out;
+
+out:
+    if (data.pctx != NULL)
+        EVP_PKEY_CTX_free(data.pctx);
+    if (rc != 1 && data.pkey != NULL) {
+        EVP_PKEY_free(data.pkey);
+        data.pkey = NULL;
+    }
+
+    /* Add pkey to cache */
+    if (data.pkey != NULL) {
+        if (EVP_PKEY_up_ref(data.pkey) == 1)
+            key->fallback_pkey_cache = data.pkey;
+    }
+
+    pthread_rwlock_unlock(&key->fallback_pkey_cache_lock);
+
+    return data.pkey;
+}
+
+void ibmca_clean_fallback_pkey_cache(struct ibmca_key *key)
+{
+    if (pthread_rwlock_wrlock(&key->fallback_pkey_cache_lock) != 0) {
+        ibmca_debug_key(key, "ERROR: pthread_rwlock_wrlock failed: %s",
+                        strerror(errno));
+        return;
+    }
+
+    if (key->fallback_pkey_cache != NULL)
+        EVP_PKEY_free(key->fallback_pkey_cache);
+    key->fallback_pkey_cache = NULL;
+
+    pthread_rwlock_unlock(&key->fallback_pkey_cache_lock);
+}
+
+int ibmca_import_from_fallback_pkey(struct ibmca_key *key, const EVP_PKEY *pkey,
+                                    int selection)
+{
+    OSSL_PARAM *params = NULL;
+    int rc = 0;
+
+    if (key == NULL || pkey == NULL)
+        return 0;
+
+    ibmca_debug_key(key, "key: %p pkey: %p selection: 0x%x", key, pkey,
+                    selection);
+
+    if (EVP_PKEY_todata(pkey, selection, &params) == 0) {
+        put_error_key(key, IBMCA_ERR_INTERNAL_ERROR,
+                      "EVP_PKEY_todata failed");
+        goto out;
+    }
+
+    rc = key->import(key, selection, params);
+    if (rc == 0)
+        goto out;
+
+    rc = 1;
+
+out:
+    if (params != NULL)
+        OSSL_PARAM_free(params);
+
+    return rc;
+}
+
+int ibmca_check_fallback_provider(const struct ibmca_prov_ctx *provctx,
+                                  EVP_PKEY_CTX *pctx)
+{
+    const char *name;
+
+    if (EVP_PKEY_CTX_get0_provider(pctx) == NULL) {
+        put_error_ctx(provctx, IBMCA_ERR_INTERNAL_ERROR,
+                      "PKEY Context is not initialized with a provider");
+        return 0;
+    }
+
+    name = OSSL_PROVIDER_get0_name(EVP_PKEY_CTX_get0_provider(pctx));
+    if (name == NULL) {
+        put_error_ctx(provctx, IBMCA_ERR_INTERNAL_ERROR,
+                      "PKEY Context is not initialized with a provider");
+        return 0;
+    }
+
+    ibmca_debug_ctx(provctx, "fallback provider: %s", name);
+
+    if (strcmp(name, provctx->name) == 0) {
+        put_error_ctx(provctx, IBMCA_ERR_INVALID_PARAM,
+                      "Fallback provider can not be the %s provider itself",
+                      provctx->name);
+        return 0;
+    }
+
+    return 1;
+}
+
+int ibmca_keygen_cb(EVP_PKEY_CTX *ctx)
+{
+    struct ibmca_keygen_cb_data *cbdata = EVP_PKEY_CTX_get_app_data(ctx);
+    OSSL_PARAM cb_params[] = { OSSL_PARAM_END, OSSL_PARAM_END, OSSL_PARAM_END };
+    int p, n;
+
+    if (cbdata == NULL || cbdata->osslcb == NULL)
+        return 0;
+
+    cb_params[0] = OSSL_PARAM_construct_int(OSSL_GEN_PARAM_POTENTIAL, &p);
+    cb_params[1] = OSSL_PARAM_construct_int(OSSL_GEN_PARAM_ITERATION, &n);
+
+    p = EVP_PKEY_CTX_get_keygen_info(ctx, 0);
+    n = EVP_PKEY_CTX_get_keygen_info(ctx, 1);
+
+    return cbdata->osslcb(cb_params, cbdata->cbarg);
+}
diff -pruN 1.4.0-1/src/provider/rsa_asym_cipher.c 2.5.0-0ubuntu1/src/provider/rsa_asym_cipher.c
--- 1.4.0-1/src/provider/rsa_asym_cipher.c	1970-01-01 00:00:00.000000000 +0000
+++ 2.5.0-0ubuntu1/src/provider/rsa_asym_cipher.c	2025-04-16 08:09:24.000000000 +0000
@@ -0,0 +1,983 @@
+/*
+ * Copyright [2021-2022] International Business Machines Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <stddef.h>
+#include <err.h>
+#include <strings.h>
+#include <string.h>
+
+#include <openssl/evp.h>
+#include <openssl/rsa.h>
+#include <openssl/core.h>
+#include <openssl/core_dispatch.h>
+#include <openssl/core_names.h>
+#include <openssl/params.h>
+#include <openssl/sha.h>
+
+#include "p_ibmca.h"
+
+
+static OSSL_FUNC_asym_cipher_newctx_fn ibmca_asym_cipher_rsa_newctx;
+static OSSL_FUNC_asym_cipher_get_ctx_params_fn
+                                    ibmca_asym_cipher_rsa_get_ctx_params;
+static OSSL_FUNC_asym_cipher_gettable_ctx_params_fn
+                                    ibmca_asym_cipher_rsa_gettable_ctx_params;
+static OSSL_FUNC_asym_cipher_set_ctx_params_fn
+                                    ibmca_asym_cipher_rsa_set_ctx_params;
+static OSSL_FUNC_asym_cipher_settable_ctx_params_fn
+                                    ibmca_asym_cipher_rsa_settable_ctx_params;
+static OSSL_FUNC_asym_cipher_encrypt_init_fn ibmca_asym_cipher_rsa_encrypt_init;
+static OSSL_FUNC_asym_cipher_encrypt_fn ibmca_asym_cipher_rsa_encrypt;
+static OSSL_FUNC_asym_cipher_decrypt_init_fn ibmca_asym_cipher_rsa_decrypt_init;
+static OSSL_FUNC_asym_cipher_decrypt_fn ibmca_asym_cipher_rsa_decrypt;
+
+static void ibmca_asym_cipher_rsa_free_cb(struct ibmca_op_ctx *ctx);
+static int ibmca_asym_cipher_rsa_dup_cb(const struct ibmca_op_ctx *ctx,
+                                        struct ibmca_op_ctx *new_ctx);
+
+static void *ibmca_asym_cipher_rsa_newctx(void *vprovctx)
+{
+    const struct ibmca_prov_ctx *provctx = vprovctx;
+    struct ibmca_op_ctx *opctx;
+
+    if (provctx == NULL)
+        return NULL;
+
+    ibmca_debug_ctx(provctx, "provctx: %p", provctx);
+
+    opctx = ibmca_op_newctx(provctx, NULL, EVP_PKEY_RSA,
+                            ibmca_asym_cipher_rsa_free_cb,
+                            ibmca_asym_cipher_rsa_dup_cb);
+    if (opctx == NULL) {
+        ibmca_debug_ctx(provctx, "ERROR: ibmca_op_newctx failed");
+        return NULL;
+    }
+
+#ifdef OSSL_ASYM_CIPHER_PARAM_IMPLICIT_REJECTION
+    opctx->rsa.cipher.implicit_rejection = 1;
+#else
+    opctx->rsa.cipher.implicit_rejection = 0;
+#endif
+
+    ibmca_debug_ctx(provctx, "opctx: %p", opctx);
+
+    return opctx;
+}
+
+static void ibmca_asym_cipher_rsa_free_cb(struct ibmca_op_ctx *ctx)
+{
+    if (ctx == NULL)
+        return;
+
+    ibmca_debug_op_ctx(ctx, "ctx: %p", ctx);
+
+    if (ctx->rsa.cipher.oaep_md != NULL)
+        EVP_MD_free(ctx->rsa.cipher.oaep_md);
+    ctx->rsa.cipher.oaep_md = NULL;
+
+    if (ctx->rsa.cipher.mgf1_md != NULL)
+        EVP_MD_free(ctx->rsa.cipher.mgf1_md);
+    ctx->rsa.cipher.mgf1_md = NULL;
+
+    if (ctx->rsa.cipher.oaep_label != NULL)
+        P_FREE(ctx->provctx, ctx->rsa.cipher.oaep_label);
+    ctx->rsa.cipher.oaep_label = NULL;
+    ctx->rsa.cipher.oaep_labellen = 0;
+}
+
+static int ibmca_asym_cipher_rsa_dup_cb(const struct ibmca_op_ctx *ctx,
+                                        struct ibmca_op_ctx *new_ctx)
+{
+    if (ctx == NULL)
+        return 0;
+
+    ibmca_debug_op_ctx(ctx, "ctx: %p new_ctx: %p", ctx, new_ctx);
+
+    new_ctx->rsa.cipher.oaep_md = ctx->rsa.cipher.oaep_md;
+    if (new_ctx->rsa.cipher.oaep_md != NULL &&
+        EVP_MD_up_ref(new_ctx->rsa.cipher.oaep_md) == 0) {
+        put_error_op_ctx(ctx, IBMCA_ERR_INTERNAL_ERROR, "EVP_MD_up_ref failed");
+        return 0;
+    }
+
+    new_ctx->rsa.cipher.mgf1_md = ctx->rsa.cipher.mgf1_md;
+    if (new_ctx->rsa.cipher.mgf1_md != NULL &&
+        EVP_MD_up_ref(new_ctx->rsa.cipher.mgf1_md) == 0) {
+        put_error_op_ctx(ctx, IBMCA_ERR_INTERNAL_ERROR, "EVP_MD_up_ref failed");
+        return 0;
+    }
+
+    new_ctx->rsa.cipher.oaep_label = NULL;
+    new_ctx->rsa.cipher.oaep_labellen = 0;
+    new_ctx->rsa.cipher.implicit_rejection = ctx->rsa.cipher.implicit_rejection;
+
+    return 1;
+}
+
+static int ibmca_asym_cipher_rsa_get_ctx_params(void *vctx, OSSL_PARAM params[])
+{
+    struct ibmca_op_ctx *ctx = vctx;
+    const OSSL_PARAM *p;
+    const char *name = NULL;
+    int i, rc;
+
+    if (ctx == NULL)
+        return 0;
+
+    ibmca_debug_op_ctx(ctx, "ctx: %p", ctx);
+    for (p = params; p != NULL && p->key != NULL; p++)
+        ibmca_debug_op_ctx(ctx, "param: %s", p->key);
+
+    /* OSSL_ASYM_CIPHER_PARAM_PAD_MODE */
+    p = OSSL_PARAM_locate(params, OSSL_ASYM_CIPHER_PARAM_PAD_MODE);
+    if (p != NULL) {
+        switch (p->data_type) {
+        case OSSL_PARAM_INTEGER:
+            rc = ibmca_param_build_set_int(ctx->provctx, NULL, params,
+                                           OSSL_ASYM_CIPHER_PARAM_PAD_MODE,
+                                           ctx->rsa.cipher.pad_mode);
+            if (rc == 0)
+                return 0;
+            break;
+        case OSSL_PARAM_UTF8_STRING:
+            for (i = 0; ibmca_rsa_padding_table[i].id != 0; i++) {
+                if ((int)ibmca_rsa_padding_table[i].id ==
+                                                ctx->rsa.cipher.pad_mode) {
+                    name = ibmca_rsa_padding_table[i].ptr;
+                    break;
+                }
+            }
+            if (name == NULL) {
+                put_error_op_ctx(ctx, IBMCA_ERR_INTERNAL_ERROR,
+                                 "Invalid RSA padding mode: %d",
+                                 ctx->rsa.cipher.pad_mode);
+                return 0;
+            }
+
+            rc = ibmca_param_build_set_utf8(ctx->provctx, NULL, params,
+                                            OSSL_ASYM_CIPHER_PARAM_PAD_MODE,
+                                            name);
+             if (rc == 0)
+                 return 0;
+            break;
+        default:
+            put_error_op_ctx(ctx, IBMCA_ERR_INVALID_PARAM,
+                             "Invalid param type for: '%s'",
+                             OSSL_ASYM_CIPHER_PARAM_PAD_MODE);
+            return 0;
+        }
+    }
+
+    /* OSSL_ASYM_CIPHER_PARAM_OAEP_DIGEST */
+    if (ctx->rsa.cipher.oaep_md != NULL)
+        name = EVP_MD_get0_name(ctx->rsa.cipher.oaep_md);
+    else
+        name = "";
+    rc = ibmca_param_build_set_utf8(ctx->provctx, NULL, params,
+                                    OSSL_ASYM_CIPHER_PARAM_OAEP_DIGEST,
+                                    name);
+    if (rc == 0)
+        return 0;
+
+    /* OSSL_ASYM_CIPHER_PARAM_MGF1_DIGEST */
+    if (ctx->rsa.cipher.mgf1_md != NULL)
+        name = EVP_MD_get0_name(ctx->rsa.cipher.mgf1_md);
+    rc = ibmca_param_build_set_utf8(ctx->provctx, NULL, params,
+                                    OSSL_ASYM_CIPHER_PARAM_MGF1_DIGEST,
+                                    name);
+    if (rc == 0)
+        return 0;
+
+    /* OSSL_ASYM_CIPHER_PARAM_OAEP_LABEL */
+    rc = ibmca_param_build_set_octet_ptr(ctx->provctx, NULL, params,
+                                         OSSL_ASYM_CIPHER_PARAM_OAEP_LABEL,
+                                         ctx->rsa.cipher.oaep_label,
+                                         ctx->rsa.cipher.oaep_labellen);
+    if (rc == 0)
+        return 0;
+
+    /* OSSL_ASYM_CIPHER_PARAM_TLS_CLIENT_VERSION */
+    rc = ibmca_param_build_set_uint(ctx->provctx, NULL, params,
+                                    OSSL_ASYM_CIPHER_PARAM_TLS_CLIENT_VERSION,
+                                    ctx->rsa.cipher.tls_clnt_version);
+    if (rc == 0)
+        return 0;
+
+    /* OSSL_ASYM_CIPHER_PARAM_TLS_NEGOTIATED_VERSION */
+    rc = ibmca_param_build_set_uint(ctx->provctx, NULL, params,
+                                    OSSL_ASYM_CIPHER_PARAM_TLS_NEGOTIATED_VERSION,
+                                    ctx->rsa.cipher.tls_alt_version);
+    if (rc == 0)
+        return 0;
+
+#ifdef OSSL_ASYM_CIPHER_PARAM_IMPLICIT_REJECTION
+    /* OSSL_ASYM_CIPHER_PARAM_IMPLICIT_REJECTION */
+    rc = ibmca_param_build_set_uint(ctx->provctx, NULL, params,
+                                    OSSL_ASYM_CIPHER_PARAM_IMPLICIT_REJECTION,
+                                    ctx->rsa.cipher.implicit_rejection);
+    if (rc == 0)
+        return 0;
+#endif
+
+    return 1;
+}
+
+static int ibmca_asym_cipher_rsa_set_ctx_params(void *vctx,
+                                                const OSSL_PARAM params[])
+{
+    struct ibmca_op_ctx *ctx = vctx;
+    const OSSL_PARAM *p;
+    const char *name, *props;
+    void *label = NULL;
+    size_t labellen = 0;
+    int i, rc;
+
+    if (ctx == NULL)
+        return 0;
+
+    ibmca_debug_op_ctx(ctx, "ctx: %p", ctx);
+    for (p = params; p != NULL && p->key != NULL; p++)
+        ibmca_debug_op_ctx(ctx, "param: %s", p->key);
+
+    /* OSSL_ASYM_CIPHER_PARAM_PAD_MODE */
+    p = OSSL_PARAM_locate((OSSL_PARAM *)params,
+                          OSSL_ASYM_CIPHER_PARAM_PAD_MODE);
+    if (p != NULL) {
+        switch (p->data_type) {
+        case OSSL_PARAM_INTEGER:
+            rc = ibmca_param_get_int(ctx->provctx, params,
+                                     OSSL_ASYM_CIPHER_PARAM_PAD_MODE,
+                                     &ctx->rsa.cipher.pad_mode);
+            if (rc == 0)
+                return 0;
+            break;
+        case OSSL_PARAM_UTF8_STRING:
+            rc = ibmca_param_get_utf8(ctx->provctx, params,
+                                      OSSL_ASYM_CIPHER_PARAM_PAD_MODE, &name);
+            if (rc == 1) {
+                ctx->rsa.cipher.pad_mode = 0;
+                for (i = 0; ibmca_rsa_padding_table[i].id != 0; i++) {
+                    if (strcmp(name, ibmca_rsa_padding_table[i].ptr) == 0) {
+                        ctx->rsa.cipher.pad_mode =
+                                            ibmca_rsa_padding_table[i].id;
+                        break;
+                    }
+                }
+            }
+            break;
+        default:
+            put_error_op_ctx(ctx, IBMCA_ERR_INVALID_PARAM,
+                             "Invalid param type for: '%s'",
+                             OSSL_ASYM_CIPHER_PARAM_PAD_MODE);
+            return 0;
+        }
+
+        switch (ctx->rsa.cipher.pad_mode) {
+        case RSA_NO_PADDING:
+        case RSA_PKCS1_PADDING:
+        case RSA_PKCS1_WITH_TLS_PADDING:
+            break;
+        case RSA_PKCS1_OAEP_PADDING:
+            /* Setup default md if not already set */
+            if (ctx->rsa.cipher.oaep_md == NULL) {
+                ctx->rsa.cipher.oaep_md = EVP_MD_fetch(ctx->provctx->libctx,
+                              OBJ_nid2sn(IBMCA_RSA_OAEP_DEFAULT_DIGEST), NULL);
+                if (ctx->rsa.cipher.oaep_md == NULL) {
+                    put_error_op_ctx(ctx, IBMCA_ERR_INTERNAL_ERROR,
+                                  "Failed to fetch default OAEP digest");
+                    return 0;
+                }
+
+                if ((EVP_MD_get_flags(ctx->rsa.cipher.oaep_md) &
+                                                        EVP_MD_FLAG_XOF) != 0) {
+                    put_error_op_ctx(ctx, IBMCA_ERR_INVALID_PARAM,
+                                     "XOF Digest '%s' is not allowed", name);
+                    EVP_MD_free(ctx->rsa.cipher.oaep_md);
+                    ctx->rsa.cipher.oaep_md = NULL;
+                    return 0;
+                }
+            }
+            break;
+        case RSA_PKCS1_PSS_PADDING: /* PSS is for signatures only */
+        case RSA_X931_PADDING: /* X.931 is for signatures only */
+        default:
+            put_error_op_ctx(ctx, IBMCA_ERR_INVALID_PARAM,
+                                      "Invalid RSA padding mode: %d",
+                                      ctx->rsa.cipher.pad_mode);
+            return 0;
+        }
+    }
+
+    /* OSSL_ASYM_CIPHER_PARAM_OAEP_DIGEST_PROPS */
+    props = NULL;
+    rc = ibmca_param_get_utf8(ctx->provctx, params,
+                              OSSL_ASYM_CIPHER_PARAM_OAEP_DIGEST_PROPS, &props);
+    if (rc == 0)
+        return 0;
+
+    /* OSSL_ASYM_CIPHER_PARAM_OAEP_DIGEST */
+    rc = ibmca_param_get_utf8(ctx->provctx, params,
+                              OSSL_ASYM_CIPHER_PARAM_OAEP_DIGEST, &name);
+    if (rc == 0)
+        return 0;
+    if (rc > 0) {
+        if (ctx->rsa.cipher.oaep_md != NULL)
+            EVP_MD_free(ctx->rsa.cipher.oaep_md);
+        ctx->rsa.cipher.oaep_md = EVP_MD_fetch(ctx->provctx->libctx, name,
+                                               props);
+        if (ctx->rsa.cipher.oaep_md == NULL) {
+            put_error_op_ctx(ctx, IBMCA_ERR_INVALID_PARAM,
+                              "Invalid RSA OAEP digest: '%s'", name);
+            return 0;
+        }
+
+        if ((EVP_MD_get_flags(ctx->rsa.cipher.oaep_md) &
+                                                EVP_MD_FLAG_XOF) != 0) {
+            put_error_op_ctx(ctx, IBMCA_ERR_INVALID_PARAM,
+                             "XOF Digest '%s' is not allowed", name);
+            EVP_MD_free(ctx->rsa.cipher.oaep_md);
+            ctx->rsa.cipher.oaep_md = NULL;
+            return 0;
+        }
+    }
+
+    /* OSSL_ASYM_CIPHER_PARAM_MGF1_DIGEST_PROPS */
+    props = NULL;
+    rc = ibmca_param_get_utf8(ctx->provctx, params,
+                              OSSL_ASYM_CIPHER_PARAM_MGF1_DIGEST_PROPS, &props);
+    if (rc == 0)
+        return 0;
+
+    /* OSSL_ASYM_CIPHER_PARAM_MGF1_DIGEST */
+    rc = ibmca_param_get_utf8(ctx->provctx, params,
+                              OSSL_ASYM_CIPHER_PARAM_MGF1_DIGEST, &name);
+    if (rc == 0)
+        return 0;
+    if (rc > 0) {
+        if (ctx->rsa.cipher.mgf1_md != NULL)
+            EVP_MD_free(ctx->rsa.cipher.mgf1_md);
+        ctx->rsa.cipher.mgf1_md = EVP_MD_fetch(ctx->provctx->libctx, name,
+                                               props);
+        if (ctx->rsa.cipher.mgf1_md == NULL) {
+            put_error_op_ctx(ctx, IBMCA_ERR_INVALID_PARAM,
+                              "Invalid RSA MGF1 digest: '%s'", name);
+            return 0;
+        }
+
+        if ((EVP_MD_get_flags(ctx->rsa.cipher.mgf1_md) &
+                                                EVP_MD_FLAG_XOF) != 0) {
+            put_error_op_ctx(ctx, IBMCA_ERR_INVALID_PARAM,
+                             "XOF Digest '%s' is not allowed", name);
+            EVP_MD_free(ctx->rsa.cipher.mgf1_md);
+            ctx->rsa.cipher.mgf1_md = NULL;
+            return 0;
+        }
+    }
+
+    /* OSSL_ASYM_CIPHER_PARAM_OAEP_LABEL */
+    rc = ibmca_param_get_octet_string(ctx->provctx, params,
+                                      OSSL_ASYM_CIPHER_PARAM_OAEP_LABEL,
+                                      &label, &labellen);
+    if (rc == 0)
+        return 0;
+    if (rc > 0) {
+        if (ctx->rsa.cipher.oaep_label != NULL)
+            P_FREE(ctx->provctx, ctx->rsa.cipher.oaep_label);
+
+        ctx->rsa.cipher.oaep_label = label;
+        ctx->rsa.cipher.oaep_labellen = labellen;
+    }
+
+    /* OSSL_ASYM_CIPHER_PARAM_TLS_CLIENT_VERSION */
+    rc = ibmca_param_get_uint(ctx->provctx, params,
+                              OSSL_ASYM_CIPHER_PARAM_TLS_CLIENT_VERSION,
+                              &ctx->rsa.cipher.tls_clnt_version);
+    if (rc == 0)
+        return 0;
+
+    /* OSSL_ASYM_CIPHER_PARAM_TLS_NEGOTIATED_VERSION */
+    rc = ibmca_param_get_uint(ctx->provctx, params,
+                              OSSL_ASYM_CIPHER_PARAM_TLS_NEGOTIATED_VERSION,
+                              &ctx->rsa.cipher.tls_alt_version);
+    if (rc == 0)
+        return 0;
+
+#ifdef OSSL_ASYM_CIPHER_PARAM_IMPLICIT_REJECTION
+    rc = ibmca_param_get_uint(ctx->provctx, params,
+                              OSSL_ASYM_CIPHER_PARAM_IMPLICIT_REJECTION,
+                              &ctx->rsa.cipher.implicit_rejection);
+    if (rc == 0)
+        return 0;
+#endif
+
+    return 1;
+}
+
+static const OSSL_PARAM ibmca_asym_cipher_rsa_gettable_params[] = {
+    OSSL_PARAM_utf8_string(OSSL_ASYM_CIPHER_PARAM_PAD_MODE, NULL, 0),
+    OSSL_PARAM_utf8_string(OSSL_ASYM_CIPHER_PARAM_OAEP_DIGEST, NULL, 0),
+    OSSL_PARAM_utf8_string(OSSL_ASYM_CIPHER_PARAM_MGF1_DIGEST, NULL, 0),
+    OSSL_PARAM_octet_ptr(OSSL_ASYM_CIPHER_PARAM_OAEP_LABEL, NULL, 0),
+    OSSL_PARAM_uint(OSSL_ASYM_CIPHER_PARAM_TLS_CLIENT_VERSION, NULL),
+    OSSL_PARAM_uint(OSSL_ASYM_CIPHER_PARAM_TLS_NEGOTIATED_VERSION, NULL),
+#ifdef OSSL_ASYM_CIPHER_PARAM_IMPLICIT_REJECTION
+    OSSL_PARAM_uint(OSSL_ASYM_CIPHER_PARAM_IMPLICIT_REJECTION, NULL),
+#endif
+    OSSL_PARAM_END
+};
+
+static const OSSL_PARAM *ibmca_asym_cipher_rsa_gettable_ctx_params(
+                                                void *vctx, void *vprovctx)
+{
+    const struct ibmca_op_ctx *ctx = vctx;
+    const struct ibmca_prov_ctx *provctx = vprovctx;
+    const OSSL_PARAM *p;
+
+    ibmca_debug_ctx(provctx, "ctx: %p", ctx);
+
+    for (p = ibmca_asym_cipher_rsa_gettable_params;
+                                    p != NULL && p->key != NULL; p++)
+        ibmca_debug_ctx(provctx, "param: %s", p->key);
+
+    return ibmca_asym_cipher_rsa_gettable_params;
+}
+
+static const OSSL_PARAM ibmca_asym_cipher_rsa_settable_params[] = {
+    OSSL_PARAM_utf8_string(OSSL_ASYM_CIPHER_PARAM_PAD_MODE, NULL, 0),
+    OSSL_PARAM_utf8_string(OSSL_ASYM_CIPHER_PARAM_OAEP_DIGEST, NULL, 0),
+    OSSL_PARAM_utf8_string(OSSL_ASYM_CIPHER_PARAM_OAEP_DIGEST_PROPS, NULL, 0),
+    OSSL_PARAM_utf8_string(OSSL_ASYM_CIPHER_PARAM_MGF1_DIGEST, NULL, 0),
+    OSSL_PARAM_utf8_string(OSSL_ASYM_CIPHER_PARAM_MGF1_DIGEST_PROPS, NULL, 0),
+    OSSL_PARAM_octet_string(OSSL_ASYM_CIPHER_PARAM_OAEP_LABEL, NULL, 0),
+    OSSL_PARAM_uint(OSSL_ASYM_CIPHER_PARAM_TLS_CLIENT_VERSION, NULL),
+    OSSL_PARAM_uint(OSSL_ASYM_CIPHER_PARAM_TLS_NEGOTIATED_VERSION, NULL),
+#ifdef OSSL_ASYM_CIPHER_PARAM_IMPLICIT_REJECTION
+    OSSL_PARAM_uint(OSSL_ASYM_CIPHER_PARAM_IMPLICIT_REJECTION, NULL),
+#endif
+    OSSL_PARAM_END
+};
+
+static const OSSL_PARAM *ibmca_asym_cipher_rsa_settable_ctx_params(
+                                                void *vctx, void *vprovctx)
+{
+    const struct ibmca_op_ctx *ctx = vctx;
+    const struct ibmca_prov_ctx *provctx = vprovctx;
+    const OSSL_PARAM *p;
+
+    ibmca_debug_ctx(provctx, "ctx: %p", ctx);
+
+    for (p = ibmca_asym_cipher_rsa_settable_params;
+                                p != NULL && p->key != NULL; p++)
+        ibmca_debug_ctx(provctx, "param: %s", p->key);
+
+    return ibmca_asym_cipher_rsa_settable_params;
+}
+
+static int ibmca_asym_cipher_rsa_op_init(struct ibmca_op_ctx *ctx,
+                                         struct ibmca_key *key,
+                                         const OSSL_PARAM params[],
+                                         int operation)
+{
+    const OSSL_PARAM *p;
+
+    if (ctx == NULL || key == NULL)
+        return 0;
+
+    ibmca_debug_op_ctx(ctx, "ctx: %p key: %p operation: %d", ctx, key,
+                       operation);
+    for (p = params; p != NULL && p->key != NULL; p++)
+        ibmca_debug_op_ctx(ctx, "param: %s", p->key);
+
+    if (ibmca_op_init(ctx, key, operation) == 0) {
+        ibmca_debug_op_ctx(ctx, "ERROR: ibmca_op_init failed");
+        return 0;
+    }
+
+    if (key->type == EVP_PKEY_RSA_PSS) {
+        put_error_op_ctx(ctx, IBMCA_ERR_INVALID_PARAM,
+                         "Encrypt/decrypt operation not support for RSA-PSS keys");
+        return 0;
+    }
+
+    /* Setup defaults for this context */
+    ibmca_asym_cipher_rsa_free_cb(ctx);
+    ctx->rsa.cipher.pad_mode = RSA_PKCS1_PADDING;
+    ctx->rsa.cipher.mgf1_md = NULL;
+    ctx->rsa.cipher.oaep_md = NULL;
+    ctx->rsa.cipher.oaep_label = NULL;
+    ctx->rsa.cipher.oaep_labellen = 0;
+#ifdef OSSL_ASYM_CIPHER_PARAM_IMPLICIT_REJECTION
+    ctx->rsa.cipher.implicit_rejection = 1;
+#else
+    ctx->rsa.cipher.implicit_rejection = 0;
+#endif
+
+    if (params != NULL) {
+        if (ibmca_asym_cipher_rsa_set_ctx_params(ctx, params) == 0) {
+            ibmca_debug_op_ctx(ctx,
+                    "ERROR: ibmca_asym_cipher_rsa_set_ctx_params failed");
+            return 0;
+        }
+    }
+
+    return 1;
+
+}
+
+static int ibmca_asym_cipher_rsa_encrypt_init(void *vctx, void *vkey,
+                                              const OSSL_PARAM params[])
+{
+    struct ibmca_op_ctx *ctx = vctx;
+    struct ibmca_key *key = vkey;
+
+    return ibmca_asym_cipher_rsa_op_init(ctx, key, params, EVP_PKEY_OP_ENCRYPT);
+}
+
+static int ibmca_asym_cipher_rsa_decrypt_init(void *vctx, void *vkey,
+                                              const OSSL_PARAM params[])
+{
+    struct ibmca_op_ctx *ctx = vctx;
+    struct ibmca_key *key = vkey;
+
+    return ibmca_asym_cipher_rsa_op_init(ctx, key, params, EVP_PKEY_OP_DECRYPT);
+}
+
+static int ibmca_asym_cipher_rsa_encrypt_fallback(struct ibmca_op_ctx *ctx,
+                                                  unsigned char *out,
+                                                  size_t outsize,
+                                                  const unsigned char *in,
+                                                  size_t inlen)
+{
+    EVP_PKEY *pkey = NULL;
+    EVP_PKEY_CTX *pctx = NULL;
+    size_t outlen;
+    int rc = 0;
+
+    ibmca_debug_op_ctx(ctx, "ctx: %p key: %p inlen: %lu out: %p outsize: %lu",
+                       ctx, ctx->key, inlen, out, outsize);
+
+    pkey = ibmca_new_fallback_pkey(ctx->key);
+    if (pkey == NULL) {
+        ibmca_debug_op_ctx(ctx,"ERROR: ibmca_new_fallback_pkey failed");
+        goto out;
+    }
+
+    pctx = ibmca_new_fallback_pkey_ctx(ctx->provctx, pkey, NULL);
+    if (pctx == NULL) {
+        ibmca_debug_op_ctx(ctx,"ERROR: ibmca_new_fallback_pkey_ctx failed");
+        goto out;
+    }
+
+    if (EVP_PKEY_encrypt_init(pctx) != 1 ||
+        EVP_PKEY_CTX_set_rsa_padding(pctx, RSA_NO_PADDING) != 1) {
+        put_error_op_ctx(ctx, IBMCA_ERR_INTERNAL_ERROR,
+                         "EVP_PKEY_encrypt_init/EVP_PKEY_CTX_set_rsa_padding failed");
+        goto out;
+    }
+
+    if (ibmca_check_fallback_provider(ctx->provctx, pctx) != 1) {
+        ibmca_debug_op_ctx(ctx, "ERROR: ibmca_check_fallback_provider failed");
+        goto out;
+    }
+
+    outlen = outsize;
+    if (EVP_PKEY_encrypt(pctx, out, &outlen, in, inlen) != 1 ||
+        outlen != outsize) {
+        put_error_op_ctx(ctx, IBMCA_ERR_INTERNAL_ERROR,
+                         "EVP_PKEY_encrypt failed");
+        goto out;
+    }
+
+    rc = 1;
+
+out:
+    if (pkey != NULL)
+        EVP_PKEY_free(pkey);
+    if (pctx != NULL)
+        EVP_PKEY_CTX_free(pctx);
+
+    return rc;
+}
+
+static int ibmca_asym_cipher_rsa_encrypt(void *vctx,
+                                         unsigned char *out, size_t *outlen,
+                                         size_t outsize,
+                                         const unsigned char *in, size_t inlen)
+{
+    struct ibmca_op_ctx *ctx = vctx;
+    unsigned char *enc_data;
+    size_t enc_data_len, rsa_size;
+    int rc = 1;
+
+    if (ctx == NULL || in == NULL || outlen == NULL)
+        return 0;
+
+    ibmca_debug_op_ctx(ctx, "ctx: %p key: %p inlen: %lu out: %p outsize: %lu",
+                       ctx, ctx->key, inlen, out, outsize);
+
+    if (ctx->operation != EVP_PKEY_OP_ENCRYPT) {
+        put_error_op_ctx(ctx, IBMCA_ERR_INTERNAL_ERROR, "Invalid operation");
+        return 0;
+    }
+
+    rsa_size = ctx->key->get_max_param_size(ctx->key);
+    *outlen = rsa_size;
+
+    if (out == NULL) /* size query */
+        goto out;
+
+    if (outsize < *outlen) {
+        put_error_op_ctx(ctx, IBMCA_ERR_INVALID_PARAM,
+                         "Output buffer too small");
+        return 0;
+    }
+
+    ibmca_debug_op_ctx(ctx, "pad_mode: %d", ctx->rsa.cipher.pad_mode);
+
+    /* Allocate padding buffer, if required by padding mode */
+    switch (ctx->rsa.cipher.pad_mode) {
+    case RSA_NO_PADDING:
+        if (inlen != rsa_size) {
+            put_error_op_ctx(ctx, IBMCA_ERR_INVALID_PARAM,
+                             "Invalid input length");
+            return 0;
+        }
+        enc_data = (unsigned char *)in;
+        enc_data_len = inlen;
+        break;
+
+    case RSA_PKCS1_PADDING:
+    case RSA_PKCS1_OAEP_PADDING:
+        if (ibmca_op_alloc_tbuf(ctx, rsa_size) == 0) {
+            ibmca_debug_op_ctx(ctx, "ERROR: ibmca_op_alloc_tbuf failed");
+            return 0;
+        }
+
+        enc_data_len = ctx->tbuf_len;
+        enc_data = ctx->tbuf;
+        break;
+
+    case RSA_X931_PADDING:
+    case RSA_PKCS1_PSS_PADDING:
+    case RSA_PKCS1_WITH_TLS_PADDING: /* Only valid for decrypt */
+    default:
+        put_error_op_ctx(ctx, IBMCA_ERR_INVALID_PARAM,
+                         "Invalid padding mode: %d", ctx->rsa.cipher.pad_mode);
+        return 0;
+    }
+
+    /* Perform padding */
+    switch (ctx->rsa.cipher.pad_mode) {
+    case RSA_NO_PADDING:
+        rc = 1;
+        break;
+
+    case RSA_PKCS1_PADDING:
+        rc = ibmca_rsa_add_pkcs1_padding(ctx->key->provctx, 2, in, inlen,
+                                         enc_data, enc_data_len);
+        break;
+
+    case RSA_PKCS1_OAEP_PADDING:
+        rc = ibmca_rsa_add_oaep_mgf1_padding(ctx->key->provctx, in, inlen,
+                                             enc_data, enc_data_len,
+                                             ctx->rsa.cipher.oaep_md,
+                                             ctx->rsa.cipher.mgf1_md,
+                                             ctx->rsa.cipher.oaep_label,
+                                             ctx->rsa.cipher.oaep_labellen);
+        break;
+
+    case RSA_X931_PADDING:
+    case RSA_PKCS1_PSS_PADDING:
+    case RSA_PKCS1_WITH_TLS_PADDING: /* Only valid for decrypt */
+    default:
+        rc = 0;
+        goto out;
+    }
+    if (rc == 0)
+        goto out;
+
+    /* Perform public key encrypt */
+    rc = ica_rsa_mod_expo(ctx->provctx->ica_adapter, enc_data,
+                          &ctx->key->rsa.public, out);
+    if (rc != 0) {
+        ibmca_debug_op_ctx(ctx, "ica_rsa_mod_expo failed with: %s",
+                           strerror(rc));
+
+        rc = ibmca_asym_cipher_rsa_encrypt_fallback(ctx, out, *outlen,
+                                                    enc_data, enc_data_len);
+        if (rc != 1) {
+            ibmca_debug_op_ctx(ctx,
+                               "ERROR: ibmca_asym_cipher_rsa_encrypt_fallback failed");
+            rc = 0;
+            goto out;
+        }
+    }
+
+    rc = 1;
+
+ out:
+     if (ctx->tbuf != NULL)
+         P_CLEANSE(ctx->provctx, ctx->tbuf, ctx->tbuf_len);
+
+    ibmca_debug_op_ctx(ctx, "outlen: %lu rc: %d", *outlen, rc);
+
+    return rc;
+
+}
+
+static int ibmca_asym_cipher_rsa_decrypt_fallback(struct ibmca_op_ctx *ctx,
+                                                  unsigned char *out,
+                                                  size_t outsize,
+                                                  const unsigned char *in,
+                                                  size_t inlen)
+{
+    EVP_PKEY *pkey = NULL;
+    EVP_PKEY_CTX *pctx = NULL;
+    size_t outlen;
+    int rc = 0;
+
+    ibmca_debug_op_ctx(ctx, "ctx: %p key: %p inlen: %lu out: %p outsize: %lu",
+                       ctx, ctx->key, inlen, out, outsize);
+
+    pkey = ibmca_new_fallback_pkey(ctx->key);
+    if (pkey == NULL) {
+        ibmca_debug_op_ctx(ctx,"ERROR: ibmca_new_fallback_pkey failed");
+        goto out;
+    }
+
+    pctx = ibmca_new_fallback_pkey_ctx(ctx->provctx, pkey, NULL);
+    if (pctx == NULL) {
+        ibmca_debug_op_ctx(ctx,"ERROR: ibmca_new_fallback_pkey_ctx failed");
+        goto out;
+    }
+
+    if (EVP_PKEY_decrypt_init(pctx) != 1 ||
+        EVP_PKEY_CTX_set_rsa_padding(pctx, RSA_NO_PADDING) != 1) {
+        put_error_op_ctx(ctx, IBMCA_ERR_INTERNAL_ERROR,
+                         "EVP_PKEY_decrypt_init/EVP_PKEY_CTX_set_rsa_padding failed");
+        goto out;
+    }
+
+    if (ibmca_check_fallback_provider(ctx->provctx, pctx) != 1) {
+        ibmca_debug_op_ctx(ctx, "ERROR: ibmca_check_fallback_provider failed");
+        goto out;
+    }
+
+    outlen = outsize;
+    if (EVP_PKEY_decrypt(pctx, out, &outlen, in, inlen) != 1 ||
+        outlen != outsize) {
+        put_error_op_ctx(ctx, IBMCA_ERR_INTERNAL_ERROR,
+                         "EVP_PKEY_decrypt failed");
+        goto out;
+    }
+
+    rc = 1;
+
+out:
+    if (pkey != NULL)
+        EVP_PKEY_free(pkey);
+    if (pctx != NULL)
+        EVP_PKEY_CTX_free(pctx);
+
+    return rc;
+}
+
+static int ibmca_asym_cipher_rsa_decrypt(void *vctx,
+                                         unsigned char *out, size_t *outlen,
+                                         size_t outsize,
+                                         const unsigned char *in, size_t inlen)
+{
+    unsigned char kdk[SHA256_DIGEST_LENGTH] = {0};
+    struct ibmca_op_ctx *ctx = vctx;
+    unsigned char *dec_data;
+    size_t dec_data_len, rsa_size;
+    int rc = 1;
+
+    if (ctx == NULL || in == NULL || outlen == NULL)
+        return 0;
+
+    ibmca_debug_op_ctx(ctx, "ctx: %p key: %p inlen: %lu out: %p outsize: %lu",
+                       ctx, ctx->key, inlen, out, outsize);
+
+    if (ctx->operation != EVP_PKEY_OP_DECRYPT) {
+        put_error_op_ctx(ctx, IBMCA_ERR_INTERNAL_ERROR, "Invalid operation");
+        return 0;
+    }
+
+    rsa_size = ctx->key->get_max_param_size(ctx->key);
+
+    if (ctx->rsa.cipher.pad_mode == RSA_PKCS1_WITH_TLS_PADDING)
+        *outlen = IBMCA_SSL_MAX_MASTER_KEY_LENGTH;
+    else
+        *outlen = rsa_size;
+
+    if (out == NULL) /* size query */
+        goto out;
+
+    if (outsize < *outlen) {
+        put_error_op_ctx(ctx, IBMCA_ERR_INVALID_PARAM, "Output buffer too small");
+        return 0;
+    }
+
+    if (inlen != rsa_size) {
+        put_error_op_ctx(ctx, IBMCA_ERR_INVALID_PARAM,
+                         "Invalid input length");
+        return 0;
+    }
+
+    ibmca_debug_op_ctx(ctx, "pad_mode: %d", ctx->rsa.cipher.pad_mode);
+
+    /* Allocate padding buffer, if required by padding mode */
+    switch (ctx->rsa.cipher.pad_mode) {
+    case RSA_NO_PADDING:
+        dec_data = out;
+        dec_data_len = *outlen;
+        break;
+
+    case RSA_PKCS1_PADDING:
+    case RSA_PKCS1_OAEP_PADDING:
+    case RSA_PKCS1_WITH_TLS_PADDING:
+        if (ibmca_op_alloc_tbuf(ctx, rsa_size) == 0) {
+            ibmca_debug_op_ctx(ctx, "ERROR: ibmca_op_alloc_tbuf failed");
+            return 0;
+        }
+
+        dec_data_len = ctx->tbuf_len;
+        dec_data = ctx->tbuf;
+        break;
+
+    case RSA_X931_PADDING:
+    case RSA_PKCS1_PSS_PADDING:
+    default:
+        put_error_op_ctx(ctx, IBMCA_ERR_INVALID_PARAM,
+                         "Invalid padding mode: %d", ctx->rsa.cipher.pad_mode);
+        return 0;
+    }
+
+    /* Perform private key decrypt */
+    rc = ibmca_rsa_priv_with_blinding(ctx->key, in, dec_data, rsa_size);
+    if (rc != 1) {
+        ibmca_debug_op_ctx(ctx, "ibmca_asym_cipher_rsa_with_blinding failed");
+
+        rc = ibmca_asym_cipher_rsa_decrypt_fallback(ctx, dec_data, dec_data_len,
+                                                    in, inlen);
+        if (rc != 1) {
+            ibmca_debug_op_ctx(ctx,
+                               "ERROR: ibmca_asym_cipher_rsa_decrypt_fallback failed");
+            rc = 0;
+            goto out;
+        }
+    }
+
+    /* Perform padding check */
+    switch (ctx->rsa.cipher.pad_mode) {
+    case RSA_NO_PADDING:
+        /* outlen is already set */
+        rc = 1;
+        break;
+
+    case RSA_PKCS1_PADDING:
+        if (ctx->rsa.cipher.implicit_rejection) {
+            rc = ibmca_keymgmt_rsa_derive_kdk(ctx->key, in, inlen,
+                                              kdk, sizeof(kdk));
+            if (rc == 0)
+                goto out;
+        }
+
+        rc = ibmca_rsa_check_pkcs1_padding_type2(ctx->key->provctx,
+                                                 dec_data, dec_data_len,
+                                                 out, outsize, outlen,
+                                                 ctx->rsa.cipher.implicit_rejection
+                                                         ? kdk : NULL,
+                                                 ctx->rsa.cipher.implicit_rejection
+                                                         ? sizeof(kdk) : 0);
+        break;
+
+    case RSA_PKCS1_OAEP_PADDING:
+        rc = ibmca_rsa_check_oaep_mgf1_padding(ctx->key->provctx,
+                                               dec_data, dec_data_len,
+                                               out, outsize, outlen,
+                                               ctx->rsa.cipher.oaep_md,
+                                               ctx->rsa.cipher.mgf1_md,
+                                               ctx->rsa.cipher.oaep_label,
+                                               ctx->rsa.cipher.oaep_labellen);
+        break;
+
+    case RSA_PKCS1_WITH_TLS_PADDING:
+        rc = ibmca_rsa_check_pkcs1_tls_padding(ctx->key->provctx,
+                                               ctx->rsa.cipher.tls_clnt_version,
+                                               ctx->rsa.cipher.tls_alt_version,
+                                               dec_data, dec_data_len,
+                                               out, outsize, outlen);
+        break;
+
+    case RSA_X931_PADDING:
+    case RSA_PKCS1_PSS_PADDING:
+    default:
+        rc = 0;
+        goto out;
+    }
+    if (rc == 0)
+        goto out;
+
+    rc = 1;
+
+out:
+    if (ctx->tbuf != NULL)
+        P_CLEANSE(ctx->provctx, ctx->tbuf, ctx->tbuf_len);
+
+    ibmca_debug_op_ctx(ctx, "outlen: %lu rc: %d", *outlen, rc);
+
+    return rc;
+}
+
+static const OSSL_DISPATCH ibmca_rsa_asym_cipher_functions[] = {
+    /* RSA context constructor, destructor */
+    { OSSL_FUNC_ASYM_CIPHER_NEWCTX,
+            (void (*)(void))ibmca_asym_cipher_rsa_newctx },
+    { OSSL_FUNC_ASYM_CIPHER_FREECTX, (void (*)(void))ibmca_op_freectx },
+    { OSSL_FUNC_ASYM_CIPHER_DUPCTX,
+            (void (*)(void))ibmca_op_dupctx },
+    /* RSA context set/get parameters */
+    { OSSL_FUNC_ASYM_CIPHER_GET_CTX_PARAMS,
+            (void (*)(void))ibmca_asym_cipher_rsa_get_ctx_params },
+    { OSSL_FUNC_ASYM_CIPHER_GETTABLE_CTX_PARAMS,
+            (void (*)(void))ibmca_asym_cipher_rsa_gettable_ctx_params },
+    { OSSL_FUNC_ASYM_CIPHER_SET_CTX_PARAMS,
+            (void (*)(void))ibmca_asym_cipher_rsa_set_ctx_params },
+    { OSSL_FUNC_ASYM_CIPHER_SETTABLE_CTX_PARAMS,
+            (void (*)(void))ibmca_asym_cipher_rsa_settable_ctx_params },
+    /* RSA encrypt */
+    { OSSL_FUNC_ASYM_CIPHER_ENCRYPT_INIT,
+            (void (*)(void))ibmca_asym_cipher_rsa_encrypt_init },
+    { OSSL_FUNC_ASYM_CIPHER_ENCRYPT,
+            (void (*)(void))ibmca_asym_cipher_rsa_encrypt },
+    /* RSA decrypt */
+    { OSSL_FUNC_ASYM_CIPHER_DECRYPT_INIT,
+            (void (*)(void))ibmca_asym_cipher_rsa_decrypt_init },
+    { OSSL_FUNC_ASYM_CIPHER_DECRYPT,
+            (void (*)(void))ibmca_asym_cipher_rsa_decrypt },
+    { 0, NULL }
+};
+
+const OSSL_ALGORITHM ibmca_rsa_asym_cipher[] = {
+    { "RSA:rsaEncryption:1.2.840.113549.1.1.1", NULL,
+      ibmca_rsa_asym_cipher_functions, "IBMCA RSA asym cipher implementation" },
+    { NULL, NULL, NULL, NULL }
+};
diff -pruN 1.4.0-1/src/provider/rsa_blinding.c 2.5.0-0ubuntu1/src/provider/rsa_blinding.c
--- 1.4.0-1/src/provider/rsa_blinding.c	1970-01-01 00:00:00.000000000 +0000
+++ 2.5.0-0ubuntu1/src/provider/rsa_blinding.c	2025-04-16 08:09:24.000000000 +0000
@@ -0,0 +1,491 @@
+/*
+ * Copyright [2023] International Business Machines Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <stddef.h>
+#include <err.h>
+#include <strings.h>
+#include <string.h>
+#include <errno.h>
+
+#include <openssl/evp.h>
+#include <openssl/bn.h>
+#include <openssl/rsa.h>
+
+#include "p_ibmca.h"
+
+#ifdef SIXTY_FOUR_BIT_LONG
+    #define BN_MASK2        (0xffffffffffffffffL)
+#endif
+#ifdef SIXTY_FOUR_BIT
+    #define BN_MASK2        (0xffffffffffffffffLL)
+#endif
+#ifdef THIRTY_TWO_BIT
+    #error "Not supported"
+#endif
+
+/*
+ * Provider context used by mod-expo callback function for generating the
+ * blinding factor by BN_BLINDING_create_param() or within BN_BLINDING_update()
+ * when a new blinding factor is generated after 32 requests.
+ * This variable must be thread local!
+ */
+static __thread const struct ibmca_prov_ctx *ibmca_mod_expo_provctx = NULL;
+
+static int ibmca_rsa_blinding_bn_mod_exp(BIGNUM *r, const BIGNUM *a,
+                                         const BIGNUM *p, const BIGNUM *m,
+                                         BN_CTX *ctx, BN_MONT_CTX *m_ctx)
+{
+    const struct ibmca_prov_ctx *provctx = ibmca_mod_expo_provctx;
+    ica_rsa_key_mod_expo_t ica_mode_expo;
+    unsigned char *buffer, *in, *out;
+    size_t size;
+    int rc = 0;
+
+    if (provctx == NULL)
+        return 0;
+
+    ibmca_debug_ctx(provctx, "provctx: %p", provctx);
+
+    size = BN_num_bytes(m);
+    buffer = P_ZALLOC(provctx, 4 * size);
+    if (buffer == NULL) {
+        ibmca_debug_ctx(provctx,
+                        "Failed to allocate a buffer for libica mod-expo");
+        goto out;
+    }
+
+    ica_mode_expo.key_length = size;
+    ica_mode_expo.modulus = buffer;
+    ica_mode_expo.exponent = buffer + size;
+
+    in = buffer + 2 * size;
+    out = buffer + 3 * size;
+
+    if (BN_bn2binpad(a, in, size) == -1 ||
+        BN_bn2binpad(p, ica_mode_expo.exponent, size) == -1 ||
+        BN_bn2binpad(m, ica_mode_expo.modulus, size) == -1) {
+        ibmca_debug_ctx(provctx, "BN_bn2binpad failed");
+        goto out;
+    }
+
+    rc = ica_rsa_mod_expo(provctx->ica_adapter, in, &ica_mode_expo, out);
+    if (rc != 0) {
+        ibmca_debug_ctx(provctx, "ica_rsa_mod_expo failed with: %s",
+                        strerror(rc));
+        rc = 0;
+        goto out;
+    }
+
+    if (BN_bin2bn(out, size, r) == NULL) {
+        ibmca_debug_ctx(provctx, "BN_bin2bn failed");
+        goto out;
+    }
+
+    rc = 1;
+
+out:
+    P_CLEAR_FREE(provctx, buffer, 4 * size);
+
+    ibmca_debug_ctx(provctx, "rc: %d", rc);
+
+    /* Use software fallback if libica operation failed */
+    return rc != 1 ? BN_mod_exp_mont(r, a, p, m, ctx, m_ctx) : 1;
+}
+
+static BN_BLINDING *ibmca_rsa_setup_blinding(struct ibmca_key *key)
+{
+    BIGNUM *n = NULL, *e = NULL, *R = NULL, *Ri = NULL, *tmod = NULL;
+    BN_CTX *bn_ctx = NULL;
+    BN_BLINDING *blinding = NULL;
+    BN_ULONG word;
+    int rc;
+
+    ibmca_debug_key(key, "key: %p", key);
+
+    bn_ctx = BN_CTX_new_ex(key->provctx->libctx);
+    if (bn_ctx == NULL) {
+        put_error_key(key, IBMCA_ERR_INTERNAL_ERROR, "BN_CTX_new_ex failed");
+        goto out;
+    }
+
+    rc =  ibmca_keymgmt_rsa_pub_as_bn(key, &n, &e);
+    if (rc == 0) {
+        put_error_key(key, IBMCA_ERR_INTERNAL_ERROR, "No public key available");
+        goto out;
+    }
+
+    BN_set_flags(n, BN_FLG_CONSTTIME);
+
+    /*
+     * Setup the BN_MONT_CTX if needed, it is required by for the mod-expo
+     * callback passed to BN_BLINDING_create_param(). The callback won't be
+     * called if BN_MONT_CTX is NULL.
+     * We hold the write lock on blinding_lock when this function is called,
+     * so no need to use BN_MONT_CTX_set_locked().
+     */
+    if (key->rsa.blinding_mont_ctx == NULL) {
+        key->rsa.blinding_mont_ctx = BN_MONT_CTX_new();
+        if (key->rsa.blinding_mont_ctx == NULL) {
+            put_error_key(key, IBMCA_ERR_INTERNAL_ERROR,
+                          "BN_MONT_CTX_new failed");
+            goto out;
+        }
+
+        if (BN_MONT_CTX_set(key->rsa.blinding_mont_ctx, n, bn_ctx) != 1) {
+            BN_MONT_CTX_free(key->rsa.blinding_mont_ctx);
+            key->rsa.blinding_mont_ctx = NULL;
+
+            put_error_key(key, IBMCA_ERR_INTERNAL_ERROR,
+                          "BN_MONT_CTX_new failed");
+            goto out;
+        }
+
+        /* Calculate blinding_mont_ctx_n0, BN_MONT_CTX is opaque */
+        R = BN_CTX_get(bn_ctx);
+        Ri = BN_CTX_get(bn_ctx);
+        tmod = BN_CTX_get(bn_ctx);
+        if (R == NULL || Ri == NULL || tmod == NULL) {
+            put_error_key(key, IBMCA_ERR_INTERNAL_ERROR, "BN_CTX_get failed");
+            goto out;
+        }
+
+        BN_zero(R);
+        if (!BN_set_bit(R, BN_BITS2)) {
+            put_error_key(key, IBMCA_ERR_INTERNAL_ERROR, "BN_set_bit failed");
+            goto out;
+        }
+
+        memcpy(&word, key->rsa.public.modulus + key->rsa.public.key_length -
+                      sizeof(BN_ULONG), sizeof(word));
+        if (!BN_set_word(tmod, word)) {
+            put_error_key(key, IBMCA_ERR_INTERNAL_ERROR, "BN_set_word failed");
+            goto out;
+        }
+
+        if (BN_is_one(tmod))
+            BN_zero(Ri);
+        else if ((BN_mod_inverse(Ri, R, tmod, bn_ctx)) == NULL) {
+            put_error_key(key, IBMCA_ERR_INTERNAL_ERROR, "BN_mod_inverse failed");
+            goto out;
+        }
+        if (!BN_lshift(Ri, Ri, BN_BITS2)) {
+            put_error_key(key, IBMCA_ERR_INTERNAL_ERROR, "BN_lshift failed");
+            goto out;
+        }
+
+        if (!BN_is_zero(Ri)) {
+            if (!BN_sub_word(Ri, 1)) {
+                put_error_key(key, IBMCA_ERR_INTERNAL_ERROR, "BN_sub_word failed");
+                goto out;
+            }
+        } else {
+            if (!BN_set_word(Ri, BN_MASK2)) {
+                put_error_key(key, IBMCA_ERR_INTERNAL_ERROR, "BN_set_word failed");
+                goto out;
+            }
+        }
+
+        if (!BN_div(Ri, NULL, Ri, tmod, bn_ctx)) {
+            put_error_key(key, IBMCA_ERR_INTERNAL_ERROR, "BN_div failed");
+            goto out;
+        }
+
+        key->rsa.blinding_mont_ctx_n0 = BN_get_word(Ri);
+    }
+
+    /*
+     * BN_BLINDING_create_param() calls the ibmca_rsa_blinding_bn_mod_exp()
+     * callback which needs to know the provider context.
+     */
+    ibmca_mod_expo_provctx = key->provctx;
+
+    blinding = BN_BLINDING_create_param(NULL, e, n, bn_ctx,
+                                        ibmca_rsa_blinding_bn_mod_exp,
+                                        key->rsa.blinding_mont_ctx);
+    if (blinding == NULL) {
+        put_error_key(key, IBMCA_ERR_INTERNAL_ERROR,
+                      "BN_BLINDING_create_param failed");
+        goto out;
+    }
+
+    BN_BLINDING_set_current_thread(blinding);
+
+out:
+    BN_free(n);
+    BN_free(e);
+    BN_CTX_free(bn_ctx);
+
+    ibmca_debug_key(key, "blinding: %p", blinding);
+
+    return blinding;
+}
+
+static BN_BLINDING *ibmca_rsa_get_blinding(struct ibmca_key *key, bool *local)
+{
+    BN_BLINDING *blinding = NULL;
+
+    ibmca_debug_key(key, "key: %p", key);
+
+    if (pthread_rwlock_rdlock(&key->rsa.blinding_lock) != 0) {
+        put_error_key(key, IBMCA_ERR_INTERNAL_ERROR,
+                      "pthread_rwlock_rdlock failed: %s", strerror(errno));
+        goto out;
+    }
+
+    while (1) {
+        blinding = key->rsa.blinding;
+        if (blinding != NULL) {
+            if (BN_BLINDING_is_current_thread(blinding)) {
+                *local = true;
+            } else {
+                /*
+                 * BN_BLINDING is shared, meaning that accesses require locks,
+                 * and that the blinding factor must be stored outside the
+                 * BN_BLINDING
+                 */
+                *local = false;
+                blinding = key->rsa.mt_blinding;
+            }
+        }
+
+        pthread_rwlock_unlock(&key->rsa.blinding_lock);
+
+        if (blinding != NULL)
+            break;
+
+        /* WR-lock the blinding lock while setting up the blinding */
+        if (pthread_rwlock_wrlock(&key->rsa.blinding_lock) != 0) {
+            put_error_key(key, IBMCA_ERR_INTERNAL_ERROR,
+                          "pthread_rwlock_wrlock failed: %s", strerror(errno));
+            goto out;
+        }
+
+        if (key->rsa.blinding == NULL) {
+            key->rsa.blinding = ibmca_rsa_setup_blinding(key);
+            if (key->rsa.blinding == NULL) {
+                pthread_rwlock_unlock(&key->rsa.blinding_lock);
+                goto out;
+            }
+
+            continue;
+        }
+
+        if (key->rsa.mt_blinding == NULL) {
+            key->rsa.mt_blinding = ibmca_rsa_setup_blinding(key);
+            if (key->rsa.mt_blinding == NULL) {
+                pthread_rwlock_unlock(&key->rsa.blinding_lock);
+                goto out;
+            }
+
+            continue;
+        }
+    }
+
+out:
+    ibmca_debug_key(key, "blinding: %p local: %d", blinding, *local);
+
+    return blinding;
+}
+
+static int ibmca_rsa_blinding_convert(struct ibmca_key *key,
+                                      BN_BLINDING *blinding,
+                                      BIGNUM *unblind, BN_CTX *bn_ctx,
+                                      const unsigned char *in,
+                                      unsigned char *out,
+                                      size_t rsa_size, bool local)
+{
+    BIGNUM *bn_in;
+    int rc = 0;
+
+    ibmca_debug_key(key, "key: %p rsa_size: %lu local: %d",
+                    key, rsa_size, local);
+
+    bn_in = BN_CTX_get(bn_ctx);
+    if (bn_in == NULL ||
+        BN_bin2bn(in, (int)rsa_size, bn_in) == NULL) {
+        put_error_key(key, IBMCA_ERR_INTERNAL_ERROR,
+                     "BN_CTX_get/BN_bin2bn failed");
+        goto out;
+    }
+
+    if (!local) {
+        /* Shared blinding requires locks */
+        if (!BN_BLINDING_lock(blinding)) {
+            put_error_key(key, IBMCA_ERR_INTERNAL_ERROR,
+                          "BN_BLINDING_lock failed");
+            goto out;
+        }
+    }
+
+    /* BN_BLINDING_convert_ex() calls BN_BLINDING_update() which may call
+     * BN_BLINDING_create_param() to generate a new blinding factor. This
+     * calls the ibmca_rsa_blinding_bn_mod_exp() callback which needs to know
+     * the provider context.
+     */
+    ibmca_mod_expo_provctx = key->provctx;
+
+    rc = BN_BLINDING_convert_ex(bn_in, unblind, blinding, bn_ctx);
+
+    if (!local)
+        BN_BLINDING_unlock(blinding);
+
+    if (rc != 1) {
+        put_error_key(key, IBMCA_ERR_INTERNAL_ERROR,
+                      "BN_BLINDING_convert_ex failed");
+        goto out;
+    }
+
+    rc = BN_bn2binpad(bn_in, out, rsa_size);
+    if (rc != (int)rsa_size) {
+        put_error_key(key, IBMCA_ERR_INTERNAL_ERROR, "BN_bn2binpad failed");
+        goto out;
+    }
+
+    rc = 1;
+
+out:
+    ibmca_debug_key(key, "rc: %d", rc);
+
+    return rc;
+}
+
+static int ibmca_rsa_blinding_invert(struct ibmca_key *key,
+                                     BIGNUM *unblind,
+                                     const unsigned char *in,
+                                     unsigned char *out,
+                                     size_t rsa_size)
+{
+    int rc;
+
+    ibmca_debug_key(key, "key: %p rsa_size: %lu", key, rsa_size);
+
+    rc = ossl_bn_rsa_do_unblind(in, unblind, key->rsa.public.modulus,
+                                out, rsa_size, key->rsa.blinding_mont_ctx,
+                                key->rsa.blinding_mont_ctx_n0);
+    if (rc <= 0) {
+        put_error_key(key, IBMCA_ERR_INTERNAL_ERROR,
+                      "ossl_bn_rsa_do_unblind failed");
+        goto out;
+    }
+
+    rc = 1;
+
+out:
+    ibmca_debug_key(key, "rc: %d", rc);
+
+    return rc;
+}
+
+int ibmca_rsa_priv_with_blinding(struct ibmca_key *key, const unsigned char *in,
+                                 unsigned char *out, size_t rsa_size)
+{
+    BN_BLINDING *blinding;
+    bool local_blinding = false;
+    BIGNUM *unblind = NULL;
+    BN_CTX *bn_ctx = NULL;
+    unsigned char *buf = NULL;
+    int rc = 0;
+
+    ibmca_debug_key(key, "key: %p rsa_size: %lu", key, rsa_size);
+
+    if (rsa_size != key->rsa.keylength) {
+        put_error_key(key, IBMCA_ERR_INTERNAL_ERROR,
+                      "rsa_size is not modulus size");
+        goto out;
+    }
+
+    bn_ctx = BN_CTX_new_ex(key->provctx->libctx);
+    if (bn_ctx == NULL) {
+        put_error_key(key, IBMCA_ERR_INTERNAL_ERROR, "BN_CTX_new_ex failed");
+        goto out;
+    }
+
+    buf = P_SECURE_ZALLOC(key->provctx, rsa_size * 2);
+    if (buf == NULL) {
+        put_error_key(key, IBMCA_ERR_MALLOC_FAILED,
+                      "Failed to allocate blinding buffer");
+        goto out;
+    }
+
+    blinding = ibmca_rsa_get_blinding(key, &local_blinding);
+    if (blinding == NULL) {
+        ibmca_debug_key(key, "ERROR: ibmca_keymgmt_rsa_get_blinding failed");
+        goto out;
+    }
+
+    unblind = BN_CTX_get(bn_ctx);
+    if (unblind == NULL) {
+        put_error_key(key, IBMCA_ERR_INTERNAL_ERROR, "BN_CTX_get failed");
+        goto out;
+    }
+
+    BN_set_flags(unblind, BN_FLG_CONSTTIME);
+
+    rc = ibmca_rsa_blinding_convert(key, blinding, unblind, bn_ctx,
+                                    in, buf, rsa_size, local_blinding);
+    if (rc == 0) {
+        ibmca_debug_key(key,
+                        "ERROR: ibmca_keymgmt_rsa_blinding_convert failed");
+        goto out;
+    }
+
+    if (ibmca_keymgmt_rsa_priv_crt_valid(&key->rsa.private_crt)) {
+        rc = ica_rsa_crt(key->provctx->ica_adapter, buf,
+                         &key->rsa.private_crt, buf + rsa_size);
+        if (rc != 0) {
+            ibmca_debug_key(key, "ERROR: ica_rsa_crt failed with: %s",
+                            strerror(rc));
+            rc = 0;
+            goto out;
+        }
+    } else  if (ibmca_keymgmt_rsa_priv_me_valid(&key->rsa.private_me)) {
+        rc = ica_rsa_mod_expo(key->provctx->ica_adapter, buf,
+                         &key->rsa.private_me, buf + rsa_size);
+        if (rc != 0) {
+            ibmca_debug_key(key, "ERROR: ica_rsa_mod_expo failed with: %s",
+                            strerror(rc));
+            rc = 0;
+            goto out;
+        }
+    } else {
+        put_error_key(key, IBMCA_ERR_INTERNAL_ERROR, "No private key");
+        rc = 0;
+        goto out;
+    }
+
+    rc = ibmca_rsa_blinding_invert(key, unblind, buf + rsa_size, out, rsa_size);
+    if (rc == 0) {
+        ibmca_debug_key(key,
+                        "ERROR: ibmca_rsa_blinding_invert failed");
+        goto out;
+    }
+
+    rc = 1;
+
+out:
+    if (buf != NULL)
+        P_SECURE_CLEAR_FREE(key->provctx, buf, rsa_size * 2);
+    if (bn_ctx != NULL)
+        BN_CTX_free(bn_ctx);
+
+    ibmca_debug_key(key, "rc: %d", rc);
+
+    return rc;
+}
+
diff -pruN 1.4.0-1/src/provider/rsa_keymgmt.c 2.5.0-0ubuntu1/src/provider/rsa_keymgmt.c
--- 1.4.0-1/src/provider/rsa_keymgmt.c	1970-01-01 00:00:00.000000000 +0000
+++ 2.5.0-0ubuntu1/src/provider/rsa_keymgmt.c	2025-04-16 08:09:24.000000000 +0000
@@ -0,0 +1,2418 @@
+/*
+ * Copyright [2021-2022] International Business Machines Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <stddef.h>
+#include <err.h>
+#include <strings.h>
+#include <string.h>
+#include <errno.h>
+
+#include <openssl/evp.h>
+#include <openssl/bn.h>
+#include <openssl/rsa.h>
+#include <openssl/core.h>
+#include <openssl/core_dispatch.h>
+#include <openssl/core_names.h>
+#include <openssl/params.h>
+#include <openssl/param_build.h>
+#include <openssl/sha.h>
+#include <openssl/hmac.h>
+
+#include "p_ibmca.h"
+
+#define ICA_P_LEN(key_len)          (((key_len) + 1) / 2 + 8)
+#define ICA_Q_LEN(key_len)          (((key_len) + 1) / 2)
+#define ICA_DP_LEN(key_len)         (((key_len) + 1) / 2 + 8)
+#define ICA_DQ_LEN(key_len)         (((key_len) + 1) / 2)
+#define ICA_QINV_LEN(key_len)       (((key_len) + 1) / 2 + 8)
+
+static const struct ibmca_pss_params ibmca_rsa_pss_defaults =
+                                            IBMCA_RSA_PSS_DEFAULTS;
+
+static OSSL_FUNC_keymgmt_new_fn ibmca_keymgmt_rsa_new;
+static OSSL_FUNC_keymgmt_new_fn ibmca_keymgmt_rsa_pss_new;
+static OSSL_FUNC_keymgmt_gen_init_fn ibmca_keymgmt_rsa_gen_init;
+static OSSL_FUNC_keymgmt_gen_init_fn ibmca_keymgmt_rsa_pss_gen_init;
+static OSSL_FUNC_keymgmt_gen_set_template_fn ibmca_keymgmt_rsa_gen_set_template;
+static OSSL_FUNC_keymgmt_gen_set_params_fn ibmca_keymgmt_rsa_gen_set_params;
+static OSSL_FUNC_keymgmt_gen_settable_params_fn
+                                        ibmca_keymgmt_rsa_gen_settable_params;
+static OSSL_FUNC_keymgmt_gen_settable_params_fn
+                                    ibmca_keymgmt_rsa_pss_gen_settable_params;
+static OSSL_FUNC_keymgmt_gen_fn ibmca_keymgmt_rsa_gen;
+static OSSL_FUNC_keymgmt_has_fn ibmca_keymgmt_rsa_has;
+static OSSL_FUNC_keymgmt_match_fn ibmca_keymgmt_rsa_match;
+static OSSL_FUNC_keymgmt_validate_fn ibmca_keymgmt_rsa_validate;
+static OSSL_FUNC_keymgmt_query_operation_name_fn
+                                        ibmca_keymgmt_rsa_query_operation_name;
+static OSSL_FUNC_keymgmt_get_params_fn ibmca_keymgmt_rsa_get_params;
+static OSSL_FUNC_keymgmt_gettable_params_fn ibmca_keymgmt_rsa_gettable_params;
+static OSSL_FUNC_keymgmt_gettable_params_fn
+                                        ibmca_keymgmt_rsa_pss_gettable_params;
+static OSSL_FUNC_keymgmt_export_fn ibmca_keymgmt_rsa_export;
+static OSSL_FUNC_keymgmt_export_types_fn ibmca_keymgmt_rsa_export_types;
+static OSSL_FUNC_keymgmt_export_types_fn ibmca_keymgmt_rsa_pss_export_types;
+static OSSL_FUNC_keymgmt_import_fn ibmca_keymgmt_rsa_import;
+static OSSL_FUNC_keymgmt_import_types_fn ibmca_keymgmt_rsa_import_types;
+static OSSL_FUNC_keymgmt_import_types_fn ibmca_keymgmt_rsa_pss_import_types;
+
+static void ibmca_keymgmt_rsa_free_cb(struct ibmca_key *key);
+static int ibmca_keymgmt_rsa_dup_cb(const struct ibmca_key *key,
+                                    struct ibmca_key *new_key);
+static int ibmca_keymgmt_rsa_calc_priv_d(const struct ibmca_prov_ctx *provctx,
+                                         BIGNUM *n, BIGNUM *e,
+                                         BIGNUM *p, BIGNUM *q,
+                                         BIGNUM **d);
+
+static int ibmca_keymgmt_rsa_pss_parms_from_data(
+                                        const struct ibmca_prov_ctx *provctx,
+                                        const OSSL_PARAM params[],
+                                        struct ibmca_pss_params *pss)
+{
+    const char *props = NULL;
+    const char *name;
+    EVP_MD *md;
+    int rc;
+
+    /* OSSL_PKEY_PARAM_RSA_DIGEST_PROPS */
+    rc = ibmca_param_get_utf8(provctx, params,
+                              OSSL_PKEY_PARAM_RSA_DIGEST_PROPS, &props);
+    if (rc == 0)
+        return 0;
+
+    /* OSSL_PKEY_PARAM_RSA_DIGEST */
+    rc = ibmca_param_get_utf8(provctx, params,
+                              OSSL_PKEY_PARAM_RSA_DIGEST, &name);
+    if (rc == 0)
+        return 0;
+    if (rc > 0) {
+        md = EVP_MD_fetch(provctx->libctx, name, props);
+        if (md == NULL) {
+            put_error_ctx(provctx, IBMCA_ERR_INVALID_PARAM,
+                          "RSA PSS '%s'='%s' could not be fetched",
+                          OSSL_PKEY_PARAM_RSA_DIGEST, name);
+            return 0;
+        }
+
+        if ((EVP_MD_get_flags(md) & EVP_MD_FLAG_XOF) != 0) {
+            put_error_ctx(provctx, IBMCA_ERR_INVALID_PARAM,
+                          "XOF Digest '%s' is not allowed", name);
+            EVP_MD_free(md);
+            return 0;
+        }
+
+        pss->digest_nid = EVP_MD_get_type(md);
+        EVP_MD_free(md);
+        pss->restricted = true;
+    }
+
+    /* OSSL_PKEY_PARAM_RSA_MASKGENFUNC */
+    rc = ibmca_param_get_utf8(provctx, params,
+                              OSSL_PKEY_PARAM_RSA_MASKGENFUNC, &name);
+    if (rc == 0)
+        return 0;
+    if (rc > 0) {
+        if (strcasecmp(name, OBJ_nid2sn(IBMCA_RSA_PSS_DEFAULT_MGF)) != 0) {
+            put_error_ctx(provctx, IBMCA_ERR_INVALID_PARAM,
+                          "RSA PSS '%s'='%s' is not supported",
+                          OSSL_PKEY_PARAM_RSA_MASKGENFUNC, name);
+            return 0;
+        }
+
+        pss->mgf_nid = IBMCA_RSA_PSS_DEFAULT_MGF;
+        pss->restricted = true;
+    }
+
+    /* OSSL_PKEY_PARAM_RSA_MGF1_DIGEST */
+    rc = ibmca_param_get_utf8(provctx, params,
+                              OSSL_PKEY_PARAM_RSA_MGF1_DIGEST, &name);
+    if (rc == 0)
+        return 0;
+    if (rc > 0) {
+        md = EVP_MD_fetch(provctx->libctx, name, props);
+        if (md == NULL) {
+            put_error_ctx(provctx, IBMCA_ERR_INVALID_PARAM,
+                          "RSA PSS '%s'='%s' could not be fetched",
+                          OSSL_PKEY_PARAM_RSA_MGF1_DIGEST, name);
+            return 0;
+        }
+
+        if ((EVP_MD_get_flags(md) & EVP_MD_FLAG_XOF) != 0) {
+            put_error_ctx(provctx, IBMCA_ERR_INVALID_PARAM,
+                          "XOF Digest '%s' is not allowed", name);
+            EVP_MD_free(md);
+            return 0;
+        }
+
+        pss->mgf_digest_nid = EVP_MD_get_type(md);
+        EVP_MD_free(md);
+        pss->restricted = true;
+    }
+
+    /* OSSL_PKEY_PARAM_RSA_PSS_SALTLEN */
+    rc = ibmca_param_get_int(provctx, params,
+                             OSSL_PKEY_PARAM_RSA_PSS_SALTLEN,
+                             &pss->saltlen);
+    if (rc == 0)
+        return 0;
+    if (rc > 0)
+        pss->restricted = true;
+
+    return 1;
+}
+
+static int ibmca_keymgmt_rsa_pss_parms_to_data(
+                                        const struct ibmca_prov_ctx *provctx,
+                                        OSSL_PARAM_BLD *bld,
+                                        OSSL_PARAM params[],
+                                        struct ibmca_pss_params *pss)
+{
+    const char *name;
+    int rc;
+
+    /* OSSL_PKEY_PARAM_RSA_DIGEST */
+    name = OBJ_nid2sn(pss->digest_nid);
+    rc = ibmca_param_build_set_utf8(provctx, bld, params,
+                                    OSSL_PKEY_PARAM_RSA_DIGEST, name);
+    if (rc == 0)
+       return 0;
+
+    /* OSSL_PKEY_PARAM_RSA_MASKGENFUNC */
+    name = OBJ_nid2sn(pss->mgf_nid);
+    rc = ibmca_param_build_set_utf8(provctx, bld, params,
+                                    OSSL_PKEY_PARAM_RSA_MASKGENFUNC, name);
+    if (rc == 0)
+       return 0;
+
+    /* OSSL_PKEY_PARAM_RSA_MGF1_DIGEST */
+    name = OBJ_nid2sn(pss->mgf_digest_nid);
+    rc = ibmca_param_build_set_utf8(provctx, bld, params,
+                                    OSSL_PKEY_PARAM_RSA_MGF1_DIGEST, name);
+    if (rc == 0)
+       return 0;
+
+    /* OSSL_PKEY_PARAM_RSA_PSS_SALTLEN */
+    rc = ibmca_param_build_set_int(provctx, bld, params,
+                                   OSSL_PKEY_PARAM_RSA_PSS_SALTLEN,
+                                   pss->saltlen);
+    if (rc == 0)
+       return 0;
+
+    return 1;
+}
+
+static int ibmca_keymgmt_rsa_pub_key_from_data(
+                                        const struct ibmca_prov_ctx *provctx,
+                                        const OSSL_PARAM params[],
+                                        BIGNUM **n, BIGNUM **e)
+{
+    int rc;
+
+    /* OSSL_PKEY_PARAM_RSA_N */
+    rc = ibmca_param_get_bn(provctx, params, OSSL_PKEY_PARAM_RSA_N, n);
+    if (rc == 0)
+        return 0;
+
+    /* OSSL_PKEY_PARAM_RSA_E */
+    rc = ibmca_param_get_bn(provctx, params, OSSL_PKEY_PARAM_RSA_E, e);
+    if (rc == 0)
+        return 0;
+
+    return 1;
+}
+
+
+static int ibmca_keymgmt_rsa_pub_key_to_data(
+                                        const struct ibmca_prov_ctx *provctx,
+                                        OSSL_PARAM_BLD *bld,
+                                        OSSL_PARAM params[],
+                                        BIGNUM *n, BIGNUM *e)
+{
+    int rc;
+
+    /* OSSL_PKEY_PARAM_RSA_N */
+    rc = (n == NULL || ibmca_param_build_set_bn(provctx, bld, params,
+                                                OSSL_PKEY_PARAM_RSA_N, n));
+    if (rc == 0)
+        return 0;
+
+    /* OSSL_PKEY_PARAM_RSA_E */
+    rc = (e == NULL || ibmca_param_build_set_bn(provctx, bld, params,
+                                                OSSL_PKEY_PARAM_RSA_E, e));
+    if (rc == 0)
+        return 0;
+
+    return 1;
+}
+
+static int ibmca_keymgmt_rsa_derive_crt_from_pq(
+                                        const struct ibmca_prov_ctx *provctx,
+                                        BIGNUM *n, BIGNUM *e, BIGNUM *p,
+                                        BIGNUM *q, BIGNUM *dp, BIGNUM *dq,
+                                        BIGNUM *qinv)
+{
+    BIGNUM *d = NULL, *p1 = NULL, *q1 = NULL;
+    BN_CTX *ctx = NULL;
+    int rc = 0;
+
+    ctx = BN_CTX_secure_new();
+    if (ctx == NULL) {
+        put_error_ctx(provctx, IBMCA_ERR_MALLOC_FAILED,
+                      "BN_CTX_secure_new failed");
+        goto out;
+    }
+
+    p1 = BN_CTX_get(ctx);
+    q1 = BN_CTX_get(ctx);
+    if (p1 == NULL || q1 == NULL) {
+        put_error_ctx(provctx, IBMCA_ERR_MALLOC_FAILED, "BN_CTX_get failed");
+        goto out;
+    }
+
+    BN_set_flags(p1, BN_FLG_CONSTTIME);
+    BN_set_flags(q1, BN_FLG_CONSTTIME);
+
+    if (!BN_sub(p1, p, BN_value_one()) || /* p-1 */
+        !BN_sub(q1, q, BN_value_one())) { /* q-1 */
+        put_error_ctx(provctx, IBMCA_ERR_MALLOC_FAILED, "BN_sub failed");
+        goto out;
+    }
+
+    BN_set_flags(dp, BN_FLG_CONSTTIME);
+    BN_set_flags(dq, BN_FLG_CONSTTIME);
+    BN_set_flags(qinv, BN_FLG_CONSTTIME);
+
+    rc = ibmca_keymgmt_rsa_calc_priv_d(provctx, n, e, p, q, &d);
+    if (rc != 1)
+        goto out;
+
+    rc = 0;
+
+    /*
+     * See SP800-56Br1 6.3.1.3 rsakpg1 - crt (Step 5)
+     *
+     * (Step 5a) dP = d mod (p-1)
+     */
+    if (!BN_mod(dp, d, p1, ctx)) {
+        put_error_ctx(provctx, IBMCA_ERR_INTERNAL_ERROR, "BN_mod failed");
+        goto out;
+    }
+
+    /* (Step 5b) dQ = d mod (q-1) */
+    if (!BN_mod(dq, d, q1, ctx)) {
+        put_error_ctx(provctx, IBMCA_ERR_INTERNAL_ERROR, "BN_mod failed");
+        goto out;
+    }
+
+    /* (Step 5c) qInv = (inverse of q) mod p */
+    if (BN_mod_inverse(qinv, q, p, ctx) == NULL) {
+        put_error_ctx(provctx, IBMCA_ERR_INTERNAL_ERROR,
+                      "BN_mod_inverse failed");
+        goto out;
+    }
+
+    rc = 1;
+
+out:
+    BN_CTX_free(ctx);
+    BN_free(d);
+
+    return rc;
+}
+
+static int ibmca_keymgmt_rsa_priv_crt_key_from_data(
+                                        const struct ibmca_prov_ctx *provctx,
+                                        const OSSL_PARAM params[],
+                                        BIGNUM *n, BIGNUM *e,
+                                        BIGNUM **p, BIGNUM **q,
+                                        BIGNUM **dp, BIGNUM **dq, BIGNUM **qinv)
+{
+    int rc, derive_from_pq = 0;
+
+    *p = BN_secure_new();
+    *q = BN_secure_new();
+    *dp = BN_secure_new();
+    *dq = BN_secure_new();
+    *qinv = BN_secure_new();
+    if (*p == NULL || *q == NULL || *dp == NULL || *dp == NULL ||
+        *qinv == NULL) {
+        put_error_ctx(provctx, IBMCA_ERR_MALLOC_FAILED, "BN_secure_new failed");
+        goto error;
+    }
+
+    /* OSSL_PKEY_PARAM_RSA_FACTOR1 */
+    rc = ibmca_param_get_bn(provctx, params, OSSL_PKEY_PARAM_RSA_FACTOR1, p);
+    if (rc <= 0)
+        goto error;
+
+    /* OSSL_PKEY_PARAM_RSA_FACTOR2 */
+    rc = ibmca_param_get_bn(provctx, params, OSSL_PKEY_PARAM_RSA_FACTOR2, q);
+    if (rc <= 0)
+        goto error;
+
+    /* OSSL_PKEY_PARAM_RSA_EXPONENT1 */
+    rc = ibmca_param_get_bn(provctx, params, OSSL_PKEY_PARAM_RSA_EXPONENT1,
+                            dp);
+    if (rc <= 0)
+        goto check_from_pq;
+
+    /* OSSL_PKEY_PARAM_RSA_EXPONENT2 */
+    rc = ibmca_param_get_bn(provctx, params, OSSL_PKEY_PARAM_RSA_EXPONENT2,
+                            dq);
+    if (rc <= 0)
+        goto check_from_pq;
+
+    /* OSSL_PKEY_PARAM_RSA_COEFFICIENT1 */
+    rc = ibmca_param_get_bn(provctx, params, OSSL_PKEY_PARAM_RSA_COEFFICIENT1,
+                            qinv);
+    if (rc <= 0)
+        goto check_from_pq;
+
+    return 1;
+
+check_from_pq:
+#ifdef OSSL_PKEY_PARAM_RSA_DERIVE_FROM_PQ
+    /* OSSL_PKEY_PARAM_RSA_DERIVE_FROM_PQ */
+    rc = ibmca_param_get_int(provctx, params,
+                             OSSL_PKEY_PARAM_RSA_DERIVE_FROM_PQ,
+                             &derive_from_pq);
+    if (rc <= 0)
+        goto error;
+#endif
+
+    if (derive_from_pq) {
+        rc = ibmca_keymgmt_rsa_derive_crt_from_pq(provctx, n, e, *p, *q,
+                                                  *dp, *dq, *qinv);
+        if (rc != 1)
+            goto error;
+
+        return 1;
+    }
+
+error:
+    BN_clear_free(*p);
+    *p = NULL;
+    BN_clear_free(*q);
+    *q = NULL;
+    BN_clear_free(*dp);
+    *dp = NULL;
+    BN_clear_free(*dq);
+    *dq = NULL;
+    BN_clear_free(*qinv);
+    *qinv = NULL;
+
+    return 0;
+}
+
+static int ibmca_keymgmt_rsa_priv_me_key_from_data(
+                                        const struct ibmca_prov_ctx *provctx,
+                                        const OSSL_PARAM params[], BIGNUM **d)
+{
+    int rc;
+
+    /* OSSL_PKEY_PARAM_RSA_D */
+    *d = BN_secure_new();
+    if (*d == NULL) {
+        put_error_ctx(provctx, IBMCA_ERR_MALLOC_FAILED, "BN_secure_new failed");
+        goto error;
+    }
+    rc = ibmca_param_get_bn(provctx, params, OSSL_PKEY_PARAM_RSA_D, d);
+    if (rc <= 0)
+        goto error;
+
+    return 1;
+
+error:
+    BN_clear_free(*d);
+    *d = NULL;
+
+    return 0;
+}
+
+static int ibmca_keymgmt_rsa_priv_key_to_data(
+                                        const struct ibmca_prov_ctx *provctx,
+                                        OSSL_PARAM_BLD *bld,
+                                        OSSL_PARAM params[],
+                                        BIGNUM *d, BIGNUM *p, BIGNUM *q,
+                                        BIGNUM *dp, BIGNUM *dq,
+                                        BIGNUM *qinv)
+{
+    int rc;
+
+    /* OSSL_PKEY_PARAM_RSA_D */
+    rc = (d == NULL || ibmca_param_build_set_bn(provctx, bld, params,
+                                                OSSL_PKEY_PARAM_RSA_D, d));
+    if (rc == 0)
+        return 0;
+
+    /* OSSL_PKEY_PARAM_RSA_FACTOR1 */
+    rc = (p == NULL || ibmca_param_build_set_bn(provctx, bld, params,
+                                                OSSL_PKEY_PARAM_RSA_FACTOR1,
+                                                p));
+    if (rc == 0)
+        return 0;
+
+    /* OSSL_PKEY_PARAM_RSA_FACTOR2 */
+    rc = (q == NULL || ibmca_param_build_set_bn(provctx, bld, params,
+                                                OSSL_PKEY_PARAM_RSA_FACTOR2,
+                                                q));
+    if (rc == 0)
+        return 0;
+
+    /* OSSL_PKEY_PARAM_RSA_EXPONENT1 */
+    rc = (dp == NULL || ibmca_param_build_set_bn(provctx, bld, params,
+                                                 OSSL_PKEY_PARAM_RSA_EXPONENT1,
+                                                 dp));
+    if (rc == 0)
+        return 0;
+
+    /* OSSL_PKEY_PARAM_RSA_EXPONENT2 */
+    rc = (dq == NULL || ibmca_param_build_set_bn(provctx, bld, params,
+                                                OSSL_PKEY_PARAM_RSA_EXPONENT2,
+                                                dq));
+    if (rc == 0)
+        return 0;
+
+    /* OSSL_PKEY_PARAM_RSA_COEFFICIENT1 */
+    rc = (qinv == NULL || ibmca_param_build_set_bn(provctx, bld, params,
+                                              OSSL_PKEY_PARAM_RSA_COEFFICIENT1,
+                                              qinv));
+    if (rc == 0)
+        return 0;
+
+    return 1;
+}
+
+static size_t ibmca_keymgmt_rsa_get_max_param_size(const struct ibmca_key *key)
+{
+    return (key->rsa.bits + 7) / 8;
+}
+
+static struct ibmca_key *ibmca_keymgmt_rsa_new_type(
+                                        const struct ibmca_prov_ctx *provctx,
+                                        int type)
+{
+    struct ibmca_key *key;
+
+    if (provctx == NULL)
+        return NULL;
+
+    ibmca_debug_ctx(provctx, "provctx: %p type: %d", provctx, type);
+
+    key = ibmca_keymgmt_new(provctx, type, "RSA",
+                            ibmca_keymgmt_rsa_free_cb,
+                            ibmca_keymgmt_rsa_dup_cb,
+                            ibmca_keymgmt_rsa_get_max_param_size,
+                            ibmca_keymgmt_rsa_export,
+                            ibmca_keymgmt_rsa_import,
+                            ibmca_keymgmt_rsa_has,
+                            ibmca_keymgmt_rsa_match);
+    if (key == NULL) {
+        ibmca_debug_ctx(provctx, "ERROR: ibmca_keymgmt_new failed");
+        return NULL;
+    }
+
+    if (pthread_rwlock_init(&key->rsa.blinding_lock, NULL) != 0) {
+        put_error_ctx(provctx, IBMCA_ERR_INTERNAL_ERROR,
+                      "pthread_rwlock_init failed: %s", strerror(errno));
+        ibmca_keymgmt_free(key);
+        return NULL;
+    }
+
+    /* Set defaults */
+    if (type == EVP_PKEY_RSA_PSS)
+        key->rsa.pss = ibmca_rsa_pss_defaults;
+
+    return key;
+}
+
+static void *ibmca_keymgmt_rsa_new(void *vprovctx)
+{
+    const struct ibmca_prov_ctx *provctx = vprovctx;
+
+    if (provctx == NULL)
+        return NULL;
+
+    ibmca_debug_ctx(provctx, "provctx: %p", provctx);
+    return ibmca_keymgmt_rsa_new_type(provctx, EVP_PKEY_RSA);
+}
+
+static void *ibmca_keymgmt_rsa_pss_new(void *vprovctx)
+{
+    const struct ibmca_prov_ctx *provctx = vprovctx;
+
+    if (provctx == NULL)
+        return NULL;
+
+    ibmca_debug_ctx(provctx, "provctx: %p", provctx);
+    return ibmca_keymgmt_rsa_new_type(provctx, EVP_PKEY_RSA_PSS);
+}
+
+static int ibmca_keymgmt_rsa_alloc_pub(struct ibmca_key *key)
+{
+    key->rsa.public.key_length = key->rsa.keylength;
+
+    key->rsa.public.modulus = P_ZALLOC(key->provctx,
+                                       key->rsa.public.key_length);
+    key->rsa.public.exponent = P_ZALLOC(key->provctx,
+                                        key->rsa.public.key_length);
+
+    if (!ibmca_keymgmt_rsa_pub_valid(&key->rsa.public)) {
+        put_error_key(key, IBMCA_ERR_MALLOC_FAILED,
+                     "Failed to allocate libica public RSA key");
+        return 0;
+    }
+
+    return 1;
+}
+
+static int ibmca_keymgmt_rsa_alloc_priv_crt(struct ibmca_key *key)
+{
+    key->rsa.private_crt.key_length = key->rsa.keylength;
+
+    key->rsa.private_crt.p = P_SECURE_ZALLOC(key->provctx,
+                                  ICA_P_LEN(key->rsa.private_crt.key_length));
+    key->rsa.private_crt.q = P_SECURE_ZALLOC(key->provctx,
+                                  ICA_Q_LEN(key->rsa.private_crt.key_length));
+    key->rsa.private_crt.dp = P_SECURE_ZALLOC(key->provctx,
+                                  ICA_DP_LEN(key->rsa.private_crt.key_length));
+    key->rsa.private_crt.dq = P_SECURE_ZALLOC(key->provctx,
+                                  ICA_DQ_LEN(key->rsa.private_crt.key_length));
+    key->rsa.private_crt.qInverse = P_SECURE_ZALLOC(key->provctx,
+                                 ICA_QINV_LEN(key->rsa.private_crt.key_length));
+
+    if (!ibmca_keymgmt_rsa_priv_crt_valid(&key->rsa.private_crt)) {
+        put_error_key(key, IBMCA_ERR_MALLOC_FAILED,
+                      "Failed to allocate libica private RSA CRT key");
+        return 0;
+    }
+
+    return 1;
+}
+
+static int ibmca_keymgmt_rsa_alloc_priv_me(struct ibmca_key *key)
+{
+    key->rsa.private_me.key_length = key->rsa.keylength;
+
+    key->rsa.private_me.modulus = P_ZALLOC(key->provctx,
+                                           key->rsa.private_me.key_length);
+    key->rsa.private_me.exponent = P_ZALLOC(key->provctx,
+                                            key->rsa.private_me.key_length);
+
+    if (!ibmca_keymgmt_rsa_priv_me_valid(&key->rsa.private_me)) {
+        put_error_key(key, IBMCA_ERR_MALLOC_FAILED,
+                     "Failed to allocate libica private RSA ME key");
+        return 0;
+    }
+
+    return 1;
+}
+
+static void ibmca_keymgmt_rsa_free_pub(struct ibmca_key *key)
+{
+    if (key->rsa.public.modulus != NULL)
+        P_CLEAR_FREE(key->provctx, key->rsa.public.modulus,
+                     key->rsa.public.key_length);
+    key->rsa.public.modulus = NULL;
+    if (key->rsa.public.exponent != NULL)
+        P_CLEAR_FREE(key->provctx, key->rsa.public.exponent,
+                     key->rsa.public.key_length);
+    key->rsa.public.exponent = NULL;
+    key->rsa.public.key_length = 0;
+}
+
+static void ibmca_keymgmt_rsa_free_priv_crt(struct ibmca_key *key)
+{
+    if (key->rsa.private_crt.p != NULL)
+         P_SECURE_CLEAR_FREE(key->provctx, key->rsa.private_crt.p,
+                             ICA_P_LEN(key->rsa.private_crt.key_length));
+     key->rsa.private_crt.p = NULL;
+     if (key->rsa.private_crt.q != NULL)
+         P_SECURE_CLEAR_FREE(key->provctx, key->rsa.private_crt.q,
+                             ICA_Q_LEN(key->rsa.private_crt.key_length));
+     key->rsa.private_crt.q = NULL;
+     if (key->rsa.private_crt.dp != NULL)
+         P_SECURE_CLEAR_FREE(key->provctx, key->rsa.private_crt.dp,
+                             ICA_DP_LEN(key->rsa.private_crt.key_length));
+     key->rsa.private_crt.dp = NULL;
+     if (key->rsa.private_crt.dq != NULL)
+         P_SECURE_CLEAR_FREE(key->provctx, key->rsa.private_crt.dq,
+                             ICA_DQ_LEN(key->rsa.private_crt.key_length));
+     key->rsa.private_crt.dq = NULL;
+     if (key->rsa.private_crt.qInverse != NULL)
+         P_SECURE_CLEAR_FREE(key->provctx, key->rsa.private_crt.qInverse,
+                             ICA_QINV_LEN(key->rsa.private_crt.key_length));
+     key->rsa.private_crt.qInverse = NULL;
+     key->rsa.private_crt.key_length = 0;
+}
+
+static void ibmca_keymgmt_rsa_free_priv_me(struct ibmca_key *key)
+{
+    if (key->rsa.private_me.modulus != NULL)
+        P_CLEAR_FREE(key->provctx, key->rsa.private_me.modulus,
+                     key->rsa.private_me.key_length);
+    key->rsa.private_me.modulus = NULL;
+    if (key->rsa.private_me.exponent != NULL)
+        P_CLEAR_FREE(key->provctx, key->rsa.private_me.exponent,
+                     key->rsa.private_me.key_length);
+    key->rsa.private_me.exponent = NULL;
+    key->rsa.private_me.key_length = 0;
+}
+
+static void ibmca_keymgmt_rsa_clean(struct ibmca_key *key)
+{
+    if (key == NULL)
+        return;
+
+    ibmca_debug_key(key, "key: %p", key);
+
+    ibmca_keymgmt_rsa_free_priv_crt(key);
+    ibmca_keymgmt_rsa_free_priv_me(key);
+    ibmca_keymgmt_rsa_free_pub(key);
+
+    if (key->type == EVP_PKEY_RSA_PSS)
+        key->rsa.pss = ibmca_rsa_pss_defaults;
+
+    if (pthread_rwlock_wrlock(&key->rsa.blinding_lock) != 0) {
+        ibmca_debug_key(key, "ERROR: pthread_rwlock_wrlock failed: %s",
+                        strerror(errno));
+        return;
+    }
+
+    if (key->rsa.blinding != NULL)
+        BN_BLINDING_free(key->rsa.blinding);
+    key->rsa.blinding = NULL;
+    if (key->rsa.mt_blinding)
+        BN_BLINDING_free(key->rsa.mt_blinding);
+    key->rsa.mt_blinding = NULL;
+    if (key->rsa.blinding_mont_ctx != NULL)
+        BN_MONT_CTX_free(key->rsa.blinding_mont_ctx);
+    key->rsa.blinding_mont_ctx = NULL;
+
+    pthread_rwlock_unlock(&key->rsa.blinding_lock);
+}
+
+static void ibmca_keymgmt_rsa_free_cb(struct ibmca_key *key)
+{
+    if (key == NULL)
+        return;
+
+    ibmca_debug_key(key, "key: %p", key);
+
+    ibmca_keymgmt_rsa_clean(key);
+
+    pthread_rwlock_destroy(&key->rsa.blinding_lock);
+}
+
+bool ibmca_keymgmt_rsa_pub_valid(const ica_rsa_key_mod_expo_t *public)
+{
+    return public->key_length != 0 &&
+           public->modulus != NULL &&
+           public->exponent != NULL;
+}
+
+bool ibmca_keymgmt_rsa_priv_crt_valid(const ica_rsa_key_crt_t *private_crt)
+{
+    return private_crt->key_length != 0 &&
+           private_crt->p != NULL &&
+           private_crt->q != NULL &&
+           private_crt->dp != NULL &&
+           private_crt->dq != NULL &&
+           private_crt->qInverse != NULL;
+}
+
+bool ibmca_keymgmt_rsa_priv_me_valid(const ica_rsa_key_mod_expo_t *private_me)
+{
+    return private_me->key_length != 0 &&
+           private_me->modulus != NULL &&
+           private_me->exponent != NULL;
+}
+
+static int ibmca_keymgmt_rsa_dup_pub(const struct ibmca_key *key,
+                                     struct ibmca_key *new_key)
+{
+    new_key->rsa.public.key_length = key->rsa.public.key_length;
+
+    new_key->rsa.public.modulus = P_MEMDUP(key->provctx,
+                                           key->rsa.public.modulus,
+                                           key->rsa.public.key_length);
+    new_key->rsa.public.exponent = P_MEMDUP(key->provctx,
+                                            key->rsa.public.exponent,
+                                            key->rsa.public.key_length);
+
+    if (!ibmca_keymgmt_rsa_pub_valid(&new_key->rsa.public)) {
+        put_error_key(key, IBMCA_ERR_MALLOC_FAILED,
+                      "Failed to allocate libica RSA key");
+        return 0;
+    }
+
+    return 1;
+}
+
+static int ibmca_keymgmt_rsa_dup_priv_crt(const struct ibmca_key *key,
+                                          struct ibmca_key *new_key)
+{
+    new_key->rsa.private_crt.key_length = key->rsa.private_crt.key_length;
+
+    new_key->rsa.private_crt.p = P_SECURE_MEMDUP(key->provctx, key->rsa.private_crt.p,
+                                      ICA_P_LEN(key->rsa.private_crt.key_length));
+    new_key->rsa.private_crt.q = P_SECURE_MEMDUP(key->provctx, key->rsa.private_crt.q,
+                                      ICA_Q_LEN(key->rsa.private_crt.key_length));
+    new_key->rsa.private_crt.dp = P_SECURE_MEMDUP(key->provctx, key->rsa.private_crt.dp,
+                                       ICA_DP_LEN(key->rsa.private_crt.key_length));
+    new_key->rsa.private_crt.dq = P_SECURE_MEMDUP(key->provctx, key->rsa.private_crt.dq,
+                                       ICA_DQ_LEN(key->rsa.private_crt.key_length));
+    new_key->rsa.private_crt.qInverse = P_SECURE_MEMDUP(key->provctx,
+                                       key->rsa.private_crt.qInverse,
+                                       ICA_QINV_LEN(
+                                                 key->rsa.private_crt.key_length));
+
+    if (!ibmca_keymgmt_rsa_priv_crt_valid(&new_key->rsa.private_crt)) {
+        put_error_key(key, IBMCA_ERR_MALLOC_FAILED,
+                      "Failed to allocate libica RSA key");
+        return 0;
+    }
+
+    return 1;
+}
+
+static int ibmca_keymgmt_rsa_dup_priv_me(const struct ibmca_key *key,
+                                         struct ibmca_key *new_key)
+{
+    new_key->rsa.private_me.key_length = key->rsa.private_me.key_length;
+
+    new_key->rsa.private_me.modulus = P_MEMDUP(key->provctx,
+                                               key->rsa.private_me.modulus,
+                                               key->rsa.private_me.key_length);
+    new_key->rsa.private_me.exponent = P_MEMDUP(key->provctx,
+                                                key->rsa.private_me.exponent,
+                                                key->rsa.private_me.key_length);
+
+    if (!ibmca_keymgmt_rsa_priv_me_valid(&new_key->rsa.private_me)) {
+        put_error_key(key, IBMCA_ERR_MALLOC_FAILED,
+                      "Failed to allocate libica RSA key");
+        return 0;
+    }
+
+    return 1;
+}
+
+static int ibmca_keymgmt_rsa_dup_cb(const struct ibmca_key *key,
+                                    struct ibmca_key *new_key)
+{
+    if (key == NULL || new_key == NULL)
+        return 0;
+
+    ibmca_debug_key(key, "key: %p new_key: %p", key, new_key);
+
+    if (pthread_rwlock_init(&new_key->rsa.blinding_lock, NULL) != 0) {
+        put_error_key(key, IBMCA_ERR_INTERNAL_ERROR,
+                      "pthread_rwlock_init failed: %s", strerror(errno));
+        return 0;
+    }
+
+    new_key->rsa.bits = key->rsa.bits;
+    new_key->rsa.keylength = key->rsa.keylength;
+
+    if (ibmca_keymgmt_rsa_pub_valid(&key->rsa.public)) {
+        if (ibmca_keymgmt_rsa_dup_pub(key, new_key) == 0)
+            return 0;
+    }
+
+    if (ibmca_keymgmt_rsa_priv_crt_valid(&key->rsa.private_crt)) {
+        if (ibmca_keymgmt_rsa_dup_priv_crt(key, new_key) == 0)
+            return 0;
+    }
+
+    if (ibmca_keymgmt_rsa_priv_me_valid(&key->rsa.private_me)) {
+        if (ibmca_keymgmt_rsa_dup_priv_me(key, new_key) == 0)
+            return 0;
+    }
+
+    if (key->type == EVP_PKEY_RSA_PSS)
+        new_key->rsa.pss = key->rsa.pss;
+
+    return 1;
+}
+
+static int ibmca_keymgmt_rsa_has(const void *vkey, int selection)
+{
+    const struct ibmca_key *key = vkey;
+    int ok = 1;
+
+    if (key == NULL)
+        return 0;
+
+    ibmca_debug_key(key, "key: %p selection: 0x%x", key, selection);
+
+    if ((selection & OSSL_KEYMGMT_SELECT_PUBLIC_KEY) != 0)
+        ok = ok && ibmca_keymgmt_rsa_pub_valid(&key->rsa.public);
+    if ((selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) != 0)
+        ok = ok && (ibmca_keymgmt_rsa_priv_crt_valid(&key->rsa.private_crt) ||
+                    ibmca_keymgmt_rsa_priv_me_valid(&key->rsa.private_me));
+
+    ibmca_debug_key(key, "ok: %d", ok);
+    return ok;
+}
+
+static bool ibmca_keymgmt_rsa_pub_equal(const ica_rsa_key_mod_expo_t *public1,
+                                        const ica_rsa_key_mod_expo_t *public2)
+{
+    return public1->key_length > 0 &&
+           public1->key_length == public2-> key_length &&
+           memcmp(public1->exponent, public2->exponent,
+                  public1->key_length) == 0 &&
+           memcmp(public1->modulus, public2->modulus,
+                  public1->key_length) == 0;
+}
+
+static bool ibmca_keymgmt_rsa_priv_crt_equal(
+                                    const ica_rsa_key_crt_t *private_crt1,
+                                    const ica_rsa_key_crt_t *private_crt2)
+{
+    return private_crt1->key_length > 0 &&
+           private_crt1->key_length == private_crt2->key_length &&
+           CRYPTO_memcmp(private_crt1->p, private_crt2->p,
+                         ICA_P_LEN(private_crt1->key_length)) == 0 &&
+           CRYPTO_memcmp(private_crt1->q, private_crt2->q,
+                         ICA_Q_LEN(private_crt1->key_length)) == 0 &&
+           CRYPTO_memcmp(private_crt1->dp, private_crt2->dp,
+                         ICA_DP_LEN(private_crt1->key_length)) == 0 &&
+           CRYPTO_memcmp(private_crt1->dq, private_crt2->dq,
+                         ICA_DQ_LEN(private_crt1->key_length)) == 0 &&
+           CRYPTO_memcmp(private_crt1->qInverse, private_crt2->qInverse,
+                         ICA_QINV_LEN(private_crt1->key_length)) == 0;
+}
+
+static bool ibmca_keymgmt_rsa_priv_me_equal(
+                                    const ica_rsa_key_mod_expo_t *private_me1,
+                                    const ica_rsa_key_mod_expo_t *private_me2)
+{
+    return private_me1->key_length > 0 &&
+           private_me1->key_length == private_me2-> key_length &&
+           CRYPTO_memcmp(private_me1->exponent, private_me2->exponent,
+                         private_me2->key_length) == 0 &&
+           CRYPTO_memcmp(private_me1->modulus, private_me2->modulus,
+                         private_me1->key_length) == 0;
+}
+
+static int ibmca_keymgmt_rsa_match(const void *vkey1, const void *vkey2,
+                                   int selection)
+{
+    const struct ibmca_key *key1 = vkey1;
+    const struct ibmca_key *key2 = vkey2;
+    int ok = 1, checked = 0;
+
+    if (key1 == NULL || key2 == NULL)
+        return 0;
+
+    ibmca_debug_key(key1, "key1: %p key2: %p selection: 0x%x", key1, key2,
+                    selection);
+
+    if (ibmca_keymgmt_match(key1, key2) == 0)
+        return 0;
+
+    if ((selection & OSSL_KEYMGMT_SELECT_PUBLIC_KEY) != 0) {
+        ok = ok && ibmca_keymgmt_rsa_pub_equal(&key1->rsa.public,
+                                               &key2->rsa.public);
+        checked = 1;
+    }
+
+    if (!checked && (selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) != 0)
+        ok = ok && (ibmca_keymgmt_rsa_priv_crt_equal(&key1->rsa.private_crt,
+                                                     &key2->rsa.private_crt) ||
+                    ibmca_keymgmt_rsa_priv_me_equal(&key1->rsa.private_me,
+                                                    &key2->rsa.private_me));
+
+    ibmca_debug_key(key1, "ok: %d", ok);
+    return ok;
+}
+
+static int ibmca_keymgmt_rsa_validate(const void *vkey, int selection,
+                                      int checktype)
+{
+    struct ibmca_key *key = (struct ibmca_key *)vkey;
+    EVP_PKEY *pkey;
+    EVP_PKEY_CTX *pctx = NULL;
+    int rc = 0;
+
+    if (key == NULL)
+        return 0;
+
+    ibmca_debug_key(key, "key: %p selection: 0x%x checktype: 0x%x", key,
+                    selection, checktype);
+
+    /*
+     * Validate RSA key with OpenSSL, not libica. Libica does not provide a
+     * way to validate an RSA key.
+     */
+    pkey = ibmca_new_fallback_pkey(key);
+    if (pkey == NULL)
+        return 0;
+
+    pctx = ibmca_new_fallback_pkey_ctx(key->provctx, pkey, NULL);
+    if (pctx == NULL)
+        goto out;
+
+    if ((selection & OSSL_KEYMGMT_SELECT_KEYPAIR)
+                            == OSSL_KEYMGMT_SELECT_KEYPAIR) {
+        rc = EVP_PKEY_check(pctx);
+    } else if ((selection & OSSL_KEYMGMT_SELECT_PUBLIC_KEY) != 0) {
+        rc = EVP_PKEY_public_check(pctx);
+    }
+
+    ibmca_debug_key(key, "valid: %d", rc);
+
+out:
+    if (pctx != NULL)
+        EVP_PKEY_CTX_free(pctx);
+    if (pkey != NULL)
+        EVP_PKEY_free(pkey);
+
+    return rc;
+}
+
+static const char *ibmca_keymgmt_rsa_query_operation_name(int operation_id)
+{
+    switch (operation_id) {
+    case OSSL_OP_SIGNATURE:
+    case OSSL_OP_ASYM_CIPHER:
+        return "RSA";
+    }
+
+    return NULL;
+}
+
+static void ibmca_keymgmt_rsa_gen_free_cb(struct ibmca_op_ctx *ctx)
+{
+    if (ctx == NULL)
+        return;
+
+    ibmca_debug_op_ctx(ctx, "ctx: %p", ctx);
+
+    if (ctx->rsa.gen.pub_exp != NULL) {
+        BN_free((BIGNUM *)ctx->rsa.gen.pub_exp);
+        ctx->rsa.gen.pub_exp = NULL;
+    }
+}
+
+static int ibmca_keymgmt_rsa_gen_dup_cb(const struct ibmca_op_ctx *ctx,
+                                        struct ibmca_op_ctx *new_ctx)
+{
+    if (ctx == NULL)
+        return 0;
+
+    ibmca_debug_op_ctx(ctx, "ctx: %p new_ctx: %p", ctx, new_ctx);
+
+    new_ctx->rsa.gen.bits = ctx->rsa.gen.bits;
+    if (ctx->rsa.gen.pub_exp != NULL) {
+        new_ctx->rsa.gen.pub_exp = BN_dup(ctx->rsa.gen.pub_exp);
+        if (new_ctx->rsa.gen.pub_exp == NULL) {
+            put_error_op_ctx(ctx, IBMCA_ERR_MALLOC_FAILED, "BN_dup failed");
+            return 0;
+        }
+    }
+
+    if (ctx->type == EVP_PKEY_RSA_PSS)
+        new_ctx->rsa.gen.pss = ctx->rsa.gen.pss;
+
+    return 1;
+}
+
+static struct ibmca_op_ctx *ibmca_keymgmt_rsa_gen_init_type(
+                            const struct ibmca_prov_ctx *provctx, int selection,
+                            const OSSL_PARAM params[], int type)
+{
+    struct ibmca_op_ctx *ctx;
+    const OSSL_PARAM *p;
+
+    if (provctx == NULL)
+        return NULL;
+
+    ibmca_debug_ctx(provctx, "provctx: %p selection: 0x%x type: %d", provctx,
+                    selection, type);
+    for (p = params; p != NULL && p->key != NULL; p++)
+        ibmca_debug_ctx(provctx, "param: %s", p->key);
+
+    if ((selection & OSSL_KEYMGMT_SELECT_KEYPAIR) == 0) {
+        put_error_ctx(provctx, IBMCA_ERR_INVALID_PARAM,
+                      "selection is not KEYPAIR");
+        return NULL;
+    }
+
+    ctx = ibmca_keymgmt_gen_init(provctx, type, ibmca_keymgmt_rsa_gen_free_cb,
+                                 ibmca_keymgmt_rsa_gen_dup_cb);
+    if (ctx == NULL) {
+        ibmca_debug_ctx(provctx, "ERROR: ibmca_keymgmt_gen_init failed");
+        return NULL;
+    }
+
+    /* set defaults */
+    ctx->rsa.gen.bits = IBMCA_RSA_DEFAULT_BITS;
+    ctx->rsa.gen.pub_exp = BN_new();
+    if (ctx->rsa.gen.pub_exp == NULL ||
+        BN_set_word(ctx->rsa.gen.pub_exp, IBMCA_RSA_DEFAULT_PUB_EXP) == 0) {
+        put_error_ctx(provctx, IBMCA_ERR_INVALID_PARAM,
+                      "BN_new/BN_set_word failed");
+        ibmca_op_freectx(ctx);
+        return NULL;
+    }
+
+    if (type == EVP_PKEY_RSA_PSS)
+        ctx->rsa.gen.pss = ibmca_rsa_pss_defaults;
+
+    if (params != NULL) {
+        if (ibmca_keymgmt_rsa_gen_set_params(ctx, params) == 0) {
+            ibmca_debug_ctx(provctx, "ERROR: ibmca_keymgmt_rsa_gen_set_params failed");
+            ibmca_op_freectx(ctx);
+            return NULL;
+        }
+    }
+
+    return ctx;
+}
+
+static void *ibmca_keymgmt_rsa_gen_init(void *vprovctx, int selection,
+                                        const OSSL_PARAM params[])
+{
+    const struct ibmca_prov_ctx *provctx = vprovctx;
+
+    if (provctx == NULL)
+        return NULL;
+
+    ibmca_debug_ctx(provctx, "provctx: %p", provctx);
+    return ibmca_keymgmt_rsa_gen_init_type(provctx, selection, params,
+                                           EVP_PKEY_RSA);
+}
+
+static void *ibmca_keymgmt_rsa_pss_gen_init(void *vprovctx, int selection,
+                                            const OSSL_PARAM params[])
+{
+    const struct ibmca_prov_ctx *provctx = vprovctx;
+
+    if (provctx == NULL)
+        return NULL;
+
+    ibmca_debug_ctx(provctx, "provctx: %p", provctx);
+    return ibmca_keymgmt_rsa_gen_init_type(provctx, selection, params,
+                                           EVP_PKEY_RSA_PSS);
+}
+
+static int ibmca_keymgmt_rsa_gen_set_template(void *vgenctx, void *vtempl)
+{
+    struct ibmca_op_ctx *genctx = vgenctx;
+    struct ibmca_key *templ = vtempl;
+
+    if (genctx == NULL || templ == NULL)
+        return 0;
+
+    ibmca_debug_op_ctx(genctx, "genctx: %p templ: %p", genctx, templ);
+
+    if (genctx->type != templ->type) {
+        put_error_op_ctx(genctx, IBMCA_ERR_INVALID_PARAM,
+                         "invalid template key type");
+        return 0;
+    }
+
+    ibmca_keymgmt_rsa_gen_free_cb(genctx);
+
+    genctx->rsa.gen.bits = templ->rsa.bits;
+    if (ibmca_keymgmt_rsa_pub_valid(&templ->rsa.public)) {
+        genctx->rsa.gen.pub_exp = BN_bin2bn(templ->rsa.public.exponent,
+                                            templ->rsa.public.key_length,
+                                            NULL);
+        if (genctx->rsa.gen.pub_exp == NULL) {
+            put_error_op_ctx(genctx, IBMCA_ERR_MALLOC_FAILED,
+                             "BN_bin2bn failed");
+            return 0;
+        }
+    }
+
+    if (genctx->type == EVP_PKEY_RSA_PSS)
+        genctx->rsa.gen.pss = templ->rsa.pss;
+
+    return 1;
+}
+
+static const OSSL_PARAM ibmca_rsa_op_ctx_settable_params[] = {
+    OSSL_PARAM_size_t(OSSL_PKEY_PARAM_RSA_BITS, NULL),
+    OSSL_PARAM_size_t(OSSL_PKEY_PARAM_RSA_PRIMES, NULL),
+    OSSL_PARAM_BN(OSSL_PKEY_PARAM_RSA_E, NULL, 0),
+    OSSL_PARAM_END
+};
+
+static const OSSL_PARAM ibmca_rsa_pss_op_ctx_settable_params[] = {
+    OSSL_PARAM_size_t(OSSL_PKEY_PARAM_RSA_BITS, NULL),
+    OSSL_PARAM_size_t(OSSL_PKEY_PARAM_RSA_PRIMES, NULL),
+    OSSL_PARAM_BN(OSSL_PKEY_PARAM_RSA_E, NULL, 0),
+    OSSL_PARAM_utf8_string(OSSL_PKEY_PARAM_RSA_DIGEST, NULL, 0),
+    OSSL_PARAM_utf8_string(OSSL_PKEY_PARAM_RSA_DIGEST_PROPS, NULL, 0),
+    OSSL_PARAM_utf8_string(OSSL_PKEY_PARAM_RSA_MASKGENFUNC, NULL, 0),
+    OSSL_PARAM_utf8_string(OSSL_PKEY_PARAM_RSA_MGF1_DIGEST, NULL, 0),
+    OSSL_PARAM_int(OSSL_PKEY_PARAM_RSA_PSS_SALTLEN, NULL),
+    OSSL_PARAM_END
+};
+
+static const OSSL_PARAM *ibmca_keymgmt_rsa_gen_settable_params(void *vgenctx,
+                                                               void *vprovctx)
+{
+    const struct ibmca_op_ctx *genctx = vgenctx;
+    const struct ibmca_prov_ctx *provctx = vprovctx;
+    const OSSL_PARAM *params, *p;
+
+    UNUSED(genctx);
+
+    if (provctx == NULL)
+        return NULL;
+
+    params = ibmca_rsa_op_ctx_settable_params;
+    for (p = params; p != NULL && p->key != NULL; p++)
+        ibmca_debug_ctx(provctx, "param: %s", p->key);
+
+    return params;
+}
+
+static const OSSL_PARAM *ibmca_keymgmt_rsa_pss_gen_settable_params(
+                                                               void *vgenctx,
+                                                               void *vprovctx)
+{
+    const struct ibmca_op_ctx *genctx = vgenctx;
+    const struct ibmca_prov_ctx *provctx = vprovctx;
+    const OSSL_PARAM *params, *p;
+
+    UNUSED(genctx);
+
+    if (provctx == NULL)
+        return NULL;
+
+    params = ibmca_rsa_pss_op_ctx_settable_params;
+    for (p = params; p != NULL && p->key != NULL; p++)
+        ibmca_debug_ctx(provctx, "param: %s", p->key);
+
+    return params;
+}
+
+static int ibmca_keymgmt_rsa_gen_set_params(void *vgenctx,
+                                            const OSSL_PARAM params[])
+{
+    struct ibmca_op_ctx *genctx = vgenctx;
+    const OSSL_PARAM *p;
+    size_t primes;
+    int rc;
+
+    if (genctx == NULL)
+        return 0;
+
+    ibmca_debug_op_ctx(genctx, "genctx: %p", genctx);
+    for (p = params; p != NULL && p->key != NULL; p++)
+        ibmca_debug_op_ctx(genctx, "param: %s", p->key);
+
+    /* OSSL_PKEY_PARAM_RSA_BITS */
+    rc = ibmca_param_get_size_t(genctx->provctx, params,
+                                OSSL_PKEY_PARAM_RSA_BITS,
+                                &genctx->rsa.gen.bits);
+    if (rc == 0)
+        return 0;
+    if (rc > 0 && genctx->rsa.gen.bits < IBMCA_RSA_MIN_MODULUS_BITS) {
+        put_error_op_ctx(genctx, IBMCA_ERR_INVALID_PARAM,
+                         "RSA '%s': %u is below minimum",
+                         OSSL_PKEY_PARAM_RSA_BITS, genctx->rsa.gen.bits);
+        return 0;
+    }
+
+    /* OSSL_PKEY_PARAM_RSA_PRIMES */
+    rc = ibmca_param_get_size_t(genctx->provctx, params,
+                                OSSL_PKEY_PARAM_RSA_PRIMES, &primes);
+    if (rc == 0)
+        return 0;
+    if (rc > 0 && primes != 2) {
+        put_error_op_ctx(genctx, IBMCA_ERR_INVALID_PARAM,
+                         "RSA '%s' value is not 2",
+                         OSSL_PKEY_PARAM_RSA_PRIMES);
+        return 0;
+    }
+
+    /* OSSL_PKEY_PARAM_RSA_E */
+    rc = ibmca_param_get_bn(genctx->provctx, params,
+                            OSSL_PKEY_PARAM_RSA_E, &genctx->rsa.gen.pub_exp);
+    if (rc == 0)
+        return 0;
+
+    if (genctx->type == EVP_PKEY_RSA_PSS) {
+        /* PSS restriction parameters */
+        rc = ibmca_keymgmt_rsa_pss_parms_from_data(genctx->provctx, params,
+                                                   &genctx->rsa.gen.pss);
+        if (rc == 0)
+            return 0;
+    }
+
+    return 1;
+}
+
+static int ibmca_keymgmt_rsa_gen_fallback(struct ibmca_op_ctx *genctx,
+                                          struct ibmca_key *key,
+                                          OSSL_CALLBACK *osslcb, void *cbarg)
+{
+    struct ibmca_keygen_cb_data cbdata;
+    EVP_PKEY_CTX *pctx = NULL;
+    EVP_PKEY *pkey = NULL;
+    int rc = 0;
+
+    ibmca_debug_op_ctx(genctx, "genctx: %p", genctx);
+
+    pctx = ibmca_new_fallback_pkey_ctx(genctx->provctx, NULL, "RSA");
+    if (pctx == NULL) {
+        ibmca_debug_op_ctx(genctx, "ERROR: ibmca_new_fallback_pkey_ctx failed");
+        goto out;
+    }
+
+    if (EVP_PKEY_keygen_init(pctx) != 1) {
+        put_error_key(key, IBMCA_ERR_INTERNAL_ERROR,
+                     "EVP_PKEY_keygen_init failed");
+        goto out;
+    }
+
+    if (ibmca_check_fallback_provider(genctx->provctx, pctx) != 1) {
+        ibmca_debug_op_ctx(genctx,
+                           "ERROR: ibmca_check_fallback_provider failed");
+        goto out;
+    }
+
+    if (EVP_PKEY_CTX_set_rsa_keygen_bits(pctx, genctx->rsa.gen.bits) != 1) {
+        put_error_key(key, IBMCA_ERR_INTERNAL_ERROR,
+                     "EVP_PKEY_CTX_set_rsa_keygen_bits failed");
+        goto out;
+    }
+
+    if (genctx->rsa.gen.pub_exp != NULL) {
+        if (EVP_PKEY_CTX_set1_rsa_keygen_pubexp(pctx,
+                                                genctx->rsa.gen.pub_exp) != 1) {
+            put_error_key(key, IBMCA_ERR_INTERNAL_ERROR,
+                         "EVP_PKEY_CTX_set1_rsa_keygen_pubexp failed");
+            goto out;
+        }
+    }
+
+    if (osslcb != NULL) {
+        cbdata.osslcb = osslcb;
+        cbdata.cbarg = cbarg;
+        EVP_PKEY_CTX_set_cb(pctx, ibmca_keygen_cb);
+        EVP_PKEY_CTX_set_app_data(pctx, &cbdata);
+    }
+
+    if (EVP_PKEY_generate(pctx, &pkey) != 1) {
+        put_error_key(key, IBMCA_ERR_INTERNAL_ERROR,
+                      "EVP_PKEY_generate failed");
+        goto out;
+    }
+
+    rc = ibmca_import_from_fallback_pkey(key, pkey,
+                                         OSSL_KEYMGMT_SELECT_KEYPAIR);
+    if (rc != 1) {
+        ibmca_debug_op_ctx(genctx,
+                           "ERROR: ibmca_import_from_fallback_pkey failed");
+        goto out;
+    }
+
+    rc = 1;
+
+out:
+    if (pctx != NULL)
+        EVP_PKEY_CTX_free(pctx);
+    if (pkey != NULL)
+        EVP_PKEY_free(pkey);
+
+    return rc;
+}
+
+static void *ibmca_keymgmt_rsa_gen(void *vgenctx, OSSL_CALLBACK *osslcb,
+                                   void *cbarg)
+{
+    struct ibmca_op_ctx *genctx = vgenctx;
+    OSSL_PARAM cb_params[] = { OSSL_PARAM_END, OSSL_PARAM_END, OSSL_PARAM_END };
+    struct ibmca_key *key = NULL;
+    int rc, p, n;
+    char *str;
+
+    if (genctx == NULL)
+        return NULL;
+
+    ibmca_debug_op_ctx(genctx, "genctx: %p", genctx);
+
+    cb_params[0] = OSSL_PARAM_construct_int(OSSL_GEN_PARAM_POTENTIAL, &p);
+    cb_params[1] = OSSL_PARAM_construct_int(OSSL_GEN_PARAM_ITERATION, &n);
+
+    key = ibmca_keymgmt_new(genctx->provctx, genctx->type, "RSA",
+                            ibmca_keymgmt_rsa_free_cb,
+                            ibmca_keymgmt_rsa_dup_cb,
+                            ibmca_keymgmt_rsa_get_max_param_size,
+                            ibmca_keymgmt_rsa_export,
+                            ibmca_keymgmt_rsa_import,
+                            ibmca_keymgmt_rsa_has,
+                            ibmca_keymgmt_rsa_match);
+    if (key == NULL) {
+        ibmca_debug_op_ctx(genctx, "ERROR: ibmca_keymgmt_new failed");
+        return NULL;
+    }
+
+    if (pthread_rwlock_init(&key->rsa.blinding_lock, NULL) != 0) {
+        put_error_key(key, IBMCA_ERR_INTERNAL_ERROR,
+                      "pthread_rwlock_init failed: %s", strerror(errno));
+        ibmca_keymgmt_free(key);
+        return NULL;
+    }
+
+    key->rsa.bits = genctx->rsa.gen.bits;
+    key->rsa.keylength = (key->rsa.bits + 7) / 8;
+    ibmca_debug_op_ctx(genctx, "bits: %lu", key->rsa.bits);
+
+    if (ibmca_keymgmt_rsa_alloc_pub(key) == 0) {
+        ibmca_keymgmt_free(key);
+        return NULL;
+    }
+
+    if (ibmca_keymgmt_rsa_alloc_priv_crt(key) == 0) {
+        ibmca_keymgmt_free(key);
+        return NULL;
+    }
+
+    if (genctx->rsa.gen.pub_exp != NULL) {
+        if (BN_bn2binpad(genctx->rsa.gen.pub_exp, key->rsa.public.exponent,
+                         key->rsa.public.key_length) <= 0) {
+            put_error_key(key, IBMCA_ERR_INTERNAL_ERROR,
+                          "BN_bn2binpad failed for public exponent");
+            ibmca_keymgmt_free(key);
+            return NULL;
+        }
+
+        str = BN_bn2hex(genctx->rsa.gen.pub_exp);
+        ibmca_debug_op_ctx(genctx, "public exponent: 0x%s (%d bits)", str,
+                        BN_num_bits(genctx->rsa.gen.pub_exp));
+        P_FREE(genctx->provctx, str);
+    }
+
+    p = 0;
+    n = 0;
+    if (osslcb != NULL && osslcb(cb_params, cbarg) == 0) {
+        put_error_op_ctx(genctx, IBMCA_ERR_INTERNAL_ERROR, "osslcb failed");
+        ibmca_keymgmt_free(key);
+        return NULL;
+    }
+
+    rc = ica_rsa_key_generate_crt(genctx->provctx->ica_adapter, key->rsa.bits,
+                                  &key->rsa.public, &key->rsa.private_crt);
+    if (rc != 0) {
+        ibmca_debug_op_ctx(genctx, "ica_rsa_key_generate_crt failed with: %s",
+                           strerror(rc));
+
+        rc = ibmca_keymgmt_rsa_gen_fallback(genctx, key, osslcb, cbarg);
+        if (rc != 1) {
+            ibmca_debug_op_ctx(genctx,
+                               "ERROR: ibmca_keymgmt_rsa_gen_fallback failed");
+            ibmca_keymgmt_free(key);
+            return NULL;
+        }
+    }
+
+    /* If p < q, swap and recalculate now */
+    rc = ica_rsa_crt_key_check(&key->rsa.private_crt);
+    if (rc > 1) {
+        put_error_op_ctx(genctx, IBMCA_ERR_INTERNAL_ERROR,
+                         "ica_rsa_crt_key_check failed");
+        ibmca_keymgmt_free(key);
+        return NULL;
+    }
+
+    p = 3;
+    n = 0;
+    if (osslcb != NULL && osslcb(cb_params, cbarg) == 0) {
+        put_error_op_ctx(genctx, IBMCA_ERR_INTERNAL_ERROR, "osslcb failed");
+        ibmca_keymgmt_free(key);
+        return NULL;
+    }
+
+    if (genctx->type == EVP_PKEY_RSA_PSS)
+        key->rsa.pss = genctx->rsa.gen.pss;
+
+    ibmca_debug_op_ctx(genctx, "key: %p", key);
+
+    return key;
+}
+
+static int ibmca_keymgmt_rsa_security_bits(size_t bits)
+{
+    switch (bits) {
+    case 512:
+        return 0; /* ??? */
+    case 1024:
+        return 80;
+    case 2048:
+        return 112;
+    case 3072:
+        return 128;
+    case 4096:
+        return 152;
+    case 6144:
+        return 176;
+    case 7680:
+        return 192;
+    case 8192:
+        return 200;
+    case 15360:
+        return 256;
+    default:
+        return 0;
+    }
+}
+
+int ibmca_keymgmt_rsa_pub_as_bn(struct ibmca_key *key, BIGNUM **n, BIGNUM **e)
+{
+    if (!ibmca_keymgmt_rsa_pub_valid(&key->rsa.public))
+        return 0;
+
+    *n = BN_bin2bn(key->rsa.public.modulus, key->rsa.public.key_length, NULL);
+    *e = BN_bin2bn(key->rsa.public.exponent, key->rsa.public.key_length, NULL);
+    if (*n == NULL || *e == NULL) {
+        put_error_key(key, IBMCA_ERR_INTERNAL_ERROR, "BN_bin2bn failed");
+       goto error;
+    }
+
+    return 1;
+
+error:
+    BN_free(*n);
+    *n = NULL;
+    BN_free(*e);
+    *e = NULL;
+
+    return 0;
+}
+
+static int ibmca_keymgmt_rsa_priv_crt_as_bn(struct ibmca_key *key, BIGNUM **p,
+                                            BIGNUM **q, BIGNUM **dp,
+                                            BIGNUM **dq, BIGNUM **qinv)
+{
+    if (!ibmca_keymgmt_rsa_priv_crt_valid(&key->rsa.private_crt))
+        return 0;
+
+    *p = BN_secure_new();
+    *q = BN_secure_new();
+    *dp = BN_secure_new();
+    *dq = BN_secure_new();
+    *qinv = BN_secure_new();
+    if (*p == NULL || *q == NULL || *dp == NULL ||
+        *dq == NULL || *qinv == NULL) {
+        put_error_key(key, IBMCA_ERR_MALLOC_FAILED, "BN_secure_new failed");
+        goto error;
+    }
+
+    *p = BN_bin2bn(key->rsa.private_crt.p,
+                   ICA_P_LEN(key->rsa.private_crt.key_length), *p);
+    *q = BN_bin2bn(key->rsa.private_crt.q,
+                   ICA_Q_LEN(key->rsa.private_crt.key_length), *q);
+    *dp = BN_bin2bn(key->rsa.private_crt.dp,
+                    ICA_DP_LEN(key->rsa.private_crt.key_length),
+                    *dp);
+    *dq = BN_bin2bn(key->rsa.private_crt.dq,
+                    ICA_DQ_LEN(key->rsa.private_crt.key_length), *dq);
+    *qinv = BN_bin2bn(key->rsa.private_crt.qInverse,
+                      ICA_QINV_LEN(key->rsa.private_crt.key_length), *qinv);
+    if (*p == NULL || *q == NULL || *dp == NULL ||
+        *dq == NULL || *qinv == NULL) {
+        put_error_key(key, IBMCA_ERR_INTERNAL_ERROR, "BN_bin2bn failed");
+       goto error;
+    }
+
+    return 1;
+
+error:
+    BN_clear_free(*p);
+    *p = NULL;
+    BN_clear_free(*q);
+    *q = NULL;
+    BN_clear_free(*dp);
+    *dp = NULL;
+    BN_clear_free(*dq);
+    *dq = NULL;
+    BN_clear_free(*qinv);
+    *qinv = NULL;
+
+    return 0;
+}
+
+int ibmca_keymgmt_rsa_priv_me_as_bn(struct ibmca_key *key, BIGNUM **d)
+{
+    if (!ibmca_keymgmt_rsa_priv_me_valid(&key->rsa.private_me))
+        return 0;
+
+    *d = BN_secure_new();
+    if (*d == NULL) {
+        put_error_key(key, IBMCA_ERR_MALLOC_FAILED, "BN_secure_new failed");
+        goto error;
+    }
+
+    *d = BN_bin2bn(key->rsa.private_me.exponent, key->rsa.private_me.key_length,
+                   *d);
+    if (*d == NULL) {
+        put_error_key(key, IBMCA_ERR_INTERNAL_ERROR, "BN_bin2bn failed");
+       goto error;
+    }
+
+    return 1;
+
+error:
+    BN_clear_free(*d);
+    *d = NULL;
+
+    return 0;
+}
+
+static int ibmca_keymgmt_rsa_calc_priv_d(const struct ibmca_prov_ctx *provctx,
+                                         BIGNUM *n, BIGNUM *e,
+                                         BIGNUM *p, BIGNUM *q,
+                                         BIGNUM **d)
+{
+    BN_CTX *bn_ctx;
+
+    /*
+     * phi(n) = (p - 1 )(q - 1) = n - p - q + 1
+     * d = e ^{-1} mod phi(n).
+     */
+    bn_ctx = BN_CTX_new();
+    *d = BN_secure_new();
+    if (bn_ctx == NULL || *d == NULL ||
+        BN_copy(*d, n) == NULL ||
+        BN_sub(*d, *d, p) == 0 ||
+        BN_sub(*d, *d, q) == 0 ||
+        BN_add_word(*d, 1) == 0 ||
+        BN_mod_inverse(*d, e, *d, bn_ctx) == NULL) {
+        put_error_ctx(provctx, IBMCA_ERR_INTERNAL_ERROR,
+                      "Failed to calculate private key part d");
+        BN_CTX_free(bn_ctx);
+        BN_clear_free(*d);
+        *d = NULL;
+        return 0;
+    }
+    BN_CTX_free(bn_ctx);
+
+    return 1;
+}
+
+static int ibmca_keymgmt_rsa_get_params(void *vkey, OSSL_PARAM params[])
+{
+    struct ibmca_key *key = vkey;
+    OSSL_PARAM *parm;
+    bool empty;
+    const char *name;
+    BIGNUM *n = NULL, *e = NULL, *d = NULL;
+    BIGNUM *p = NULL, *q = NULL, *dp = NULL, *dq = NULL, *qinv = NULL;
+    int rc;
+
+    if (key == NULL)
+        return 0;
+
+    ibmca_debug_key(key, "key: %p", key);
+    for (parm = params; parm != NULL && parm->key != NULL; parm++)
+        ibmca_debug_key(key, "param: %s", parm->key);
+
+    empty = (!ibmca_keymgmt_rsa_pub_valid(&key->rsa.public) &&
+             !ibmca_keymgmt_rsa_priv_crt_valid(&key->rsa.private_crt) &&
+             !ibmca_keymgmt_rsa_priv_me_valid(&key->rsa.private_me));
+
+    if (!empty) {
+        /* OSSL_PKEY_PARAM_BITS */
+        rc = ibmca_param_build_set_int(key->provctx, NULL, params,
+                                       OSSL_PKEY_PARAM_BITS, key->rsa.bits);
+        if (rc == 0)
+            return 0;
+
+        /* OSSL_PKEY_PARAM_SECURITY_BITS */
+        rc = ibmca_param_build_set_int(key->provctx, NULL, params,
+                                       OSSL_PKEY_PARAM_SECURITY_BITS,
+                                       ibmca_keymgmt_rsa_security_bits(
+                                                               key->rsa.bits));
+        if (rc == 0)
+            return 0;
+
+        /* OSSL_PKEY_PARAM_MAX_SIZE */
+        rc = ibmca_param_build_set_int(key->provctx, NULL, params,
+                                       OSSL_PKEY_PARAM_MAX_SIZE,
+                                       ibmca_keymgmt_rsa_get_max_param_size(
+                                                                       key));
+        if (rc == 0)
+            return 0;
+    }
+
+    /*
+     * For non-PSS keys or unrestricted RSA-PSS keys only:
+     * OSSL_PKEY_PARAM_DEFAULT_DIGEST
+     */
+    if ((key->type != EVP_PKEY_RSA_PSS || !key->rsa.pss.restricted)) {
+        name = OBJ_nid2sn(IBMCA_RSA_DEFAULT_DIGEST);
+        rc = ibmca_param_build_set_utf8(key->provctx, NULL, params,
+                                        OSSL_PKEY_PARAM_DEFAULT_DIGEST, name);
+        if (rc == 0)
+            return 0;
+    }
+
+    /*
+     * For restricted RSA-PSS keys only:
+     * OSSL_PKEY_PARAM_MANDATORY_DIGEST
+     */
+    if ((key->type == EVP_PKEY_RSA_PSS && key->rsa.pss.restricted)) {
+        name = OBJ_nid2sn(key->rsa.pss.digest_nid);
+        rc = ibmca_param_build_set_utf8(key->provctx, NULL, params,
+                                        OSSL_PKEY_PARAM_MANDATORY_DIGEST, name);
+        if (rc == 0)
+            return 0;
+    }
+
+    /* Public key parts */
+    rc =  ibmca_keymgmt_rsa_pub_as_bn(key, &n, &e);
+    if (rc == 1) {
+        rc = ibmca_keymgmt_rsa_pub_key_to_data(key->provctx, NULL, params, n, e);
+        if (rc == 0)
+            goto out;
+    }
+
+    /* Private key parts */
+    rc = ibmca_keymgmt_rsa_priv_crt_as_bn(key, &p, &q, &dp, &dq, &qinv);
+    if (rc == 1) {
+        /* CRT format */
+        rc = ibmca_keymgmt_rsa_priv_me_as_bn(key, &d);
+        if (rc == 0) {
+            rc = ibmca_keymgmt_rsa_calc_priv_d(key->provctx, n, e, p, q, &d);
+            if (rc == 0)
+                goto out;
+        }
+
+        rc = ibmca_keymgmt_rsa_priv_key_to_data(key->provctx, NULL, params, d,
+                                                p, q, dp, dq, qinv);
+        if (rc == 0)
+            goto out;
+    } else {
+        rc = ibmca_keymgmt_rsa_priv_me_as_bn(key, &d);
+        if (rc == 1) {
+            /* ME format */
+            rc = ibmca_keymgmt_rsa_priv_key_to_data(key->provctx, NULL, params,
+                                                    d, NULL, NULL, NULL, NULL,
+                                                    NULL);
+            if (rc == 0)
+                goto out;
+        }
+    }
+
+    /* Return RSA-PSS parameters only for restricted RSA-PSS keys */
+    if (key->type == EVP_PKEY_RSA_PSS && key->rsa.pss.restricted) {
+        rc = ibmca_keymgmt_rsa_pss_parms_to_data(key->provctx, NULL, params,
+                                                 &key->rsa.pss);
+        if (rc == 0)
+            goto out;
+    }
+
+    rc = 1;
+
+out:
+    BN_free(n);
+    BN_free(e);
+    BN_free(d);
+    BN_free(p);
+    BN_free(q);
+    BN_free(dp);
+    BN_free(dq);
+    BN_free(qinv);
+
+    return rc;
+}
+
+static const OSSL_PARAM ibmca_rsa_gettable_params[] = {
+    OSSL_PARAM_int(OSSL_PKEY_PARAM_BITS, NULL),
+    OSSL_PARAM_int(OSSL_PKEY_PARAM_SECURITY_BITS, NULL),
+    OSSL_PARAM_int(OSSL_PKEY_PARAM_MAX_SIZE, NULL),
+    OSSL_PARAM_utf8_string(OSSL_PKEY_PARAM_DEFAULT_DIGEST, NULL, 0),
+    OSSL_PARAM_utf8_string(OSSL_PKEY_PARAM_MANDATORY_DIGEST, NULL, 0),
+    OSSL_PARAM_BN(OSSL_PKEY_PARAM_RSA_N, NULL, 0),
+    OSSL_PARAM_BN(OSSL_PKEY_PARAM_RSA_E, NULL, 0),
+    OSSL_PARAM_BN(OSSL_PKEY_PARAM_RSA_D, NULL, 0),
+    OSSL_PARAM_BN(OSSL_PKEY_PARAM_RSA_FACTOR1, NULL, 0),
+    OSSL_PARAM_BN(OSSL_PKEY_PARAM_RSA_FACTOR2, NULL, 0),
+    OSSL_PARAM_BN(OSSL_PKEY_PARAM_RSA_EXPONENT1, NULL, 0),
+    OSSL_PARAM_BN(OSSL_PKEY_PARAM_RSA_EXPONENT2, NULL, 0),
+    OSSL_PARAM_BN(OSSL_PKEY_PARAM_RSA_COEFFICIENT1, NULL, 0),
+    OSSL_PARAM_END
+};
+
+static const OSSL_PARAM *ibmca_keymgmt_rsa_gettable_params(void *vprovctx)
+{
+    const struct ibmca_prov_ctx *provctx = vprovctx;
+    const OSSL_PARAM *p;
+
+    if (provctx == NULL)
+        return NULL;
+
+    for (p = ibmca_rsa_gettable_params; p != NULL && p->key != NULL; p++)
+        ibmca_debug_ctx(provctx, "param: %s", p->key);
+
+    return ibmca_rsa_gettable_params;
+}
+
+static const OSSL_PARAM ibmca_rsa_pss_gettable_params[] = {
+    OSSL_PARAM_int(OSSL_PKEY_PARAM_BITS, NULL),
+    OSSL_PARAM_int(OSSL_PKEY_PARAM_SECURITY_BITS, NULL),
+    OSSL_PARAM_int(OSSL_PKEY_PARAM_MAX_SIZE, NULL),
+    OSSL_PARAM_utf8_string(OSSL_PKEY_PARAM_DEFAULT_DIGEST, NULL, 0),
+    OSSL_PARAM_utf8_string(OSSL_PKEY_PARAM_MANDATORY_DIGEST, NULL, 0),
+    OSSL_PARAM_utf8_string(OSSL_PKEY_PARAM_RSA_DIGEST, NULL, 0),
+    OSSL_PARAM_utf8_string(OSSL_PKEY_PARAM_RSA_DIGEST_PROPS, NULL, 0),
+    OSSL_PARAM_utf8_string(OSSL_PKEY_PARAM_RSA_MASKGENFUNC, NULL, 0),
+    OSSL_PARAM_utf8_string(OSSL_PKEY_PARAM_RSA_MGF1_DIGEST, NULL, 0),
+    OSSL_PARAM_int(OSSL_PKEY_PARAM_RSA_PSS_SALTLEN, NULL),
+    OSSL_PARAM_BN(OSSL_PKEY_PARAM_RSA_N, NULL, 0),
+    OSSL_PARAM_BN(OSSL_PKEY_PARAM_RSA_E, NULL, 0),
+    OSSL_PARAM_BN(OSSL_PKEY_PARAM_RSA_D, NULL, 0),
+    OSSL_PARAM_BN(OSSL_PKEY_PARAM_RSA_FACTOR1, NULL, 0),
+    OSSL_PARAM_BN(OSSL_PKEY_PARAM_RSA_FACTOR2, NULL, 0),
+    OSSL_PARAM_BN(OSSL_PKEY_PARAM_RSA_EXPONENT1, NULL, 0),
+    OSSL_PARAM_BN(OSSL_PKEY_PARAM_RSA_EXPONENT2, NULL, 0),
+    OSSL_PARAM_BN(OSSL_PKEY_PARAM_RSA_COEFFICIENT1, NULL, 0),
+    OSSL_PARAM_END
+};
+
+static const OSSL_PARAM *ibmca_keymgmt_rsa_pss_gettable_params(void *vprovctx)
+{
+    const struct ibmca_prov_ctx *provctx = vprovctx;
+    const OSSL_PARAM *p;
+
+    if (provctx == NULL)
+        return NULL;
+
+    for (p = ibmca_rsa_pss_gettable_params; p != NULL && p->key != NULL; p++)
+        ibmca_debug_ctx(provctx, "param: %s", p->key);
+
+    return ibmca_rsa_pss_gettable_params;
+}
+
+static const OSSL_PARAM ibmca_rsa_eximport_types[] = {
+    OSSL_PARAM_BN(OSSL_PKEY_PARAM_RSA_N, NULL, 0),
+    OSSL_PARAM_BN(OSSL_PKEY_PARAM_RSA_E, NULL, 0),
+    OSSL_PARAM_BN(OSSL_PKEY_PARAM_RSA_D, NULL, 0),
+    OSSL_PARAM_BN(OSSL_PKEY_PARAM_RSA_FACTOR1, NULL, 0),
+    OSSL_PARAM_BN(OSSL_PKEY_PARAM_RSA_FACTOR2, NULL, 0),
+    OSSL_PARAM_BN(OSSL_PKEY_PARAM_RSA_EXPONENT1, NULL, 0),
+    OSSL_PARAM_BN(OSSL_PKEY_PARAM_RSA_EXPONENT2, NULL, 0),
+    OSSL_PARAM_BN(OSSL_PKEY_PARAM_RSA_COEFFICIENT1, NULL, 0),
+    OSSL_PARAM_END
+};
+
+static const OSSL_PARAM ibmca_rsa_pss_params_eximport_types[] = {
+    OSSL_PARAM_utf8_string(OSSL_PKEY_PARAM_RSA_DIGEST, NULL, 0),
+    OSSL_PARAM_utf8_string(OSSL_PKEY_PARAM_RSA_DIGEST_PROPS, NULL, 0),
+    OSSL_PARAM_utf8_string(OSSL_PKEY_PARAM_RSA_MASKGENFUNC, NULL, 0),
+    OSSL_PARAM_utf8_string(OSSL_PKEY_PARAM_RSA_MGF1_DIGEST, NULL, 0),
+    OSSL_PARAM_int(OSSL_PKEY_PARAM_RSA_PSS_SALTLEN, NULL),
+    OSSL_PARAM_END
+};
+
+static const OSSL_PARAM ibmca_rsa_pss_export_types[] = {
+    OSSL_PARAM_BN(OSSL_PKEY_PARAM_RSA_N, NULL, 0),
+    OSSL_PARAM_BN(OSSL_PKEY_PARAM_RSA_E, NULL, 0),
+    OSSL_PARAM_BN(OSSL_PKEY_PARAM_RSA_D, NULL, 0),
+    OSSL_PARAM_BN(OSSL_PKEY_PARAM_RSA_FACTOR1, NULL, 0),
+    OSSL_PARAM_BN(OSSL_PKEY_PARAM_RSA_FACTOR2, NULL, 0),
+    OSSL_PARAM_BN(OSSL_PKEY_PARAM_RSA_EXPONENT1, NULL, 0),
+    OSSL_PARAM_BN(OSSL_PKEY_PARAM_RSA_EXPONENT2, NULL, 0),
+    OSSL_PARAM_BN(OSSL_PKEY_PARAM_RSA_COEFFICIENT1, NULL, 0),
+    OSSL_PARAM_utf8_string(OSSL_PKEY_PARAM_RSA_DIGEST, NULL, 0),
+    OSSL_PARAM_utf8_string(OSSL_PKEY_PARAM_RSA_DIGEST_PROPS, NULL, 0),
+    OSSL_PARAM_utf8_string(OSSL_PKEY_PARAM_RSA_MASKGENFUNC, NULL, 0),
+    OSSL_PARAM_utf8_string(OSSL_PKEY_PARAM_RSA_MGF1_DIGEST, NULL, 0),
+    OSSL_PARAM_int(OSSL_PKEY_PARAM_RSA_PSS_SALTLEN, NULL),
+    OSSL_PARAM_END
+};
+
+static const OSSL_PARAM ibmca_rsa_pss_import_types[] = {
+    OSSL_PARAM_BN(OSSL_PKEY_PARAM_RSA_N, NULL, 0),
+    OSSL_PARAM_BN(OSSL_PKEY_PARAM_RSA_E, NULL, 0),
+    OSSL_PARAM_BN(OSSL_PKEY_PARAM_RSA_D, NULL, 0),
+    OSSL_PARAM_BN(OSSL_PKEY_PARAM_RSA_FACTOR1, NULL, 0),
+    OSSL_PARAM_BN(OSSL_PKEY_PARAM_RSA_FACTOR2, NULL, 0),
+    OSSL_PARAM_BN(OSSL_PKEY_PARAM_RSA_EXPONENT1, NULL, 0),
+    OSSL_PARAM_BN(OSSL_PKEY_PARAM_RSA_EXPONENT2, NULL, 0),
+    OSSL_PARAM_BN(OSSL_PKEY_PARAM_RSA_COEFFICIENT1, NULL, 0),
+    OSSL_PARAM_utf8_string(OSSL_PKEY_PARAM_RSA_DIGEST_PROPS, NULL, 0),
+    OSSL_PARAM_utf8_string(OSSL_PKEY_PARAM_RSA_DIGEST, NULL, 0),
+    OSSL_PARAM_utf8_string(OSSL_PKEY_PARAM_RSA_DIGEST_PROPS, NULL, 0),
+    OSSL_PARAM_utf8_string(OSSL_PKEY_PARAM_RSA_MASKGENFUNC, NULL, 0),
+    OSSL_PARAM_utf8_string(OSSL_PKEY_PARAM_RSA_MGF1_DIGEST, NULL, 0),
+    OSSL_PARAM_int(OSSL_PKEY_PARAM_RSA_PSS_SALTLEN, NULL),
+    OSSL_PARAM_END
+};
+
+static const OSSL_PARAM *ibmca_keymgmt_rsa_export_types(int selection)
+{
+    if ((selection & OSSL_KEYMGMT_SELECT_KEYPAIR) != 0)
+        return ibmca_rsa_eximport_types;
+
+    return NULL;
+}
+
+static const OSSL_PARAM *ibmca_keymgmt_rsa_import_types(int selection)
+{
+    if ((selection & OSSL_KEYMGMT_SELECT_KEYPAIR) != 0)
+        return ibmca_rsa_eximport_types;
+
+    return NULL;
+}
+
+static const OSSL_PARAM *ibmca_keymgmt_rsa_pss_export_types(int selection)
+{
+    if (selection == OSSL_KEYMGMT_SELECT_OTHER_PARAMETERS)
+        return ibmca_rsa_pss_params_eximport_types;
+
+    if ((selection & OSSL_KEYMGMT_SELECT_OTHER_PARAMETERS) == 0 &&
+        (selection & OSSL_KEYMGMT_SELECT_KEYPAIR) != 0)
+        return ibmca_rsa_eximport_types;
+
+    if ((selection & OSSL_KEYMGMT_SELECT_OTHER_PARAMETERS) != 0 &&
+        (selection & OSSL_KEYMGMT_SELECT_KEYPAIR) != 0)
+        return ibmca_rsa_pss_export_types;
+
+    return NULL;
+}
+
+static const OSSL_PARAM *ibmca_keymgmt_rsa_pss_import_types(int selection)
+{
+    if (selection == OSSL_KEYMGMT_SELECT_OTHER_PARAMETERS)
+        return ibmca_rsa_pss_params_eximport_types;
+
+    if ((selection & OSSL_KEYMGMT_SELECT_OTHER_PARAMETERS) == 0 &&
+        (selection & OSSL_KEYMGMT_SELECT_KEYPAIR) != 0)
+        return ibmca_rsa_eximport_types;
+
+    if ((selection & OSSL_KEYMGMT_SELECT_OTHER_PARAMETERS) != 0 &&
+        (selection & OSSL_KEYMGMT_SELECT_KEYPAIR) != 0)
+        return ibmca_rsa_pss_import_types;
+
+    return NULL;
+}
+
+int ibmca_keymgmt_rsa_export(void *vkey, int selection,
+                             OSSL_CALLBACK *param_callback, void *cbarg)
+{
+    struct ibmca_key *key = vkey;
+    OSSL_PARAM_BLD *bld;
+    OSSL_PARAM *params = NULL;
+    BIGNUM *n = NULL, *e = NULL, *d = NULL, *p = NULL, *q = NULL, *dp = NULL;
+    BIGNUM *dq = NULL, *qinv = NULL;
+    int rc = 1;
+
+    if (key == NULL || param_callback == NULL)
+        return 0;
+
+    ibmca_debug_key(key, "key: %p selection: 0x%x", key, selection);
+
+    bld = OSSL_PARAM_BLD_new();
+    if (bld == NULL) {
+        put_error_key(key, IBMCA_ERR_MALLOC_FAILED,
+                      "OSSL_PARAM_BLD_new failed");
+        return 0;
+    }
+
+    /* Public key is required if private key is exported */
+    if ((selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) != 0)
+        selection |= OSSL_KEYMGMT_SELECT_PUBLIC_KEY;
+
+    if ((selection & OSSL_KEYMGMT_SELECT_PUBLIC_KEY) != 0) {
+        /* Public key parts */
+        rc =  ibmca_keymgmt_rsa_pub_as_bn(key, &n, &e);
+        if (rc == 1) {
+            rc = ibmca_keymgmt_rsa_pub_key_to_data(key->provctx, bld, NULL,
+                                                   n, e);
+            if (rc == 0)
+                goto error;
+        }
+    }
+
+    if ((selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) != 0) {
+        /* Private key parts */
+        rc = ibmca_keymgmt_rsa_priv_crt_as_bn(key, &p, &q, &dp, &dq, &qinv);
+        if (rc == 1) {
+            /* CRT format */
+            rc = ibmca_keymgmt_rsa_priv_me_as_bn(key, &d);
+            if (rc == 0) {
+                rc = ibmca_keymgmt_rsa_calc_priv_d(key->provctx, n, e, p, q,
+                                                   &d);
+                if (rc == 0)
+                    goto error;
+            }
+
+            rc = ibmca_keymgmt_rsa_priv_key_to_data(key->provctx, bld, NULL, d,
+                                                    p, q, dp, dq, qinv);
+            if (rc == 0)
+                goto error;
+        } else {
+            rc = ibmca_keymgmt_rsa_priv_me_as_bn(key, &d);
+            if (rc == 1) {
+                /* ME format */
+                rc = ibmca_keymgmt_rsa_priv_key_to_data(key->provctx, bld, NULL,
+                                                        d, NULL, NULL, NULL,
+                                                        NULL, NULL);
+                if (rc == 0)
+                    goto error;
+            }
+        }
+    }
+
+    if ((selection & OSSL_KEYMGMT_SELECT_OTHER_PARAMETERS) != 0 &&
+        key->type == EVP_PKEY_RSA_PSS &&
+        key->rsa.pss.restricted) {
+        /* PSS parameters */
+        rc = ibmca_keymgmt_rsa_pss_parms_to_data(key->provctx, bld, NULL,
+                                                 &key->rsa.pss);
+        if (rc == 0)
+            goto error;
+    }
+
+    params = OSSL_PARAM_BLD_to_param(bld);
+    if (params == NULL) {
+        put_error_key(key, IBMCA_ERR_INTERNAL_ERROR,
+                      "OSSL_PARAM_BLD_to_param failed");
+        rc = 0;
+        goto error;
+    }
+
+    rc = param_callback(params, cbarg);
+    OSSL_PARAM_free(params);
+
+error:
+    OSSL_PARAM_BLD_free(bld);
+
+    if (n != NULL)
+        BN_free(n);
+    if (e != NULL)
+        BN_free(e);
+    if (d != NULL)
+        BN_free(d);
+    if (p != NULL)
+        BN_free(p);
+    if (q != NULL)
+        BN_free(q);
+    if (dp != NULL)
+        BN_free(dp);
+    if (dq != NULL)
+        BN_free(dq);
+    if (qinv != NULL)
+        BN_free(qinv);
+
+    return rc;
+}
+
+int ibmca_keymgmt_rsa_import(void *vkey, int selection,
+                             const OSSL_PARAM params[])
+{
+    struct ibmca_key *key = vkey;
+    const OSSL_PARAM *parm;
+    BIGNUM *n = NULL, *e = NULL, *d = NULL, *p = NULL, *q = NULL;
+    BIGNUM *dp = NULL, *dq = NULL, *qinv = NULL;
+    int rc = 0;
+
+    if (key == NULL)
+        return 0;
+
+    ibmca_debug_key(key, "key: %p selection: 0x%x", key, selection);
+    for (parm = params; parm != NULL && parm->key != NULL; parm++)
+        ibmca_debug_key(key, "param: %s", parm->key);
+
+    /* Clear any already existing key components */
+    ibmca_keymgmt_rsa_clean(key);
+    ibmca_clean_fallback_pkey_cache(key);
+
+    /* Import public key parts */
+    if ((selection & OSSL_KEYMGMT_SELECT_PUBLIC_KEY) == 0) {
+        put_error_key(key, IBMCA_ERR_INVALID_PARAM,
+                      "RSA public key parts are mandatory");
+        return 0;
+    }
+
+    rc = ibmca_keymgmt_rsa_pub_key_from_data(key->provctx, params, &n, &e);
+    if (rc == 0)
+        return 0;
+
+    key->rsa.bits = BN_num_bits(n);
+    key->rsa.keylength = (key->rsa.bits + 7) / 8;
+    ibmca_debug_key(key, "key: %p bits: %u", key, key->rsa.bits);
+
+    if (ibmca_keymgmt_rsa_alloc_pub(key) == 0)
+        goto out;
+
+    if (BN_bn2binpad(n, key->rsa.public.modulus,
+                     key->rsa.public.key_length) <= 0) {
+        put_error_key(key, IBMCA_ERR_INTERNAL_ERROR,
+                      "BN_bn2binpad failed for modulus");
+        goto out;
+    }
+
+    if (BN_bn2binpad(e, key->rsa.public.exponent,
+                     key->rsa.public.key_length) <= 0) {
+        put_error_key(key, IBMCA_ERR_INTERNAL_ERROR,
+                      "BN_bn2binpad failed for public exponent");
+        goto out;
+    }
+
+    if ((selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) != 0) {
+        /* Import private key parts */
+        rc = ibmca_keymgmt_rsa_priv_crt_key_from_data(key->provctx, params,
+                                                      n, e,
+                                                      &p, &q, &dp, &dq, &qinv);
+        if (rc == 1) {
+            /* CRT components */
+            if (ibmca_keymgmt_rsa_alloc_priv_crt(key) == 0)
+                goto out;
+
+            if (BN_bn2binpad(p, key->rsa.private_crt.p,
+                             ICA_P_LEN(key->rsa.private_crt.key_length)) <= 0) {
+                put_error_key(key, IBMCA_ERR_INTERNAL_ERROR,
+                              "BN_bn2binpad failed for private p");
+                goto out;
+            }
+            if (BN_bn2binpad(q, key->rsa.private_crt.q,
+                             ICA_Q_LEN(key->rsa.private_crt.key_length)) <= 0) {
+                put_error_key(key, IBMCA_ERR_INTERNAL_ERROR,
+                              "BN_bn2binpad failed for private q");
+                goto out;
+            }
+            if (BN_bn2binpad(dp, key->rsa.private_crt.dp,
+                             ICA_DP_LEN(key->rsa.private_crt.key_length)) <= 0) {
+                put_error_key(key, IBMCA_ERR_INTERNAL_ERROR,
+                              "BN_bn2binpad failed for private dp");
+                goto out;
+            }
+            if (BN_bn2binpad(dq, key->rsa.private_crt.dq,
+                             ICA_DQ_LEN(key->rsa.private_crt.key_length)) <= 0) {
+                put_error_key(key, IBMCA_ERR_INTERNAL_ERROR,
+                              "BN_bn2binpad failed for private dq");
+                goto out;
+            }
+            if (BN_bn2binpad(qinv, key->rsa.private_crt.qInverse,
+                             ICA_QINV_LEN(key->rsa.private_crt.key_length)) <= 0) {
+                put_error_key(key, IBMCA_ERR_INTERNAL_ERROR,
+                              "BN_bn2binpad failed for private qinv");
+                goto out;
+            }
+
+            /* If p < q, swap and recalculate now */
+            rc = ica_rsa_crt_key_check(&key->rsa.private_crt);
+            if (rc > 1) {
+                rc = 0;
+                put_error_key(key, IBMCA_ERR_INTERNAL_ERROR,
+                              "ica_rsa_crt_key_check failed");
+                goto out;
+            }
+        }
+
+        rc = ibmca_keymgmt_rsa_priv_me_key_from_data(key->provctx, params, &d);
+        if (rc == 1) {
+            /* ME components */
+            if (ibmca_keymgmt_rsa_alloc_priv_me(key) == 0)
+                goto out;
+
+            if (BN_bn2binpad(n, key->rsa.private_me.modulus,
+                             key->rsa.private_me.key_length) <= 0) {
+                put_error_key(key, IBMCA_ERR_INTERNAL_ERROR,
+                              "BN_bn2binpad failed for modulus");
+                goto out;
+            }
+
+            if (BN_bn2binpad(d, key->rsa.private_me.exponent,
+                             key->rsa.private_me.key_length) <= 0) {
+                put_error_key(key, IBMCA_ERR_INTERNAL_ERROR,
+                              "BN_bn2binpad failed for private d");
+                goto out;
+            }
+        }
+    }
+
+    if ((selection & OSSL_KEYMGMT_SELECT_OTHER_PARAMETERS) != 0 &&
+        key->type == EVP_PKEY_RSA_PSS) {
+        /* Import PSS restriction parameters */
+        rc = ibmca_keymgmt_rsa_pss_parms_from_data(key->provctx, params,
+                                                   &key->rsa.pss);
+        if (rc == 0)
+            goto out;
+    }
+
+    rc = 1;
+
+out:
+    BN_free(n);
+    BN_free(e);
+    BN_free(d);
+    BN_free(p);
+    BN_free(q);
+    BN_free(dp);
+    BN_free(dq);
+    BN_free(qinv);
+
+    return rc;
+}
+
+int ibmca_keymgmt_rsa_derive_kdk(struct ibmca_key *key,
+                                 const unsigned char *in, size_t inlen,
+                                 unsigned char *kdk, size_t kdklen)
+{
+    BIGNUM *n = NULL, *e = NULL, *d = NULL;
+    BIGNUM *p = NULL, *q = NULL, *dp = NULL, *dq = NULL, *qinv = NULL;
+    unsigned char *buf = NULL;
+    EVP_MAC_CTX *ctx = NULL;
+    EVP_MAC *hmac = NULL;
+    EVP_MD *md = NULL;
+    size_t md_len;
+    unsigned char d_hash[SHA256_DIGEST_LENGTH] = { 0 };
+    OSSL_PARAM params[2];
+    int rc, ret = 0;
+
+    /*
+     * The implementation of this function is copied from OpenSSL's function
+     * derive_kdk() in crypto/rsa/rsa_ossl.c and is slightly modified to fit to
+     * the providers environment.
+     * Changes include:
+     * - Different variable and define names.
+     * - Usage of put_error_ctx and ibmca_debug_ctx to report errors and issue
+     *   debug messages.
+     * - Different code to get the private key component 'd'.
+     * - Use of the EVP APIs instead of the internal APIs for Digest and HMAC
+     *   operations.
+     */
+
+    ibmca_debug_key(key, "key: %p inlen: %lu kdklen: %lu", key, inlen, kdklen);
+
+    if (kdklen != SHA256_DIGEST_LENGTH) {
+        put_error_key(key, IBMCA_ERR_INVALID_PARAM, "KDK length is wrong");
+        return 0;
+    }
+
+    rc = ibmca_keymgmt_rsa_priv_me_as_bn(key, &d);
+    if (rc == 0) {
+        rc = ibmca_keymgmt_rsa_priv_crt_as_bn(key, &p, &q, &dp, &dq, &qinv);
+        if (rc == 0)
+            goto out;
+
+        rc =  ibmca_keymgmt_rsa_pub_as_bn(key, &n, &e);
+        if (rc == 0)
+            goto out;
+
+        rc = ibmca_keymgmt_rsa_calc_priv_d(key->provctx, n, e, p, q, &d);
+        if (rc == 0)
+            goto out;
+    }
+
+    buf = P_SECURE_ZALLOC(key->provctx, key->rsa.keylength);
+    if (buf == NULL) {
+        put_error_key(key, IBMCA_ERR_MALLOC_FAILED,
+                     "Failed to allocate buffer for private key");
+        goto out;
+    }
+
+    BN_set_flags(d, BN_FLG_CONSTTIME);
+    if (BN_bn2binpad(d, buf, key->rsa.keylength) < 0) {
+        put_error_key(key, IBMCA_ERR_INTERNAL_ERROR,
+                     "BN_bn2binpad failed");
+        goto out;
+    }
+
+    /*
+     * we use hardcoded hash so that migrating between versions that use
+     * different hash doesn't provide a Bleichenbacher oracle:
+     * if the attacker can see that different versions return different
+     * messages for the same ciphertext, they'll know that the message is
+     * syntethically generated, which means that the padding check failed
+     */
+    md = EVP_MD_fetch(key->provctx->libctx, "sha256", NULL);
+    if (md == NULL) {
+        put_error_key(key, IBMCA_ERR_INTERNAL_ERROR,
+                      "EVP_MD_fetch failed for sha256");
+        goto out;
+    }
+
+    if (EVP_Digest(buf, key->rsa.keylength, d_hash, NULL, md, NULL) <= 0) {
+        put_error_key(key, IBMCA_ERR_INTERNAL_ERROR, "EVP_Digest failed");
+        goto out;
+    }
+
+    hmac = EVP_MAC_fetch(key->provctx->libctx, "HMAC", NULL);
+    if (hmac == NULL) {
+        put_error_key(key, IBMCA_ERR_INTERNAL_ERROR,
+                      "EVP_MAC_fetch failed for HMAC");
+        goto out;
+    }
+
+    ctx = EVP_MAC_CTX_new(hmac);
+    if (hmac == NULL) {
+        put_error_key(key, IBMCA_ERR_INTERNAL_ERROR, "EVP_MAC_CTX_new failed");
+        goto out;
+    }
+
+    params[0] = OSSL_PARAM_construct_utf8_string(
+                                        OSSL_MAC_PARAM_DIGEST, "sha256", 0);
+    params[1] = OSSL_PARAM_construct_end();
+
+    if (EVP_MAC_init(ctx, d_hash, sizeof(d_hash), params) != 1) {
+        put_error_key(key, IBMCA_ERR_INTERNAL_ERROR,
+                      "EVP_MAC_init failed for HMAC with sha256");
+        goto out;
+    }
+
+    if (inlen < key->rsa.keylength) {
+        memset(buf, 0, key->rsa.keylength - inlen);
+        if (EVP_MAC_update(ctx, buf, key->rsa.keylength - inlen) != 1) {
+            put_error_key(key, IBMCA_ERR_INTERNAL_ERROR,
+                          "EVP_MAC_update failed");
+            goto out;
+        }
+    }
+    if (EVP_MAC_update(ctx, in, inlen) != 1) {
+        put_error_key(key, IBMCA_ERR_INTERNAL_ERROR, "EVP_MAC_update failed");
+        goto out;
+    }
+
+    if (EVP_MAC_final(ctx, kdk, &md_len, kdklen) != 1 ||
+        md_len != kdklen) {
+        put_error_key(key, IBMCA_ERR_INTERNAL_ERROR, "EVP_MAC_final failed");
+        goto out;
+    }
+
+    ret = 1;
+
+out:
+    BN_free(n);
+    BN_free(e);
+    BN_free(d);
+    BN_free(p);
+    BN_free(q);
+    BN_free(dp);
+    BN_free(dq);
+    BN_free(qinv);
+    if (buf != NULL)
+        P_SECURE_CLEAR_FREE(key->provctx, buf, key->rsa.keylength);
+    EVP_MAC_free(hmac);
+    EVP_MAC_CTX_free(ctx);
+    EVP_MD_free(md);
+
+    ibmca_debug_key(key, "ret: %d", ret);
+
+    return ret;
+}
+
+static const OSSL_DISPATCH ibmca_rsa_keymgmt_functions[] = {
+    /* Constructor, destructor */
+    { OSSL_FUNC_KEYMGMT_NEW, (void (*)(void))ibmca_keymgmt_rsa_new },
+    { OSSL_FUNC_KEYMGMT_FREE, (void (*)(void))ibmca_keymgmt_free },
+    { OSSL_FUNC_KEYMGMT_DUP, (void (*)(void))ibmca_keymgmt_dup },
+
+    /* Key generation and loading */
+    { OSSL_FUNC_KEYMGMT_GEN_INIT,
+            (void (*)(void))ibmca_keymgmt_rsa_gen_init },
+    { OSSL_FUNC_KEYMGMT_GEN_SET_TEMPLATE,
+            (void (*)(void))ibmca_keymgmt_rsa_gen_set_template },
+    { OSSL_FUNC_KEYMGMT_GEN_SET_PARAMS,
+            (void (*)(void))ibmca_keymgmt_rsa_gen_set_params },
+    { OSSL_FUNC_KEYMGMT_GEN_SETTABLE_PARAMS,
+            (void (*)(void))ibmca_keymgmt_rsa_gen_settable_params },
+    { OSSL_FUNC_KEYMGMT_GEN, (void (*)(void))ibmca_keymgmt_rsa_gen },
+    { OSSL_FUNC_KEYMGMT_GEN_CLEANUP,
+            (void (*)(void))ibmca_keymgmt_gen_cleanup },
+    { OSSL_FUNC_KEYMGMT_LOAD, (void (*)(void))ibmca_keymgmt_load },
+
+    /* Key object checking */
+    { OSSL_FUNC_KEYMGMT_HAS, (void (*)(void))ibmca_keymgmt_rsa_has },
+    { OSSL_FUNC_KEYMGMT_MATCH, (void (*)(void))ibmca_keymgmt_rsa_match },
+    { OSSL_FUNC_KEYMGMT_VALIDATE, (void (*)(void))ibmca_keymgmt_rsa_validate },
+    { OSSL_FUNC_KEYMGMT_QUERY_OPERATION_NAME,
+            (void (*)(void))ibmca_keymgmt_rsa_query_operation_name },
+
+    /* Key object information */
+    { OSSL_FUNC_KEYMGMT_GET_PARAMS,
+            (void (*) (void))ibmca_keymgmt_rsa_get_params },
+    { OSSL_FUNC_KEYMGMT_GETTABLE_PARAMS,
+            (void (*) (void))ibmca_keymgmt_rsa_gettable_params },
+
+    /* Import and export routines */
+    { OSSL_FUNC_KEYMGMT_EXPORT, (void (*)(void))ibmca_keymgmt_rsa_export },
+    { OSSL_FUNC_KEYMGMT_EXPORT_TYPES,
+            (void (*)(void))ibmca_keymgmt_rsa_export_types },
+    { OSSL_FUNC_KEYMGMT_IMPORT, (void (*)(void))ibmca_keymgmt_rsa_import },
+    { OSSL_FUNC_KEYMGMT_IMPORT_TYPES,
+            (void (*)(void))ibmca_keymgmt_rsa_import_types },
+
+    { 0, NULL }
+};
+
+static const OSSL_DISPATCH ibmca_rsapss_keymgmt_functions[] = {
+    /* Constructor, destructor */
+    { OSSL_FUNC_KEYMGMT_NEW, (void (*)(void))ibmca_keymgmt_rsa_pss_new },
+    { OSSL_FUNC_KEYMGMT_FREE, (void (*)(void))ibmca_keymgmt_free },
+    { OSSL_FUNC_KEYMGMT_DUP, (void (*)(void))ibmca_keymgmt_dup },
+
+    /* Key generation and loading */
+    { OSSL_FUNC_KEYMGMT_GEN_INIT,
+            (void (*)(void))ibmca_keymgmt_rsa_pss_gen_init },
+    { OSSL_FUNC_KEYMGMT_GEN_SET_TEMPLATE,
+            (void (*)(void))ibmca_keymgmt_rsa_gen_set_template },
+    { OSSL_FUNC_KEYMGMT_GEN_SET_PARAMS,
+            (void (*)(void))ibmca_keymgmt_rsa_gen_set_params },
+    { OSSL_FUNC_KEYMGMT_GEN_SETTABLE_PARAMS,
+            (void (*)(void))ibmca_keymgmt_rsa_pss_gen_settable_params },
+    { OSSL_FUNC_KEYMGMT_GEN, (void (*)(void))ibmca_keymgmt_rsa_gen },
+    { OSSL_FUNC_KEYMGMT_GEN_CLEANUP,
+            (void (*)(void))ibmca_keymgmt_gen_cleanup },
+    { OSSL_FUNC_KEYMGMT_LOAD, (void (*)(void))ibmca_keymgmt_load },
+
+    /* Key object checking */
+    { OSSL_FUNC_KEYMGMT_HAS, (void (*)(void))ibmca_keymgmt_rsa_has },
+    { OSSL_FUNC_KEYMGMT_MATCH, (void (*)(void))ibmca_keymgmt_rsa_match },
+    { OSSL_FUNC_KEYMGMT_VALIDATE, (void (*)(void))ibmca_keymgmt_rsa_validate },
+    { OSSL_FUNC_KEYMGMT_QUERY_OPERATION_NAME,
+        (void (*)(void))ibmca_keymgmt_rsa_query_operation_name },
+
+    /* Key object information */
+    { OSSL_FUNC_KEYMGMT_GET_PARAMS,
+            (void (*) (void))ibmca_keymgmt_rsa_get_params },
+    { OSSL_FUNC_KEYMGMT_GETTABLE_PARAMS,
+        (void (*) (void))ibmca_keymgmt_rsa_pss_gettable_params },
+
+    /* Import and export routines */
+    { OSSL_FUNC_KEYMGMT_EXPORT, (void (*)(void))ibmca_keymgmt_rsa_export },
+    { OSSL_FUNC_KEYMGMT_EXPORT_TYPES,
+            (void (*)(void))ibmca_keymgmt_rsa_pss_export_types },
+    { OSSL_FUNC_KEYMGMT_IMPORT, (void (*)(void))ibmca_keymgmt_rsa_import },
+    { OSSL_FUNC_KEYMGMT_IMPORT_TYPES,
+            (void (*)(void))ibmca_keymgmt_rsa_pss_import_types },
+
+    { 0, NULL }
+};
+
+const OSSL_ALGORITHM ibmca_rsa_keymgmt[] = {
+    { "RSA:rsaEncryption:1.2.840.113549.1.1.1", NULL,
+      ibmca_rsa_keymgmt_functions, "IBMCA RSA implementation" },
+    { "RSA-PSS:RSASSA-PSS:1.2.840.113549.1.1.10", NULL,
+      ibmca_rsapss_keymgmt_functions, "IBMCA RSA-PSS implementation" },
+    { NULL, NULL, NULL, NULL }
+};
+
+
diff -pruN 1.4.0-1/src/provider/rsa_padding.c 2.5.0-0ubuntu1/src/provider/rsa_padding.c
--- 1.4.0-1/src/provider/rsa_padding.c	1970-01-01 00:00:00.000000000 +0000
+++ 2.5.0-0ubuntu1/src/provider/rsa_padding.c	2025-04-16 08:09:24.000000000 +0000
@@ -0,0 +1,1497 @@
+/*
+ * Copyright [2021-2023] International Business Machines Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <stddef.h>
+#include <err.h>
+#include <strings.h>
+#include <string.h>
+
+#include <openssl/evp.h>
+#include <openssl/rsa.h>
+#include <openssl/rand.h>
+#include <openssl/x509.h>
+#include <openssl/core_names.h>
+
+#include "p_ibmca.h"
+#include "constant_time.h"
+
+const OSSL_ITEM ibmca_rsa_padding_table[] = {
+    { RSA_PKCS1_PADDING,        OSSL_PKEY_RSA_PAD_MODE_PKCSV15 },
+    { RSA_NO_PADDING,           OSSL_PKEY_RSA_PAD_MODE_NONE },
+    { RSA_PKCS1_OAEP_PADDING,   OSSL_PKEY_RSA_PAD_MODE_OAEP },
+    { RSA_PKCS1_OAEP_PADDING,   "oeap"   },
+    { RSA_X931_PADDING,         OSSL_PKEY_RSA_PAD_MODE_X931 },
+    { RSA_PKCS1_PSS_PADDING,    OSSL_PKEY_RSA_PAD_MODE_PSS },
+    { RSA_PKCS1_WITH_TLS_PADDING, "" }, /* Will only be set as integer param */
+    { 0,                        NULL     }
+};
+
+#define ASN1_SEQUENCE        0x30
+#define ASN1_OCTET_STRING    0x04
+
+int ibmca_rsa_build_digest_info(const struct ibmca_prov_ctx *provctx,
+                                const EVP_MD *md, const unsigned char *data,
+                                size_t data_len, unsigned char *out,
+                                size_t outsize, size_t *outlen)
+{
+    X509_ALGOR *algid;
+    int aid_len, md_len, seq_len, rc = 0;
+    unsigned char *p;
+
+    ibmca_debug_ctx(provctx, "md: '%s' data_len: %lu outsize: %lu",
+                    EVP_MD_get0_name(md), data_len, outsize);
+
+    /*
+     DigestInfo ::= SEQUENCE {
+           digestAlgorithm AlgorithmIdentifier,
+           digest OCTET STRING
+     }
+     */
+
+    md_len = EVP_MD_get_size(md);
+    if (md_len <= 0 || md_len > 0x7F) {
+        put_error_ctx(provctx, IBMCA_ERR_INTERNAL_ERROR,
+                      "EVP_MD_get_size failed or invalid digest size");
+        return 0;
+    }
+
+    if (data_len != (size_t)md_len) {
+        put_error_ctx(provctx, IBMCA_ERR_INVALID_PARAM,
+                      "Input data size is incorrect, it must match the digest size: data size: %lu expected: %d",
+                      data_len, md_len);
+        return 0;
+    }
+
+    algid = X509_ALGOR_new();
+    if (algid == NULL) {
+        put_error_ctx(provctx, IBMCA_ERR_MALLOC_FAILED,
+                       "X509_ALGOR_new failed");
+        goto out;
+    }
+
+    X509_ALGOR_set0(algid, OBJ_nid2obj(EVP_MD_get_type(md)),
+                    V_ASN1_NULL, NULL);
+    aid_len = i2d_X509_ALGOR(algid, NULL);
+    if (aid_len <= 0) {
+        put_error_ctx(provctx, IBMCA_ERR_INTERNAL_ERROR,
+                      "i2d_X509_ALGOR failed");
+        goto out;
+    }
+
+    seq_len = aid_len + 2 + md_len;
+    if (seq_len > 0x7F)
+        *outlen = 4 + seq_len;
+    else
+        *outlen = 2 + seq_len;
+
+    if (outsize < *outlen) {
+        put_error_ctx(provctx, IBMCA_ERR_INVALID_PARAM,
+                      "Output buffer size too small");
+        goto out;
+    }
+
+    p = out;
+    *(p++) = ASN1_SEQUENCE;
+    if (seq_len > 0x7F) {
+        *(p++) = 0x82;
+        *(p++) = (seq_len >> 8);
+        *(p++) = (seq_len & 0xff);
+    } else {
+        *(p++) = seq_len;
+    }
+
+    if (i2d_X509_ALGOR(algid, &p) != aid_len) {
+        put_error_ctx(provctx, IBMCA_ERR_INTERNAL_ERROR,
+                      "i2d_X509_ALGOR failed");
+        goto out;
+    }
+
+    *(p++) = ASN1_OCTET_STRING;
+    *(p++) = md_len;
+    memcpy(p, data, md_len);
+
+    ibmca_debug_ctx(provctx, "outlen: %lu", *outlen);
+
+    rc = 1;
+
+out:
+    if (algid != NULL)
+        X509_ALGOR_free(algid);
+
+    return rc;
+}
+
+static int ibmca_rsa_prf(const struct ibmca_prov_ctx *provctx,
+                         unsigned char *out, size_t outlen,
+                         const char *label, size_t labellen,
+                         const unsigned char *kdk, size_t kdklen,
+                         uint16_t bitlen)
+{
+    size_t pos;
+    int ret = 0;
+    uint16_t iter = 0;
+    unsigned char be_iter[sizeof(iter)];
+    unsigned char be_bitlen[sizeof(bitlen)];
+    EVP_MAC_CTX *ctx = NULL;
+    EVP_MAC *hmac = NULL;
+    unsigned char hmac_out[SHA256_DIGEST_LENGTH];
+    OSSL_PARAM params[2];
+    size_t md_len;
+
+    /*
+     * The implementation of this function is copied from OpenSSL's function
+     * ossl_rsa_prf() in crypto/rsa/rsapk1.c and is slightly modified to fit to
+     * the providers environment.
+     * Changes include:
+     * - Different variable and define names.
+     * - Usage of put_error_ctx and ibmca_debug_ctx to report errors and issue
+     *   debug messages.
+     * - Use of the EVP API instead of the internal APIs for HMAC operations.
+     */
+
+    ibmca_debug_ctx(provctx, "outlen: %lu labellen: %lu kdk: %p kdklen: %lu",
+                    outlen, labellen, kdk, kdklen);
+
+    if (outlen * 8 != bitlen) {
+        put_error_ctx(provctx, IBMCA_ERR_INVALID_PARAM, "invalid outlen");
+        return 0;
+    }
+
+    be_bitlen[0] = (bitlen >> 8) & 0xff;
+    be_bitlen[1] = bitlen & 0xff;
+
+    hmac = EVP_MAC_fetch(provctx->libctx, "HMAC", NULL);
+    if (hmac == NULL) {
+        put_error_ctx(provctx, IBMCA_ERR_INTERNAL_ERROR,
+                      "EVP_MAC_fetch failed for HMAC");
+        goto out;
+    }
+
+    ctx = EVP_MAC_CTX_new(hmac);
+    if (hmac == NULL) {
+        put_error_ctx(provctx, IBMCA_ERR_INTERNAL_ERROR,
+                      "EVP_MAC_CTX_new failed");
+        goto out;
+    }
+
+    /*
+     * we use hardcoded hash so that migrating between versions that use
+     * different hash doesn't provide a Bleichenbacher oracle:
+     * if the attacker can see that different versions return different
+     * messages for the same ciphertext, they'll know that the message is
+     * syntethically generated, which means that the padding check failed
+     */
+    params[0] = OSSL_PARAM_construct_utf8_string(
+                                        OSSL_MAC_PARAM_DIGEST, "sha256", 0);
+    params[1] = OSSL_PARAM_construct_end();
+
+    if (EVP_MAC_init(ctx, kdk, kdklen, params) != 1) {
+        put_error_ctx(provctx, IBMCA_ERR_INTERNAL_ERROR,
+                      "EVP_MAC_init failed for HMAC with sha256");
+        goto out;
+    }
+
+    for (pos = 0; pos < outlen; pos += SHA256_DIGEST_LENGTH, iter++) {
+        if (EVP_MAC_init(ctx, NULL, 0, NULL) != 1) {
+            put_error_ctx(provctx, IBMCA_ERR_INTERNAL_ERROR,
+                          "EVP_MAC_init failed");
+            goto out;
+        }
+
+        be_iter[0] = (iter >> 8) & 0xff;
+        be_iter[1] = iter & 0xff;
+
+        if (EVP_MAC_update(ctx, be_iter, sizeof(be_iter)) != 1) {
+            put_error_ctx(provctx, IBMCA_ERR_INTERNAL_ERROR,
+                          "EVP_MAC_update failed");
+            goto out;
+        }
+        if (EVP_MAC_update(ctx, (unsigned char *)label, labellen) != 1) {
+            put_error_ctx(provctx, IBMCA_ERR_INTERNAL_ERROR,
+                          "EVP_MAC_update failed");
+            goto out;
+        }
+        if (EVP_MAC_update(ctx, be_bitlen, sizeof(be_bitlen)) != 1) {
+            put_error_ctx(provctx, IBMCA_ERR_INTERNAL_ERROR,
+                          "EVP_MAC_update failed");
+            goto out;
+        }
+
+        /*
+         * HMAC_Final requires the output buffer to fit the whole MAC
+         * value, so we need to use the intermediate buffer for the last
+         * unaligned block
+         */
+        md_len = SHA256_DIGEST_LENGTH;
+        if (pos + SHA256_DIGEST_LENGTH > outlen) {
+            if (EVP_MAC_final(ctx, hmac_out, &md_len, sizeof(hmac_out)) != 1) {
+                put_error_ctx(provctx, IBMCA_ERR_INTERNAL_ERROR,
+                              "EVP_MAC_final failed");
+                goto out;
+            }
+            memcpy(out + pos, hmac_out, outlen - pos);
+        } else {
+            if (EVP_MAC_final(ctx, out + pos, &md_len, outlen - pos) != 1) {
+                put_error_ctx(provctx, IBMCA_ERR_INTERNAL_ERROR,
+                              "EVP_MAC_final failed");
+                goto out;
+            }
+        }
+    }
+
+    ret = 1;
+
+out:
+    EVP_MAC_free(hmac);
+    EVP_MAC_CTX_free(ctx);
+
+    ibmca_debug_ctx(provctx, "ret: %d", ret);
+
+    return ret;
+}
+
+int ibmca_rsa_add_pkcs1_padding(const struct ibmca_prov_ctx *provctx, int type,
+                                const unsigned char *in, size_t inlen,
+                                unsigned char *out, size_t outlen)
+{
+    int i, pad_len;
+    unsigned char *p;
+
+    ibmca_debug_ctx(provctx, "type: %d inlen: %lu outlen: %lu", type,
+                    inlen, outlen);
+
+    /*
+     * The format is
+     * 00 || BT || PS || 00 || D
+     * BT - block type
+     * PS - padding string, at least 8 bytes of FF for BT = 1 or at least 8
+     *      bytes of random non-zero data for BT = 2
+     * D  - data.
+     */
+    if (outlen < (inlen + RSA_PKCS1_PADDING_SIZE)) {
+        put_error_ctx(provctx, IBMCA_ERR_INVALID_PARAM,
+                      "Data too large for the key type");
+        return 0;
+    }
+
+    pad_len = outlen - 3 - inlen;
+    if (pad_len < 8) {
+        put_error_ctx(provctx, IBMCA_ERR_INVALID_PARAM,
+                      "Data too large for the key type");
+        return 0;
+    }
+
+    p = out;
+
+    *(p++) = 0;
+    *(p++) = type;
+
+    switch (type) {
+    case 1:
+        memset(p, 0xff, pad_len);
+        p += pad_len;
+        break;
+
+    case 2:
+        if (RAND_bytes_ex(provctx->libctx, p, pad_len, 0) <= 0) {
+            put_error_ctx(provctx, IBMCA_ERR_INTERNAL_ERROR,
+                                  "RAND_bytes_ex failed");
+            return 0;
+        }
+
+        for (i = 0; i < pad_len; i++, p++) {
+            while (*p == '\0') {
+                if (RAND_bytes_ex(provctx->libctx, p, 1, 0) <= 0) {
+                    put_error_ctx(provctx, IBMCA_ERR_INTERNAL_ERROR,
+                                  "RAND_bytes_ex failed");
+                    return 0;
+                }
+            }
+        }
+        break;
+
+    default:
+        put_error_ctx(provctx, IBMCA_ERR_INVALID_PARAM,
+                       "Invalid PKCS1 block type: %d", type);
+        return 0;
+    }
+
+    *(p++) = 0;
+    memcpy(p, in, inlen);
+
+    return 1;
+}
+
+int ibmca_rsa_check_pkcs1_padding_type1(const struct ibmca_prov_ctx *provctx,
+                                        const unsigned char *in, size_t inlen,
+                                        unsigned char *out, size_t outsize,
+                                        unsigned char **outptr, size_t *outlen)
+{
+    const unsigned char *p;
+    int found = 0;
+
+    ibmca_debug_ctx(provctx, "inlen: %lu outsize: %lu", inlen, outsize);
+
+    /*
+     * The format is
+     * 00 || BT || PS || 00 || D
+     * BT - block type
+     * PS - padding string, at least 8 bytes of FF
+     * D  - data.
+     */
+    if (inlen < RSA_PKCS1_PADDING_SIZE) {
+        put_error_ctx(provctx, IBMCA_ERR_INVALID_PARAM, "PKCS1 encoding error");
+        return 0;
+    }
+
+    p = in;
+
+    if (*(p++) != 0 || *(p++) != 1) {
+        put_error_ctx(provctx, IBMCA_ERR_INVALID_PARAM, "PKCS1 encoding error");
+        return 0;
+    }
+
+
+    while (p < in + inlen) {
+        if (*p != 0xff) {
+            if (*p == 0x00) {
+                found = 1;
+                break;
+            }
+
+            put_error_ctx(provctx, IBMCA_ERR_INVALID_PARAM,
+                          "PKCS1 encoding error");
+            return 0;
+        }
+        p++;
+    }
+
+    if (!found) {
+        put_error_ctx(provctx, IBMCA_ERR_INVALID_PARAM,
+                      "PKCS1 encoding error");
+        return 0;
+    }
+
+    p++;
+
+    *outlen = inlen - (p - in);
+
+    if (outsize < *outlen) {
+        put_error_ctx(provctx, IBMCA_ERR_INVALID_PARAM,
+                      "Output buffer too small");
+        return 0;
+    }
+
+    if (out != NULL)
+        memcpy(out, p, *outlen);
+    if (outptr != NULL)
+        *outptr = (unsigned char *)p;
+
+    ibmca_debug_ctx(provctx, "outlen: %lu", *outlen);
+
+    return 1;
+}
+
+#define MAX_LEN_GEN_TRIES 128
+
+int ibmca_rsa_check_pkcs1_padding_type2(const struct ibmca_prov_ctx *provctx,
+                                        const unsigned char *in, size_t inlen,
+                                        unsigned char *out, size_t outsize,
+                                        size_t *outlen,
+                                        const unsigned char *kdk,
+                                        size_t kdklen)
+{
+    unsigned int ok = 0, found, zero;
+    size_t zero_index = 0, msg_index, mlen;
+    unsigned char *synthetic = NULL;
+    int synthethic_length;
+    uint16_t len_candidate;
+    unsigned char candidate_lengths[MAX_LEN_GEN_TRIES * sizeof(len_candidate)];
+    uint16_t len_mask;
+    uint16_t max_sep_offset;
+    int synth_msg_index = 0;
+
+    size_t i, j;
+
+    /*
+     * The implementation of this function is copied from OpenSSL's function
+     * ossl_rsa_padding_check_PKCS1_type_2() in crypto/rsa/rsa_pk1.c
+     * and is slightly modified to fit to the providers environment.
+     * Changes include:
+     * - Different variable, function and define names.
+     * - Usage of put_error_ctx and ibmca_debug_ctx to report errors and issue
+     *   debug messages.
+     */
+
+    ibmca_debug_ctx(provctx, "inlen: %lu outsize: %lu kdk: %p kdklen: %lu",
+                    inlen, outsize, kdk, kdklen);
+
+    /*
+     * The format is
+     * 00 || BT || PS || 00 || D
+     * BT - block type
+     * PS - padding string, at least 8 bytes of random non-zero data for BT = 2
+     * D  - data.
+     */
+
+    /*
+     * PKCS#1 v1.5 decryption. See "PKCS #1 v2.2: RSA Cryptography Standard",
+     * section 7.2.2.
+     */
+    if (inlen < RSA_PKCS1_PADDING_SIZE) {
+        put_error_ctx(provctx, IBMCA_ERR_INVALID_PARAM, "PKCS1 encoding error");
+        return 0;
+    }
+
+
+    if (kdk != NULL && kdklen > 0) {
+        /*
+         * Implicit rejection is enabled.
+         * Generate a random message to return in case the padding checks fail.
+         */
+        synthetic = P_ZALLOC(provctx, inlen);
+        if (synthetic == NULL) {
+            put_error_ctx(provctx, IBMCA_ERR_MALLOC_FAILED,
+                          "Failed to allocate synthetic buffer");
+            return 0;
+        }
+
+        if (ibmca_rsa_prf(provctx, synthetic, inlen, "message", 7, kdk, kdklen,
+                          inlen * 8) != 1)
+            goto out;
+
+        /* decide how long the random message should be */
+        if (ibmca_rsa_prf(provctx, candidate_lengths, sizeof(candidate_lengths),
+                         "length", 6, kdk, kdklen,
+                         MAX_LEN_GEN_TRIES * sizeof(len_candidate) * 8) != 1)
+            goto out;
+
+        /*
+         * max message size is the size of the modulus size minus 2 bytes for
+         * version and padding type and a minimum of 8 bytes padding
+         */
+        len_mask = max_sep_offset = inlen - 2 - 8;
+        /*
+         * we want a mask so lets propagate the high bit to all positions less
+         * significant than it
+         */
+        len_mask |= len_mask >> 1;
+        len_mask |= len_mask >> 2;
+        len_mask |= len_mask >> 4;
+        len_mask |= len_mask >> 8;
+
+        synthethic_length = 0;
+        for (i = 0; i < MAX_LEN_GEN_TRIES * (int)sizeof(len_candidate);
+                                                i += sizeof(len_candidate)) {
+            len_candidate = (candidate_lengths[i] << 8) |
+                                                    candidate_lengths[i + 1];
+            len_candidate &= len_mask;
+
+            synthethic_length = constant_time_select_int(
+                            constant_time_lt(len_candidate, max_sep_offset),
+                                             len_candidate, synthethic_length);
+        }
+
+        synth_msg_index = inlen - synthethic_length;
+    }
+
+    ok = constant_time_is_zero(in[0]);
+    ok &= constant_time_eq(in[1], 2);
+
+    /* scan over padding data */
+    found = 0;
+    for (i = 2; i < inlen; i++) {
+        zero = constant_time_is_zero(in[i]);
+
+        zero_index = constant_time_select_int(~found & zero, i, zero_index);
+        found |= zero;
+    }
+
+    /*
+     * PS must be at least 8 bytes long, and it starts two bytes into |enc_msg|.
+     * If we never found a 0-byte, then |zero_index| is 0 and the check
+     * also fails.
+     */
+    ok &= constant_time_ge(zero_index, 2 + 8);
+
+    /*
+     * Skip the zero byte. This is incorrect if we never found a zero-byte
+     * but in this case we also do not copy the message out.
+     */
+    msg_index = zero_index + 1;
+    mlen = inlen - msg_index;
+
+    /*
+     * For good measure, do this check in constant time as well.
+     */
+    ok &= constant_time_ge(outsize, mlen);
+
+    /*
+     * since at this point the |msg_index| does not provide the signal
+     * indicating if the padding check failed or not, we don't have to worry
+     * about leaking the length of returned message, we still need to ensure
+     * that we read contents of both buffers so that cache accesses don't leak
+     * the value of |good|
+     */
+    if (kdk != NULL) {
+        msg_index = constant_time_select_int(ok, msg_index, synth_msg_index);
+
+        for (i = msg_index, j = 0; i < inlen && j < outsize; i++, j++)
+            out[j] = constant_time_select_8(ok, in[i], synthetic[i]);
+    } else {
+        for (i = msg_index, j = 0; i < inlen && j < outsize; i++, j++)
+            out[j] = constant_time_select_8(ok, in[i], out[j]);
+    }
+
+    *outlen = j;
+
+out:
+    if (synthetic != NULL)
+        P_FREE(provctx, synthetic);
+
+    ibmca_debug_ctx(provctx, "ok: %d outlen: %lu",
+                    constant_time_select_int(ok, 1, 0), *outlen);
+
+    if (kdk != NULL)
+        return 1;
+    else
+        return constant_time_select_int(ok, 1, 0);
+}
+
+static int ibmca_rsa_pkcs1_mgf1(const struct ibmca_prov_ctx *provctx,
+                                unsigned char *mask, long mask_len,
+                                const unsigned char *seed, long seed_len,
+                                const EVP_MD *mgf1_md)
+{
+    long i, outlen = 0;
+    unsigned char cnt[4];
+    EVP_MD_CTX *md_ctx = EVP_MD_CTX_new();
+    unsigned char md[EVP_MAX_MD_SIZE];
+    int mdlen;
+    int rc = 0;
+
+    if (md_ctx == NULL) {
+        put_error_ctx(provctx, IBMCA_ERR_INTERNAL_ERROR,
+                      "EVP_MD_CTX_new failed");
+        return 0;
+    }
+
+    mdlen = EVP_MD_get_size(mgf1_md);
+    if (mdlen < 0) {
+        put_error_ctx(provctx, IBMCA_ERR_INTERNAL_ERROR,
+                      "EVP_MD_get_size failed");
+        goto err;
+    }
+
+    /* step 4 */
+    for (i = 0; outlen < mask_len; i++) {
+        /* step 4a: D = I2BS(counter, 4) */
+        cnt[0] = (unsigned char)((i >> 24) & 255);
+        cnt[1] = (unsigned char)((i >> 16) & 255);
+        cnt[2] = (unsigned char)((i >> 8)) & 255;
+        cnt[3] = (unsigned char)(i & 255);
+
+        /* step 4b: T =T || hash(mgfSeed || D) */
+        if (!EVP_DigestInit_ex(md_ctx, mgf1_md, NULL)
+            || !EVP_DigestUpdate(md_ctx, seed, seed_len)
+            || !EVP_DigestUpdate(md_ctx, cnt, 4)) {
+            put_error_ctx(provctx, IBMCA_ERR_INTERNAL_ERROR,
+                          "EVP_DigestInit_ex/EVP_DigestUpdate failed");
+            goto err;
+        }
+
+        if (outlen + mdlen <= mask_len) {
+            if (!EVP_DigestFinal_ex(md_ctx, mask + outlen, NULL)) {
+                put_error_ctx(provctx, IBMCA_ERR_INTERNAL_ERROR,
+                              "EVP_DigestFinal_ex failed");
+                goto err;
+            }
+
+            outlen += mdlen;
+        } else {
+            if (!EVP_DigestFinal_ex(md_ctx, md, NULL)) {
+                put_error_ctx(provctx, IBMCA_ERR_INTERNAL_ERROR,
+                              "EVP_DigestFinal_ex failed");
+                goto err;
+            }
+
+            memcpy(mask + outlen, md, mask_len - outlen);
+            outlen = mask_len;
+        }
+    }
+
+    rc = 1;
+
+ err:
+    OPENSSL_cleanse(md, sizeof(md));
+    EVP_MD_CTX_free(md_ctx);
+
+    return rc;
+}
+
+int ibmca_rsa_add_oaep_mgf1_padding(const struct ibmca_prov_ctx *provctx,
+                                    const unsigned char *in, size_t inlen,
+                                    unsigned char *out, size_t outlen,
+                                    const EVP_MD *oaep_md,
+                                    const EVP_MD *mgf1_md,
+                                    const unsigned char *label,
+                                    size_t label_len)
+{
+    int ps_len;
+    size_t oaep_md_len, dbmask_len = 0, i;
+    unsigned char *masked_seed, *masked_db, *dbmask = NULL;
+    unsigned char seed[EVP_MAX_MD_SIZE];
+    int rc = 0;
+
+    if (oaep_md == NULL) {
+        put_error_ctx(provctx, IBMCA_ERR_INVALID_PARAM,
+                      "No OAEP digest available");
+        return 0;
+    }
+
+    if (mgf1_md == NULL)
+        mgf1_md = oaep_md;
+
+    ibmca_debug_ctx(provctx,
+                    "inlen: %lu outlen: %lu oaep_md: '%s' mgf1_md: '%s' label_len: %lu",
+                    inlen, outlen, EVP_MD_get0_name(oaep_md),
+                    EVP_MD_get0_name(mgf1_md), label_len);
+
+    oaep_md_len = EVP_MD_get_size(oaep_md);
+    if (oaep_md_len <= 0) {
+        put_error_ctx(provctx, IBMCA_ERR_INTERNAL_ERROR,
+                      "EVP_MD_get_size failed");
+        goto done;
+    }
+
+    if (inlen > outlen - (2 * oaep_md_len) - 2) {
+        put_error_ctx(provctx, IBMCA_ERR_INVALID_PARAM,
+                      "Data too large for the key type");
+        return 0;
+    }
+
+    /*
+     * pkcs1v2.2 Step i:
+     * The encoded messages is a concatenated single octet, 0x00 with
+     * maskedSeed and maskedDB to create encoded message EM.
+     * So lets mark of the places in our output buffer.
+     */
+    memset(out, 0, outlen);
+    masked_seed = out + 1;
+    masked_db = out + oaep_md_len + 1;
+
+    /*
+     * pkcs1v2.2, Step b:
+     * Generate an octet string PS and concatenate to DB.
+     */
+    ps_len = outlen - inlen - (2 * oaep_md_len) - 2;
+    if (EVP_Digest((void *)label, label_len, masked_db, NULL,
+                   oaep_md, NULL) == 0) {
+        put_error_ctx(provctx, IBMCA_ERR_INTERNAL_ERROR, "EVP_Digest failed");
+        goto done;
+    }
+    memset(masked_db + oaep_md_len, 0, ps_len);
+
+    /*
+     * pkcs1v2.2, Step c:
+     * We have already concatenated hash and PS to maskedDB.
+     * Now just concatenate 0x01 and message.
+     */
+    masked_db[oaep_md_len + ps_len] = 0x01;
+    memcpy(masked_db + (oaep_md_len + ps_len + 1), in, inlen);
+
+    /*
+     * pkcs1v2.2, Step d:
+     * Generate a random seed.
+     */
+    if (RAND_bytes_ex(provctx->libctx, seed, oaep_md_len, 0) <= 0) {
+        put_error_ctx(provctx, IBMCA_ERR_INTERNAL_ERROR, "RAND_bytes_ex failed");
+        goto done;
+    }
+
+    /* pkcs1v2.2, Step e:
+     * Compute dbmask using MGF1.
+     */
+    dbmask_len = outlen - oaep_md_len - 1;
+    dbmask = P_ZALLOC(provctx, dbmask_len);
+    if (dbmask == NULL) {
+        put_error_ctx(provctx, IBMCA_ERR_MALLOC_FAILED,
+                      "Failed to allocate mask buffer");
+        goto done;
+    }
+
+    if (ibmca_rsa_pkcs1_mgf1(provctx, dbmask, dbmask_len, seed, oaep_md_len,
+                             mgf1_md) == 0)
+        goto done;
+
+    /* pkcs1v2.2, Step f:
+     * Compute maskedDB.
+     */
+    for (i = 0; i < dbmask_len; i++)
+        masked_db[i] ^= dbmask[i];
+
+    /* pkcs1v2.2, Step g:
+     * Compute seedMask using MGF1.
+     */
+    memset(masked_seed, 0, oaep_md_len);
+    if (ibmca_rsa_pkcs1_mgf1(provctx, masked_seed, oaep_md_len, masked_db,
+                             dbmask_len, mgf1_md) == 0)
+        goto done;
+
+    /* pkcs1v2.2, Step h:
+     * Compute maskedSeed.
+     */
+    for (i = 0; i < oaep_md_len; i++)
+        masked_seed[i] ^= seed[i];
+
+    rc = 1;
+
+done:
+    if (dbmask)
+        P_CLEAR_FREE(provctx, dbmask, dbmask_len);
+    P_CLEANSE(provctx, seed, sizeof(seed));
+
+    return rc;
+}
+
+int ibmca_rsa_check_oaep_mgf1_padding(const struct ibmca_prov_ctx *provctx,
+                                      const unsigned char *in, size_t inlen,
+                                      unsigned char *out, size_t outsize,
+                                      size_t *outlen,
+                                      const EVP_MD *oaep_md,
+                                      const EVP_MD *mgf1_md,
+                                      const unsigned char *label,
+                                      size_t label_len)
+{
+    size_t i, dblen = 0, mlen = -1, one_index = 0, msg_index, mdlen;
+    unsigned int ok = 0, found_one_byte, mask;
+    const unsigned char *maskedseed, *maskeddb;
+    unsigned char *db = NULL;
+    unsigned char seed[EVP_MAX_MD_SIZE], phash[EVP_MAX_MD_SIZE];
+
+    /*
+     * The implementation of this function is copied from OpenSSL's function
+     * RSA_padding_check_PKCS1_OAEP_mgf1() in crypto/rsa/rsa_oaep.c
+     * and is slightly modified to fit to the providers environment.
+     * Changes include:
+     * - Different variable and define names.
+     * - Usage of put_error_ctx and ibmca_debug_ctx to report errors and issue
+     *   debug messages.
+     * - No need for copying the input to an allocated 'em' buffer. The caller
+     *   guarantees that the size of the input is already the size of the
+     *   modulus.
+     */
+
+    if (oaep_md == NULL) {
+        put_error_ctx(provctx, IBMCA_ERR_INVALID_PARAM,
+                      "No OAEP digest available");
+        return 0;
+    }
+
+    if (mgf1_md == NULL)
+        mgf1_md = oaep_md;
+
+    ibmca_debug_ctx(provctx,
+                    "inlen: %lu outsize: %lu oaep_md: '%s' mgf1_md: '%s' label_len: %lu",
+                    inlen, outsize, EVP_MD_get0_name(oaep_md),
+                    EVP_MD_get0_name(mgf1_md), label_len);
+
+    mdlen = EVP_MD_get_size(oaep_md);
+
+    /*
+     * |inlen| is guaranteed by the caller to be the modulus size.
+     * |inlen| >= 2 * |mdlen| + 2 must hold for the modulus
+     * irrespective of the ciphertext, see PKCS #1 v2.2, section 7.1.2.
+     * This does not leak any side-channel information.
+     */
+    if (inlen < 2 * mdlen + 2) {
+        put_error_ctx(provctx, IBMCA_ERR_INVALID_PARAM,
+                      "Input parameters wrong");
+        goto done;
+    }
+
+    dblen = inlen - mdlen - 1;
+    db = P_ZALLOC(provctx, dblen);
+    if (db == NULL) {
+        put_error_ctx(provctx, IBMCA_ERR_MALLOC_FAILED,
+                      "Failed to allocate mask or message buffer");
+        goto done;
+    }
+
+    /*
+     * The first byte must be zero, however we must not leak if this is
+     * true. See James H. Manger, "A Chosen Ciphertext  Attack on RSA
+     * Optimal Asymmetric Encryption Padding (OAEP) [...]", CRYPTO 2001).
+     */
+    ok = constant_time_is_zero(in[0]);
+
+    maskedseed = in + 1;
+    maskeddb = in + 1 + mdlen;
+
+    if (ibmca_rsa_pkcs1_mgf1(provctx, seed, mdlen, maskeddb, dblen,
+                             mgf1_md) == 0)
+        goto done;
+
+    for (i = 0; i < mdlen; i++)
+        seed[i] ^= maskedseed[i];
+
+    if (ibmca_rsa_pkcs1_mgf1(provctx, db, dblen, seed, mdlen, mgf1_md) == 0)
+        goto done;
+
+    for (i = 0; i < dblen; i++)
+        db[i] ^= maskeddb[i];
+
+    if (EVP_Digest((void *)label, label_len, phash, NULL, oaep_md, NULL) == 0) {
+        put_error_ctx(provctx, IBMCA_ERR_INTERNAL_ERROR, "EVP_Digest failed");
+        goto done;
+    }
+
+    ok &= constant_time_is_zero(CRYPTO_memcmp(db, phash, mdlen));
+
+    found_one_byte = 0;
+    for (i = mdlen; i < dblen; i++) {
+        /*
+         * Padding consists of a number of 0-bytes, followed by a 1.
+         */
+        unsigned int equals1 = constant_time_eq(db[i], 1);
+        unsigned int equals0 = constant_time_is_zero(db[i]);
+        one_index = constant_time_select_int(~found_one_byte & equals1,
+                                             i, one_index);
+        found_one_byte |= equals1;
+        ok &= (found_one_byte | equals0);
+    }
+
+    ok &= found_one_byte;
+
+    /*
+     * At this point |good| is zero unless the plaintext was valid,
+     * so plaintext-awareness ensures timing side-channels are no longer a
+     * concern.
+     */
+    msg_index = one_index + 1;
+    mlen = dblen - msg_index;
+
+    /*
+     * For good measure, do this check in constant time as well.
+     */
+    ok &= constant_time_ge(outsize, mlen);
+
+    /*
+     * Move the result in-place by |dblen| - |mdlen| - 1 - |mlen| bytes
+     * to the left.
+     * Then if |good| move |mlen| bytes from |db| + |mdlen| + 1 to |out|.
+     * Otherwise leave |out| unchanged.
+     * Copy the memory back in a way that does not reveal the size of
+     * the data being copied via a timing side channel. This requires copying
+     * parts of the buffer multiple times based on the bits set in the real
+     * length. Clear bits do a non-copy with identical access pattern.
+     * The loop below has overall complexity of O(N*log(N)).
+     */
+    outsize = constant_time_select_int(constant_time_lt(dblen - mdlen - 1,
+                                                        outsize),
+                                       dblen - mdlen - 1, outsize);
+    for (msg_index = 1; msg_index < dblen - mdlen - 1; msg_index <<= 1) {
+        mask = ~constant_time_eq(msg_index & (dblen - mdlen - 1 - mlen),
+                                 0);
+        for (i = mdlen + 1; i < dblen - msg_index; i++)
+            db[i] = constant_time_select_8(mask, db[i + msg_index], db[i]);
+    }
+    for (i = 0; i < outsize; i++) {
+        mask = ok & constant_time_lt(i, mlen);
+        out[i] = constant_time_select_8(mask, db[i + mdlen + 1], out[i]);
+    }
+
+done:
+    P_CLEANSE(provctx, seed, sizeof(seed));
+    if (db)
+        P_CLEAR_FREE(provctx, db, dblen);
+
+    *outlen = constant_time_select_int(ok, mlen, 0);
+
+    ibmca_debug_ctx(provctx, "ok: %d outlen: %lu",
+                    constant_time_select_int(ok, 1, 0), *outlen);
+
+    return constant_time_select_int(ok, 1, 0);
+}
+
+int ibmca_rsa_check_pkcs1_tls_padding(const struct ibmca_prov_ctx *provctx,
+                                      unsigned int client_version,
+                                      unsigned int alt_version,
+                                      const unsigned char *in, size_t inlen,
+                                      unsigned char *out, size_t outsize,
+                                      size_t *outlen)
+{
+    size_t i;
+    unsigned int ok, version_ok, alt_ok;
+    unsigned char rand_buf[IBMCA_SSL_MAX_MASTER_KEY_LENGTH];
+
+    ibmca_debug_ctx(provctx,
+                    "inlen: %lu outsize: %lu client_version: 0x%04x alt_version: 0x%04x" ,
+                    inlen, outsize, client_version, alt_version);
+
+    /*
+     * The implementation of this function is copied from OpenSSL's function
+     * ossl_rsa_padding_check_PKCS1_type_2_TLS() in crypto/rsa/rsa_pk1.c
+     * and is slightly modified to fit to the providers environment.
+     * Changes include:
+     * - Different variable and define names.
+     * - Usage of put_error_ctx and ibmca_debug_ctx to report errors and issue
+     *   debug messages.
+     */
+
+    /*
+     * The format is
+     * 00 || 02 || PS || 00 || PreMasterSecret
+     * BT - block type
+     * PS - at least 8 bytes of random non-zero data for BT = 2
+     * D  - data = PreMasterSecret (48 bytes)
+     * PreMasterSecret:  Version-major | Version-minor | 64 bytes secret
+     */
+
+    /*
+     * If these checks fail then either the message in publicly invalid, or
+     * we've been called incorrectly. We can fail immediately.
+     */
+    if (inlen < RSA_PKCS1_PADDING_SIZE + IBMCA_SSL_MAX_MASTER_KEY_LENGTH) {
+        put_error_ctx(provctx, IBMCA_ERR_INVALID_PARAM, "PKCS1 encoding error");
+        return 0;
+    }
+    if (outsize < IBMCA_SSL_MAX_MASTER_KEY_LENGTH) {
+        put_error_ctx(provctx, IBMCA_ERR_INVALID_PARAM,
+                      "Output buffer too small");
+        return 0;
+    }
+
+    /*
+     * Generate a random premaster secret to use in the event that we fail
+     * to decrypt.
+     */
+    if (RAND_priv_bytes_ex(provctx->libctx, rand_buf,
+                           IBMCA_SSL_MAX_MASTER_KEY_LENGTH, 0) <= 0) {
+        put_error_ctx(provctx, IBMCA_ERR_INTERNAL_ERROR,
+                      "RAND_priv_bytes_ex failed");
+        return 0;
+    }
+
+    ok = constant_time_is_zero(in[0]);
+    ok &= constant_time_eq(in[1], 2);
+
+    /* Check we have the expected padding data */
+    for (i = 2; i < inlen - IBMCA_SSL_MAX_MASTER_KEY_LENGTH - 1; i++)
+        ok &= ~constant_time_is_zero_8(in[i]);
+    ok &= constant_time_is_zero_8(
+                            in[inlen - IBMCA_SSL_MAX_MASTER_KEY_LENGTH - 1]);
+
+    /*
+     * If the version in the decrypted pre-master secret is correct then
+     * version_good will be 0xff, otherwise it'll be zero. The
+     * Klima-Pokorny-Rosa extension of Bleichenbacher's attack
+     * (http://eprint.iacr.org/2003/052/) exploits the version number
+     * check as a "bad version oracle". Thus version checks are done in
+     * constant time and are treated like any other decryption error.
+     */
+    version_ok =
+        constant_time_eq(in[inlen - IBMCA_SSL_MAX_MASTER_KEY_LENGTH],
+                         (client_version >> 8) & 0xff);
+    version_ok &=
+        constant_time_eq(in[inlen - IBMCA_SSL_MAX_MASTER_KEY_LENGTH + 1],
+                         client_version & 0xff);
+
+    /*
+     * The premaster secret must contain the same version number as the
+     * ClientHello to detect version rollback attacks (strangely, the
+     * protocol does not offer such protection for DH ciphersuites).
+     * However, buggy clients exist that send the negotiated protocol
+     * version instead if the server does not support the requested
+     * protocol version. If SSL_OP_TLS_ROLLBACK_BUG is set then we tolerate
+     * such clients. In that case alt_version will be non-zero and set to
+     * the negotiated version.
+     */
+    if (alt_version > 0) {
+        alt_ok = constant_time_eq(in[inlen - IBMCA_SSL_MAX_MASTER_KEY_LENGTH],
+                             (alt_version >> 8) & 0xff);
+        alt_ok &= constant_time_eq(
+                             in[inlen - IBMCA_SSL_MAX_MASTER_KEY_LENGTH + 1],
+                             alt_version & 0xff);
+        version_ok |= alt_ok;
+    }
+
+    ok &= version_ok;
+
+    /*
+     * Now copy the result over to the buffer if good, or random data if
+     * not good.
+     */
+    *outlen = IBMCA_SSL_MAX_MASTER_KEY_LENGTH;
+    for (i = 0; out != NULL && i < IBMCA_SSL_MAX_MASTER_KEY_LENGTH; i++) {
+        out[i] = constant_time_select_8(ok,
+                                        in[inlen -
+                                           IBMCA_SSL_MAX_MASTER_KEY_LENGTH + i],
+                                        rand_buf[i]);
+    }
+
+    ibmca_debug_ctx(provctx, "ok: %d outlen: %lu",
+                    constant_time_select_int(ok, 1, 0), *outlen);
+
+    /*
+     * We must not leak whether a decryption failure occurs because of
+     * Bleichenbacher's attack on PKCS #1 v1.5 RSA padding (see RFC 2246,
+     * section 7.4.7.1). The code follows that advice of the TLS RFC and
+     * generates a random premaster secret for the case that the decrypt
+     * fails. See https://tools.ietf.org/html/rfc5246#section-7.4.7.1
+     * So, whether we actually succeeded or not, return success.
+     */
+    return 1;
+}
+
+static int ibmca_rsa_x931_padding_hash_id(int nid)
+{
+    switch (nid) {
+    case NID_sha1:
+        return 0x33;
+    case NID_sha256:
+        return 0x34;
+    case NID_sha384:
+        return 0x36;
+    case NID_sha512:
+        return 0x35;
+    }
+    return -1;
+}
+
+int ibmca_rsa_add_x931_padding(const struct ibmca_prov_ctx *provctx,
+                               const unsigned char *in, size_t inlen,
+                               unsigned char *out, size_t outlen,
+                               int digest_nid)
+{
+    int j, hash_id;
+    unsigned char *p;
+
+    ibmca_debug_ctx(provctx, "inlen: %lu outlen: %lu digest_nid: %d",
+                    inlen, outlen, digest_nid);
+
+    hash_id = ibmca_rsa_x931_padding_hash_id(digest_nid);
+    if (hash_id == -1) {
+        put_error_ctx(provctx, IBMCA_ERR_INVALID_PARAM,
+                      "Unsupported signature digest: %d", digest_nid);
+        return 0;
+    }
+
+    /*
+     * Absolute minimum amount of padding is 1 header nibble, 1 padding
+     * nibble and 2 trailer bytes.
+     */
+    j = outlen - inlen - 3;
+
+    if (j < 0) {
+        put_error_ctx(provctx, IBMCA_ERR_INVALID_PARAM,
+                      "Data too large for the key type");
+        return 0;
+    }
+
+    p = out;
+
+    /* If no padding start and end nibbles are in one byte */
+    if (j == 0) {
+        *p++ = 0x6A;
+    } else {
+        *p++ = 0x6B;
+        if (j > 1) {
+            memset(p, 0xBB, j - 1);
+            p += j - 1;
+        }
+        *p++ = 0xBA;
+    }
+    memcpy(p, in, inlen);
+    p += inlen;
+    *p++ = hash_id;
+    *p = 0xCC;
+    return 1;
+}
+
+int ibmca_rsa_check_X931_padding(const struct ibmca_prov_ctx *provctx,
+                                 const unsigned char *in, int inlen,
+                                 unsigned char *out, size_t outsize,
+                                 unsigned char **outptr, size_t *outlen,
+                                 int digest_nid)
+{
+    int i = 0, j, hash_id;
+    const unsigned char *p;
+
+    ibmca_debug_ctx(provctx, "inlen: %lu outsize: %lu digest_nid: %d",
+                    inlen, outsize, digest_nid);
+
+    hash_id = ibmca_rsa_x931_padding_hash_id(digest_nid);
+    if (hash_id == -1) {
+        put_error_ctx(provctx, IBMCA_ERR_INVALID_PARAM,
+                      "Unsupported signature digest: %d", digest_nid);
+        return 0;
+    }
+
+    p = in;
+    if (((*p != 0x6A) && (*p != 0x6B))) {
+        put_error_ctx(provctx, IBMCA_ERR_INTERNAL_ERROR, "Invalid X931 padding");
+        return 0;
+    }
+
+    if (*p++ == 0x6B) {
+        j = inlen - 3;
+        for (i = 0; i < j; i++) {
+            unsigned char c = *p++;
+            if (c == 0xBA)
+                break;
+            if (c != 0xBB) {
+                put_error_ctx(provctx, IBMCA_ERR_INTERNAL_ERROR,
+                              "Invalid X931 padding");
+                return 0;
+            }
+        }
+
+        j -= i;
+
+        if (i == 0) {
+            put_error_ctx(provctx, IBMCA_ERR_INTERNAL_ERROR,
+                          "Invalid X931 padding");
+            return 0;
+        }
+
+    } else {
+        j = inlen - 2;
+    }
+
+    if (p[j - 1] != hash_id || p[j] != 0xCC) {
+        put_error_ctx(provctx, IBMCA_ERR_INTERNAL_ERROR, "Invalid X931 trailer");
+        return 0;
+    }
+
+    *outlen = j - 1;
+
+    if (outsize < *outlen) {
+        put_error_ctx(provctx, IBMCA_ERR_INVALID_PARAM,
+                      "Output buffer size too small");
+        return 0;
+    }
+
+    if (out != NULL)
+        memcpy(out, p, *outlen);
+    if (outptr != NULL)
+        *outptr = (unsigned char *)p;
+
+    ibmca_debug_ctx(provctx, "outlen: %lu", *outlen);
+
+    return 1;
+}
+
+int ibmca_rsa_add_pss_mgf1_padding(const struct ibmca_prov_ctx *provctx,
+                                   const unsigned char *in, size_t inlen,
+                                   unsigned char *out, size_t outlen,
+                                   const EVP_MD *pss_md, const EVP_MD *mgf1_md,
+                                   int saltlen)
+{
+    int i, rc = 0, maskeddb_len, msbits;
+    unsigned char *h, *salt = NULL, *p;
+    EVP_MD_CTX *ctx = NULL;
+    static const unsigned char zeroes[] = { 0, 0, 0, 0, 0, 0, 0, 0 };
+
+    if (pss_md == NULL) {
+        put_error_ctx(provctx, IBMCA_ERR_INVALID_PARAM,
+                      "No PSS digest available");
+        return 0;
+    }
+
+    if (mgf1_md == NULL)
+        mgf1_md = pss_md;
+
+    ibmca_debug_ctx(provctx,
+                    "inlen: %lu outlen: %lu pss_md: '%s' mgf1_md: '%s' saltlen: %d",
+                    inlen, outlen, EVP_MD_get0_name(pss_md),
+                    EVP_MD_get0_name(mgf1_md), saltlen);
+
+    msbits = ((outlen * 8) - 1) & 0x7;
+    if (msbits == 0) {
+        *out++ = 0;
+        outlen--;
+    }
+
+    if (outlen < inlen + 2) {
+        put_error_ctx(provctx, IBMCA_ERR_INVALID_PARAM,
+                              "Data too large for the key type");
+        goto err;
+    }
+
+    if (inlen != (size_t)EVP_MD_get_size(pss_md)) {
+        put_error_ctx(provctx, IBMCA_ERR_INVALID_PARAM,
+                       "Data size is not the size of the digest");
+        goto err;
+    }
+
+    switch (saltlen) {
+    case RSA_PSS_SALTLEN_DIGEST:
+        saltlen = EVP_MD_get_size(pss_md);
+        break;
+    case RSA_PSS_SALTLEN_MAX_SIGN:
+    case RSA_PSS_SALTLEN_MAX:
+        saltlen = outlen - inlen - 2;
+        break;
+    default:
+        if (saltlen < 0) {
+            put_error_ctx(provctx, IBMCA_ERR_INVALID_PARAM,
+                          "Invalid salt len: %d", saltlen);
+            goto err;
+        }
+    }
+    if ((size_t)saltlen > outlen - inlen - 2) {
+        put_error_ctx(provctx, IBMCA_ERR_INVALID_PARAM,
+                      "Data too large for the key type");
+        goto err;
+    }
+
+    ibmca_debug_ctx(provctx,"saltlen: %d", saltlen);
+
+    if (saltlen > 0) {
+        salt = P_MALLOC(provctx, saltlen);
+        if (salt == NULL) {
+            put_error_ctx(provctx, IBMCA_ERR_MALLOC_FAILED,
+                          "Failed to allocate salt buffer");
+            goto err;
+        }
+        if (RAND_bytes_ex(provctx->libctx, salt, saltlen, 0) <= 0) {
+            put_error_ctx(provctx, IBMCA_ERR_INTERNAL_ERROR,
+                          "RAND_bytes_ex failed");
+            goto err;
+        }
+    }
+
+    maskeddb_len = outlen - inlen - 1;
+    h = out + maskeddb_len;
+
+    ctx = EVP_MD_CTX_new();
+    if (ctx == NULL) {
+        put_error_ctx(provctx, IBMCA_ERR_INTERNAL_ERROR,
+                      "EVP_MD_CTX_new failed");
+        goto err;
+    }
+
+    if (EVP_DigestInit_ex(ctx, pss_md, NULL) == 0 ||
+        EVP_DigestUpdate(ctx, zeroes, sizeof(zeroes)) == 0 ||
+        EVP_DigestUpdate(ctx, in, inlen) == 0) {
+        put_error_ctx(provctx, IBMCA_ERR_INTERNAL_ERROR,
+                      "EVP_DigestInit_ex/EVP_DigestUpdate failed");
+        goto err;
+    }
+
+    if (saltlen != 0 && EVP_DigestUpdate(ctx, salt, saltlen) == 0) {
+        put_error_ctx(provctx, IBMCA_ERR_INTERNAL_ERROR,
+                      "EVP_DigestUpdate failed");
+        goto err;
+    }
+    if (EVP_DigestFinal_ex(ctx, h, NULL) == 0) {
+        put_error_ctx(provctx, IBMCA_ERR_INTERNAL_ERROR,
+                      "EVP_DigestFinal_ex failed");
+        goto err;
+    }
+
+    /* Generate dbMask in place then perform XOR on it */
+    if (ibmca_rsa_pkcs1_mgf1(provctx, out, maskeddb_len, h, inlen,
+                             mgf1_md) == 0)
+        goto err;
+
+    p = out;
+    p += outlen - saltlen - inlen - 2;
+
+    *p++ ^= 0x1;
+
+    if (saltlen > 0) {
+        for (i = 0; i < saltlen; i++)
+            *p++ ^= salt[i];
+    }
+
+    if (msbits != 0)
+        out[0] &= 0xFF >> (8 - msbits);
+
+    out[outlen - 1] = 0xbc;
+
+    rc = 1;
+
+err:
+    if (ctx != NULL)
+        EVP_MD_CTX_free(ctx);
+    if (salt != NULL)
+        P_CLEAR_FREE(provctx, salt, (size_t)saltlen);
+
+    return rc;
+}
+
+int ibmca_rsa_check_pss_mgf1_padding(const struct ibmca_prov_ctx *provctx,
+                                     const unsigned char *in, size_t inlen,
+                                     const unsigned char *data, size_t datalen,
+                                     const EVP_MD *pss_md,
+                                     const EVP_MD *mgf1_md,
+                                     int saltlen)
+{
+    int i, rc = 0, maskeddb_len, msbits;
+    const unsigned char *h;
+    unsigned char *db = NULL;
+    EVP_MD_CTX *ctx = NULL;
+    unsigned char h_[EVP_MAX_MD_SIZE];
+    static const unsigned char zeroes[] = { 0, 0, 0, 0, 0, 0, 0, 0 };
+
+    if (pss_md == NULL) {
+        put_error_ctx(provctx, IBMCA_ERR_INVALID_PARAM,
+                      "No PSS digest available");
+        return 0;
+    }
+
+    if (mgf1_md == NULL)
+        mgf1_md = pss_md;
+
+    ibmca_debug_ctx(provctx,
+                    "inlen: %lu datalen: %lu pss_md: '%s' mgf1_md: '%s' saltlen: %d",
+                    inlen, datalen, EVP_MD_get0_name(pss_md),
+                    EVP_MD_get0_name(mgf1_md), saltlen);
+
+    if (datalen != (size_t)EVP_MD_get_size(pss_md)) {
+        put_error_ctx(provctx, IBMCA_ERR_INVALID_PARAM,
+                       "Data size is not the size of the digest");
+        goto err;
+    }
+
+    msbits = ((inlen * 8) - 1) & 0x7;
+    if (in[0] & (0xFF << msbits)) {
+        put_error_ctx(provctx, IBMCA_ERR_INTERNAL_ERROR,
+                      "Invalid PSS encoding");
+        goto err;
+    }
+    if (msbits == 0) {
+        in++;
+        inlen--;
+    }
+
+    switch (saltlen) {
+    case RSA_PSS_SALTLEN_DIGEST:
+        saltlen = EVP_MD_get_size(pss_md);
+        break;
+    case RSA_PSS_SALTLEN_MAX:
+        saltlen = inlen - datalen - 2;
+        break;
+    case RSA_PSS_SALTLEN_AUTO:
+        break;
+    default:
+        if (saltlen < 0) {
+            put_error_ctx(provctx, IBMCA_ERR_INVALID_PARAM,
+                          "Invalid salt len: %d", saltlen);
+            goto err;
+        }
+    }
+    ibmca_debug_ctx(provctx,"saltlen: %d", saltlen);
+
+    if (saltlen > (int)(inlen - datalen - 2)) {
+        put_error_ctx(provctx, IBMCA_ERR_INVALID_PARAM,
+                      "Data too large for the key type");
+        goto err;
+    }
+
+    if (in[inlen - 1] != 0xbc) {
+        put_error_ctx(provctx, IBMCA_ERR_INTERNAL_ERROR,
+                      "Invalid PSS encoding");
+        goto err;
+    }
+
+    maskeddb_len = inlen - datalen - 1;
+    h = in + maskeddb_len;
+
+    db = P_MALLOC(provctx, maskeddb_len);
+    if (db == NULL) {
+        put_error_ctx(provctx, IBMCA_ERR_MALLOC_FAILED,
+                      "Failed to allocate DB buffer");
+        goto err;
+    }
+
+    if (ibmca_rsa_pkcs1_mgf1(provctx, db, maskeddb_len, h, datalen,
+                             mgf1_md) < 0)
+        goto err;
+
+    for (i = 0; i < maskeddb_len; i++)
+        db[i] ^= in[i];
+
+    if (msbits != 0)
+        db[0] &= 0xFF >> (8 - msbits);
+
+    for (i = 0; db[i] == 0 && i < (maskeddb_len - 1); i++)
+        ;
+
+    if (db[i++] != 0x1) {
+        put_error_ctx(provctx, IBMCA_ERR_INTERNAL_ERROR,
+                              "Saltlen recovery failed");
+        goto err;
+    }
+
+    if (saltlen != RSA_PSS_SALTLEN_AUTO && (maskeddb_len - i) != saltlen) {
+        put_error_ctx(provctx, IBMCA_ERR_INTERNAL_ERROR,
+                      "Saltlen check failed. Expected: %d retrieved: %d",
+                      saltlen, maskeddb_len - i);
+        goto err;
+    }
+
+    ctx = EVP_MD_CTX_new();
+    if (ctx == NULL) {
+        put_error_ctx(provctx, IBMCA_ERR_INTERNAL_ERROR,
+                      "EVP_MD_CTX_new failed");
+        goto err;
+    }
+
+    if (EVP_DigestInit_ex(ctx, pss_md, NULL) == 0 ||
+        EVP_DigestUpdate(ctx, zeroes, sizeof(zeroes)) == 0 ||
+        EVP_DigestUpdate(ctx, data, datalen) == 0) {
+        put_error_ctx(provctx, IBMCA_ERR_INTERNAL_ERROR,
+                      "EVP_DigestInit_ex/EVP_DigestUpdate failed");
+        goto err;
+    }
+
+    if (maskeddb_len - i > 0) {
+        if (EVP_DigestUpdate(ctx, db + i, maskeddb_len - i) == 0) {
+            put_error_ctx(provctx, IBMCA_ERR_INTERNAL_ERROR,
+                          "EVP_DigestUpdate failed");
+            goto err;
+        }
+    }
+
+    if (EVP_DigestFinal_ex(ctx, h_, NULL) == 0) {
+        put_error_ctx(provctx, IBMCA_ERR_INTERNAL_ERROR,
+                      "EVP_DigestFinal_exe failed");
+        goto err;
+    }
+
+    if (memcmp(h_, h, datalen)) {
+        put_error_ctx(provctx, IBMCA_ERR_SIGNATURE_BAD, "Bad signature");
+        rc = 0;
+    } else {
+        rc = 1;
+    }
+
+    ibmca_debug_ctx(provctx, "rc: %d", rc);
+
+err:
+    if (db != NULL)
+        P_CLEAR_FREE(provctx, db, maskeddb_len);
+    if (ctx != NULL)
+        EVP_MD_CTX_free(ctx);
+
+    return rc;
+}
diff -pruN 1.4.0-1/src/provider/rsa_signature.c 2.5.0-0ubuntu1/src/provider/rsa_signature.c
--- 1.4.0-1/src/provider/rsa_signature.c	1970-01-01 00:00:00.000000000 +0000
+++ 2.5.0-0ubuntu1/src/provider/rsa_signature.c	2025-04-16 08:09:24.000000000 +0000
@@ -0,0 +1,2253 @@
+/*
+ * Copyright [2021-2022] International Business Machines Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <stddef.h>
+#include <err.h>
+#include <strings.h>
+#include <string.h>
+
+#include <openssl/evp.h>
+#include <openssl/bn.h>
+#include <openssl/rsa.h>
+#include <openssl/x509.h>
+#include <openssl/core.h>
+#include <openssl/core_dispatch.h>
+#include <openssl/core_names.h>
+#include <openssl/params.h>
+
+#include "p_ibmca.h"
+
+static OSSL_FUNC_signature_newctx_fn ibmca_signature_rsa_newctx;
+static OSSL_FUNC_signature_sign_init_fn ibmca_signature_rsa_sign_init;
+static OSSL_FUNC_signature_sign_fn ibmca_signature_rsa_sign;
+static OSSL_FUNC_signature_verify_init_fn ibmca_signature_rsa_verify_init;
+static OSSL_FUNC_signature_verify_fn ibmca_signature_rsa_verify;
+static OSSL_FUNC_signature_verify_recover_init_fn
+                                ibmca_signature_rsa_verifyrecover_init;
+static OSSL_FUNC_signature_verify_recover_fn ibmca_signature_rsa_verify_recover;
+static OSSL_FUNC_signature_digest_sign_init_fn
+                                ibmca_signature_rsa_digest_sign_init;
+static OSSL_FUNC_signature_digest_sign_update_fn
+                                ibmca_signature_rsa_digest_signverify_update;
+static OSSL_FUNC_signature_digest_sign_final_fn
+                                ibmca_signature_rsa_digest_sign_final;
+static OSSL_FUNC_signature_digest_verify_init_fn
+                                ibmca_signature_rsa_digest_verify_init;
+static OSSL_FUNC_signature_digest_verify_final_fn
+                                ibmca_signature_rsa_digest_verify_final;
+static OSSL_FUNC_signature_get_ctx_params_fn ibmca_signature_rsa_get_ctx_params;
+static OSSL_FUNC_signature_gettable_ctx_params_fn
+                                ibmca_signature_rsa_gettable_ctx_params;
+static OSSL_FUNC_signature_set_ctx_params_fn ibmca_signature_rsa_set_ctx_params;
+static OSSL_FUNC_signature_settable_ctx_params_fn
+                                ibmca_signature_rsa_settable_ctx_params;
+static OSSL_FUNC_signature_get_ctx_md_params_fn
+                                ibmca_signature_rsa_get_ctx_md_params;
+static OSSL_FUNC_signature_gettable_ctx_md_params_fn
+                                ibmca_signature_rsa_gettable_ctx_md_params;
+static OSSL_FUNC_signature_set_ctx_md_params_fn
+                               ibmca_signature_rsa_set_ctx_md_params;
+static OSSL_FUNC_signature_settable_ctx_md_params_fn
+                                ibmca_signature_rsa_settable_ctx_md_params;
+#ifdef EVP_PKEY_OP_SIGNMSG
+static OSSL_FUNC_signature_sign_message_update_fn
+                                ibmca_signature_rsa_signverify_message_update;
+static OSSL_FUNC_signature_sign_message_final_fn
+                                ibmca_signature_rsa_sign_message_final;
+static OSSL_FUNC_signature_verify_message_final_fn
+                                ibmca_signature_rsa_verify_message_final;
+static OSSL_FUNC_signature_query_key_types_fn
+                                ibmca_signature_rsa_query_key_types;
+#endif
+
+static void ibmca_signature_rsa_free_cb(struct ibmca_op_ctx *ctx);
+static int ibmca_signature_rsa_dup_cb(const struct ibmca_op_ctx *ctx,
+                                      struct ibmca_op_ctx *new_ctx);
+
+static const struct ibmca_pss_params ibmca_rsa_pss_defaults =
+                                            IBMCA_RSA_PSS_DEFAULTS;
+
+#ifdef EVP_PKEY_OP_SIGNMSG
+static const char *ibmca_signature_rsa_keytypes[] = { "RSA", NULL };
+
+static const char **ibmca_signature_rsa_query_key_types(void)
+{
+    return ibmca_signature_rsa_keytypes;
+}
+#endif
+
+static void *ibmca_signature_rsa_newctx(void *vprovctx, const char *propq)
+{
+    const struct ibmca_prov_ctx *provctx = vprovctx;
+    struct ibmca_op_ctx *opctx;
+
+    if (provctx == NULL)
+        return NULL;
+
+    ibmca_debug_ctx(provctx, "provctx: %p", provctx);
+
+    opctx = ibmca_op_newctx(provctx, propq, EVP_PKEY_RSA,
+                            ibmca_signature_rsa_free_cb,
+                            ibmca_signature_rsa_dup_cb);
+    if (opctx == NULL) {
+        ibmca_debug_ctx(provctx, "ERROR: ibmca_op_newctx failed");
+        return NULL;
+    }
+
+    ibmca_debug_ctx(provctx, "opctx: %p", opctx);
+
+    return opctx;
+}
+
+static void ibmca_signature_rsa_free_cb(struct ibmca_op_ctx *ctx)
+{
+    if (ctx == NULL)
+        return;
+
+    ibmca_debug_op_ctx(ctx, "ctx: %p", ctx);
+
+    if (ctx->rsa.signature.md != NULL)
+        EVP_MD_free(ctx->rsa.signature.md);
+    ctx->rsa.signature.md = NULL;
+    ctx->rsa.signature.set_md_allowed = true;
+
+    if (ctx->rsa.signature.mgf1_md != NULL)
+        EVP_MD_free(ctx->rsa.signature.mgf1_md);
+    ctx->rsa.signature.mgf1_md = NULL;
+
+    if (ctx->rsa.signature.md_ctx != NULL)
+        EVP_MD_CTX_free(ctx->rsa.signature.md_ctx);
+    ctx->rsa.signature.md_ctx = NULL;
+
+    if (ctx->rsa.signature.signature != NULL)
+        P_FREE(ctx->provctx, ctx->rsa.signature.signature);
+    ctx->rsa.signature.signature = NULL;
+    ctx->rsa.signature.signature_len = 0;
+}
+
+static int ibmca_signature_rsa_dup_cb(const struct ibmca_op_ctx *ctx,
+                                      struct ibmca_op_ctx *new_ctx)
+{
+    if (ctx == NULL)
+        return 0;
+
+    ibmca_debug_op_ctx(ctx, "ctx: %p new_ctx: %p", ctx, new_ctx);
+
+    new_ctx->rsa.signature.pad_mode = ctx->rsa.signature.pad_mode;
+
+    new_ctx->rsa.signature.md = ctx->rsa.signature.md;
+    if (new_ctx->rsa.signature.md != NULL) {
+        if (EVP_MD_up_ref(new_ctx->rsa.signature.md) == 0) {
+            put_error_op_ctx(ctx, IBMCA_ERR_INTERNAL_ERROR,
+                             "EVP_MD_up_ref failed");
+            return 0;
+        }
+
+    }
+    new_ctx->rsa.signature.set_md_allowed = ctx->rsa.signature.set_md_allowed;
+
+    new_ctx->rsa.signature.mgf1_md = ctx->rsa.signature.mgf1_md;
+    if (new_ctx->rsa.signature.mgf1_md != NULL) {
+        if (EVP_MD_up_ref(new_ctx->rsa.signature.mgf1_md) == 0) {
+            put_error_op_ctx(ctx, IBMCA_ERR_INTERNAL_ERROR,
+                             "EVP_MD_up_ref failed");
+            return 0;
+        }
+    }
+
+    new_ctx->rsa.signature.saltlen = ctx->rsa.signature.saltlen;
+    new_ctx->rsa.signature.pss = ctx->rsa.signature.pss;
+
+    if (ctx->rsa.signature.md_ctx != NULL) {
+        new_ctx->rsa.signature.md_ctx = EVP_MD_CTX_new();
+        if (new_ctx->rsa.signature.md_ctx == NULL ||
+            EVP_MD_CTX_copy(new_ctx->rsa.signature.md_ctx,
+                            ctx->rsa.signature.md_ctx) == 0) {
+            put_error_op_ctx(ctx, IBMCA_ERR_INTERNAL_ERROR,
+                             "EVP_MD_CTX_copy failed");
+            return 0;
+        }
+    }
+
+    new_ctx->rsa.signature.signature_len = 0;
+    new_ctx->rsa.signature.signature = NULL;
+    if (ctx->rsa.signature.signature != NULL) {
+        new_ctx->rsa.signature.signature = P_MEMDUP(ctx->provctx,
+                                             ctx->rsa.signature.signature,
+                                             ctx->rsa.signature.signature_len);
+        if (new_ctx->rsa.signature.signature == NULL) {
+            put_error_op_ctx(ctx, IBMCA_ERR_MALLOC_FAILED,
+                             "P_MEMDUP failed");
+            return 0;
+        }
+        new_ctx->rsa.signature.signature_len = ctx->rsa.signature.signature_len;
+    }
+
+
+    return 1;
+}
+
+static int ibmca_signature_rsa_set_md(struct ibmca_op_ctx *ctx,
+                                      const char *mdname, int md_nid,
+                                      const char *props)
+{
+    EVP_MD *md;
+
+    if (mdname == NULL)
+        mdname = OBJ_nid2sn(md_nid);
+
+    ibmca_debug_op_ctx(ctx, "ctx: %p mdname: '%s'", ctx, mdname);
+
+    if (!ctx->rsa.signature.set_md_allowed) {
+        put_error_op_ctx(ctx, IBMCA_ERR_INVALID_PARAM,
+                         "Digest not allowed to be set in the current state");
+        return 0;
+    }
+
+    md = EVP_MD_fetch(ctx->provctx->libctx, mdname,
+                      props != NULL ? props : ctx->propq);
+    if (md == NULL) {
+        put_error_op_ctx(ctx, IBMCA_ERR_INVALID_PARAM,
+                         "Digest '%s' could not be fetched", mdname);
+        return 0;
+    }
+
+    if ((EVP_MD_get_flags(md) & EVP_MD_FLAG_XOF) != 0) {
+        put_error_op_ctx(ctx, IBMCA_ERR_INVALID_PARAM,
+                         "XOF Digest '%s' is not allowed", mdname);
+        EVP_MD_free(md);
+        return 0;
+    }
+
+    if (ctx->key->type == EVP_PKEY_RSA_PSS &&
+        ctx->rsa.signature.pss.restricted &&
+        EVP_MD_get_type(md) != ctx->rsa.signature.pss.digest_nid) {
+        put_error_op_ctx(ctx, IBMCA_ERR_INVALID_PARAM,
+                         "RSA-PSS key is restricted, digest not allowed");
+        EVP_MD_free(md);
+        return 0;
+    }
+
+    if (ctx->rsa.signature.md != NULL)
+        EVP_MD_free(ctx->rsa.signature.md);
+
+    ctx->rsa.signature.md = md;
+
+    return 1;
+}
+
+static int ibmca_signature_rsa_set_mgf1_md(struct ibmca_op_ctx *ctx,
+                                           const char *mdname, int md_nid,
+                                           const char *props)
+{
+    EVP_MD *md;
+
+    if (mdname == NULL)
+        mdname = OBJ_nid2sn(md_nid);
+
+    ibmca_debug_op_ctx(ctx, "ctx: %p mdname: '%s'", ctx, mdname);
+
+    md = EVP_MD_fetch(ctx->provctx->libctx, mdname,
+                      props != NULL ? props : ctx->propq);
+    if (md == NULL) {
+        put_error_op_ctx(ctx, IBMCA_ERR_INVALID_PARAM,
+                         "Digest '%s' could not be fetched", mdname);
+        return 0;
+    }
+
+    if ((EVP_MD_get_flags(md) & EVP_MD_FLAG_XOF) != 0) {
+        put_error_op_ctx(ctx, IBMCA_ERR_INVALID_PARAM,
+                         "XOF Digest '%s' is not allowed", mdname);
+        EVP_MD_free(md);
+        return 0;
+    }
+
+    if (ctx->key->type == EVP_PKEY_RSA_PSS &&
+        ctx->rsa.signature.pss.restricted &&
+        EVP_MD_get_type(md) != ctx->rsa.signature.pss.mgf_digest_nid) {
+        put_error_op_ctx(ctx, IBMCA_ERR_INVALID_PARAM,
+                         "RSA-PSS key is restricted, mgf1 digest not allowed");
+        EVP_MD_free(md);
+        return 0;
+    }
+
+    if (ctx->rsa.signature.mgf1_md != NULL)
+        EVP_MD_free(ctx->rsa.signature.mgf1_md);
+
+    ctx->rsa.signature.mgf1_md = md;
+
+    return 1;
+}
+
+static int ibmca_signature_rsa_pss_check_params(struct ibmca_op_ctx *ctx)
+{
+    int saltlen;
+
+    ibmca_debug_op_ctx(ctx, "ctx: %p", ctx);
+
+    if (ctx->rsa.signature.pss.restricted == false)
+        return 1;
+
+    if (EVP_MD_get_type(ctx->rsa.signature.md) !=
+                                    ctx->rsa.signature.pss.digest_nid) {
+        put_error_op_ctx(ctx, IBMCA_ERR_INVALID_PARAM,
+                         "RSA-PSS key is restricted, digest not allowed");
+        return 0;
+    }
+
+    if (EVP_MD_get_type(ctx->rsa.signature.mgf1_md != NULL ?
+                                ctx->rsa.signature.mgf1_md :
+                                ctx->rsa.signature.md) !=
+                                    ctx->rsa.signature.pss.mgf_digest_nid) {
+        put_error_op_ctx(ctx, IBMCA_ERR_INVALID_PARAM,
+                         "RSA-PSS key is restricted, mgf1 digest not allowed");
+        return 0;
+    }
+
+    switch (ctx->rsa.signature.saltlen) {
+    case RSA_PSS_SALTLEN_DIGEST:
+        if (EVP_MD_get_size(ctx->rsa.signature.md) <
+                              ctx->rsa.signature.pss.saltlen) {
+            put_error_op_ctx(ctx, IBMCA_ERR_INVALID_PARAM,
+                             "Saltlen should be >= %d, but digest size is %d",
+                             ctx->rsa.signature.pss.saltlen,
+                             EVP_MD_get_size(
+                                     ctx->rsa.signature.md));
+            return 0;
+        }
+        break;
+    case RSA_PSS_SALTLEN_MAX_SIGN:
+    case RSA_PSS_SALTLEN_MAX:
+#ifdef RSA_PSS_SALTLEN_AUTO_DIGEST_MAX
+    case RSA_PSS_SALTLEN_AUTO_DIGEST_MAX:
+#endif
+        saltlen = ctx->key->get_max_param_size(ctx->key) -
+                    EVP_MD_get_size(ctx->rsa.signature.md) - 2;
+        if ((ctx->key->rsa.bits & 0x7) == 1)
+            saltlen--;
+#ifdef RSA_PSS_SALTLEN_AUTO_DIGEST_MAX
+        if (ctx->rsa.signature.saltlen == RSA_PSS_SALTLEN_AUTO_DIGEST_MAX &&
+            saltlen > EVP_MD_get_size(ctx->rsa.signature.md))
+            saltlen = EVP_MD_get_size(ctx->rsa.signature.md);
+#endif
+        if (saltlen < ctx->rsa.signature.pss.saltlen) {
+            put_error_op_ctx(ctx, IBMCA_ERR_INVALID_PARAM,
+                             "Saltlen should be >= %d, but max salt len is %d",
+                             ctx->rsa.signature.pss.saltlen, saltlen);
+            return 0;
+        }
+        break;
+    default:
+        if (ctx->rsa.signature.saltlen < ctx->rsa.signature.pss.saltlen) {
+            put_error_op_ctx(ctx, IBMCA_ERR_INVALID_PARAM,
+                             "Saltlen should be >= %d, but salt len is %d",
+                             ctx->rsa.signature.pss.saltlen,
+                             ctx->rsa.signature.saltlen);
+            return 0;
+        }
+        break;
+    }
+
+    return 1;
+}
+
+static int ibmca_signature_rsa_op_init(struct ibmca_op_ctx *ctx,
+                                       struct ibmca_key *key,
+                                       const OSSL_PARAM params[],
+                                       int operation, const char *mdname)
+{
+    const OSSL_PARAM *p;
+
+    if (ctx == NULL || key == NULL)
+        return 0;
+
+    ibmca_debug_op_ctx(ctx, "ctx: %p key: %p operation: %d mdname: %s", ctx,
+                       key, operation, mdname != NULL ? mdname : "(null)");
+    for (p = params; p != NULL && p->key != NULL; p++)
+        ibmca_debug_op_ctx(ctx, "param: %s", p->key);
+
+    if (ibmca_op_init(ctx, key, operation) == 0) {
+        ibmca_debug_op_ctx(ctx, "ERROR: ibmca_op_init failed");
+        return 0;
+    }
+
+    /* Setup defaults for this context */
+    ibmca_signature_rsa_free_cb(ctx);
+
+    ctx->rsa.signature.pss = ibmca_rsa_pss_defaults;
+    /* Max for sign, auto for verify */
+#ifdef RSA_PSS_SALTLEN_AUTO_DIGEST_MAX
+    ctx->rsa.signature.pss.saltlen = RSA_PSS_SALTLEN_AUTO_DIGEST_MAX;
+#else
+    ctx->rsa.signature.pss.saltlen = RSA_PSS_SALTLEN_AUTO;
+#endif
+
+    if (key->type == EVP_PKEY_RSA_PSS) {
+        ctx->rsa.signature.pad_mode = RSA_PKCS1_PSS_PADDING;
+
+        ibmca_debug_op_ctx(ctx,"RSA-PSS restricted: %d",
+                           key->rsa.pss.restricted);
+
+        if (key->rsa.pss.restricted)
+            ctx->rsa.signature.pss = key->rsa.pss;
+
+        if (ibmca_signature_rsa_set_md(ctx, NULL,
+                                       ctx->rsa.signature.pss.digest_nid,
+                                       NULL) == 0)
+            return 0;
+        if (ctx->rsa.signature.pss.mgf_digest_nid !=
+                                 ctx->rsa.signature.pss.digest_nid &&
+            ibmca_signature_rsa_set_mgf1_md(ctx, NULL,
+                                        ctx->rsa.signature.pss.mgf_digest_nid,
+                                        NULL) == 0)
+            return 0;
+    } else {
+        ctx->rsa.signature.pad_mode = RSA_PKCS1_PADDING;
+    }
+
+    ctx->rsa.signature.set_md_allowed = true;
+
+    if (mdname != NULL) {
+        if (ibmca_signature_rsa_set_md(ctx, mdname, 0, NULL) == 0)
+            return 0;
+    }
+
+    ctx->rsa.signature.saltlen = ctx->rsa.signature.pss.saltlen;
+
+    if (params != NULL) {
+        if (ibmca_signature_rsa_set_ctx_params(ctx, params) == 0) {
+            ibmca_debug_op_ctx(ctx,
+                    "ERROR: ibmca_signature_rsa_set_ctx_params failed");
+            return 0;
+        }
+    }
+
+    switch (operation) {
+    case EVP_PKEY_OP_SIGNCTX:
+    case EVP_PKEY_OP_VERIFYCTX:
+#ifdef EVP_PKEY_OP_SIGNMSG
+    case EVP_PKEY_OP_SIGNMSG:
+    case EVP_PKEY_OP_VERIFYMSG:
+#endif
+        ctx->rsa.signature.md_ctx = EVP_MD_CTX_new();
+        if (ctx->rsa.signature.md_ctx == NULL) {
+            put_error_op_ctx(ctx, IBMCA_ERR_INTERNAL_ERROR,
+                             "EVP_MD_CTX_new failed");
+            return 0;
+        }
+
+        if (EVP_DigestInit_ex2(ctx->rsa.signature.md_ctx,
+                               ctx->rsa.signature.md, params) == 0) {
+            put_error_op_ctx(ctx, IBMCA_ERR_INTERNAL_ERROR,
+                             "EVP_DigestInit_ex2 failed");
+            return 0;
+        }
+
+        ctx->rsa.signature.set_md_allowed = false;
+        break;
+    }
+
+    return 1;
+
+}
+
+static int ibmca_signature_rsa_sign_init(void *vctx, void *vkey,
+                                         const OSSL_PARAM params[])
+{
+    struct ibmca_op_ctx *ctx = vctx;
+    struct ibmca_key *key = vkey;
+
+    return ibmca_signature_rsa_op_init(ctx, key, params,
+                                       EVP_PKEY_OP_SIGN, NULL);
+}
+
+static int ibmca_signature_rsa_verify_init(void *vctx, void *vkey,
+                                           const OSSL_PARAM params[])
+{
+    struct ibmca_op_ctx *ctx = vctx;
+    struct ibmca_key *key = vkey;
+
+    return ibmca_signature_rsa_op_init(ctx, key, params,
+                                       EVP_PKEY_OP_VERIFY, NULL);
+}
+
+static int ibmca_signature_rsa_verifyrecover_init(void *vctx, void *vkey,
+                                                  const OSSL_PARAM params[])
+{
+    struct ibmca_op_ctx *ctx = vctx;
+    struct ibmca_key *key = vkey;
+
+    return ibmca_signature_rsa_op_init(ctx, key, params,
+                                       EVP_PKEY_OP_VERIFYRECOVER, NULL);
+}
+
+static int ibmca_signature_rsa_sign_x931_post_process(struct ibmca_op_ctx *ctx,
+                                                      unsigned char *data,
+                                                      size_t data_len)
+{
+    BIGNUM *bn_n = NULL, *bn_new_data = NULL, *bn_data = NULL;
+    int rc = 0;
+
+    /* Special handling for X.931 */
+    bn_n = BN_bin2bn(ctx->key->rsa.public.modulus,
+                     ctx->key->rsa.public.key_length, NULL);
+    bn_data = BN_bin2bn(data, data_len, NULL);
+    bn_new_data = BN_new();
+    if(bn_n == NULL || bn_new_data == NULL || bn_data == NULL) {
+        put_error_op_ctx(ctx, IBMCA_ERR_INTERNAL_ERROR,
+                         "BN_bin2bn/BN_new failed");
+        rc = 0;
+        goto out;
+    }
+
+    if (BN_sub(bn_new_data, bn_n, bn_data) == 0) {
+        put_error_op_ctx(ctx, IBMCA_ERR_INTERNAL_ERROR, "BN_bin2bn failed");
+        rc = 0;
+        goto out;
+    }
+
+    if (BN_cmp(bn_data, bn_new_data) > 0) {
+        if (BN_bn2binpad(bn_new_data, data, data_len)<= 0) {
+            put_error_op_ctx(ctx, IBMCA_ERR_INTERNAL_ERROR,
+                             "BN_bn2binpad failed");
+            rc = 0;
+            goto out;
+        }
+    }
+
+    rc = 1;
+
+out:
+    if (bn_n != NULL)
+        BN_free(bn_n);
+    if (bn_data != NULL)
+        BN_free(bn_data);
+    if (bn_new_data != NULL)
+        BN_free(bn_new_data);
+
+    return rc;
+}
+
+static int ibmca_signature_rsa_verify_x931_post_process(
+                                        struct ibmca_op_ctx *ctx,
+                                        unsigned char *data, size_t data_len)
+{
+    int rc = 0;
+    BIGNUM *bn_n = NULL, *bn_sig = NULL;
+
+    if ((data[data_len - 1] & 0xf) != 12) {
+        bn_n = BN_bin2bn(ctx->key->rsa.public.modulus,
+                         ctx->key->rsa.public.key_length, NULL);
+        bn_sig = BN_bin2bn(data, data_len, NULL);
+
+        if (bn_n == NULL || bn_sig == NULL ||
+            BN_sub(bn_sig, bn_n, bn_sig) == 0 ||
+            BN_bn2binpad(bn_sig, data, data_len) <= 0) {
+            put_error_op_ctx(ctx, IBMCA_ERR_INTERNAL_ERROR,
+                             "BN_bin2bn/BN_sub/BN_bn2binpad failed");
+            goto out;
+        }
+    }
+
+    rc = 1;
+
+out:
+    if (bn_n != NULL)
+        BN_free(bn_n);
+    if (bn_sig != NULL)
+        BN_free(bn_sig);
+
+    return rc;
+}
+
+static int ibmca_signature_rsa_sign_fallback(struct ibmca_op_ctx *ctx,
+                                             unsigned char *sig, size_t sigsize,
+                                             const unsigned char *tbs,
+                                             size_t tbslen)
+{
+    EVP_PKEY *pkey = NULL;
+    EVP_PKEY_CTX *pctx = NULL;
+    size_t siglen;
+    int rc = 0;
+
+    ibmca_debug_op_ctx(ctx, "ctx: %p key: %p tbslen: %lu sig: %p sigsize: %lu",
+                       ctx, ctx->key, tbslen, sig, sigsize);
+
+    pkey = ibmca_new_fallback_pkey(ctx->key);
+    if (pkey == NULL) {
+        ibmca_debug_op_ctx(ctx,"ERROR: ibmca_new_fallback_pkey failed");
+        goto out;
+    }
+
+    pctx = ibmca_new_fallback_pkey_ctx(ctx->provctx, pkey, NULL);
+    if (pctx == NULL) {
+        ibmca_debug_op_ctx(ctx,"ERROR: ibmca_new_fallback_pkey_ctx failed");
+        goto out;
+    }
+
+    if (EVP_PKEY_sign_init(pctx) != 1 ||
+        EVP_PKEY_CTX_set_rsa_padding(pctx, RSA_NO_PADDING) != 1) {
+        put_error_op_ctx(ctx, IBMCA_ERR_INTERNAL_ERROR,
+                         "EVP_PKEY_sign_init/EVP_PKEY_CTX_set_rsa_padding failed");
+        goto out;
+    }
+
+    if (ibmca_check_fallback_provider(ctx->provctx, pctx) != 1) {
+        ibmca_debug_op_ctx(ctx, "ERROR: ibmca_check_fallback_provider failed");
+        goto out;
+    }
+
+    siglen = sigsize;
+    if (EVP_PKEY_sign(pctx, sig, &siglen, tbs, tbslen) != 1 ||
+        siglen != sigsize) {
+        put_error_op_ctx(ctx, IBMCA_ERR_INTERNAL_ERROR,
+                         "EVP_PKEY_sign failed");
+        goto out;
+    }
+
+    rc = 1;
+
+out:
+    if (pkey != NULL)
+        EVP_PKEY_free(pkey);
+    if (pctx != NULL)
+        EVP_PKEY_CTX_free(pctx);
+
+    return rc;
+}
+
+static int ibmca_signature_rsa_sign(void *vctx,
+                                    unsigned char *sig, size_t *siglen,
+                                    size_t sigsize, const unsigned char *tbs,
+                                    size_t tbslen)
+{
+    struct ibmca_op_ctx *ctx = vctx;
+    unsigned char diginfo[MAX_DIGINFO_SIZE + EVP_MAX_MD_SIZE];
+    unsigned char *enc_data;
+    size_t enc_data_len, diginfo_len, rsa_size;
+    int rc = 1;
+
+    if (ctx == NULL || siglen == NULL || tbs == NULL)
+        return 0;
+
+    ibmca_debug_op_ctx(ctx, "ctx: %p key: %p tbslen: %lu sigsize: %lu",
+                       ctx, ctx->key, tbslen, sigsize);
+
+    if (ctx->key == NULL ||
+        (ctx->operation != EVP_PKEY_OP_SIGN &&
+#ifdef EVP_PKEY_OP_SIGNMSG
+         ctx->operation != EVP_PKEY_OP_SIGNCTX &&
+         ctx->operation != EVP_PKEY_OP_SIGNMSG)) {
+#else
+         ctx->operation != EVP_PKEY_OP_SIGNCTX)) {
+#endif
+        put_error_op_ctx(ctx, IBMCA_ERR_INTERNAL_ERROR,
+                         "sign operation not initialized");
+        return 0;
+    }
+
+#ifdef EVP_PKEY_OP_SIGNMSG
+    if (ctx->operation == EVP_PKEY_OP_SIGNMSG) {
+        rc = ibmca_signature_rsa_signverify_message_update(ctx, tbs, tbslen);
+        if (rc != 1)
+            goto out;
+
+        rc = ibmca_signature_rsa_sign_message_final(ctx, sig, siglen, sigsize);
+        goto out;
+    }
+#endif
+
+    rsa_size = ctx->key->get_max_param_size(ctx->key);
+    *siglen = rsa_size;
+
+    if (sig == NULL) /* size query */
+        goto out;
+
+    if (sigsize < *siglen) {
+        put_error_op_ctx(ctx, IBMCA_ERR_INVALID_PARAM,
+                         "Output buffer too small");
+        return 0;
+    }
+
+    if (ctx->rsa.signature.md != NULL) {
+        if (tbslen != (size_t)EVP_MD_get_size(ctx->rsa.signature.md)) {
+            put_error_op_ctx(ctx, IBMCA_ERR_INVALID_PARAM,
+                             "Invalid input data size: %lu expected: %d",
+                             tbslen,
+                             EVP_MD_get_size(ctx->rsa.signature.md));
+            return 0;
+        }
+    }
+
+    ibmca_debug_op_ctx(ctx, "pad_mode: %d", ctx->rsa.signature.pad_mode);
+
+    /* Allocate padding buffer, if required by padding mode */
+    switch (ctx->rsa.signature.pad_mode) {
+    case RSA_NO_PADDING:
+        if (tbslen != rsa_size) {
+            put_error_op_ctx(ctx, IBMCA_ERR_INVALID_PARAM,
+                             "Invalid input length");
+            return 0;
+        }
+        enc_data = (unsigned char *)tbs;
+        enc_data_len = tbslen;
+        break;
+
+    case RSA_PKCS1_PADDING:
+    case RSA_PKCS1_PSS_PADDING:
+    case RSA_X931_PADDING:
+        if (ibmca_op_alloc_tbuf(ctx, rsa_size) == 0) {
+            ibmca_debug_op_ctx(ctx, "ERROR: ibmca_op_alloc_tbuf failed");
+            return 0;
+        }
+
+        enc_data_len = ctx->tbuf_len;
+        enc_data = ctx->tbuf;
+        break;
+
+    case RSA_PKCS1_OAEP_PADDING:
+    case RSA_PKCS1_WITH_TLS_PADDING:
+    default:
+        put_error_op_ctx(ctx, IBMCA_ERR_INVALID_PARAM,
+                         "Invalid padding mode: %d",
+                         ctx->rsa.signature.pad_mode);
+        return 0;
+    }
+
+    /* Perform padding */
+    switch (ctx->rsa.signature.pad_mode) {
+    case RSA_NO_PADDING:
+        rc = 1;
+        break;
+
+    case RSA_PKCS1_PADDING:
+        if (ctx->rsa.signature.md != NULL) {
+            rc = ibmca_rsa_build_digest_info(ctx->key->provctx,
+                                             ctx->rsa.signature.md,
+                                             tbs, tbslen,
+                                             diginfo, sizeof(diginfo),
+                                             &diginfo_len);
+            if (rc == 0)
+                break;
+
+            rc = ibmca_rsa_add_pkcs1_padding(ctx->key->provctx, 1,
+                                             diginfo, diginfo_len,
+                                             enc_data, enc_data_len);
+        } else {
+            rc = ibmca_rsa_add_pkcs1_padding(ctx->key->provctx, 1,
+                                             tbs, tbslen,
+                                             enc_data, enc_data_len);
+        }
+        break;
+
+    case RSA_X931_PADDING:
+        if (ctx->rsa.signature.md == NULL) {
+            put_error_op_ctx(ctx, IBMCA_ERR_INVALID_PARAM,
+                             "X.931 padding requires a digest");
+            rc = 0;
+            break;
+        }
+
+        rc = ibmca_rsa_add_x931_padding(ctx->key->provctx, tbs, tbslen,
+                                        enc_data, enc_data_len,
+                                        EVP_MD_get_type(
+                                            ctx->rsa.signature.md));
+        break;
+
+    case RSA_PKCS1_PSS_PADDING:
+        rc = ibmca_signature_rsa_pss_check_params(ctx);
+        if (rc == 0)
+           break;
+
+        rc = ibmca_rsa_add_pss_mgf1_padding(ctx->key->provctx, tbs, tbslen,
+                                           enc_data, enc_data_len,
+                                           ctx->rsa.signature.md,
+                                           ctx->rsa.signature.mgf1_md,
+                                           ctx->rsa.signature.saltlen);
+        break;
+
+    case RSA_PKCS1_OAEP_PADDING:
+    case RSA_PKCS1_WITH_TLS_PADDING:
+    default:
+        rc = 0;
+        goto out;
+    }
+    if (rc == 0)
+        goto out;
+
+    /* Perform private key encrypt */
+    rc = ibmca_rsa_priv_with_blinding(ctx->key, enc_data, sig, rsa_size);
+    if (rc != 1) {
+        ibmca_debug_op_ctx(ctx, "ibmca_asym_cipher_rsa_with_blinding failed");
+
+        rc = ibmca_signature_rsa_sign_fallback(ctx, sig, *siglen,
+                                               enc_data, enc_data_len);
+        if (rc != 1) {
+            ibmca_debug_op_ctx(ctx,
+                               "ERROR: ibmca_signature_rsa_sign_fallback failed");
+            rc = 0;
+            goto out;
+        }
+    }
+
+    if (ctx->rsa.signature.pad_mode == RSA_X931_PADDING &&
+        ibmca_signature_rsa_sign_x931_post_process(ctx, sig, *siglen) == 0) {
+        rc = 0;
+        goto out;
+    }
+
+    rc = 1;
+
+ out:
+    if (ctx->tbuf != NULL)
+        P_CLEANSE(ctx->provctx, ctx->tbuf, ctx->tbuf_len);
+
+    ibmca_debug_op_ctx(ctx, "siglen: %lu rc: %d", *siglen, rc);
+
+    return rc;
+}
+
+static int ibmca_signature_rsa_verify_fallback(struct ibmca_op_ctx *ctx,
+                                               unsigned char *out,
+                                               size_t outsize,
+                                               const unsigned char *tbs,
+                                               size_t tbslen)
+{
+    EVP_PKEY *pkey = NULL;
+    EVP_PKEY_CTX *pctx = NULL;
+    size_t outlen;
+    int rc = 0;
+
+    ibmca_debug_op_ctx(ctx, "ctx: %p key: %p tbslen: %lu out: %p outsize: %lu",
+                       ctx, ctx->key, tbslen, out, outsize);
+
+    pkey = ibmca_new_fallback_pkey(ctx->key);
+    if (pkey == NULL) {
+        ibmca_debug_op_ctx(ctx,"ERROR: ibmca_new_fallback_pkey failed");
+        goto out;
+    }
+
+    pctx = ibmca_new_fallback_pkey_ctx(ctx->provctx, pkey, NULL);
+    if (pctx == NULL) {
+        ibmca_debug_op_ctx(ctx,"ERROR: ibmca_new_fallback_pkey_ctx failed");
+        goto out;
+    }
+
+    if (EVP_PKEY_verify_recover_init(pctx) != 1 ||
+        EVP_PKEY_CTX_set_rsa_padding(pctx, RSA_NO_PADDING) != 1) {
+        put_error_op_ctx(ctx, IBMCA_ERR_INTERNAL_ERROR,
+                         "EVP_PKEY_verify_recover_init/EVP_PKEY_CTX_set_rsa_padding failed");
+        goto out;
+    }
+
+    if (ibmca_check_fallback_provider(ctx->provctx, pctx) != 1) {
+        ibmca_debug_op_ctx(ctx, "ERROR: ibmca_check_fallback_provider failed");
+        goto out;
+    }
+
+    outlen = outsize;
+    if (EVP_PKEY_verify_recover(pctx, out, &outlen, tbs, tbslen) != 1 ||
+        outlen != outsize) {
+        put_error_op_ctx(ctx, IBMCA_ERR_INTERNAL_ERROR,
+                         "EVP_PKEY_verify_recover failed");
+        goto out;
+    }
+
+    rc = 1;
+
+out:
+    if (pkey != NULL)
+        EVP_PKEY_free(pkey);
+    if (pctx != NULL)
+        EVP_PKEY_CTX_free(pctx);
+
+    return rc;
+}
+
+static int ibmca_signature_rsa_verify(void *vctx,
+                                      const unsigned char *sig, size_t siglen,
+                                      const unsigned char *tbs, size_t tbslen)
+{
+    struct ibmca_op_ctx *ctx = vctx;
+    unsigned char diginfo[MAX_DIGINFO_SIZE + EVP_MAX_MD_SIZE];
+    unsigned char *dec_data, *data = NULL;
+    size_t dec_data_len, data_len = 0, diginfo_len, rsa_size;
+#ifdef EVP_PKEY_OP_SIGNMSG
+    OSSL_PARAM params[2];
+#endif
+    int rc = 1;
+
+    if (ctx == NULL || sig == NULL || tbs == NULL)
+        return -1;
+
+    ibmca_debug_op_ctx(ctx, "ctx: %p key: %p tbslen: %lu siglen: %lu",
+                       ctx, ctx->key, tbslen, siglen);
+
+    if (ctx->key == NULL ||
+        (ctx->operation != EVP_PKEY_OP_VERIFY &&
+#ifdef EVP_PKEY_OP_SIGNMSG
+         ctx->operation != EVP_PKEY_OP_VERIFYCTX &&
+         ctx->operation != EVP_PKEY_OP_VERIFYMSG)) {
+#else
+         ctx->operation != EVP_PKEY_OP_VERIFYCTX)) {
+#endif
+        put_error_op_ctx(ctx, IBMCA_ERR_INTERNAL_ERROR,
+                         "verify operation not initialized");
+        return -1;
+    }
+
+#ifdef EVP_PKEY_OP_SIGNMSG
+    if (ctx->operation == EVP_PKEY_OP_VERIFYMSG) {
+        params[0] = OSSL_PARAM_construct_octet_string(
+                                          OSSL_SIGNATURE_PARAM_SIGNATURE,
+                                          (unsigned char *)sig, siglen);
+        params[1] = OSSL_PARAM_construct_end();
+
+        rc = ibmca_signature_rsa_set_ctx_params(ctx, params);
+        if (rc != 1)
+            goto out;
+
+        rc = ibmca_signature_rsa_signverify_message_update(ctx, tbs, tbslen);
+        if (rc != 1)
+            goto out;
+
+        rc = ibmca_signature_rsa_verify_message_final(ctx);
+        goto out;
+    }
+#endif
+
+    rsa_size = ctx->key->get_max_param_size(ctx->key);
+    if (siglen != rsa_size) {
+        put_error_op_ctx(ctx, IBMCA_ERR_INVALID_PARAM,
+                         "Invalid signature length");
+        return -1;
+    }
+
+    ibmca_debug_op_ctx(ctx, "pad_mode: %d", ctx->rsa.signature.pad_mode);
+
+    /* Allocate decryption buffer */
+    if (ibmca_op_alloc_tbuf(ctx, rsa_size) == 0) {
+        ibmca_debug_op_ctx(ctx, "ERROR: ibmca_op_alloc_tbuf failed");
+        return -1;
+    }
+
+    dec_data_len = ctx->tbuf_len;
+    dec_data = ctx->tbuf;
+
+    /* Perform public key decrypt */
+    rc = ica_rsa_mod_expo(ctx->provctx->ica_adapter, sig,
+                          &ctx->key->rsa.public, dec_data);
+    if (rc != 0) {
+        ibmca_debug_op_ctx(ctx, "ica_rsa_mod_expo failed with: %s",
+                           strerror(rc));
+
+        rc = ibmca_signature_rsa_verify_fallback(ctx, dec_data, dec_data_len,
+                                                 sig, siglen);
+        if (rc != 1) {
+            ibmca_debug_op_ctx(ctx,
+                               "ERROR: ibmca_signature_rsa_verify_fallback failed");
+            rc = -1;
+            goto out;
+        }
+    }
+
+    /* Perform padding check */
+    switch (ctx->rsa.signature.pad_mode) {
+    case RSA_NO_PADDING:
+        data = dec_data;
+        data_len = dec_data_len;
+        rc = 1;
+        break;
+
+    case RSA_PKCS1_PADDING:
+        if (ctx->rsa.signature.md != NULL) {
+            rc = ibmca_rsa_build_digest_info(ctx->key->provctx,
+                                             ctx->rsa.signature.md,
+                                             tbs, tbslen,
+                                             diginfo, sizeof(diginfo),
+                                             &diginfo_len);
+            if (rc == 0)
+                break;
+
+            tbs = diginfo;
+            tbslen = diginfo_len;
+        }
+
+        rc = ibmca_rsa_check_pkcs1_padding_type1(ctx->key->provctx,
+                                                 dec_data, dec_data_len, NULL,
+                                                 dec_data_len, &data,
+                                                 &data_len);
+        break;
+
+    case RSA_X931_PADDING:
+        if (ctx->rsa.signature.md == NULL) {
+            put_error_op_ctx(ctx, IBMCA_ERR_INVALID_PARAM,
+                             "X.931 padding requires a digest");
+            rc = 0;
+            break;
+        }
+
+        rc = ibmca_signature_rsa_verify_x931_post_process(ctx, dec_data,
+                                                          dec_data_len);
+        if (rc == 0)
+            break;
+
+        rc = ibmca_rsa_check_X931_padding(ctx->key->provctx,
+                                          dec_data, dec_data_len, NULL,
+                                          dec_data_len, &data, &data_len,
+                                          EVP_MD_get_type(
+                                            ctx->rsa.signature.md));
+        break;
+
+    case RSA_PKCS1_PSS_PADDING:
+        rc = ibmca_signature_rsa_pss_check_params(ctx);
+        if (rc == 0)
+           break;
+
+        rc = ibmca_rsa_check_pss_mgf1_padding(ctx->key->provctx,
+                                              dec_data, dec_data_len,
+                                              tbs, tbslen,
+                                              ctx->rsa.signature.md,
+                                              ctx->rsa.signature.mgf1_md,
+                                              ctx->rsa.signature.saltlen);
+        break;
+
+    case RSA_PKCS1_OAEP_PADDING:
+    case RSA_PKCS1_WITH_TLS_PADDING:
+    default:
+        rc = -1;
+        break;
+    }
+    if (rc == 0)
+        goto out;
+
+    /* If data is NULL, padding check has already verified the signature */
+    if (data != NULL && data_len > 0) {
+        if (data_len != tbslen ||
+            memcmp(data, tbs, tbslen) != 0) {
+            put_error_op_ctx(ctx, IBMCA_ERR_SIGNATURE_BAD, "Bad signature");
+            rc = 0;
+            goto out;
+        }
+    }
+
+    rc = 1;
+
+out:
+    P_CLEANSE(ctx->provctx, ctx->tbuf, ctx->tbuf_len);
+
+    ibmca_debug_op_ctx(ctx, "rc: %d", rc);
+
+    return rc;
+}
+
+static int ibmca_signature_rsa_verify_recover(void *vctx,
+                                              unsigned char *rout,
+                                              size_t *routlen,
+                                              size_t routsize,
+                                              const unsigned char *sig,
+                                              size_t siglen)
+{
+    struct ibmca_op_ctx *ctx = vctx;
+    unsigned char diginfo[MAX_DIGINFO_SIZE + EVP_MAX_MD_SIZE];
+    unsigned char *dec_data, *msg;
+    size_t dec_data_len, diginfo_len, msg_len, md_len, rsa_size;
+    int rc = 1;
+
+    if (ctx == NULL || routlen == NULL || sig == NULL)
+        return 0;
+
+    ibmca_debug_op_ctx(ctx, "ctx: %p key: %p routsize: %lu siglen: %lu",
+                       ctx, ctx->key, routsize, siglen);
+
+    if (ctx->key == NULL || ctx->operation != EVP_PKEY_OP_VERIFYRECOVER) {
+        put_error_op_ctx(ctx, IBMCA_ERR_INTERNAL_ERROR,
+                         "verify recover operation not initialized");
+        return 0;
+    }
+
+    rsa_size = ctx->key->get_max_param_size(ctx->key);
+    *routlen = rsa_size;
+
+    if (rout == NULL) /* size query */
+        goto out;
+
+    if (routsize < *routlen) {
+        put_error_op_ctx(ctx, IBMCA_ERR_INVALID_PARAM, "Output buffer too small");
+        return 0;
+    }
+
+    if (siglen != rsa_size) {
+        put_error_op_ctx(ctx, IBMCA_ERR_INVALID_PARAM,
+                         "Invalid signature length");
+        return 0;
+    }
+
+    ibmca_debug_op_ctx(ctx, "pad_mode: %d", ctx->rsa.signature.pad_mode);
+
+    /* Allocate padding buffer, if required by padding mode */
+    switch (ctx->rsa.signature.pad_mode) {
+    case RSA_NO_PADDING:
+        dec_data = rout;
+        dec_data_len = *routlen;
+        break;
+
+    case RSA_PKCS1_PADDING:
+    case RSA_X931_PADDING:
+        if (ibmca_op_alloc_tbuf(ctx, rsa_size) == 0) {
+            ibmca_debug_op_ctx(ctx, "ERROR: ibmca_op_alloc_tbuf failed");
+            return 0;
+        }
+
+        dec_data_len = ctx->tbuf_len;
+        dec_data = ctx->tbuf;
+        break;
+
+    case RSA_PKCS1_PSS_PADDING:
+    case RSA_PKCS1_OAEP_PADDING:
+    case RSA_PKCS1_WITH_TLS_PADDING:
+    default:
+        put_error_op_ctx(ctx, IBMCA_ERR_INVALID_PARAM,
+                         "Invalid padding mode: %d", ctx->rsa.signature.pad_mode);
+        return 0;
+    }
+
+    /* Perform public key decrypt */
+    rc = ica_rsa_mod_expo(ctx->provctx->ica_adapter, sig,
+                          &ctx->key->rsa.public, dec_data);
+    if (rc != 0) {
+        ibmca_debug_op_ctx(ctx, "ica_rsa_mod_expo failed with: %s",
+                           strerror(rc));
+
+        rc = ibmca_signature_rsa_verify_fallback(ctx, dec_data, dec_data_len,
+                                                 sig, siglen);
+        if (rc != 1) {
+            ibmca_debug_op_ctx(ctx,
+                               "ERROR: ibmca_signature_rsa_verify_fallback failed");
+            rc = 0;
+            goto out;
+        }
+    }
+
+    /* Perform padding check */
+    switch (ctx->rsa.signature.pad_mode) {
+    case RSA_NO_PADDING:
+        rc = 1;
+        break;
+
+    case RSA_PKCS1_PADDING:
+        if (ctx->rsa.signature.md != NULL) {
+            rc = ibmca_rsa_check_pkcs1_padding_type1(ctx->key->provctx,
+                                                     dec_data, dec_data_len,
+                                                     NULL, *routlen, &msg,
+                                                     &msg_len);
+            if (rc == 0)
+                break;
+
+            md_len = EVP_MD_get_size(ctx->rsa.signature.md);
+            if (md_len <= 0) {
+                put_error_op_ctx(ctx, IBMCA_ERR_INTERNAL_ERROR,
+                              "EVP_MD_get_size failed");
+                break;
+            }
+
+            /* Rightmost md_len bytes of msg is the hash */
+            rc = ibmca_rsa_build_digest_info(ctx->key->provctx,
+                                             ctx->rsa.signature.md,
+                                             msg + msg_len - md_len, md_len,
+                                             diginfo, sizeof(diginfo),
+                                             &diginfo_len);
+            if (rc == 0)
+                break;
+
+            if (diginfo_len != dec_data_len ||
+                memcmp(diginfo, dec_data, diginfo_len) != 0) {
+                put_error_op_ctx(ctx, IBMCA_ERR_SIGNATURE_BAD, "Bad signature");
+                break;
+            }
+
+            if (routsize < md_len) {
+                put_error_op_ctx(ctx, IBMCA_ERR_INVALID_PARAM,
+                                 "Output buffer size too small");
+                break;
+            }
+
+            memcpy(rout, msg + msg_len - md_len, md_len);
+            *routlen = md_len;
+        } else {
+            rc = ibmca_rsa_check_pkcs1_padding_type1(ctx->key->provctx,
+                                                     dec_data, dec_data_len,
+                                                     rout, *routlen, NULL,
+                                                     routlen);
+        }
+        break;
+
+    case RSA_X931_PADDING:
+        if (ctx->rsa.signature.md == NULL) {
+            put_error_op_ctx(ctx, IBMCA_ERR_INVALID_PARAM,
+                             "X.931 padding requires a digest");
+            rc = 0;
+            break;
+        }
+
+        rc = ibmca_signature_rsa_verify_x931_post_process(ctx, dec_data,
+                                                          dec_data_len);
+        if (rc == 0)
+            break;
+
+        rc = ibmca_rsa_check_X931_padding(ctx->key->provctx,
+                                          dec_data, dec_data_len, rout,
+                                          *routlen, NULL, routlen,
+                                          EVP_MD_get_type(
+                                            ctx->rsa.signature.md));
+        break;
+
+    case RSA_PKCS1_PSS_PADDING:
+    case RSA_PKCS1_OAEP_PADDING:
+    case RSA_PKCS1_WITH_TLS_PADDING:
+    default:
+        rc = 0;
+        break;
+    }
+    if (rc == 0)
+        goto out;
+
+    rc = 1;
+
+out:
+    P_CLEANSE(ctx->provctx, ctx->tbuf, ctx->tbuf_len);
+
+    ibmca_debug_op_ctx(ctx, "routlen: %lu rc: %d", *routlen, rc);
+
+    return rc;
+}
+
+#ifdef EVP_PKEY_OP_SIGNMSG
+static int ibmca_signature_rsa_signverify_message_update(void *vctx,
+                                            const unsigned char *data,
+                                            size_t datalen)
+{
+    struct ibmca_op_ctx *ctx = vctx;
+
+    return ibmca_digest_signverify_update(ctx, ctx->rsa.signature.md_ctx,
+                                          data, datalen);
+}
+
+static int ibmca_signature_rsa_sign_message_final(void *vctx,
+                                                 unsigned char *sig,
+                                                 size_t *siglen,
+                                                 size_t sigsize)
+{
+    struct ibmca_op_ctx *ctx = vctx;
+    int rc;
+
+    ctx->operation = EVP_PKEY_OP_SIGNCTX;
+    rc = ibmca_digest_sign_final(ctx, ctx->rsa.signature.md_ctx,
+                                 ibmca_signature_rsa_sign,
+                                 sig, siglen, sigsize);
+    ctx->operation = EVP_PKEY_OP_SIGNMSG;
+
+    return rc;
+}
+
+static int ibmca_signature_rsa_verify_message_final(void *vctx)
+{
+    struct ibmca_op_ctx *ctx = vctx;
+    int rc;
+
+    ctx->operation = EVP_PKEY_OP_VERIFYCTX;
+    rc = ibmca_digest_verify_final(ctx, ctx->rsa.signature.md_ctx,
+                                   ibmca_signature_rsa_verify,
+                                   ctx->rsa.signature.signature,
+                                   ctx->rsa.signature.signature_len);
+    ctx->operation = EVP_PKEY_OP_VERIFYMSG;
+
+    return rc;
+}
+#endif
+
+
+static int ibmca_signature_rsa_digest_sign_init(void *vctx, const char *mdname,
+                                                void *vkey,
+                                                const OSSL_PARAM params[])
+{
+    struct ibmca_op_ctx *ctx = vctx;
+    struct ibmca_key *key = vkey;
+
+    return ibmca_signature_rsa_op_init(ctx, key, params,
+                                       EVP_PKEY_OP_SIGNCTX, mdname);
+}
+
+static int ibmca_signature_rsa_digest_verify_init(void *vctx,
+                                                  const char *mdname,
+                                                  void *vkey,
+                                                  const OSSL_PARAM params[])
+{
+    struct ibmca_op_ctx *ctx = vctx;
+    struct ibmca_key *key = vkey;
+
+    return ibmca_signature_rsa_op_init(ctx, key, params,
+                                       EVP_PKEY_OP_VERIFYCTX, mdname);
+}
+
+static int ibmca_signature_rsa_digest_signverify_update(void *vctx,
+                                                    const unsigned char *data,
+                                                    size_t datalen)
+{
+    struct ibmca_op_ctx *ctx = vctx;
+
+    return ibmca_digest_signverify_update(ctx, ctx->rsa.signature.md_ctx,
+                                          data, datalen);
+}
+
+static int ibmca_signature_rsa_digest_sign_final(void *vctx,
+                                                 unsigned char *sig,
+                                                 size_t *siglen,
+                                                 size_t sigsize)
+{
+    struct ibmca_op_ctx *ctx = vctx;
+
+    return ibmca_digest_sign_final(ctx, ctx->rsa.signature.md_ctx,
+                                   ibmca_signature_rsa_sign,
+                                   sig, siglen, sigsize);
+}
+
+static int ibmca_signature_rsa_digest_verify_final(void *vctx,
+                                                   const unsigned char *sig,
+                                                   size_t siglen)
+{
+    struct ibmca_op_ctx *ctx = vctx;
+
+    return ibmca_digest_verify_final(ctx, ctx->rsa.signature.md_ctx,
+                                     ibmca_signature_rsa_verify,
+                                     sig, siglen);
+}
+
+static int ibmca_signature_rsa_get_algid_pss_parms(struct ibmca_op_ctx *ctx,
+                                                   int *ptype, void **param)
+{
+    RSA_PSS_PARAMS *pss = NULL;
+    X509_ALGOR *mgf_md_algo = NULL;
+    ASN1_STRING *mgf_md_alfo_str = NULL;
+    int rc = 0, saltlen;
+
+    pss = RSA_PSS_PARAMS_new();
+    if (pss == NULL) {
+        put_error_op_ctx(ctx, IBMCA_ERR_MALLOC_FAILED,
+                         "RSA_PSS_PARAMS_new failed");
+        goto done;
+    }
+
+    switch (ctx->rsa.signature.saltlen) {
+    case RSA_PSS_SALTLEN_DIGEST:
+        saltlen = EVP_MD_get_size(ctx->rsa.signature.md);
+        break;
+
+    case RSA_PSS_SALTLEN_AUTO:
+    case RSA_PSS_SALTLEN_MAX:
+        saltlen = ctx->key->get_max_param_size(ctx->key) -
+                    EVP_MD_get_size(ctx->rsa.signature.md) - 2;
+        if ((ctx->key->rsa.bits & 0x7) == 1)
+            saltlen--;
+        break;
+
+    default:
+        saltlen = ctx->rsa.signature.saltlen;
+        if (saltlen < 0) {
+            put_error_op_ctx(ctx, IBMCA_ERR_INTERNAL_ERROR,
+                             "Invalid saltlen value");
+            goto done;
+        }
+    }
+
+    pss->saltLength = ASN1_INTEGER_new();
+    if (pss->saltLength == NULL ||
+        ASN1_INTEGER_set(pss->saltLength, saltlen) == 0) {
+        put_error_op_ctx(ctx, IBMCA_ERR_INTERNAL_ERROR,
+                         "ASN1_INTEGER_new/ASN1_INTEGER_set failed");
+        goto done;
+    }
+
+    pss->hashAlgorithm = X509_ALGOR_new();
+    if (pss->hashAlgorithm == NULL ||
+        X509_ALGOR_set0(pss->hashAlgorithm,
+                        OBJ_nid2obj(EVP_MD_get_type(ctx->rsa.signature.md)),
+                        V_ASN1_NULL, NULL) == 0) {
+        put_error_op_ctx(ctx, IBMCA_ERR_INTERNAL_ERROR,
+                         "X509_ALGOR_new/X509_ALGOR_set0 failed");
+        goto done;
+    }
+
+    mgf_md_algo = X509_ALGOR_new();
+    if (mgf_md_algo == NULL ||
+        X509_ALGOR_set0(mgf_md_algo,
+                        OBJ_nid2obj(EVP_MD_get_type(
+                                    ctx->rsa.signature.mgf1_md != NULL ?
+                                            ctx->rsa.signature.mgf1_md :
+                                            ctx->rsa.signature.md)),
+                        V_ASN1_NULL, NULL) == 0) {
+        put_error_op_ctx(ctx, IBMCA_ERR_INTERNAL_ERROR,
+                         "X509_ALGOR_new/X509_ALGOR_set0 failed");
+        goto done;
+    }
+
+    if (ASN1_item_pack(mgf_md_algo, ASN1_ITEM_rptr(X509_ALGOR),
+                       &mgf_md_alfo_str) == NULL) {
+        put_error_op_ctx(ctx, IBMCA_ERR_INTERNAL_ERROR,
+                         "ASN1_item_pack failed");
+        goto done;
+    }
+
+    pss->maskGenAlgorithm = X509_ALGOR_new();
+    if (pss->maskGenAlgorithm == NULL ||
+        X509_ALGOR_set0(pss->maskGenAlgorithm, OBJ_nid2obj(NID_mgf1),
+                         V_ASN1_SEQUENCE, mgf_md_alfo_str) == 0) {
+        put_error_op_ctx(ctx, IBMCA_ERR_INTERNAL_ERROR,
+                         "X509_ALGOR_new/X509_ALGOR_set0 failed");
+        goto done;
+    }
+    mgf_md_alfo_str = NULL;
+
+    pss->maskHash = X509_ALGOR_new();
+    if (pss->maskHash == NULL ||
+        X509_ALGOR_set0(pss->maskHash,
+                        OBJ_nid2obj(EVP_MD_get_type(
+                                    ctx->rsa.signature.mgf1_md != NULL ?
+                                            ctx->rsa.signature.mgf1_md :
+                                            ctx->rsa.signature.md)),
+                        V_ASN1_NULL, NULL) == 0) {
+        put_error_op_ctx(ctx, IBMCA_ERR_INTERNAL_ERROR,
+                         "X509_ALGOR_new/X509_ALGOR_set0 failed");
+        goto done;
+    }
+
+    /* We always use the default trailer field, so we can omit it */
+
+    *ptype = V_ASN1_SEQUENCE;
+    *param = ASN1_item_pack(pss, ASN1_ITEM_rptr(RSA_PSS_PARAMS), NULL);
+    if (*param == NULL) {
+        put_error_op_ctx(ctx, IBMCA_ERR_INTERNAL_ERROR,
+                         "ASN1_item_pack failed");
+        goto done;
+    }
+
+    rc = 1;
+
+done:
+    if (mgf_md_alfo_str != NULL)
+        ASN1_STRING_free(mgf_md_alfo_str);
+    if (mgf_md_algo != NULL)
+        X509_ALGOR_free(mgf_md_algo);
+    RSA_PSS_PARAMS_free(pss);
+
+    return rc;
+}
+
+static int ibmca_signature_rsa_get_algid(struct ibmca_op_ctx *ctx,
+                                         OSSL_PARAM *p)
+{
+    ASN1_OBJECT *oid = NULL;
+    X509_ALGOR * algid = NULL;
+    void *alg_param = NULL;
+    int alg_ptype = V_ASN1_NULL;
+    unsigned char *aid_buf = NULL;
+    size_t aid_len;
+
+    if (ctx->rsa.signature.md == NULL) {
+        put_error_op_ctx(ctx, IBMCA_ERR_INVALID_PARAM, "No digest is set");
+        return 0;
+    }
+
+    switch (ctx->rsa.signature.pad_mode) {
+    case RSA_PKCS1_PADDING:
+        switch (EVP_MD_get_type(ctx->rsa.signature.md)) {
+        case NID_sha1:
+            oid = OBJ_nid2obj(NID_sha1WithRSAEncryption);
+            break;
+        case NID_sha224:
+             oid = OBJ_nid2obj(NID_sha224WithRSAEncryption);
+             break;
+        case NID_sha256:
+             oid = OBJ_nid2obj(NID_sha256WithRSAEncryption);
+             break;
+        case NID_sha384:
+             oid = OBJ_nid2obj(NID_sha384WithRSAEncryption);
+             break;
+        case NID_sha512:
+             oid = OBJ_nid2obj(NID_sha512WithRSAEncryption);
+             break;
+        case NID_sha512_224:
+             oid = OBJ_nid2obj(NID_sha512_224WithRSAEncryption);
+             break;
+        case NID_sha512_256:
+             oid = OBJ_nid2obj(NID_sha512_256WithRSAEncryption);
+             break;
+        case NID_sha3_224:
+             oid = OBJ_nid2obj(NID_RSA_SHA3_224);
+             break;
+        case NID_sha3_256:
+             oid = OBJ_nid2obj(NID_RSA_SHA3_256);
+             break;
+        case NID_sha3_384:
+             oid = OBJ_nid2obj(NID_RSA_SHA3_384);
+             break;
+        case NID_sha3_512:
+             oid = OBJ_nid2obj(NID_RSA_SHA3_512);
+             break;
+        default:
+            put_error_op_ctx(ctx, IBMCA_ERR_INVALID_PARAM,
+                             "AlgorithmID not supported for digest '%s'",
+                             EVP_MD_get0_name(ctx->rsa.signature.md));
+            return 0;
+        }
+        break;
+
+    case RSA_PKCS1_PSS_PADDING:
+        if (ctx->key->type == EVP_PKEY_RSA_PSS) {
+            oid = OBJ_nid2obj(NID_rsassaPss);
+
+            if (ibmca_signature_rsa_get_algid_pss_parms(ctx, &alg_ptype,
+                                                        &alg_param) == 0)
+                return 0;
+        } else {
+            oid = OBJ_nid2obj(NID_rsaEncryption);
+        }
+        break;
+
+    default:
+        put_error_op_ctx(ctx, IBMCA_ERR_INVALID_PARAM,
+                         "AlgorithmID not supported for pad mode %d",
+                         ctx->rsa.signature.pad_mode);
+        return 0;
+    }
+
+    algid = X509_ALGOR_new();
+    if (algid == NULL ||
+        X509_ALGOR_set0(algid, oid, alg_ptype, alg_param) == 0 ||
+        (aid_len = i2d_X509_ALGOR(algid, &aid_buf)) <= 0) {
+        put_error_op_ctx(ctx, IBMCA_ERR_INTERNAL_ERROR,
+                         "X509_ALGOR_new/X509_ALGOR_set0/i2d_X509_ALGOR failed");
+        X509_ALGOR_free(algid);
+        return 0;
+    }
+
+    if (OSSL_PARAM_set_octet_string(p, aid_buf, aid_len) == 0) {
+        put_error_op_ctx(ctx, IBMCA_ERR_INTERNAL_ERROR,
+                         "Failed to return param '%s'",
+                         OSSL_SIGNATURE_PARAM_ALGORITHM_ID);
+        P_FREE(ctx->provctx, aid_buf);
+        X509_ALGOR_free(algid);
+        return 0;
+    }
+    P_FREE(ctx->provctx, aid_buf);
+    X509_ALGOR_free(algid);
+
+    ibmca_debug_op_ctx(ctx, "param '%s': [octet string] (%lu bytes)",
+                       OSSL_SIGNATURE_PARAM_ALGORITHM_ID, aid_len);
+
+    return 1;
+}
+
+static int ibmca_signature_rsa_get_ctx_params(void *vctx, OSSL_PARAM params[])
+{
+    struct ibmca_op_ctx *ctx = vctx;
+    OSSL_PARAM *p;
+    const char *name = NULL;
+    int i, rc;
+
+    if (ctx == NULL)
+        return 0;
+
+    ibmca_debug_op_ctx(ctx, "ctx: %p", ctx);
+    for (p = params; p != NULL && p->key != NULL; p++)
+        ibmca_debug_op_ctx(ctx, "param: %s", p->key);
+
+    /* OSSL_SIGNATURE_PARAM_ALGORITHM_ID */
+    p = OSSL_PARAM_locate(params, OSSL_SIGNATURE_PARAM_ALGORITHM_ID);
+    if (p != NULL && ibmca_signature_rsa_get_algid(ctx, p) == 0)
+        return 0;
+
+    /* OSSL_SIGNATURE_PARAM_PAD_MODE */
+    p = OSSL_PARAM_locate(params, OSSL_SIGNATURE_PARAM_PAD_MODE);
+    if (p != NULL) {
+        switch (p->data_type) {
+        case OSSL_PARAM_INTEGER:
+            rc = ibmca_param_build_set_int(ctx->provctx, NULL, params,
+                                           OSSL_SIGNATURE_PARAM_PAD_MODE,
+                                           ctx->rsa.signature.pad_mode);
+            if (rc == 0)
+                return 0;
+            break;
+        case OSSL_PARAM_UTF8_STRING:
+            for (i = 0; ibmca_rsa_padding_table[i].id != 0; i++) {
+                if ((int)ibmca_rsa_padding_table[i].id ==
+                                                ctx->rsa.signature.pad_mode) {
+                    name = ibmca_rsa_padding_table[i].ptr;
+                    break;
+                }
+            }
+            if (name == NULL) {
+                put_error_op_ctx(ctx, IBMCA_ERR_INTERNAL_ERROR,
+                                 "Invalid RSA padding mode: %d",
+                                 ctx->rsa.signature.pad_mode);
+                return 0;
+            }
+
+            rc = ibmca_param_build_set_utf8(ctx->provctx, NULL, params,
+                                            OSSL_SIGNATURE_PARAM_PAD_MODE,
+                                            name);
+             if (rc == 0)
+                 return 0;
+            break;
+        default:
+            put_error_op_ctx(ctx, IBMCA_ERR_INVALID_PARAM,
+                             "Invalid param type for: '%s'",
+                             OSSL_SIGNATURE_PARAM_PAD_MODE);
+            return 0;
+        }
+    }
+
+    /* OSSL_SIGNATURE_PARAM_DIGEST */
+    if (ctx->rsa.signature.md != NULL)
+        name = EVP_MD_get0_name(ctx->rsa.signature.md);
+    else name = "";
+    rc = ibmca_param_build_set_utf8(ctx->provctx, NULL, params,
+                                    OSSL_SIGNATURE_PARAM_DIGEST, name);
+    if (rc == 0)
+       return 0;
+
+    /* OSSL_SIGNATURE_PARAM_MGF1_DIGEST */
+    if (ctx->rsa.signature.mgf1_md != NULL)
+        name = EVP_MD_get0_name(ctx->rsa.signature.mgf1_md);
+    else if (ctx->rsa.signature.md != NULL)
+        name = EVP_MD_get0_name(ctx->rsa.signature.md);
+    else name = "";
+    rc = ibmca_param_build_set_utf8(ctx->provctx, NULL, params,
+                                    OSSL_SIGNATURE_PARAM_MGF1_DIGEST, name);
+    if (rc == 0)
+       return 0;
+
+    /* OSSL_SIGNATURE_PARAM_PSS_SALTLEN */
+    p = OSSL_PARAM_locate(params, OSSL_SIGNATURE_PARAM_PSS_SALTLEN);
+    if (p != NULL) {
+        if (p->data_type == OSSL_PARAM_INTEGER) {
+            rc = ibmca_param_build_set_int(ctx->provctx, NULL, params,
+                                           OSSL_SIGNATURE_PARAM_PSS_SALTLEN,
+                                           ctx->rsa.signature.saltlen);
+            if (rc == 0)
+               return 0;
+        } else if (p->data_type == OSSL_PARAM_UTF8_STRING) {
+            switch (ctx->rsa.signature.saltlen) {
+            case RSA_PSS_SALTLEN_DIGEST:
+                rc = ibmca_param_build_set_utf8(ctx->provctx, NULL, params,
+                                            OSSL_SIGNATURE_PARAM_PSS_SALTLEN,
+                                            OSSL_PKEY_RSA_PSS_SALT_LEN_DIGEST);
+                break;
+            case RSA_PSS_SALTLEN_MAX:
+                rc = ibmca_param_build_set_utf8(ctx->provctx, NULL, params,
+                                            OSSL_SIGNATURE_PARAM_PSS_SALTLEN,
+                                            OSSL_PKEY_RSA_PSS_SALT_LEN_MAX);
+                break;
+            case RSA_PSS_SALTLEN_AUTO:
+                rc = ibmca_param_build_set_utf8(ctx->provctx, NULL, params,
+                                            OSSL_SIGNATURE_PARAM_PSS_SALTLEN,
+                                            OSSL_PKEY_RSA_PSS_SALT_LEN_AUTO);
+                break;
+#ifdef RSA_PSS_SALTLEN_AUTO_DIGEST_MAX
+            case RSA_PSS_SALTLEN_AUTO_DIGEST_MAX:
+                rc = ibmca_param_build_set_utf8(ctx->provctx, NULL, params,
+                                            OSSL_SIGNATURE_PARAM_PSS_SALTLEN,
+                                            OSSL_PKEY_RSA_PSS_SALT_LEN_AUTO_DIGEST_MAX);
+                break;
+#endif
+            default:
+                rc = snprintf(p->data, p->data_size, "%d",
+                              ctx->rsa.signature.saltlen);
+                if (rc <= 0) {
+                    put_error_op_ctx(ctx, IBMCA_ERR_INVALID_PARAM,
+                                     "Failed to return param '%s'",
+                                     OSSL_SIGNATURE_PARAM_PSS_SALTLEN);
+                    return 0;
+                }
+                p->return_size = rc;
+                ibmca_debug_op_ctx(ctx, "param '%s': '%s'",
+                                   OSSL_SIGNATURE_PARAM_PSS_SALTLEN, p->data);
+                break;
+            }
+            if (rc <= 0)
+                return 0;
+        }
+    }
+
+    return 1;
+}
+
+static int ibmca_signature_rsa_set_ctx_params(void *vctx,
+                                              const OSSL_PARAM params[])
+{
+    struct ibmca_op_ctx *ctx = vctx;
+    const OSSL_PARAM *p;
+    const char *name, *props = NULL;
+    int i, rc, saltlen;
+#ifdef EVP_PKEY_OP_SIGNMSG
+    size_t len;
+    unsigned char *ptr = NULL;
+#endif
+
+    if (ctx == NULL)
+        return 0;
+
+    ibmca_debug_op_ctx(ctx, "ctx: %p", ctx);
+    for (p = params; p != NULL && p->key != NULL; p++)
+        ibmca_debug_op_ctx(ctx, "param: %s", p->key);
+
+#ifdef EVP_PKEY_OP_SIGNMSG
+    switch (ctx->operation) {
+    case EVP_PKEY_OP_SIGNMSG:
+    case EVP_PKEY_OP_VERIFYMSG:
+        /* OSSL_SIGNATURE_PARAM_SIGNATURE */
+        rc = ibmca_param_get_octet_string(ctx->provctx, params,
+                                          OSSL_SIGNATURE_PARAM_SIGNATURE,
+                                          (void **)&ptr, &len);
+        if (rc == 0)
+            return 0;
+        if (rc > 0) {
+            if (ctx->rsa.signature.signature != NULL)
+                P_CLEAR_FREE(ctx->provctx, ctx->rsa.signature.signature,
+                        ctx->rsa.signature.signature_len);
+            ctx->rsa.signature.signature = ptr;
+            ctx->rsa.signature.signature_len = len;
+        }
+
+        /* No further params are allowed */
+        return 1;
+    }
+#endif
+
+    /* OSSL_SIGNATURE_PARAM_PAD_MODE */
+    p = OSSL_PARAM_locate_const((OSSL_PARAM *)params,
+                                OSSL_SIGNATURE_PARAM_PAD_MODE);
+    if (p != NULL) {
+        switch (p->data_type) {
+        case OSSL_PARAM_INTEGER:
+            rc = ibmca_param_get_int(ctx->provctx, params,
+                                     OSSL_SIGNATURE_PARAM_PAD_MODE,
+                                     &ctx->rsa.signature.pad_mode);
+            if (rc == 0)
+                return 0;
+            break;
+        case OSSL_PARAM_UTF8_STRING:
+            rc = ibmca_param_get_utf8(ctx->provctx, params,
+                                      OSSL_SIGNATURE_PARAM_PAD_MODE, &name);
+            if (rc == 1) {
+                ctx->rsa.signature.pad_mode = 0;
+                for (i = 0; ibmca_rsa_padding_table[i].id != 0; i++) {
+                    if (strcmp(name, ibmca_rsa_padding_table[i].ptr) == 0) {
+                        ctx->rsa.signature.pad_mode =
+                                            ibmca_rsa_padding_table[i].id;
+                        break;
+                    }
+                }
+            }
+            break;
+        default:
+            put_error_op_ctx(ctx, IBMCA_ERR_INVALID_PARAM,
+                             "Invalid param type for: '%s'",
+                             OSSL_SIGNATURE_PARAM_PAD_MODE);
+            return 0;
+        }
+
+        switch (ctx->rsa.signature.pad_mode) {
+        case RSA_NO_PADDING:
+        case RSA_PKCS1_PADDING:
+        case RSA_X931_PADDING:
+            if (ctx->key->type == EVP_PKEY_RSA_PSS) {
+                put_error_op_ctx(ctx, IBMCA_ERR_INVALID_PARAM,
+                                 "Padding mode %d not allowed with RSA-PSS",
+                                 ctx->rsa.signature.pad_mode);
+                return 0;
+            }
+            break;
+
+        case RSA_PKCS1_PSS_PADDING:
+            if (ctx->operation != EVP_PKEY_OP_SIGN &&
+                ctx->operation != EVP_PKEY_OP_SIGNCTX &&
+                ctx->operation != EVP_PKEY_OP_VERIFY &&
+                ctx->operation != EVP_PKEY_OP_VERIFYCTX) {
+                put_error_op_ctx(ctx, IBMCA_ERR_INVALID_PARAM,
+                                 "PSS padding only allowed for sign and verify operations");
+                return 0;
+            }
+
+            /* Setup md defaults if not already set */
+            if (ctx->rsa.signature.md == NULL &&
+                ibmca_signature_rsa_set_md(ctx, NULL,
+                                           ctx->rsa.signature.pss.digest_nid,
+                                           NULL) == 0)
+                return 0;
+            if (ctx->rsa.signature.mgf1_md == NULL &&
+                 ctx->rsa.signature.pss.mgf_digest_nid !=
+                                     ctx->rsa.signature.pss.digest_nid &&
+                ibmca_signature_rsa_set_mgf1_md(ctx, NULL,
+                                     ctx->rsa.signature.pss.mgf_digest_nid,
+                                     NULL) == 0)
+                return 0;
+            break;
+
+        case RSA_PKCS1_OAEP_PADDING: /* OAEP is for encrypt/decrypt only */
+        case RSA_PKCS1_WITH_TLS_PADDING: /* TLS is for decrypt only */
+        default:
+            put_error_op_ctx(ctx, IBMCA_ERR_INVALID_PARAM,
+                                      "Invalid RSA padding mode: %d",
+                                      ctx->rsa.signature.pad_mode);
+            return 0;
+        }
+    }
+
+    /* OSSL_SIGNATURE_PARAM_PROPERTIES */
+    rc = ibmca_param_get_utf8(ctx->provctx, params,
+                              OSSL_SIGNATURE_PARAM_PROPERTIES, &props);
+    if (rc == 0)
+        return 0;
+
+    /* OSSL_SIGNATURE_PARAM_DIGEST */
+    rc = ibmca_param_get_utf8(ctx->provctx, params,
+                              OSSL_SIGNATURE_PARAM_DIGEST, &name);
+    if (rc == 0)
+        return 0;
+    if (rc > 0 &&
+        ibmca_signature_rsa_set_md(ctx, name, 0, props) == 0)
+        return 0;
+
+    /* OSSL_SIGNATURE_PARAM_PSS_SALTLEN */
+    p = OSSL_PARAM_locate_const((OSSL_PARAM *)params,
+                                OSSL_SIGNATURE_PARAM_PSS_SALTLEN);
+    if (p != NULL) {
+        switch (p->data_type) {
+        case OSSL_PARAM_INTEGER:
+            rc = ibmca_param_get_int(ctx->provctx, params,
+                                     OSSL_SIGNATURE_PARAM_PSS_SALTLEN,
+                                     &saltlen);
+            if (rc == 0)
+                return 0;
+            break;
+        case OSSL_PARAM_UTF8_STRING:
+            if (strcmp(p->data, OSSL_PKEY_RSA_PSS_SALT_LEN_DIGEST) == 0)
+                saltlen = RSA_PSS_SALTLEN_DIGEST;
+            else if (strcmp(p->data, OSSL_PKEY_RSA_PSS_SALT_LEN_MAX) == 0)
+                saltlen = RSA_PSS_SALTLEN_MAX;
+            else if (strcmp(p->data, OSSL_PKEY_RSA_PSS_SALT_LEN_AUTO) == 0)
+                saltlen = RSA_PSS_SALTLEN_AUTO;
+#ifdef RSA_PSS_SALTLEN_AUTO_DIGEST_MAX
+            else if (strcmp(p->data, OSSL_PKEY_RSA_PSS_SALT_LEN_AUTO_DIGEST_MAX) == 0)
+                saltlen = RSA_PSS_SALTLEN_AUTO_DIGEST_MAX;
+#endif
+            else
+                saltlen = atoi(p->data);
+
+            ibmca_debug_op_ctx(ctx, "param '%s': '%s'",
+                               OSSL_SIGNATURE_PARAM_PSS_SALTLEN, p->data);
+            break;
+        default:
+            return 0;
+        }
+
+#ifdef RSA_PSS_SALTLEN_AUTO_DIGEST_MAX
+        if (saltlen < RSA_PSS_SALTLEN_AUTO_DIGEST_MAX) {
+#else
+        if (saltlen < RSA_PSS_SALTLEN_MAX) {
+#endif
+            put_error_op_ctx(ctx, IBMCA_ERR_INVALID_PARAM,
+                             "Invalid salt length: %d", saltlen);
+            return 0;
+        }
+
+        if (ctx->key->type == EVP_PKEY_RSA_PSS &&
+            ctx->rsa.signature.pss.restricted) {
+            switch (saltlen) {
+            case RSA_PSS_SALTLEN_AUTO:
+#ifdef RSA_PSS_SALTLEN_AUTO_DIGEST_MAX
+            case RSA_PSS_SALTLEN_AUTO_DIGEST_MAX:
+#endif
+                if (ctx->operation == EVP_PKEY_OP_VERIFY) {
+                    put_error_op_ctx(ctx, IBMCA_ERR_INVALID_PARAM,
+                                     "Cannot use auto-detected salt length");
+                    return 0;
+                }
+                break;
+            case RSA_PSS_SALTLEN_DIGEST:
+                if (ctx->rsa.signature.md != NULL &&
+                    EVP_MD_get_size(ctx->rsa.signature.md) <
+                                            ctx->rsa.signature.pss.saltlen ) {
+                    put_error_op_ctx(ctx, IBMCA_ERR_INVALID_PARAM,
+                                     "Saltlen should be >= %d, but digest size is %d",
+                                     ctx->rsa.signature.pss.saltlen,
+                                     EVP_MD_get_size(
+                                             ctx->rsa.signature.md));
+                    return 0;
+                }
+                break;
+            default:
+                if (saltlen >= 0 && saltlen < ctx->rsa.signature.pss.saltlen) {
+                    put_error_op_ctx(ctx, IBMCA_ERR_INVALID_PARAM,
+                                   "Saltlen should be more than %d, but is %d",
+                                   ctx->rsa.signature.pss.saltlen, saltlen);
+                    return 0;
+                }
+            }
+        }
+
+        ctx->rsa.signature.saltlen = saltlen;
+        ibmca_debug_op_ctx(ctx, "saltlen: %d", saltlen);
+    }
+
+    /* OSSL_SIGNATURE_PARAM_MGF1_PROPERTIES */
+    rc = ibmca_param_get_utf8(ctx->provctx, params,
+                              OSSL_SIGNATURE_PARAM_MGF1_PROPERTIES, &props);
+    if (rc == 0)
+        return 0;
+
+    /* OSSL_SIGNATURE_PARAM_MGF1_DIGEST */
+    rc = ibmca_param_get_utf8(ctx->provctx, params,
+                              OSSL_SIGNATURE_PARAM_MGF1_DIGEST, &name);
+    if (rc == 0)
+        return 0;
+    if (rc > 0 &&
+        ibmca_signature_rsa_set_mgf1_md(ctx, name, 0, props) == 0)
+        return 0;
+
+    return 1;
+}
+
+static const OSSL_PARAM ibmca_signature_rsa_gettable_params[] = {
+    OSSL_PARAM_octet_string(OSSL_SIGNATURE_PARAM_ALGORITHM_ID, NULL, 0),
+    OSSL_PARAM_utf8_string(OSSL_SIGNATURE_PARAM_PAD_MODE, NULL, 0),
+    OSSL_PARAM_utf8_string(OSSL_SIGNATURE_PARAM_DIGEST, NULL, 0),
+    OSSL_PARAM_utf8_string(OSSL_SIGNATURE_PARAM_MGF1_DIGEST, NULL, 0),
+    OSSL_PARAM_utf8_string(OSSL_SIGNATURE_PARAM_PSS_SALTLEN, NULL, 0),
+    OSSL_PARAM_END
+};
+
+static const OSSL_PARAM *ibmca_signature_rsa_gettable_ctx_params(
+                                                void *vctx, void *vprovctx)
+{
+    const struct ibmca_op_ctx *ctx = vctx;
+    const struct ibmca_prov_ctx *provctx = vprovctx;
+    const OSSL_PARAM *p;
+
+    ibmca_debug_ctx(provctx, "ctx: %p", ctx);
+
+    for (p = ibmca_signature_rsa_gettable_params;
+                                    p != NULL && p->key != NULL; p++)
+        ibmca_debug_ctx(provctx, "param: %s", p->key);
+
+    return ibmca_signature_rsa_gettable_params;
+}
+
+static const OSSL_PARAM ibmca_signature_rsa_settable_params[] = {
+    OSSL_PARAM_utf8_string(OSSL_SIGNATURE_PARAM_DIGEST, NULL, 0),
+    OSSL_PARAM_utf8_string(OSSL_SIGNATURE_PARAM_PROPERTIES, NULL, 0),
+    OSSL_PARAM_utf8_string(OSSL_SIGNATURE_PARAM_PAD_MODE, NULL, 0),
+    OSSL_PARAM_utf8_string(OSSL_SIGNATURE_PARAM_MGF1_DIGEST, NULL, 0),
+    OSSL_PARAM_utf8_string(OSSL_SIGNATURE_PARAM_MGF1_PROPERTIES, NULL, 0),
+    OSSL_PARAM_utf8_string(OSSL_SIGNATURE_PARAM_PSS_SALTLEN, NULL, 0),
+    OSSL_PARAM_END
+};
+
+static const OSSL_PARAM ibmca_signature_rsa_settable_params_no_digest[] = {
+    OSSL_PARAM_utf8_string(OSSL_SIGNATURE_PARAM_PAD_MODE, NULL, 0),
+    OSSL_PARAM_utf8_string(OSSL_SIGNATURE_PARAM_MGF1_DIGEST, NULL, 0),
+    OSSL_PARAM_utf8_string(OSSL_SIGNATURE_PARAM_MGF1_PROPERTIES, NULL, 0),
+    OSSL_PARAM_utf8_string(OSSL_SIGNATURE_PARAM_PSS_SALTLEN, NULL, 0),
+    OSSL_PARAM_END
+};
+
+#ifdef EVP_PKEY_OP_SIGNMSG
+static const OSSL_PARAM ibmca_signature_rsa_sigalg_settable_params[] = {
+    OSSL_PARAM_octet_string(OSSL_SIGNATURE_PARAM_SIGNATURE, NULL, 0),
+    OSSL_PARAM_END
+};
+#endif
+
+static const OSSL_PARAM *ibmca_signature_rsa_settable_ctx_params(
+                                                    void *vctx, void *vprovctx)
+{
+    const struct ibmca_op_ctx *ctx = vctx;
+    const struct ibmca_prov_ctx *provctx = vprovctx;
+    const OSSL_PARAM *p, *params;
+
+    ibmca_debug_ctx(provctx, "ctx: %p", ctx);
+
+#ifdef EVP_PKEY_OP_SIGNMSG
+    if (ctx != NULL && ctx->operation == EVP_PKEY_OP_VERIFYMSG)
+        params = ibmca_signature_rsa_sigalg_settable_params;
+    else if (ctx != NULL && ctx->operation == EVP_PKEY_OP_SIGNMSG)
+        params = NULL;
+    else if (ctx == NULL || ctx->rsa.signature.set_md_allowed)
+#else
+    if (ctx == NULL || ctx->rsa.signature.set_md_allowed)
+#endif
+        params = ibmca_signature_rsa_settable_params;
+    else
+        params = ibmca_signature_rsa_settable_params_no_digest;
+
+    for (p = params; p != NULL && p->key != NULL; p++)
+        ibmca_debug_ctx(provctx, "param: %s", p->key);
+
+    return params;
+}
+
+static int ibmca_signature_rsa_get_ctx_md_params(void *vctx, OSSL_PARAM *params)
+{
+    struct ibmca_op_ctx *ctx = vctx;
+
+    return ibmca_get_ctx_md_params(ctx, ctx->rsa.signature.md_ctx, params);
+}
+
+static int ibmca_signature_rsa_set_ctx_md_params(void *vctx,
+                                                 const OSSL_PARAM params[])
+{
+    struct ibmca_op_ctx *ctx = vctx;
+
+    return ibmca_set_ctx_md_params(ctx, ctx->rsa.signature.md_ctx, params);
+}
+
+static const OSSL_PARAM *ibmca_signature_rsa_gettable_ctx_md_params(void *vctx)
+{
+    struct ibmca_op_ctx *ctx = vctx;
+
+    return ibmca_gettable_ctx_md_params(ctx, ctx->rsa.signature.md);
+}
+
+static const OSSL_PARAM *ibmca_signature_rsa_settable_ctx_md_params(void *vctx)
+{
+    struct ibmca_op_ctx *ctx = vctx;
+
+    return ibmca_settable_ctx_md_params(ctx, ctx->rsa.signature.md);
+}
+
+static const OSSL_DISPATCH ibmca_rsa_signature_functions[] = {
+    /* Signature context constructor, destructor */
+    { OSSL_FUNC_SIGNATURE_NEWCTX, (void (*)(void))ibmca_signature_rsa_newctx },
+    { OSSL_FUNC_SIGNATURE_FREECTX, (void (*)(void))ibmca_op_freectx },
+    { OSSL_FUNC_SIGNATURE_DUPCTX, (void (*)(void))ibmca_op_dupctx },
+    /* Signing */
+    { OSSL_FUNC_SIGNATURE_SIGN_INIT,
+            (void (*)(void))ibmca_signature_rsa_sign_init },
+    { OSSL_FUNC_SIGNATURE_SIGN, (void (*)(void))ibmca_signature_rsa_sign },
+    /* Verifying */
+    { OSSL_FUNC_SIGNATURE_VERIFY_INIT,
+            (void (*)(void))ibmca_signature_rsa_verify_init },
+    { OSSL_FUNC_SIGNATURE_VERIFY, (void (*)(void))ibmca_signature_rsa_verify },
+    /* Verify recover */
+    { OSSL_FUNC_SIGNATURE_VERIFY_RECOVER_INIT,
+            (void (*)(void))ibmca_signature_rsa_verifyrecover_init },
+    { OSSL_FUNC_SIGNATURE_VERIFY_RECOVER,
+            (void (*)(void))ibmca_signature_rsa_verify_recover },
+    /* Digest Sign */
+    { OSSL_FUNC_SIGNATURE_DIGEST_SIGN_INIT,
+            (void (*)(void))ibmca_signature_rsa_digest_sign_init },
+    { OSSL_FUNC_SIGNATURE_DIGEST_SIGN_UPDATE,
+           (void (*)(void))ibmca_signature_rsa_digest_signverify_update },
+    { OSSL_FUNC_SIGNATURE_DIGEST_SIGN_FINAL,
+            (void (*)(void))ibmca_signature_rsa_digest_sign_final },
+    /* Digest Verify */
+    { OSSL_FUNC_SIGNATURE_DIGEST_VERIFY_INIT,
+            (void (*)(void))ibmca_signature_rsa_digest_verify_init },
+    { OSSL_FUNC_SIGNATURE_DIGEST_VERIFY_UPDATE,
+            (void (*)(void))ibmca_signature_rsa_digest_signverify_update },
+    { OSSL_FUNC_SIGNATURE_DIGEST_VERIFY_FINAL,
+            (void (*)(void))ibmca_signature_rsa_digest_verify_final },
+    /* Signature parameters */
+    { OSSL_FUNC_SIGNATURE_GET_CTX_PARAMS,
+            (void (*)(void))ibmca_signature_rsa_get_ctx_params },
+    { OSSL_FUNC_SIGNATURE_GETTABLE_CTX_PARAMS,
+            (void (*)(void))ibmca_signature_rsa_gettable_ctx_params },
+    { OSSL_FUNC_SIGNATURE_SET_CTX_PARAMS, (void
+            (*)(void))ibmca_signature_rsa_set_ctx_params },
+    { OSSL_FUNC_SIGNATURE_SETTABLE_CTX_PARAMS,
+            (void (*)(void))ibmca_signature_rsa_settable_ctx_params },
+    /* MD parameters */
+    { OSSL_FUNC_SIGNATURE_GET_CTX_MD_PARAMS,
+            (void (*)(void))ibmca_signature_rsa_get_ctx_md_params },
+    { OSSL_FUNC_SIGNATURE_GETTABLE_CTX_MD_PARAMS,
+        (void (*)(void))ibmca_signature_rsa_gettable_ctx_md_params },
+    { OSSL_FUNC_SIGNATURE_SET_CTX_MD_PARAMS,
+            (void (*)(void))ibmca_signature_rsa_set_ctx_md_params },
+    { OSSL_FUNC_SIGNATURE_SETTABLE_CTX_MD_PARAMS,
+        (void (*)(void))ibmca_signature_rsa_settable_ctx_md_params },
+    { 0, NULL }
+};
+
+#ifdef EVP_PKEY_OP_SIGNMSG
+#define IBMCA_IMPL_RSA_SIGALG(md, MD)                                          \
+    static OSSL_FUNC_signature_sign_init_fn                                    \
+                     ibmca_signature_rsa_##md##_sign_init;                     \
+    static OSSL_FUNC_signature_verify_init_fn                                  \
+                     ibmca_signature_rsa_##md##_verify_init;                   \
+    static OSSL_FUNC_signature_sign_message_init_fn                            \
+                     ibmca_signature_rsa_##md##_sign_message_init;             \
+    static OSSL_FUNC_signature_verify_message_init_fn                          \
+                     ibmca_signature_rsa_##md##_verify_message_init;           \
+                                                                               \
+    static int ibmca_signature_rsa_##md##_sign_init(void *vctx, void *vkey,    \
+                                                   const OSSL_PARAM params[])  \
+    {                                                                          \
+        struct ibmca_op_ctx *ctx = vctx;                                       \
+        struct ibmca_key *key = vkey;                                          \
+                                                                               \
+        return ibmca_signature_rsa_op_init(ctx, key, params,                   \
+                                          EVP_PKEY_OP_SIGN, #MD);              \
+    }                                                                          \
+                                                                               \
+    static int ibmca_signature_rsa_##md##_verify_init(void *vctx, void *vkey,  \
+                                                     const OSSL_PARAM params[])\
+    {                                                                          \
+        struct ibmca_op_ctx *ctx = vctx;                                       \
+        struct ibmca_key *key = vkey;                                          \
+                                                                               \
+        return ibmca_signature_rsa_op_init(ctx, key, params,                   \
+                                          EVP_PKEY_OP_VERIFY, #MD);            \
+    }                                                                          \
+                                                                               \
+    static int ibmca_signature_rsa_##md##_sign_message_init(                   \
+                                                    void *vctx, void *vkey,    \
+                                                    const OSSL_PARAM params[]) \
+    {                                                                          \
+        struct ibmca_op_ctx *ctx = vctx;                                       \
+        struct ibmca_key *key = vkey;                                          \
+                                                                               \
+        return ibmca_signature_rsa_op_init(ctx, key, params,                   \
+                                          EVP_PKEY_OP_SIGNMSG, #MD);           \
+    }                                                                          \
+                                                                               \
+       static int ibmca_signature_rsa_##md##_verify_message_init(              \
+                                                    void *vctx, void *vkey,    \
+                                                    const OSSL_PARAM params[]) \
+       {                                                                       \
+           struct ibmca_op_ctx *ctx = vctx;                                    \
+           struct ibmca_key *key = vkey;                                       \
+                                                                               \
+           return ibmca_signature_rsa_op_init(ctx, key, params,                \
+                                             EVP_PKEY_OP_VERIFYMSG, #MD);      \
+       }                                                                       \
+                                                                               \
+    static const OSSL_DISPATCH ibmca_rsa_##md##_signature_functions[] = {      \
+        /* Signature context constructor, destructor */                        \
+        { OSSL_FUNC_SIGNATURE_NEWCTX,                                          \
+                (void (*)(void))ibmca_signature_rsa_newctx },                  \
+        { OSSL_FUNC_SIGNATURE_FREECTX, (void (*)(void))ibmca_op_freectx },     \
+        { OSSL_FUNC_SIGNATURE_DUPCTX, (void (*)(void))ibmca_op_dupctx },       \
+        { OSSL_FUNC_SIGNATURE_QUERY_KEY_TYPES,                                 \
+                (void (*)(void))ibmca_signature_rsa_query_key_types },         \
+        /* Signing */                                                          \
+        { OSSL_FUNC_SIGNATURE_SIGN_INIT,                                       \
+                (void (*)(void))ibmca_signature_rsa_##md##_sign_init },        \
+        { OSSL_FUNC_SIGNATURE_SIGN,                                            \
+                (void (*)(void))ibmca_signature_rsa_sign },                    \
+        /* Verifying */                                                        \
+        { OSSL_FUNC_SIGNATURE_VERIFY_INIT,                                     \
+                (void (*)(void))ibmca_signature_rsa_##md##_verify_init },      \
+        { OSSL_FUNC_SIGNATURE_VERIFY,                                          \
+                (void (*)(void))ibmca_signature_rsa_verify },                  \
+        /* Sign Message */                                                     \
+        { OSSL_FUNC_SIGNATURE_SIGN_MESSAGE_INIT,                               \
+                (void (*)(void))ibmca_signature_rsa_##md##_sign_message_init },\
+        { OSSL_FUNC_SIGNATURE_SIGN_MESSAGE_UPDATE,                             \
+               (void (*)(void))ibmca_signature_rsa_signverify_message_update },\
+        { OSSL_FUNC_SIGNATURE_SIGN_MESSAGE_FINAL,                              \
+                (void (*)(void))ibmca_signature_rsa_sign_message_final },      \
+        /* Verify Message */                                                   \
+        { OSSL_FUNC_SIGNATURE_VERIFY_MESSAGE_INIT,                             \
+              (void (*)(void))ibmca_signature_rsa_##md##_verify_message_init },\
+        { OSSL_FUNC_SIGNATURE_VERIFY_MESSAGE_UPDATE,                           \
+               (void (*)(void))ibmca_signature_rsa_signverify_message_update },\
+        { OSSL_FUNC_SIGNATURE_VERIFY_MESSAGE_FINAL,                            \
+                (void (*)(void))ibmca_signature_rsa_verify_message_final },    \
+        /* Signature parameters */                                             \
+        { OSSL_FUNC_SIGNATURE_GET_CTX_PARAMS,                                  \
+                (void (*)(void))ibmca_signature_rsa_get_ctx_params },          \
+        { OSSL_FUNC_SIGNATURE_GETTABLE_CTX_PARAMS,                             \
+                (void (*)(void))ibmca_signature_rsa_gettable_ctx_params },     \
+        { OSSL_FUNC_SIGNATURE_SET_CTX_PARAMS,                                  \
+                (void (*)(void))ibmca_signature_rsa_set_ctx_params },          \
+        { OSSL_FUNC_SIGNATURE_SETTABLE_CTX_PARAMS,                             \
+               (void (*)(void))ibmca_signature_rsa_settable_ctx_params },      \
+        { 0, NULL }                                                            \
+    }
+
+#define IBMCA_DEF_RSA_SIGALG(md, MD, names)                                    \
+    { names, NULL, ibmca_rsa_##md##_signature_functions,                       \
+      "IBMCA RSA " #MD " signature implementation" }
+
+IBMCA_IMPL_RSA_SIGALG(sha1, SHA1);
+IBMCA_IMPL_RSA_SIGALG(sha224, SHA2-224);
+IBMCA_IMPL_RSA_SIGALG(sha256, SHA2-256);
+IBMCA_IMPL_RSA_SIGALG(sha384, SHA2-384);
+IBMCA_IMPL_RSA_SIGALG(sha512, SHA2-512);
+IBMCA_IMPL_RSA_SIGALG(sha512_224, SHA2-512/224);
+IBMCA_IMPL_RSA_SIGALG(sha512_256, SHA2-512/256);
+IBMCA_IMPL_RSA_SIGALG(sha3_224, SHA3-224);
+IBMCA_IMPL_RSA_SIGALG(sha3_256, SHA3-256);
+IBMCA_IMPL_RSA_SIGALG(sha3_384, SHA3-384);
+IBMCA_IMPL_RSA_SIGALG(sha3_512, SHA3-512);
+
+#endif
+
+const OSSL_ALGORITHM ibmca_rsa_signature[] = {
+    { "RSA:rsaEncryption:1.2.840.113549.1.1.1", NULL,
+      ibmca_rsa_signature_functions, "IBMCA RSA signature implementation" },
+#ifdef EVP_PKEY_OP_SIGNMSG
+    IBMCA_DEF_RSA_SIGALG(sha1, SHA1,
+                         "RSA-SHA1:RSA-SHA-1:sha1WithRSAEncryption:"
+                         "1.2.840.113549.1.1.5"),
+    IBMCA_DEF_RSA_SIGALG(sha224, SHA2-224,
+                         "RSA-SHA2-224:RSA-SHA224:sha224WithRSAEncryption:"
+                         "1.2.840.113549.1.1.14"),
+    IBMCA_DEF_RSA_SIGALG(sha256, SHA2-256,
+                         "RSA-SHA2-256:RSA-SHA256:sha256WithRSAEncryption:"
+                         "1.2.840.113549.1.1.11"),
+    IBMCA_DEF_RSA_SIGALG(sha384, SHA2-384,
+                         "RSA-SHA2-384:RSA-SHA384:sha384WithRSAEncryption:"
+                         "1.2.840.113549.1.1.12"),
+    IBMCA_DEF_RSA_SIGALG(sha512, SHA2-512,
+                         "RSA-SHA2-512:RSA-SHA512:sha512WithRSAEncryption:"
+                         "1.2.840.113549.1.1.13"),
+    IBMCA_DEF_RSA_SIGALG(sha512_224, SHA2-512/224,
+                         "RSA-SHA2-512/224:RSA-SHA512-224:"
+                         "sha512-224WithRSAEncryption:1.2.840.113549.1.1.15"),
+    IBMCA_DEF_RSA_SIGALG(sha512_256, SHA2-512/256,
+                         "RSA-SHA2-512/256:RSA-SHA512-256:"
+                         "sha512-256WithRSAEncryption:1.2.840.113549.1.1.16"),
+    IBMCA_DEF_RSA_SIGALG(sha3_224, SHA3-224,
+                         "RSA-SHA3-224:id-rsassa-pkcs1-v1_5-with-sha3-224:"
+                         "2.16.840.1.101.3.4.3.13"),
+    IBMCA_DEF_RSA_SIGALG(sha3_256, SHA3-256,
+                         "RSA-SHA3-256:id-rsassa-pkcs1-v1_5-with-sha3-256:"
+                         "2.16.840.1.101.3.4.3.14"),
+    IBMCA_DEF_RSA_SIGALG(sha3_384, SHA3-384,
+                         "RSA-SHA3-384:id-rsassa-pkcs1-v1_5-with-sha3-384:"
+                         "2.16.840.1.101.3.4.3.15"),
+    IBMCA_DEF_RSA_SIGALG(sha3_512, SHA3-512,
+                         "RSA-SHA3-512:id-rsassa-pkcs1-v1_5-with-sha3-512:"
+                         "2.16.840.1.101.3.4.3.16"),
+#endif
+    { NULL, NULL, NULL, NULL }
+};
+
diff -pruN 1.4.0-1/src/provider/rsa_sup_mul.c 2.5.0-0ubuntu1/src/provider/rsa_sup_mul.c
--- 1.4.0-1/src/provider/rsa_sup_mul.c	1970-01-01 00:00:00.000000000 +0000
+++ 2.5.0-0ubuntu1/src/provider/rsa_sup_mul.c	2025-04-16 08:09:24.000000000 +0000
@@ -0,0 +1,662 @@
+/*
+ * Copyright 2022-2023 The OpenSSL Project Authors. All Rights Reserved.
+ * Copyright 2023 International Business Machines Corp.
+ *
+ * Licensed under the Apache License 2.0 (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ *
+ * Copied from OpenSSL crypto/bn/rsa_sup_mul.c and modified to fit to
+ * the providers environment.
+ *
+ * Changes include:
+ * - The intermediate message and the modulus are supplied as byte arrays
+ *   instead of BIGNUMs. The code has been adjusted to handle byte arrays as
+ *   input.
+ * - Remove parameters 'BN_CTX ctx' and 'BN_BLINDING *blinding' since they are
+ *   not used.
+ * - Includes have been adjusted to only include external OpenSSL headers.
+ * - Remove access to internal fields of BN_BLINDING.
+ * - Remove access to internal fields of BIGNUM. Function BN_to_limb() is
+ *   replaced by BN_bn2binpad(). Note that the only BIGNUM is containing the
+ *   unblinding factor. The message (and the modulus) are passed in in byte
+ *   arrays, and thus do not need to be converted.
+ * - This provider only works on Linux on IBM Z, and it's big endian and 64 bit.
+ */
+
+#include <openssl/e_os2.h>
+#include <stddef.h>
+#include <sys/types.h>
+#include <string.h>
+#include <openssl/bn.h>
+#include <openssl/err.h>
+#include <openssl/rsaerr.h>
+#include "constant_time.h"
+
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wunused-function"
+
+#define DECLARE_IS_ENDIAN
+#define IS_LITTLE_ENDIAN     0
+#define IS_BIG_ENDIAN        1
+
+# ifndef INT128_MAX
+#  if defined(__SIZEOF_INT128__) && __SIZEOF_INT128__ == 16
+typedef __int128_t int128_t;
+typedef __uint128_t uint128_t;
+#   define INT128_MIN __MININT__(int128_t)
+#   define INT128_MAX __MAXINT__(int128_t)
+#   define UINT128_MAX __MAXUINT__(uint128_t)
+#  endif
+# endif
+
+# if BN_BYTES == 8
+typedef uint64_t limb_t;
+#  if defined(__SIZEOF_INT128__) && __SIZEOF_INT128__ == 16
+typedef uint128_t limb2_t;
+#   define HAVE_LIMB2_T
+#  endif
+#  define LIMB_BIT_SIZE 64
+#  define LIMB_BYTE_SIZE 8
+# else
+#  error "Not supported"
+# endif
+
+static inline void memcpy_r_allign(void *dest, int dest_bs,
+                                   const void *src, int src_bs, int size)
+{
+    memcpy((unsigned char *)dest + (dest_bs - size),
+           (unsigned char *)src + (src_bs - size), size);
+}
+
+/*
+ * For multiplication we're using schoolbook multiplication,
+ * so if we have two numbers, each with 6 "digits" (words)
+ * the multiplication is calculated as follows:
+ *                        A B C D E F
+ *                     x  I J K L M N
+ *                     --------------
+ *                                N*F
+ *                              N*E
+ *                            N*D
+ *                          N*C
+ *                        N*B
+ *                      N*A
+ *                              M*F
+ *                            M*E
+ *                          M*D
+ *                        M*C
+ *                      M*B
+ *                    M*A
+ *                            L*F
+ *                          L*E
+ *                        L*D
+ *                      L*C
+ *                    L*B
+ *                  L*A
+ *                          K*F
+ *                        K*E
+ *                      K*D
+ *                    K*C
+ *                  K*B
+ *                K*A
+ *                        J*F
+ *                      J*E
+ *                    J*D
+ *                  J*C
+ *                J*B
+ *              J*A
+ *                      I*F
+ *                    I*E
+ *                  I*D
+ *                I*C
+ *              I*B
+ *         +  I*A
+ *         ==========================
+ *                        N*B N*D N*F
+ *                    + N*A N*C N*E
+ *                    + M*B M*D M*F
+ *                  + M*A M*C M*E
+ *                  + L*B L*D L*F
+ *                + L*A L*C L*E
+ *                + K*B K*D K*F
+ *              + K*A K*C K*E
+ *              + J*B J*D J*F
+ *            + J*A J*C J*E
+ *            + I*B I*D I*F
+ *          + I*A I*C I*E
+ *
+ *                1+1 1+3 1+5
+ *              1+0 1+2 1+4
+ *              0+1 0+3 0+5
+ *            0+0 0+2 0+4
+ *
+ *            0 1 2 3 4 5 6
+ * which requires n^2 multiplications and 2n full length additions
+ * as we can keep every other result of limb multiplication in two separate
+ * limbs
+ */
+
+#if defined HAVE_LIMB2_T
+static ossl_inline void _mul_limb(limb_t *hi, limb_t *lo, limb_t a, limb_t b)
+{
+    limb2_t t;
+    /*
+     * this is idiomatic code to tell compiler to use the native mul
+     * those three lines will actually compile to single instruction
+     */
+
+    t = (limb2_t)a * b;
+    *hi = t >> LIMB_BIT_SIZE;
+    *lo = (limb_t)t;
+}
+#elif (BN_BYTES == 8) && (defined _MSC_VER)
+# if defined(_M_X64)
+/*
+ * on x86_64 (x64) we can use the _umul128 intrinsic to get one `mul`
+ * instruction to get both high and low 64 bits of the multiplication.
+ * https://learn.microsoft.com/en-us/cpp/intrinsics/umul128?view=msvc-140
+ */
+#include <intrin.h>
+#pragma intrinsic(_umul128)
+static ossl_inline void _mul_limb(limb_t *hi, limb_t *lo, limb_t a, limb_t b)
+{
+    *lo = _umul128(a, b, hi);
+}
+# elif defined(_M_ARM64) || defined (_M_IA64)
+/*
+ * We can't use the __umulh() on x86_64 as then msvc generates two `mul`
+ * instructions; so use this more portable intrinsic on platforms that
+ * don't support _umul128 (like aarch64 (ARM64) or ia64)
+ * https://learn.microsoft.com/en-us/cpp/intrinsics/umulh?view=msvc-140
+ */
+#include <intrin.h>
+static ossl_inline void _mul_limb(limb_t *hi, limb_t *lo, limb_t a, limb_t b)
+{
+    *lo = a * b;
+    *hi = __umulh(a, b);
+}
+# else
+# error Only x64, ARM64 and IA64 supported.
+# endif /* defined(_M_X64) */
+#else
+/*
+ * if the compiler doesn't have either a 128bit data type nor a "return
+ * high 64 bits of multiplication"
+ */
+static ossl_inline void _mul_limb(limb_t *hi, limb_t *lo, limb_t a, limb_t b)
+{
+    limb_t a_low = (limb_t)(uint32_t)a;
+    limb_t a_hi = a >> 32;
+    limb_t b_low = (limb_t)(uint32_t)b;
+    limb_t b_hi = b >> 32;
+
+    limb_t p0 = a_low * b_low;
+    limb_t p1 = a_low * b_hi;
+    limb_t p2 = a_hi * b_low;
+    limb_t p3 = a_hi * b_hi;
+
+    uint32_t cy = (uint32_t)(((p0 >> 32) + (uint32_t)p1 + (uint32_t)p2) >> 32);
+
+    *lo = p0 + (p1 << 32) + (p2 << 32);
+    *hi = p3 + (p1 >> 32) + (p2 >> 32) + cy;
+}
+#endif
+
+/* add two limbs with carry in, return carry out */
+static ossl_inline limb_t _add_limb(limb_t *ret, limb_t a, limb_t b, limb_t carry)
+{
+    limb_t carry1, carry2, t;
+    /*
+     * `c = a + b; if (c < a)` is idiomatic code that makes compilers
+     * use add with carry on assembly level
+     */
+
+    *ret = a + carry;
+    if (*ret < a)
+        carry1 = 1;
+    else
+        carry1 = 0;
+
+    t = *ret;
+    *ret = t + b;
+    if (*ret < t)
+        carry2 = 1;
+    else
+        carry2 = 0;
+
+    return carry1 + carry2;
+}
+
+/*
+ * add two numbers of the same size, return overflow
+ *
+ * add a to b, place result in ret; all arrays need to be n limbs long
+ * return overflow from addition (0 or 1)
+ */
+static ossl_inline limb_t add(limb_t *ret, limb_t *a, limb_t *b, size_t n)
+{
+    limb_t c = 0;
+    ossl_ssize_t i;
+
+    for(i = n - 1; i > -1; i--)
+        c = _add_limb(&ret[i], a[i], b[i], c);
+
+    return c;
+}
+
+/*
+ * return number of limbs necessary for temporary values
+ * when multiplying numbers n limbs large
+ */
+static ossl_inline size_t mul_limb_numb(size_t n)
+{
+    return  2 * n * 2;
+}
+
+/*
+ * multiply two numbers of the same size
+ *
+ * multiply a by b, place result in ret; a and b need to be n limbs long
+ * ret needs to be 2*n limbs long, tmp needs to be mul_limb_numb(n) limbs
+ * long
+ */
+static void limb_mul(limb_t *ret, limb_t *a, limb_t *b, size_t n, limb_t *tmp)
+{
+    limb_t *r_odd, *r_even;
+    size_t i, j, k;
+
+    r_odd = tmp;
+    r_even = &tmp[2 * n];
+
+    memset(ret, 0, 2 * n * sizeof(limb_t));
+
+    for (i = 0; i < n; i++) {
+        for (k = 0; k < i + n + 1; k++) {
+            r_even[k] = 0;
+            r_odd[k] = 0;
+        }
+        for (j = 0; j < n; j++) {
+            /*
+             * place results from even and odd limbs in separate arrays so that
+             * we don't have to calculate overflow every time we get individual
+             * limb multiplication result
+             */
+            if (j % 2 == 0)
+                _mul_limb(&r_even[i + j], &r_even[i + j + 1], a[i], b[j]);
+            else
+                _mul_limb(&r_odd[i + j], &r_odd[i + j + 1], a[i], b[j]);
+        }
+        /*
+         * skip the least significant limbs when adding multiples of
+         * more significant limbs (they're zero anyway)
+         */
+        add(ret, ret, r_even, n + i + 1);
+        add(ret, ret, r_odd, n + i + 1);
+    }
+}
+
+/* modifies the value in place by performing a right shift by one bit */
+static ossl_inline void rshift1(limb_t *val, size_t n)
+{
+    limb_t shift_in = 0, shift_out = 0;
+    size_t i;
+
+    for (i = 0; i < n; i++) {
+        shift_out = val[i] & 1;
+        val[i] = shift_in << (LIMB_BIT_SIZE - 1) | (val[i] >> 1);
+        shift_in = shift_out;
+    }
+}
+
+/* extend the LSB of flag to all bits of limb */
+static ossl_inline limb_t mk_mask(limb_t flag)
+{
+    flag |= flag << 1;
+    flag |= flag << 2;
+    flag |= flag << 4;
+    flag |= flag << 8;
+    flag |= flag << 16;
+#if (LIMB_BYTE_SIZE == 8)
+    flag |= flag << 32;
+#endif
+    return flag;
+}
+
+/*
+ * copy from either a or b to ret based on flag
+ * when flag == 0, then copies from b
+ * when flag == 1, then copies from a
+ */
+static ossl_inline void cselect(limb_t flag, limb_t *ret, limb_t *a, limb_t *b, size_t n)
+{
+    /*
+     * would be more efficient with non volatile mask, but then gcc
+     * generates code with jumps
+     */
+    volatile limb_t mask;
+    size_t i;
+
+    mask = mk_mask(flag);
+    for (i = 0; i < n; i++) {
+#if (LIMB_BYTE_SIZE == 8)
+        ret[i] = constant_time_select_64(mask, a[i], b[i]);
+#else
+        ret[i] = constant_time_select_32(mask, a[i], b[i]);
+#endif
+    }
+}
+
+static limb_t _sub_limb(limb_t *ret, limb_t a, limb_t b, limb_t borrow)
+{
+    limb_t borrow1, borrow2, t;
+    /*
+     * while it doesn't look constant-time, this is idiomatic code
+     * to tell compilers to use the carry bit from subtraction
+     */
+
+    *ret = a - borrow;
+    if (*ret > a)
+        borrow1 = 1;
+    else
+        borrow1 = 0;
+
+    t = *ret;
+    *ret = t - b;
+    if (*ret > t)
+        borrow2 = 1;
+    else
+        borrow2 = 0;
+
+    return borrow1 + borrow2;
+}
+
+/*
+ * place the result of a - b into ret, return the borrow bit.
+ * All arrays need to be n limbs long
+ */
+static limb_t sub(limb_t *ret, limb_t *a, limb_t *b, size_t n)
+{
+    limb_t borrow = 0;
+    ossl_ssize_t i;
+
+    for (i = n - 1; i > -1; i--)
+        borrow = _sub_limb(&ret[i], a[i], b[i], borrow);
+
+    return borrow;
+}
+
+/* return the number of limbs necessary to allocate for the mod() tmp operand */
+static ossl_inline size_t mod_limb_numb(size_t anum, size_t modnum)
+{
+    return (anum + modnum) * 3;
+}
+
+/*
+ * calculate a % mod, place the result in ret
+ * size of a is defined by anum, size of ret and mod is modnum,
+ * size of tmp is returned by mod_limb_numb()
+ */
+static void mod(limb_t *ret, limb_t *a, size_t anum, limb_t *mod,
+               size_t modnum, limb_t *tmp)
+{
+    limb_t *atmp, *modtmp, *rettmp;
+    limb_t res;
+    size_t i;
+
+    memset(tmp, 0, mod_limb_numb(anum, modnum) * LIMB_BYTE_SIZE);
+
+    atmp = tmp;
+    modtmp = &tmp[anum + modnum];
+    rettmp = &tmp[(anum + modnum) * 2];
+
+    for (i = modnum; i <modnum + anum; i++)
+        atmp[i] = a[i-modnum];
+
+    for (i = 0; i < modnum; i++)
+        modtmp[i] = mod[i];
+
+    for (i = 0; i < anum * LIMB_BIT_SIZE; i++) {
+        rshift1(modtmp, anum + modnum);
+        res = sub(rettmp, atmp, modtmp, anum+modnum);
+        cselect(res, atmp, atmp, rettmp, anum+modnum);
+    }
+
+    memcpy(ret, &atmp[anum], sizeof(limb_t) * modnum);
+}
+
+/* necessary size of tmp for a _mul_add_limb() call with provided anum */
+static ossl_inline size_t _mul_add_limb_numb(size_t anum)
+{
+    return 2 * (anum + 1);
+}
+
+/* multiply a by m, add to ret, return carry */
+static limb_t _mul_add_limb(limb_t *ret, limb_t *a, size_t anum,
+                           limb_t m, limb_t *tmp)
+{
+    limb_t carry = 0;
+    limb_t *r_odd, *r_even;
+    size_t i;
+
+    memset(tmp, 0, sizeof(limb_t) * (anum + 1) * 2);
+
+    r_odd = tmp;
+    r_even = &tmp[anum + 1];
+
+    for (i = 0; i < anum; i++) {
+        /*
+         * place the results from even and odd limbs in separate arrays
+         * so that we have to worry about carry just once
+         */
+        if (i % 2 == 0)
+            _mul_limb(&r_even[i], &r_even[i + 1], a[i], m);
+        else
+            _mul_limb(&r_odd[i], &r_odd[i + 1], a[i], m);
+    }
+    /* assert: add() carry here will be equal zero */
+    add(r_even, r_even, r_odd, anum + 1);
+    /*
+     * while here it will not overflow as the max value from multiplication
+     * is -2 while max overflow from addition is 1, so the max value of
+     * carry is -1 (i.e. max int)
+     */
+    carry = add(ret, ret, &r_even[1], anum) + r_even[0];
+
+    return carry;
+}
+
+static ossl_inline size_t mod_montgomery_limb_numb(size_t modnum)
+{
+    return modnum * 2 + _mul_add_limb_numb(modnum);
+}
+
+/*
+ * calculate a % mod, place result in ret
+ * assumes that a is in Montgomery form with the R (Montgomery modulus) being
+ * smallest power of two big enough to fit mod and that's also a power
+ * of the count of number of bits in limb_t (B).
+ * For calculation, we also need n', such that mod * n' == -1 mod B.
+ * anum must be <= 2 * modnum
+ * ret needs to be modnum words long
+ * tmp needs to be mod_montgomery_limb_numb(modnum) limbs long
+ */
+static void mod_montgomery(limb_t *ret, limb_t *a, size_t anum, limb_t *mod,
+                          size_t modnum, limb_t ni0, limb_t *tmp)
+{
+    limb_t carry, v;
+    limb_t *res, *rp, *tmp2;
+    ossl_ssize_t i;
+
+    res = tmp;
+    /*
+     * for intermediate result we need an integer twice as long as modulus
+     * but keep the input in the least significant limbs
+     */
+    memset(res, 0, sizeof(limb_t) * (modnum * 2));
+    memcpy(&res[modnum * 2 - anum], a, sizeof(limb_t) * anum);
+    rp = &res[modnum];
+    tmp2 = &res[modnum * 2];
+
+    carry = 0;
+
+    /* add multiples of the modulus to the value until R divides it cleanly */
+    for (i = modnum; i > 0; i--, rp--) {
+        v = _mul_add_limb(rp, mod, modnum, rp[modnum-1] * ni0, tmp2);
+        v = v + carry + rp[-1];
+        carry |= (v != rp[-1]);
+        carry &= (v <= rp[-1]);
+        rp[-1] = v;
+    }
+
+    /* perform the final reduction by mod... */
+    carry -= sub(ret, rp, mod, modnum);
+
+    /* ...conditionally */
+    cselect(carry, ret, rp, ret, modnum);
+}
+
+#if LIMB_BYTE_SIZE == 8
+static ossl_inline uint64_t be64(uint64_t host)
+{
+    uint64_t big = 0;
+    DECLARE_IS_ENDIAN;
+
+    if (!IS_LITTLE_ENDIAN)
+        return host;
+
+    big |= (host & 0xff00000000000000) >> 56;
+    big |= (host & 0x00ff000000000000) >> 40;
+    big |= (host & 0x0000ff0000000000) >> 24;
+    big |= (host & 0x000000ff00000000) >>  8;
+    big |= (host & 0x00000000ff000000) <<  8;
+    big |= (host & 0x0000000000ff0000) << 24;
+    big |= (host & 0x000000000000ff00) << 40;
+    big |= (host & 0x00000000000000ff) << 56;
+    return big;
+}
+
+#else
+/* Not all platforms have htobe32(). */
+static ossl_inline uint32_t be32(uint32_t host)
+{
+    uint32_t big = 0;
+    DECLARE_IS_ENDIAN;
+
+    if (!IS_LITTLE_ENDIAN)
+        return host;
+
+    big |= (host & 0xff000000) >> 24;
+    big |= (host & 0x00ff0000) >> 8;
+    big |= (host & 0x0000ff00) << 8;
+    big |= (host & 0x000000ff) << 24;
+    return big;
+}
+#endif
+
+/*
+ * We assume that intermediate and unblind are used similar to
+ * BN_BLINDING_invert_ex() arguments.
+ * to_mod is RSA modulus.
+ * buf and num is the serialization buffer and its length.
+ * num is always the RSA modulus size.
+ *
+ * Here we use classic/Montgomery multiplication and modulo. After the calculation finished
+ * we serialize the new structure instead of BIGNUMs taking endianness into account.
+ */
+int ossl_bn_rsa_do_unblind(const unsigned char *intermediate,
+                           const BIGNUM *unblind,
+                           const unsigned char *to_mod,
+                           unsigned char *buf, int num,
+                           BN_MONT_CTX *m_ctx, BN_ULONG n0)
+{
+    limb_t *l_im = NULL, *l_mul = NULL, *l_mod = NULL;
+    limb_t *l_ret = NULL, *l_tmp = NULL, l_buf;
+    size_t l_im_count = 0, l_mul_count = 0, l_size = 0, l_mod_count = 0;
+    size_t l_tmp_count = 0;
+    int ret = 0;
+    size_t i;
+    unsigned char *tmp;
+
+    l_im_count  = (num   + LIMB_BYTE_SIZE - 1) / LIMB_BYTE_SIZE;
+    l_mul_count = (BN_num_bytes(unblind)   + LIMB_BYTE_SIZE - 1) / LIMB_BYTE_SIZE;
+    l_mod_count = (num + LIMB_BYTE_SIZE - 1) / LIMB_BYTE_SIZE;
+
+    l_size = l_im_count > l_mul_count ? l_im_count : l_mul_count;
+    if (l_size * LIMB_BYTE_SIZE == (size_t)num)
+        l_im = (limb_t *)intermediate;
+    else
+        l_im  = OPENSSL_zalloc(l_size * LIMB_BYTE_SIZE);
+    l_mul = OPENSSL_zalloc(l_size * LIMB_BYTE_SIZE);
+    if (l_mod_count * LIMB_BYTE_SIZE == (size_t)num)
+        l_mod = (limb_t *)to_mod;
+    else
+        l_mod = OPENSSL_zalloc(l_mod_count * LIMB_BYTE_SIZE);
+
+    if ((l_im == NULL) || (l_mul == NULL) || (l_mod == NULL))
+        goto err;
+
+    if (l_im != (limb_t *)intermediate)
+        memcpy_r_allign(l_im, l_size * LIMB_BYTE_SIZE, intermediate, num, num);
+    BN_bn2binpad(unblind, (unsigned char *)l_mul, l_size * LIMB_BYTE_SIZE);
+    if (l_mod != (limb_t *)to_mod)
+        memcpy_r_allign(l_mod, l_mod_count * LIMB_BYTE_SIZE, to_mod, num, num);
+
+    l_ret = OPENSSL_malloc(2 * l_size * LIMB_BYTE_SIZE);
+
+    if (m_ctx != NULL) {
+        l_tmp_count = mul_limb_numb(l_size) > mod_montgomery_limb_numb(l_mod_count) ?
+                      mul_limb_numb(l_size) : mod_montgomery_limb_numb(l_mod_count);
+        l_tmp = OPENSSL_malloc(l_tmp_count * LIMB_BYTE_SIZE);
+    } else {
+        l_tmp_count = mul_limb_numb(l_size) > mod_limb_numb(2 * l_size, l_mod_count) ?
+                      mul_limb_numb(l_size) : mod_limb_numb(2 * l_size, l_mod_count);
+        l_tmp = OPENSSL_malloc(l_tmp_count * LIMB_BYTE_SIZE);
+    }
+
+    if ((l_ret == NULL) || (l_tmp == NULL))
+        goto err;
+
+    if (m_ctx != NULL) {
+        limb_mul(l_ret, l_im, l_mul, l_size, l_tmp);
+        mod_montgomery(l_ret, l_ret, 2 * l_size, l_mod, l_mod_count,
+                       n0, l_tmp);
+    } else {
+        limb_mul(l_ret, l_im, l_mul, l_size, l_tmp);
+        mod(l_ret, l_ret, 2 * l_size, l_mod, l_mod_count, l_tmp);
+    }
+
+    memset(buf, 0, num);
+    tmp = buf;
+    for (i = 0; i < l_mod_count; i++) {
+#if LIMB_BYTE_SIZE == 8
+        l_buf = be64(l_ret[i]);
+#else
+        l_buf = be32(l_ret[i]);
+#endif
+        if (i == 0) {
+            int delta = LIMB_BYTE_SIZE - ((l_mod_count * LIMB_BYTE_SIZE) - num);
+
+            memcpy(tmp, ((char *)&l_buf) + LIMB_BYTE_SIZE - delta, delta);
+            tmp += delta;
+        } else {
+            memcpy(tmp, &l_buf, LIMB_BYTE_SIZE);
+            tmp += LIMB_BYTE_SIZE;
+        }
+    }
+    ret = num;
+
+ err:
+    if (l_im != (limb_t *)intermediate)
+        OPENSSL_free(l_im);
+    OPENSSL_free(l_mul);
+    if (l_mod != (limb_t *)to_mod)
+        OPENSSL_free(l_mod);
+    OPENSSL_free(l_tmp);
+    OPENSSL_free(l_ret);
+
+    return ret;
+}
+
+#pragma GCC diagnostic pop
diff -pruN 1.4.0-1/src/test/Makefile.linux 2.5.0-0ubuntu1/src/test/Makefile.linux
--- 1.4.0-1/src/test/Makefile.linux	2017-09-08 17:54:06.000000000 +0000
+++ 2.5.0-0ubuntu1/src/test/Makefile.linux	1970-01-01 00:00:00.000000000 +0000
@@ -1,14 +0,0 @@
-PTS = -O0 -g -Wall -fprofile-arcs -ftest-coverage -fPIC
-#OPTS = -O0 -g -Wall -m31 -D_LINUX_S390_
-OPTS = -O0 -g -Wall -D_LINUX_S390_ -std=gnu99
-
-TARGETS = ibmca_mechaList_test
-
-all: $(TARGETS)
-
-# Every target is created from a single .c file.
-%: %.c
-	gcc $(OPTS) -lica -lcrypto -o $@ $^
-
-clean:
-	rm -f $(TARGETS)
diff -pruN 1.4.0-1/src/test/ibmca_mechaList_test.c 2.5.0-0ubuntu1/src/test/ibmca_mechaList_test.c
--- 1.4.0-1/src/test/ibmca_mechaList_test.c	2017-09-08 17:54:06.000000000 +0000
+++ 2.5.0-0ubuntu1/src/test/ibmca_mechaList_test.c	1970-01-01 00:00:00.000000000 +0000
@@ -1,372 +0,0 @@
-/*
- * Copyright [2015-2017] International Business Machines Corp.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
-
-#include <openssl/engine.h>
-#include <ica_api.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <assert.h>
-#include <getopt.h>
-#include <dirent.h>
-#include <string.h>
-#include <stdbool.h>
-
-#define CIPH 1
-#define DIG  2
-#define SET 1
-#define UNSET 0
-
-
-typedef struct{
-	int nid;
-	int ica_id;
-	int dig_ciph;
-} id_map;
-
-#define AP_PATH  "/sys/devices/ap"
-#define IBMCA_PATH "/usr/lib64/openssl/engines/libibmca.so"
-
-
-id_map ica_to_ssl_map[] = {
-#ifndef OPENSSL_NO_SHA1
-	{NID_sha1, SHA1, DIG},
-#endif
-#ifndef OPENSSL_NO_SHA256
-        {NID_sha256, SHA256, DIG},
-#endif
-#ifndef OPENSSL_NO_SHA512
-	{NID_sha512, SHA512, DIG},
-#endif
-        {NID_des_ecb, DES_ECB, CIPH},
-        {NID_des_cbc, DES_CBC, CIPH},
-        {NID_des_ofb64, DES_OFB, CIPH},
-        {NID_des_cfb64, DES_CFB, CIPH},
-        {NID_des_ede3_ecb, DES3_ECB, CIPH},
-        {NID_des_ede3_cbc, DES3_CBC, CIPH},
-        {NID_des_ede3_ofb64, DES3_OFB, CIPH},
-        {NID_des_ede3_cfb64, DES3_CFB, CIPH},
-        {NID_aes_128_ecb, AES_ECB, CIPH},
-        {NID_aes_192_ecb, AES_ECB, CIPH},
-        {NID_aes_256_ecb, AES_ECB, CIPH},
-        {NID_aes_128_cbc, AES_CBC, CIPH},
-        {NID_aes_192_cbc, AES_CBC, CIPH},
-        {NID_aes_256_cbc, AES_CBC, CIPH},
-        {NID_aes_128_ofb128, AES_OFB, CIPH},
-        {NID_aes_192_ofb128, AES_OFB, CIPH},
-        {NID_aes_256_ofb128, AES_OFB, CIPH},
-        {NID_aes_128_cfb128, AES_CFB, CIPH},
-        {NID_aes_192_cfb128, AES_CFB, CIPH},
-        {NID_aes_256_cfb128, AES_CFB, CIPH},
-        {0, 0, 0}
-};
-
-ENGINE *eng;
-int failure = 0;
-
-int init_engine(char *id)
-{
-	ENGINE_load_builtin_engines();
-        eng = ENGINE_by_id("dynamic");
-	if(!eng){
-		return 1;
-	return 1;
-	}
-        if(!ENGINE_ctrl_cmd_string(eng, "SO_PATH", id, 0)){
-                return 1;
-        }
-        if (!ENGINE_ctrl_cmd_string(eng, "LOAD", NULL, 0)){
-                return 1;
-        }
-        if (!ENGINE_init(eng)){
-                return 1;
-	}
-	if(!ENGINE_set_default_RSA(eng))
-		return 1;
-        ENGINE_set_default_DSA(eng);
-	ENGINE_set_default_RAND(eng);
-	ENGINE_set_default_DH(eng);
-        ENGINE_set_default_ciphers(eng);
-        ENGINE_set_default_digests(eng);
-
-	return 0;
-}
-
-void exit_engine()
-{
-        /* Release the functional reference from ENGINE_init() */
-        ENGINE_finish(eng);
-        /* Release the structural reference from ENGINE_by_id() */
-        ENGINE_free(eng);
-}
-
-void nid_failure(int nid, int set)
-{
-	failure++;
-	if(set == SET){
-		fprintf(stderr, "ERROR: NID %d not set in Engine!\n", nid);
-	} else if(set == UNSET)
-		fprintf(stderr, "ERROR: NID %d set despite missing hardware support!", nid);
-}
-
-int is_crypto_card_loaded()
-{
-        DIR* sysDir;
-        FILE *file;
-        char dev[PATH_MAX] = AP_PATH;
-        struct dirent *direntp;
-        char *type = NULL;
-        size_t size;
-        char c;
-
-        if ((sysDir = opendir(dev)) == NULL )
-                return 0;
-
-        while((direntp = readdir(sysDir)) != NULL){
-                if(strstr(direntp->d_name, "card") != 0){
-                        snprintf(dev, PATH_MAX, "%s/%s/type", AP_PATH,
-                                 direntp->d_name);
-
-                        if ((file = fopen(dev, "r")) == NULL){
-                                closedir(sysDir);
-                                return 0;
-                        }
-
-                        if (getline(&type, &size, file) == -1){
-                                fclose(file);
-                                closedir(sysDir);
-                                return 0;
-                        }
-
-                        /* ignore \n
-                         * looking for CEX??A and CEX??C
-                         * Skip type CEX??P cards
-                         */
-                        if (type[strlen(type)-2] == 'P'){
-                                free(type);
-                                type = NULL;
-                                fclose(file);
-                                continue;
-                        }
-                        free(type);
-                        type = NULL;
-                        fclose(file);
-
-                        snprintf(dev, PATH_MAX, "%s/%s/online", AP_PATH,
-                                direntp->d_name);
-
-                        if ((file = fopen(dev, "r")) == NULL){
-                                closedir(sysDir);
-                                return 0;
-                        }
-                        if((c = fgetc(file)) == '1'){
-                                fclose(file);
-                                return 1;
-                        }
-                        fclose(file);
-                }
-        }
-        closedir(sysDir);
-        return 0;
-}
-
-void check_mech(int i, int j, libica_func_list_element *pmech_list)
-{
-	if(!(pmech_list[j].flags & (ICA_FLAG_SHW))){
-		if(ica_to_ssl_map[i].dig_ciph == CIPH){
-			if(ENGINE_get_cipher_engine(ica_to_ssl_map[i].nid)){
-				nid_failure(ica_to_ssl_map[i].nid, UNSET);
-			} else{
-				printf("NID %d not found! SUCCESS\n",
-				       ica_to_ssl_map[i].nid);
-			}
-		} else{
-			if(ENGINE_get_digest_engine(ica_to_ssl_map[i].nid)){
-				nid_failure(ica_to_ssl_map[i].nid, UNSET);
-			} else{
-				printf("NID %d not found! SUCCESS\n",
-				       ica_to_ssl_map[i].nid);
-			}
-		}
-        } else{
-		if(ica_to_ssl_map[i].dig_ciph == CIPH){
-			if(!ENGINE_get_cipher_engine(ica_to_ssl_map[i].nid)){
-				nid_failure(ica_to_ssl_map[i].nid, SET);
-			} else{
-				printf("NID %d found! SUCCESS %d\n",
-				       ica_to_ssl_map[i].nid, j);
-			}
-		} else{
-			if(!ENGINE_get_digest_engine(ica_to_ssl_map[i].nid)){
-				nid_failure(ica_to_ssl_map[i].nid, SET);
-			} else{
-				printf("NID %d found! SUCCESS %d\n",
-                                       ica_to_ssl_map[i].nid, j);
-			}
-		}
-	}
-}
-
-int main (int argc, char *argv[])
-{
-	int i, j, opt, option_index = 0;
-	int card_loaded;
-	unsigned int mech_len;
-	bool found = false;
-	libica_func_list_element *pmech_list = NULL;
-	char *engine_id = IBMCA_PATH;
-	struct option long_options[] = {
-                   {"help", no_argument, 0, 'h'},
-		   {"file", required_argument, 0, 'f'},
-		   {0 ,0 ,0, 0}
-	};
-
-	while ((opt = getopt_long(argc, argv, "hf:",
-				long_options, &option_index)) != -1) {
-               switch (opt) {
-               case 'f':
-                   engine_id = optarg;
-                   break;
-               case 'h':
-			printf("This test checks with the engine API of libcrypto if a\n"
-                          "crypto mechanism is enabled or not.\n"
-                          "If one mechanism is not found the NID is returned.\n"
-                          "The NID can be mapped to a name in the file \n"
-                          "/usr/include/openssl/obj_mac.h\n");
-
-                   printf("Usage: %s [-f | --file ibmca.so] [-h | --help]\n",
-                           argv[0]);
-			exit(EXIT_SUCCESS);
-               default: /* '?' */
-                   fprintf(stderr, "Usage: %s [-t nsecs] [-n] name\n",
-                           argv[0]);
-                   exit(EXIT_FAILURE);
-               }
-	}
-
-	printf("This test checks with the engine API of libcrypto if a\n"
-	       "crypto mechanism is enabled or not.\n"
-	       "If one mechanism is not found the NID is returned.\n"
-	       "The NID can be mapped to a name in the file \n"
-	       "/usr/include/openssl/obj_mac.h\n");
-	printf("IBMCA path: %s\n", engine_id);
-	printf("--------------------------------------------------------------\n\n");
-
-	if(init_engine(engine_id)){
-		fprintf(stderr, "Could not initialize Ibmca engine\n");
-		return EXIT_FAILURE;
-	}
-	if (ica_get_functionlist(NULL, &mech_len) != 0){
-		perror("get_functionlist");
-		return EXIT_FAILURE;
-	}
-	pmech_list = malloc(sizeof(libica_func_list_element)*mech_len);
-	if (ica_get_functionlist(pmech_list, &mech_len) != 0){
-		perror("get_functionlist");
-		free(pmech_list);
-		return EXIT_FAILURE;
-	}
-
-	card_loaded = is_crypto_card_loaded();
-        for(i=0;ica_to_ssl_map[i].nid;i++){
-		for(j=0;j<mech_len;j++){
-
-                        if(ica_to_ssl_map[i].ica_id == pmech_list[j].mech_mode_id){
-				found=true;
-				check_mech(i, j, pmech_list);
-				break;
-			}
-		}
-			assert(found);
-			found = false;
-
-	}
-
-	for(i=0;i<mech_len;i++){
-		if(pmech_list[i].mech_mode_id == P_RNG){
-			if(pmech_list[i].flags & (ICA_FLAG_SHW)){
-				if(!ENGINE_get_default_RAND()){
-					failure++;
-					fprintf(stderr, "ERROR: Engine has no enabled PRNG support!\n");
-				} else{
-					printf("PRNG Support found! SUCCESS\n");
-				}
-			} else{
-                               if(ENGINE_get_default_RAND()){
-                                        failure++;
-                                        fprintf(stderr, "ERROR: Engine has enabled PRNG support"
-							", despite no hardware support!\n");
-                                } else{
-					printf("PRNG Support not found! SUCCESS\n");
-				}
-			}
-
-		}
-		if(pmech_list[i].mech_mode_id == RSA_ME){
-			if(card_loaded){
-				if(!ENGINE_get_default_RSA()){
-					failure++;
-					fprintf(stderr, "ERROR: Engine has no enabeled RSA support!\n");
-				} else{
-					printf("RSA Support found! SUCCESS\n");
-				}
-				if(!ENGINE_get_default_DSA()){
-					failure++;
-					fprintf(stderr, "ERROR: Engine has no enabled DSA support!\n");
-				} else{
-					printf("DSA Support found! SUCCESS\n");
-				}
-                                if(!ENGINE_get_default_DH()){
-                                        failure++;
-                                        fprintf(stderr, "ERROR: Engine has no enabled DH support!\n");
-                                } else{
-					printf("DH Support found! SUCCESS\n");
-				}
-
-			} else{
-				if(ENGINE_get_default_RSA()){
-                                        failure++;
-                                        fprintf(stderr, "ERROR: Engine has enabled RSA support,"
-							"despite no hardware support!\n");
-                                } else{
-                                        printf("RSA Support not found! SUCCESS\n");
-                                }
-                                if(ENGINE_get_default_DSA()){
-                                        failure++;
-                                        fprintf(stderr, "ERROR: Engine has no enabled DSA support,"
-							"despite no hardware support!\n");
-                                } else{
-                                        printf("DSA Support not found! SUCCESS\n");
-                                }
-                                if(ENGINE_get_default_DH()){
-                                        failure++;
-                                        fprintf(stderr, "ERROR: Engine has no enabled DH support,"
-                                                        "despite no hardware support!\n");
-                                } else{
-                                        printf("DH Support not found! SUCCESS\n");
-                                }
-			}
-		}
-	}
-
-	printf("\n\n--------------------------------------------------------------\n");
-	printf("TEST Summary:\n"
-	       "Failure Counter:  %d\n", failure);
-	if(failure)
-		return EXIT_FAILURE;
-	else
-		return EXIT_SUCCESS;
-}
diff -pruN 1.4.0-1/test/Makefile.am 2.5.0-0ubuntu1/test/Makefile.am
--- 1.4.0-1/test/Makefile.am	1970-01-01 00:00:00.000000000 +0000
+++ 2.5.0-0ubuntu1/test/Makefile.am	2025-04-16 08:09:24.000000000 +0000
@@ -0,0 +1,7 @@
+SUBDIRS=
+if IBMCA_ENGINE
+SUBDIRS += engine
+endif
+if IBMCA_PROVIDER
+SUBDIRS += provider
+endif
diff -pruN 1.4.0-1/test/engine/3des-cbc-test.pl 2.5.0-0ubuntu1/test/engine/3des-cbc-test.pl
--- 1.4.0-1/test/engine/3des-cbc-test.pl	1970-01-01 00:00:00.000000000 +0000
+++ 2.5.0-0ubuntu1/test/engine/3des-cbc-test.pl	2025-04-16 08:09:24.000000000 +0000
@@ -0,0 +1,7 @@
+#!/usr/bin/env perl
+
+use strict;
+use warnings;
+use test;
+
+test::cipher("des-ede3-cbc", 24, 8);
diff -pruN 1.4.0-1/test/engine/3des-cfb-test.pl 2.5.0-0ubuntu1/test/engine/3des-cfb-test.pl
--- 1.4.0-1/test/engine/3des-cfb-test.pl	1970-01-01 00:00:00.000000000 +0000
+++ 2.5.0-0ubuntu1/test/engine/3des-cfb-test.pl	2025-04-16 08:09:24.000000000 +0000
@@ -0,0 +1,7 @@
+#!/usr/bin/env perl
+
+use strict;
+use warnings;
+use test;
+
+test::cipher("des-ede3-cfb", 24, 8);
diff -pruN 1.4.0-1/test/engine/3des-ecb-test.pl 2.5.0-0ubuntu1/test/engine/3des-ecb-test.pl
--- 1.4.0-1/test/engine/3des-ecb-test.pl	1970-01-01 00:00:00.000000000 +0000
+++ 2.5.0-0ubuntu1/test/engine/3des-ecb-test.pl	2025-04-16 08:09:24.000000000 +0000
@@ -0,0 +1,8 @@
+#!/usr/bin/env perl
+
+use strict;
+use warnings;
+use test;
+
+# No iv needed, but openssl app version 3 somehow requires one...
+test::cipher("des-ede3", 24, test::osslversion3 ? 8 : 0);
diff -pruN 1.4.0-1/test/engine/3des-ofb-test.pl 2.5.0-0ubuntu1/test/engine/3des-ofb-test.pl
--- 1.4.0-1/test/engine/3des-ofb-test.pl	1970-01-01 00:00:00.000000000 +0000
+++ 2.5.0-0ubuntu1/test/engine/3des-ofb-test.pl	2025-04-16 08:09:24.000000000 +0000
@@ -0,0 +1,7 @@
+#!/usr/bin/env perl
+
+use strict;
+use warnings;
+use test;
+
+test::cipher("des-ede3-ofb", 24, 8);
diff -pruN 1.4.0-1/test/engine/Makefile.am 2.5.0-0ubuntu1/test/engine/Makefile.am
--- 1.4.0-1/test/engine/Makefile.am	1970-01-01 00:00:00.000000000 +0000
+++ 2.5.0-0ubuntu1/test/engine/Makefile.am	2025-04-16 08:09:24.000000000 +0000
@@ -0,0 +1,83 @@
+TESTS_CIPHERS = \
+des-ecb-test.pl \
+des-cbc-test.pl \
+des-cfb-test.pl \
+des-ofb-test.pl \
+3des-ecb-test.pl \
+3des-cbc-test.pl \
+3des-cfb-test.pl \
+3des-ofb-test.pl \
+aes-128-ecb-test.pl \
+aes-128-cbc-test.pl \
+aes-128-cfb-test.pl \
+aes-128-ofb-test.pl \
+aes-192-ecb-test.pl \
+aes-192-cbc-test.pl \
+aes-192-cfb-test.pl \
+aes-192-ofb-test.pl \
+aes-256-ecb-test.pl \
+aes-256-cbc-test.pl \
+aes-256-cfb-test.pl \
+aes-256-ofb-test.pl
+
+if FULL_LIBICA
+TESTS_CIPHERS_EXEC=${TESTS_CIPHERS}
+endif
+
+TESTS_PERL = \
+rsa2k.pl 	\
+rsa4k.pl 	\
+rsa8k.pl	\
+rsa16k.pl	\
+dsa2k.pl	\
+dsa4k.pl	\
+dsa8k.pl	\
+dsamax.pl
+
+TESTS = ${TESTS_PERL} \
+${TESTS_CIPHERS_EXEC} \
+loadtest \
+loadtest-ec \
+threadtest \
+libica-link.sh \
+eckey \
+enginectrl
+
+check_PROGRAMS = loadtest loadtest-ec threadtest eckey enginectrl
+check_SCRIPTS = libica-link.sh
+
+loadtest_SOURCES = loadtest.c
+loadtest_LDADD = -lssl -lcrypto
+
+loadtest_ec_SOURCES = loadtest-ec.c
+loadtest_ec_LDADD = -lssl -lcrypto
+
+threadtest_SOURCES = threadtest.c
+threadtest_LDADD = -lcrypto -lpthread
+
+eckey_SOURCES = eckey.c
+eckey_LDADD = -lcrypto
+
+enginectrl_SOURCES = enginectrl.c
+enginectrl_LDADD = -lcrypto -ldl
+
+AM_TESTS_ENVIRONMENT = export IBMCA_TEST_PATH=${top_builddir}/src/engine/.libs/ibmca.so IBMCA_OPENSSL_TEST_CONF=${srcdir}/openssl-test.cnf IBMCA_OPENSSL_TEST_NOINIT_CONF=${srcdir}/openssl-test-noinit.cnf PERL5LIB=${srcdir};
+EXTRA_DIST = ${TESTS_PERL} ${TESTS_CIPHERS} test.pm openssl-test.cnf 	\
+	openssl-test-noinit.cnf dsa2k.key dsa2k_pub.key dsa4k.key	\
+	dsa4k_pub.key dsa8k.key dsa8k_pub.key dsamax.key dsamax_pub.key	\
+	rsa2k.key rsa4k.key rsa8k.key rsa16k.key dsaparam2k.key		\
+	dsaparam4k.key dsaparam8k.key dsaparammax.key
+
+libica-link.sh:
+	@echo '#!/bin/bash' > libica-link.sh
+	@echo "IBMCA_SO=\"${abs_top_builddir}/src/engine/.libs/ibmca.so\"" > libica-link.sh
+	@echo "if ldd \"\$${IBMCA_SO}\" | grep -q libica; then" >> libica-link.sh
+	@echo "    echo \"Linking against libica detected\"" >> libica-link.sh
+	@echo "    echo \"Check these ica symbols:\"" >> libica-link.sh
+	@echo "    nm --undefined-only \"\$${IBMCA_SO}\" | grep 'ica_'" >> libica-link.sh
+	@echo "    exit 99" >> libica-link.sh
+	@echo "fi" >> libica-link.sh
+	@echo "exit 0" >> libica-link.sh
+	@chmod u+x libica-link.sh
+
+CLEANFILES = *.out *.dec libica-link.sh
diff -pruN 1.4.0-1/test/engine/aes-128-cbc-test.pl 2.5.0-0ubuntu1/test/engine/aes-128-cbc-test.pl
--- 1.4.0-1/test/engine/aes-128-cbc-test.pl	1970-01-01 00:00:00.000000000 +0000
+++ 2.5.0-0ubuntu1/test/engine/aes-128-cbc-test.pl	2025-04-16 08:09:24.000000000 +0000
@@ -0,0 +1,7 @@
+#!/usr/bin/env perl
+
+use strict;
+use warnings;
+use test;
+
+test::cipher("aes-128-cbc", 16, 16);
diff -pruN 1.4.0-1/test/engine/aes-128-cfb-test.pl 2.5.0-0ubuntu1/test/engine/aes-128-cfb-test.pl
--- 1.4.0-1/test/engine/aes-128-cfb-test.pl	1970-01-01 00:00:00.000000000 +0000
+++ 2.5.0-0ubuntu1/test/engine/aes-128-cfb-test.pl	2025-04-16 08:09:24.000000000 +0000
@@ -0,0 +1,7 @@
+#!/usr/bin/env perl
+
+use strict;
+use warnings;
+use test;
+
+test::cipher("aes-128-cfb", 16, 16);
diff -pruN 1.4.0-1/test/engine/aes-128-ecb-test.pl 2.5.0-0ubuntu1/test/engine/aes-128-ecb-test.pl
--- 1.4.0-1/test/engine/aes-128-ecb-test.pl	1970-01-01 00:00:00.000000000 +0000
+++ 2.5.0-0ubuntu1/test/engine/aes-128-ecb-test.pl	2025-04-16 08:09:24.000000000 +0000
@@ -0,0 +1,7 @@
+#!/usr/bin/env perl
+
+use strict;
+use warnings;
+use test;
+
+test::cipher("aes-128-ecb", 16, 0);
diff -pruN 1.4.0-1/test/engine/aes-128-ofb-test.pl 2.5.0-0ubuntu1/test/engine/aes-128-ofb-test.pl
--- 1.4.0-1/test/engine/aes-128-ofb-test.pl	1970-01-01 00:00:00.000000000 +0000
+++ 2.5.0-0ubuntu1/test/engine/aes-128-ofb-test.pl	2025-04-16 08:09:24.000000000 +0000
@@ -0,0 +1,7 @@
+#!/usr/bin/env perl
+
+use strict;
+use warnings;
+use test;
+
+test::cipher("aes-128-ofb", 16, 16);
diff -pruN 1.4.0-1/test/engine/aes-192-cbc-test.pl 2.5.0-0ubuntu1/test/engine/aes-192-cbc-test.pl
--- 1.4.0-1/test/engine/aes-192-cbc-test.pl	1970-01-01 00:00:00.000000000 +0000
+++ 2.5.0-0ubuntu1/test/engine/aes-192-cbc-test.pl	2025-04-16 08:09:24.000000000 +0000
@@ -0,0 +1,7 @@
+#!/usr/bin/env perl
+
+use strict;
+use warnings;
+use test;
+
+test::cipher("aes-192-cbc", 24, 16);
diff -pruN 1.4.0-1/test/engine/aes-192-cfb-test.pl 2.5.0-0ubuntu1/test/engine/aes-192-cfb-test.pl
--- 1.4.0-1/test/engine/aes-192-cfb-test.pl	1970-01-01 00:00:00.000000000 +0000
+++ 2.5.0-0ubuntu1/test/engine/aes-192-cfb-test.pl	2025-04-16 08:09:24.000000000 +0000
@@ -0,0 +1,7 @@
+#!/usr/bin/env perl
+
+use strict;
+use warnings;
+use test;
+
+test::cipher("aes-192-cfb", 24, 16);
diff -pruN 1.4.0-1/test/engine/aes-192-ecb-test.pl 2.5.0-0ubuntu1/test/engine/aes-192-ecb-test.pl
--- 1.4.0-1/test/engine/aes-192-ecb-test.pl	1970-01-01 00:00:00.000000000 +0000
+++ 2.5.0-0ubuntu1/test/engine/aes-192-ecb-test.pl	2025-04-16 08:09:24.000000000 +0000
@@ -0,0 +1,7 @@
+#!/usr/bin/env perl
+
+use strict;
+use warnings;
+use test;
+
+test::cipher("aes-192-ecb", 24, 0);
diff -pruN 1.4.0-1/test/engine/aes-192-ofb-test.pl 2.5.0-0ubuntu1/test/engine/aes-192-ofb-test.pl
--- 1.4.0-1/test/engine/aes-192-ofb-test.pl	1970-01-01 00:00:00.000000000 +0000
+++ 2.5.0-0ubuntu1/test/engine/aes-192-ofb-test.pl	2025-04-16 08:09:24.000000000 +0000
@@ -0,0 +1,7 @@
+#!/usr/bin/env perl
+
+use strict;
+use warnings;
+use test;
+
+test::cipher("aes-192-ofb", 24, 16);
diff -pruN 1.4.0-1/test/engine/aes-256-cbc-test.pl 2.5.0-0ubuntu1/test/engine/aes-256-cbc-test.pl
--- 1.4.0-1/test/engine/aes-256-cbc-test.pl	1970-01-01 00:00:00.000000000 +0000
+++ 2.5.0-0ubuntu1/test/engine/aes-256-cbc-test.pl	2025-04-16 08:09:24.000000000 +0000
@@ -0,0 +1,7 @@
+#!/usr/bin/env perl
+
+use strict;
+use warnings;
+use test;
+
+test::cipher("aes-256-cbc", 32, 16);
diff -pruN 1.4.0-1/test/engine/aes-256-cfb-test.pl 2.5.0-0ubuntu1/test/engine/aes-256-cfb-test.pl
--- 1.4.0-1/test/engine/aes-256-cfb-test.pl	1970-01-01 00:00:00.000000000 +0000
+++ 2.5.0-0ubuntu1/test/engine/aes-256-cfb-test.pl	2025-04-16 08:09:24.000000000 +0000
@@ -0,0 +1,7 @@
+#!/usr/bin/env perl
+
+use strict;
+use warnings;
+use test;
+
+test::cipher("aes-256-cfb", 32, 16);
diff -pruN 1.4.0-1/test/engine/aes-256-ecb-test.pl 2.5.0-0ubuntu1/test/engine/aes-256-ecb-test.pl
--- 1.4.0-1/test/engine/aes-256-ecb-test.pl	1970-01-01 00:00:00.000000000 +0000
+++ 2.5.0-0ubuntu1/test/engine/aes-256-ecb-test.pl	2025-04-16 08:09:24.000000000 +0000
@@ -0,0 +1,7 @@
+#!/usr/bin/env perl
+
+use strict;
+use warnings;
+use test;
+
+test::cipher("aes-256-ecb", 32, 0);
diff -pruN 1.4.0-1/test/engine/aes-256-ofb-test.pl 2.5.0-0ubuntu1/test/engine/aes-256-ofb-test.pl
--- 1.4.0-1/test/engine/aes-256-ofb-test.pl	1970-01-01 00:00:00.000000000 +0000
+++ 2.5.0-0ubuntu1/test/engine/aes-256-ofb-test.pl	2025-04-16 08:09:24.000000000 +0000
@@ -0,0 +1,7 @@
+#!/usr/bin/env perl
+
+use strict;
+use warnings;
+use test;
+
+test::cipher("aes-256-ofb", 32, 16);
diff -pruN 1.4.0-1/test/engine/des-cbc-test.pl 2.5.0-0ubuntu1/test/engine/des-cbc-test.pl
--- 1.4.0-1/test/engine/des-cbc-test.pl	1970-01-01 00:00:00.000000000 +0000
+++ 2.5.0-0ubuntu1/test/engine/des-cbc-test.pl	2025-04-16 08:09:24.000000000 +0000
@@ -0,0 +1,9 @@
+#!/usr/bin/env perl
+
+use strict;
+use warnings;
+use test;
+
+# Version 3 only supports DES in legacy mode which might not be available.
+test::osslversion1 || exit (77);
+test::cipher("des-cbc", 8, 8);
diff -pruN 1.4.0-1/test/engine/des-cfb-test.pl 2.5.0-0ubuntu1/test/engine/des-cfb-test.pl
--- 1.4.0-1/test/engine/des-cfb-test.pl	1970-01-01 00:00:00.000000000 +0000
+++ 2.5.0-0ubuntu1/test/engine/des-cfb-test.pl	2025-04-16 08:09:24.000000000 +0000
@@ -0,0 +1,9 @@
+#!/usr/bin/env perl
+
+use strict;
+use warnings;
+use test;
+
+# Version 3 only supports DES in legacy mode which might not be available.
+test::osslversion1 || exit (77);
+test::cipher("des-cfb", 8, 8);
diff -pruN 1.4.0-1/test/engine/des-ecb-test.pl 2.5.0-0ubuntu1/test/engine/des-ecb-test.pl
--- 1.4.0-1/test/engine/des-ecb-test.pl	1970-01-01 00:00:00.000000000 +0000
+++ 2.5.0-0ubuntu1/test/engine/des-ecb-test.pl	2025-04-16 08:09:24.000000000 +0000
@@ -0,0 +1,9 @@
+#!/usr/bin/env perl
+
+use strict;
+use warnings;
+use test;
+
+# Version 3 only supports DES in legacy mode which might not be available.
+test::osslversion1 || exit (77);
+test::cipher("des-ecb", 8, 0);
diff -pruN 1.4.0-1/test/engine/des-ofb-test.pl 2.5.0-0ubuntu1/test/engine/des-ofb-test.pl
--- 1.4.0-1/test/engine/des-ofb-test.pl	1970-01-01 00:00:00.000000000 +0000
+++ 2.5.0-0ubuntu1/test/engine/des-ofb-test.pl	2025-04-16 08:09:24.000000000 +0000
@@ -0,0 +1,9 @@
+#!/usr/bin/env perl
+
+use strict;
+use warnings;
+use test;
+
+# Version 3 only supports DES in legacy mode which might not be available.
+test::osslversion1 || exit (77);
+test::cipher("des-ofb", 8, 8);
diff -pruN 1.4.0-1/test/engine/dsa2k.key 2.5.0-0ubuntu1/test/engine/dsa2k.key
--- 1.4.0-1/test/engine/dsa2k.key	1970-01-01 00:00:00.000000000 +0000
+++ 2.5.0-0ubuntu1/test/engine/dsa2k.key	2025-04-16 08:09:24.000000000 +0000
@@ -0,0 +1,20 @@
+-----BEGIN DSA PRIVATE KEY-----
+MIIDVQIBAAKCAQEA9PsXA/O3soomi8tuZOLnjLSH8lBkpfSvOsnrKjegF/wGafTp
+AIwxucPyRvC3B03lI6IHH/pTH42/Lbkk46Qnm7nLOQKkBq5SOvx4DecZfewtNXvP
+Z4KcqrFCs/xjR+qHJ10HntcHBW7yKzoNX7Q2TXj78FUSIQe0f4PCaiHJwMgzibcJ
+UBfOxegjY38/MQkTGyaOEzFwTrcTCO6O5xokhTmqbPP3Zw2FOqEwcm7vnCXmUU+f
+fE5Eb6HIpUYOy947A6hEHz4mbFAPx3/IqslKICegqmu6sVtWAVVPhn7BCbjvNFZT
+znuW3nkp7bGoeOG1q6ssM096OxLs9MZS7aGeOwIhAInYRUBxrHUZ3ji+E3MflNjw
+T7pWQV50gthrPe8k+Ov/AoIBAQCcqYFWlR3hVHtlLtewB85Wo/vbyq/NvilF+RyT
+q6Q0U0R+9+1E/wmoBvhRpiyYh75fDVD838rVq8FeCdk3drdZ0PlvnReLQN25AJeO
+I5uNYWIjAlPwhpsYUA9f/GaD6zJPldCv9xCHeB7KyJwl2L9bcUFs+S0S8IK3Gh2l
+guURh4byBcPSKUmnZf0EELXe2tf/U+wNd0xJh/inRnFfZ6LgxY3KTun5Xv7efEQh
+CaXEaSfQDqEDaAfC1VVhJVF+jKTNaqwpwWWe+OWj83IseFOFOyRGz2YrlEFcPDMU
+bG20FocKan8RjKDAmo8zBD15Ieid9NceJzujetJF8VFGHVd4AoIBAA9Qm42Yz7KA
+lkTAjVgRsx6x8+Rf6Yc1I46hRd0Ei1qBVxDec95I3YW+zR2eMJPQbjr5uN2/eLNv
+Dv7Ud4NQfAz0mQOHSJen9OjqZagpYWOe6uYmshHkQPwO15TGsxjdfE4MLGk/1dO8
+pUowsMYomCah6QQQhGQSHJaKRx7/2AYOulcsSNdDWLKE0S7uud1OgTynIigCv1q5
+vc/u1gM7euE5RMsOofsunBOjP6vRmF9B2I2uveTas4oiCfZSjHtY0gfDCutGx2US
+FgpWVZLNEiNDQ49Gb4pQafp4QyBa8N9/MFE8L5BfrvXaEchH/phYCt6uLHpf8OzF
+r8Zg499aMAwCH12/vkIjCtnRI3k85QuQEITQr6ADFQx2K6IOT2vsIk8=
+-----END DSA PRIVATE KEY-----
diff -pruN 1.4.0-1/test/engine/dsa2k.pl 2.5.0-0ubuntu1/test/engine/dsa2k.pl
--- 1.4.0-1/test/engine/dsa2k.pl	1970-01-01 00:00:00.000000000 +0000
+++ 2.5.0-0ubuntu1/test/engine/dsa2k.pl	2025-04-16 08:09:24.000000000 +0000
@@ -0,0 +1,8 @@
+#!/usr/bin/env perl
+
+use strict;
+use warnings;
+use test;
+
+test::dsasignverify("2k");
+
diff -pruN 1.4.0-1/test/engine/dsa2k_pub.key 2.5.0-0ubuntu1/test/engine/dsa2k_pub.key
--- 1.4.0-1/test/engine/dsa2k_pub.key	1970-01-01 00:00:00.000000000 +0000
+++ 2.5.0-0ubuntu1/test/engine/dsa2k_pub.key	2025-04-16 08:09:24.000000000 +0000
@@ -0,0 +1,20 @@
+-----BEGIN PUBLIC KEY-----
+MIIDRzCCAjoGByqGSM44BAEwggItAoIBAQD0+xcD87eyiiaLy25k4ueMtIfyUGSl
+9K86yesqN6AX/AZp9OkAjDG5w/JG8LcHTeUjogcf+lMfjb8tuSTjpCebucs5AqQG
+rlI6/HgN5xl97C01e89ngpyqsUKz/GNH6ocnXQee1wcFbvIrOg1ftDZNePvwVRIh
+B7R/g8JqIcnAyDOJtwlQF87F6CNjfz8xCRMbJo4TMXBOtxMI7o7nGiSFOaps8/dn
+DYU6oTBybu+cJeZRT598TkRvocilRg7L3jsDqEQfPiZsUA/Hf8iqyUogJ6Cqa7qx
+W1YBVU+GfsEJuO80VlPOe5beeSntsah44bWrqywzT3o7Euz0xlLtoZ47AiEAidhF
+QHGsdRneOL4Tcx+U2PBPulZBXnSC2Gs97yT46/8CggEBAJypgVaVHeFUe2Uu17AH
+zlaj+9vKr82+KUX5HJOrpDRTRH737UT/CagG+FGmLJiHvl8NUPzfytWrwV4J2Td2
+t1nQ+W+dF4tA3bkAl44jm41hYiMCU/CGmxhQD1/8ZoPrMk+V0K/3EId4HsrInCXY
+v1txQWz5LRLwgrcaHaWC5RGHhvIFw9IpSadl/QQQtd7a1/9T7A13TEmH+KdGcV9n
+ouDFjcpO6fle/t58RCEJpcRpJ9AOoQNoB8LVVWElUX6MpM1qrCnBZZ745aPzcix4
+U4U7JEbPZiuUQVw8MxRsbbQWhwpqfxGMoMCajzMEPXkh6J301x4nO6N60kXxUUYd
+V3gDggEFAAKCAQAPUJuNmM+ygJZEwI1YEbMesfPkX+mHNSOOoUXdBItagVcQ3nPe
+SN2Fvs0dnjCT0G46+bjdv3izbw7+1HeDUHwM9JkDh0iXp/To6mWoKWFjnurmJrIR
+5ED8DteUxrMY3XxODCxpP9XTvKVKMLDGKJgmoekEEIRkEhyWikce/9gGDrpXLEjX
+Q1iyhNEu7rndToE8pyIoAr9aub3P7tYDO3rhOUTLDqH7LpwToz+r0ZhfQdiNrr3k
+2rOKIgn2Uox7WNIHwwrrRsdlEhYKVlWSzRIjQ0OPRm+KUGn6eEMgWvDffzBRPC+Q
+X6712hHIR/6YWArerix6X/Dsxa/GYOPfWjAM
+-----END PUBLIC KEY-----
diff -pruN 1.4.0-1/test/engine/dsa4k.key 2.5.0-0ubuntu1/test/engine/dsa4k.key
--- 1.4.0-1/test/engine/dsa4k.key	1970-01-01 00:00:00.000000000 +0000
+++ 2.5.0-0ubuntu1/test/engine/dsa4k.key	2025-04-16 08:09:24.000000000 +0000
@@ -0,0 +1,36 @@
+-----BEGIN DSA PRIVATE KEY-----
+MIIGVQIBAAKCAgEAyJscZddSVlbzJWUul/6UyYNMDQ5Tw1Hw7qCKGWmpAAZlASED
+5TPD60SgxOBMZK37oce07VBxe/pqXLdMEGN52Fo7ST1mil5/Rb4JUvexLX9NCyz7
+LBNghIXQtb7826dK79Q6t5nXAHmUl/1WeHOIgwSDD9ECU6km/O10CPKrWlN187ob
+8lk//zA0uU/J7ZN8jFei8d0+E+tSRRhDtNjJPXw9uHc+Vd2H8rfGh2riP0x7mH/h
++32UhcPbKyRtzgweeCYbdS5rLaWCoKAawNg2QbpotFf9LFWqohnkHINpgHQuBCDA
+uriNeuRX4yNR/SA1BXx/esKRxqnHs34qxSSZzHea1uIWi/IbmU0xFT02tWwZ2MKF
+pmfrky3eJY/L6wSyIrrbRp42m6sV4R5jjTg8nTOl1d4N/JVe6ttXH4fx/VDc/3vL
+ktiv69+dKCgsGrEo+vBKpqY/6CXuHFYSVZM+7NTMKGilsT59gP3Oc9I4ApF/Z/Mu
+N9jvk3Uyz1uUDszbmVYjHhWg5g/O56G8wj+QH4XPfW4IS0AiaHkuo008xJDRcJbB
+Vzbxz+gxZqL143OQG9ciXxw+imnbSNsyB+a5cpKOEwgoeU3lIRhe8eRtvWfzfT8T
+94LN8xU7w6tzoSk0JxLTdIBKHDST17jmato/SA4uwz31uaWA7eU3gv4u1DECIQDH
+oioDWzjed2EIPznryUN1YsmeTbyn53t4Ud4o4DLOmQKCAgA104Ps34soiOkNr9e+
+e8tbkbOhix2Brr2OM2c0V5CGU6Oofjila1hNp0DfoN/jrPzZT/qeiyuIleqVjlpa
+V/f6PJn2FX9Amx3g8Yhy4ywP3PPU71Orbj+LiP56cHanvHEe941HKxUpXcYSHOsk
+E6AQgl/pz6leBqfnWUjAPQbO7jeYVhuaz8ovfke7G6gZNDpiVV8BLdJ9c4OF+83S
+imDJCdFnRYrbtDXsYHCv2FK7bLDaujJEOqjNFjQX5/7OCSb7f17zJtM7BZkAZR89
++N8KuOjQKVqzCFqGaNAB/7VPgzHjzAoi9YGNHoM2Jy+aY0kv1hBjkwdw9m/gnNZA
+REMotXtVbxnV9+ppHpacA9eqHE1XIeIE7SN8D1snQAGhRmvlOUAuyhnDOnkBvvCX
+noAmlprDX4P+WtBdGXFDKGkYz7LRZt6jT1GasgXdjscYlEUb/Y7Hklyn56tTlLEV
+ICpZqWt+mRB0gXrt5NDpq6rvq2Xv7A545H48dQ4Q8HisOkn6VoedhR6jMfXCM93S
+Ox0lg20cmfYMp3H2OYW1z+j1K4Lgsqdf+RX1t1glvS+1vP/Mp/xqFvc0T8D7YYMY
+njQjZE8zgDKqlGeeXYKxzD9EOeVAzftS37E2A3TGSG0JOPN/c4jtO1EAIXJobRmP
+kTFaAbIF7OuseZPHf6QYGuelbgKCAgBdujKVLos71yqssMF6/X+abr+26nSy7n9I
+WLtKKk8neYEsQ/mcY7e0aFm53fWRpiccSm5psp/EijUZX0c4iGnmuUPW3GjStlYL
+evTboJ7R8vINeWGZXs8DKrDF2ee0S+OgnGIWgllRSGbA5AwnDX7NoyvBueYdOWCF
+OMeMyAEpSPE3opJrmVwPy6pBaS1ECzPADR1xOpWC5Th4DtQW8WdbQLcmuAwQKMgd
+FU1LWXUpMAPwhWXGMyrTGvUTg9hlIsEHpBMc/ae6qJeIO66U/xzWdhU2pP1WDjdQ
++fI293+TOs9B3d/icLFxybe5IH8m4CPBCiW8tc6bj5MTBOObhJyx7q97rvRLApar
+EZqNvIplyz0Zs/bfLux82N2s8nUQn5vuKTRH2Dfz20n+LeqhcAjbRStisGd5aYYg
+WxeWXB0u+i4LU7c8QqQuK/77ttnE3OhJX+d6ebMAeG5//k/psewwvWwnWE3snaUG
+goxR2XBB8jyqaf09jKIZQKg3bf0Vk/lLIWvRKZUxPfJFPHnu7ywBNN/xyvpwG5Wi
+d/1Zr3XuVu4ZJz+H15isN6pMyKlFUyEGTiPjy8GFu1NcQ2RMmRJ8RkJgpO0Q18f3
+qQP3LshDCdkseGHV8uL7idbCamR3ylwwrrvFcAWCm1tYO4rQpK2Y0o2k7AC5fMcF
+OKHpkimzGAIgMBphbTS6CkuHLr29kU+4x7n6j82x3gK9aVgTR/3zVa4=
+-----END DSA PRIVATE KEY-----
diff -pruN 1.4.0-1/test/engine/dsa4k.pl 2.5.0-0ubuntu1/test/engine/dsa4k.pl
--- 1.4.0-1/test/engine/dsa4k.pl	1970-01-01 00:00:00.000000000 +0000
+++ 2.5.0-0ubuntu1/test/engine/dsa4k.pl	2025-04-16 08:09:24.000000000 +0000
@@ -0,0 +1,7 @@
+#!/usr/bin/env perl
+
+use strict;
+use warnings;
+use test;
+
+test::dsasignverify("4k");
diff -pruN 1.4.0-1/test/engine/dsa4k_pub.key 2.5.0-0ubuntu1/test/engine/dsa4k_pub.key
--- 1.4.0-1/test/engine/dsa4k_pub.key	1970-01-01 00:00:00.000000000 +0000
+++ 2.5.0-0ubuntu1/test/engine/dsa4k_pub.key	2025-04-16 08:09:24.000000000 +0000
@@ -0,0 +1,36 @@
+-----BEGIN PUBLIC KEY-----
+MIIGRjCCBDkGByqGSM44BAEwggQsAoICAQDImxxl11JWVvMlZS6X/pTJg0wNDlPD
+UfDuoIoZaakABmUBIQPlM8PrRKDE4Exkrfuhx7TtUHF7+mpct0wQY3nYWjtJPWaK
+Xn9FvglS97Etf00LLPssE2CEhdC1vvzbp0rv1Dq3mdcAeZSX/VZ4c4iDBIMP0QJT
+qSb87XQI8qtaU3XzuhvyWT//MDS5T8ntk3yMV6Lx3T4T61JFGEO02Mk9fD24dz5V
+3Yfyt8aHauI/THuYf+H7fZSFw9srJG3ODB54Jht1LmstpYKgoBrA2DZBumi0V/0s
+VaqiGeQcg2mAdC4EIMC6uI165FfjI1H9IDUFfH96wpHGqcezfirFJJnMd5rW4haL
+8huZTTEVPTa1bBnYwoWmZ+uTLd4lj8vrBLIiuttGnjabqxXhHmONODydM6XV3g38
+lV7q21cfh/H9UNz/e8uS2K/r350oKCwasSj68Eqmpj/oJe4cVhJVkz7s1MwoaKWx
+Pn2A/c5z0jgCkX9n8y432O+TdTLPW5QOzNuZViMeFaDmD87nobzCP5Afhc99bghL
+QCJoeS6jTTzEkNFwlsFXNvHP6DFmovXjc5Ab1yJfHD6KadtI2zIH5rlyko4TCCh5
+TeUhGF7x5G29Z/N9PxP3gs3zFTvDq3OhKTQnEtN0gEocNJPXuOZq2j9IDi7DPfW5
+pYDt5TeC/i7UMQIhAMeiKgNbON53YQg/OevJQ3ViyZ5NvKfne3hR3ijgMs6ZAoIC
+ADXTg+zfiyiI6Q2v1757y1uRs6GLHYGuvY4zZzRXkIZTo6h+OKVrWE2nQN+g3+Os
+/NlP+p6LK4iV6pWOWlpX9/o8mfYVf0CbHeDxiHLjLA/c89TvU6tuP4uI/npwdqe8
+cR73jUcrFSldxhIc6yQToBCCX+nPqV4Gp+dZSMA9Bs7uN5hWG5rPyi9+R7sbqBk0
+OmJVXwEt0n1zg4X7zdKKYMkJ0WdFitu0NexgcK/YUrtssNq6MkQ6qM0WNBfn/s4J
+Jvt/XvMm0zsFmQBlHz343wq46NApWrMIWoZo0AH/tU+DMePMCiL1gY0egzYnL5pj
+SS/WEGOTB3D2b+Cc1kBEQyi1e1VvGdX36mkelpwD16ocTVch4gTtI3wPWydAAaFG
+a+U5QC7KGcM6eQG+8JeegCaWmsNfg/5a0F0ZcUMoaRjPstFm3qNPUZqyBd2OxxiU
+RRv9jseSXKfnq1OUsRUgKlmpa36ZEHSBeu3k0Omrqu+rZe/sDnjkfjx1DhDweKw6
+SfpWh52FHqMx9cIz3dI7HSWDbRyZ9gyncfY5hbXP6PUrguCyp1/5FfW3WCW9L7W8
+/8yn/GoW9zRPwPthgxieNCNkTzOAMqqUZ55dgrHMP0Q55UDN+1LfsTYDdMZIbQk4
+839ziO07UQAhcmhtGY+RMVoBsgXs66x5k8d/pBga56VuA4ICBQACggIAXboylS6L
+O9cqrLDBev1/mm6/tup0su5/SFi7SipPJ3mBLEP5nGO3tGhZud31kaYnHEpuabKf
+xIo1GV9HOIhp5rlD1txo0rZWC3r026Ce0fLyDXlhmV7PAyqwxdnntEvjoJxiFoJZ
+UUhmwOQMJw1+zaMrwbnmHTlghTjHjMgBKUjxN6KSa5lcD8uqQWktRAszwA0dcTqV
+guU4eA7UFvFnW0C3JrgMECjIHRVNS1l1KTAD8IVlxjMq0xr1E4PYZSLBB6QTHP2n
+uqiXiDuulP8c1nYVNqT9Vg43UPnyNvd/kzrPQd3f4nCxccm3uSB/JuAjwQolvLXO
+m4+TEwTjm4Scse6ve670SwKWqxGajbyKZcs9GbP23y7sfNjdrPJ1EJ+b7ik0R9g3
+89tJ/i3qoXAI20UrYrBneWmGIFsXllwdLvouC1O3PEKkLiv++7bZxNzoSV/nenmz
+AHhuf/5P6bHsML1sJ1hN7J2lBoKMUdlwQfI8qmn9PYyiGUCoN239FZP5SyFr0SmV
+MT3yRTx57u8sATTf8cr6cBuVonf9Wa917lbuGSc/h9eYrDeqTMipRVMhBk4j48vB
+hbtTXENkTJkSfEZCYKTtENfH96kD9y7IQwnZLHhh1fLi+4nWwmpkd8pcMK67xXAF
+gptbWDuK0KStmNKNpOwAuXzHBTih6ZIpsxg=
+-----END PUBLIC KEY-----
diff -pruN 1.4.0-1/test/engine/dsa8k.key 2.5.0-0ubuntu1/test/engine/dsa8k.key
--- 1.4.0-1/test/engine/dsa8k.key	1970-01-01 00:00:00.000000000 +0000
+++ 2.5.0-0ubuntu1/test/engine/dsa8k.key	2025-04-16 08:09:24.000000000 +0000
@@ -0,0 +1,68 @@
+-----BEGIN DSA PRIVATE KEY-----
+MIIMVQIBAAKCBAEA/buSBt58jvUKAG7eazsdnUEmS0JRrgBQj4fLGiFpe2gsDkxF
+nXiDa5NZQY+2Ui6GrfgB96rED73GH1lDl8FhHYkgc9vSgROc0opeGU760QpN5LCe
+VADG9WLoBcwGAjrvkDeDm9iHC/HRwkj1au0SKAeSJcM7RT6swnvAnn97SwdVwFt8
+n1gTxbAoFBpudjXxjFuaKbsBvJ1ln60llOHywVpk4gkA+tfzIIJzix4fhJmuLWUQ
+STqA2lKJO5gOxglIpPB3ffPkqvE2WQA/PEyeLChGz6YyDA/EkXt6i0JL3chz/oOB
+P/KUeJ57HmtSc9HworLb4EB6ANPjVxnAQrs75qFBbA4BhE0UDMNzOI3JDo6VxVPL
+46E5unlmZ+XCnJkWZl9FHX/bhxiDMz7jJqef9Sx5NjexKhd9vsbAw2GV0V4kHu4A
+SIpWA53DOj9dmif8fK9fXRohACKFlzdf44S8zLSUJ5+J9tsDBCjcjNjy+EnF2eEf
+9QMzQf5E+vqcoP75xud4BtBSqIIrqjvVPuWJm8RfNg3FyXKwoatXh1BuicHWo+bT
+bTmVQXWGYQ4Co0asDdoLfsPKrMKeTKVyj8GuxbSWRKbNDXPJCKF05gGx5vr5SpmD
+4CJHEf4scDSlBe7ATF24tj9/vpCht4WflXK4nwlfNLdurF9vQaNntT3fEFqtjQsB
+LF0dEKpetwEBxrGEph3WC3XJT2zZu6Wbvnjg70TMBw9d9PD1poqTQH4l5ca70ct1
+9Tc6gtFL2C/nKowV5KZBhfkNe8+01CbenFy+1GPy2+iJq/SQRz+cAXKy+3hEtgk4
+AkguzeBqSizYuJPvUujAiHFdbkWzg9fl0dDtJVU3mZRrJK0Znyn1CiEkK9iMdhtA
+m+SovbhwpLU3f85fTK6+QebD65a7NZucJI6u7v6wbFjkuh6+CBIaZwYsgTV4HjV+
+lOaibbbMDxdnTeXaGnxdPnA9s6SyTvRB4YskzgmckE+rmBJpGCDpyIQ/h0Nx1755
+ZwhGiZ8rJ7FbTzsa6z6x1Z7j5Dk5f+dkfQPWk8H5xrVsoKNUqODxT2K91eWW0jIa
+8+rsbq96t4b2fUilRjY771lQ5lmJju1IvZJRVFztup7H+lD42S9knPmurop52lHK
+MuiAp62oQKBO34d1ZAtziQnUkhThHDYJWoL/FzNmN09l8ruU8jE6Mr1b+3F0Ggfd
+bFAzrK0BMANVuCCT4Yh9iLd2/04YImJJF0FdH/GP5ngsGZBsSZd7pjUjkJ6ERrJt
+4dvj9i7T3bnVMHWM1xYSIUw/z7b+Q4ED7itoYA1XP3dY0+tIV8BM0/RYnbqlWPcw
+sknNLiXyRHf8UVcB6rdGQ9FvOVpU/14KCkInbQIhALBSPrcNsGUGR3AOrsvlvhWz
+5HJo9c9AeKmlls9Y2rjTAoIEAGxiLBTfQJ2nQUXecIfTJBqQwiBumeMQwEFYXTDd
++HtjFMQTBwcMJg+N8MO5iZQcwAvFLYHT+dMHLgyHo41rdvGJtCbB7fPtcRYZNSse
+YONHs5ga1pN1Mpj5M3yFmQlGR58lWa6MHlXR2bheanmKHQq5h8t85JYHtlrWZg7o
+ZC9mYQ5EKPfW7ITlpLlEB4rn7Doky3jOPSMtwBfpzU6u5zEgOwVSSnf3GW0ktByG
+/+DbAkQtQswDUyJYmLo9wc7Y5ye6FSCovtifoKVmduvFOaZe5BI3I0cgt1LtDPOT
+2UGkj03exS9DaCZ73YkQCjqof+wEJ4lH/egrTCPNax6E1ggX/z+0+IZFEKXEE5iG
+nIsShTgl0MDDdBnwA8pfJ3ku5sEtCiUYAja9KIIvY4e2oLTpGjZYP+WMjdVtOWBT
++QT+TjPV3Ni8c02nDeNZTZYuIEre8xh2jPizTk/lOcOkY8KPpwgHQ+wlxWFbiaDw
+i2zfU0Z+op4j/CiHviODH0+7NuZTY7PXextb/ZEIS54vNjWmBiNL+20+/mJR/HWp
+mOtw8Oj4ggbREBAUpINM/q29ISY4EqXV5Fj7RpWUSCCVrhB51G7AWx9pobmenCD0
+qoqQCu/lxSJ3ictDiQ76aYk+MqXsvduRki/KsrcK384vD5MnWSkPLSA0b10I/V4M
+PqF5x0EKPbjoM1AyAgf5UZzW9PxADrW+ibNvXVqSAka3SYDT88mAlhTfs2FWROGo
+7peMzcHFCwtwAoPTnIGIW6IPDswp4aVVZ//0nebg2Dd/L7QN9dYBf2nH8rLBrnk4
+HHOhDKmHBv7EJx0krgLds0TaJt9PwfbEpMjHuLoMC46UhIv6qmHTLdZpmTM97aS3
+2IvDZdfxRpQ1CiaiKD2WvVv3CKsMJSuMQztNP9nWjDrX4AQwdBTz96wvnxZqdZHr
+M+wPTKT2TOrsFS9ShfmW8rT5MsUoFivSsuEwps2x3+3g92fmjwdT1+ONDdJ6+Zp/
+Tw6hhYUu+IKjqenGY1Ac76NpxjJSS01Yv9g7KImLGuyg0ZryMN+CMEwTdtAZhEjt
+GFJySi2gR8LiXe2PuQrA4laIjGgIZd9ay8LpOBlInCU2DfmZNVD7p08RGZuYHi2T
+osjk6RIuAF0gLwnYMHqyuW+bnj8/4wopF5br5/21nDhDN1BjDq8NFBHaCwBnVoiM
+Q2b4gWdYk/mvKZcH6Tty/ZbcsTzghmJBI9b8nwQJU1erbdVk815FSEtBt+Tp55Yk
+9wR2TtV823t9QiWuqnnrOAxurfVamGfsUjxXXMkzplZAosoQI22CEyxQUGQrySDY
+Q+J3YfSbUsCGzaYadGnf2s/IwBbEitTlLIZgqn/JMnVU71wCggQAX6n2i0rgMJaa
+TUsTSIgPQDTDBOJSJmPOmExjizbA/C4/d6KbJO1uiLAvBndNPlVmYSJawDFHwWgC
+a2nJr7LgvS/sI3zA1MJVzUG+TGvcgNj7CHWDt1MJJC1cQTSeSPG/pYE+nVl/IADo
+VZ89ElJySQotTnKcn0vVs0cqRh+LZH0FXG0aWaE24y+5MPGeZWnaUj4FYMo8DO3/
+OJ/NDm37BInS4qHIiTdV8wd/CJCFK3K2w5o1ayxZX4Gmggyky0vf4xaZ0l1LSUQb
+LSnng+WlHor7jCrQeRFlqSXpENjsA8aVkSaCToW366VVaZ4r2bsuFzQWOk0vG+cv
+uGZYewaUD1dVuvGUIvWEzaiUK4PntU+Bi0BPAG0XDvVo02lGN5dQN5MswjaY5OtH
+vjFk0piIKnpDf92bDD1d82/icGBkz0FCJVJJFZv986a4CkAJG+Fupp/mT44zhVjz
+dYBRBwnw70ydwVn7mMhJnbH1v/8GVeh0MzPBLUSzcQY2kgXodQmoj9T2cTfWdFel
+O5cgFxs6UXt1x3KyHjofh0FJH4hyH3EP9ouXDuC2OIpYR119IEz1TC9WmK0kZ4+m
+gWlPXtsihSCpQBy5pW/g3xg6R6+PzbrXIrebWRM1cvDjrtRQC6LJVoEUtxKhhmB4
+wv0Q2BgIFQYtWdDwHEWWCtgtJfsRqXmXibsU3u+DGSlJHtXaVOeE7zoRqOgaPKDR
+aoA/UpYNs4v5Z4Frwn+LuXwtXVGhrlbw0hXEhyNgpsTfl+JfJJ2DsM4d1fJ5csAJ
+5dqP+JChWc+ZelTnFtYHvbZef41k4H85d8KA23uwvvY9emh22tQkRSKVL8Iy76D/
+Ro4eUM3zEhD0/qBOfz5eu3xnbYacvqsKbYCnnUbbT4ZxpIi/HchocTI37e1ceip4
+1XUswWYSRSphtnuJBn29pzsK/EqxQc9CMhihKBDZEqpkOdKxa4oYuKjs568zwTBO
+FB4nhU9u4bDD/Ga7Jjt+ftGg1dn/YdBL4wWrjga0j8g5Ud2K7+cRPr1pYBK7Y/z9
+Zz7o0cYt/v/7VsX/H0ev/+vaUdsKxU00PDin7mAhBoR7ZmNrWdQzzqXv4O43pg2H
+Yw3600Fbt1Vbv5+2G+tTfgEA06EvVuonxS8q5q/A++ZpmtaGdFF5HRXdNF6aOd4T
+0QSYWhUuo4BwgQfuIkLru9xGKUbwZonDGJ/Dy50sSF91T7eENcSUtvK1uEx/4AiM
+CEmK1VTIC3tH4L1aaE1bT0NutHvjNKbMqALaO62IFTIaCkFvvacFEGrDMwbwShAl
+vgn2vlGWLq7j3+89CC3gjiYLkFOyK0csAWFWmeBZ4nOdc6aNaFAT3fmzwMLhdjjf
+nT17/mCifwIgVYRdkbanrkD51REtbqlcqzivMQjunFLlXQ+GEAZL5Rw=
+-----END DSA PRIVATE KEY-----
diff -pruN 1.4.0-1/test/engine/dsa8k.pl 2.5.0-0ubuntu1/test/engine/dsa8k.pl
--- 1.4.0-1/test/engine/dsa8k.pl	1970-01-01 00:00:00.000000000 +0000
+++ 2.5.0-0ubuntu1/test/engine/dsa8k.pl	2025-04-16 08:09:24.000000000 +0000
@@ -0,0 +1,7 @@
+#!/usr/bin/env perl
+
+use strict;
+use warnings;
+use test;
+
+test::dsasignverify("8k");
diff -pruN 1.4.0-1/test/engine/dsa8k_pub.key 2.5.0-0ubuntu1/test/engine/dsa8k_pub.key
--- 1.4.0-1/test/engine/dsa8k_pub.key	1970-01-01 00:00:00.000000000 +0000
+++ 2.5.0-0ubuntu1/test/engine/dsa8k_pub.key	2025-04-16 08:09:24.000000000 +0000
@@ -0,0 +1,68 @@
+-----BEGIN PUBLIC KEY-----
+MIIMRjCCCDkGByqGSM44BAEwgggsAoIEAQD9u5IG3nyO9QoAbt5rOx2dQSZLQlGu
+AFCPh8saIWl7aCwOTEWdeINrk1lBj7ZSLoat+AH3qsQPvcYfWUOXwWEdiSBz29KB
+E5zSil4ZTvrRCk3ksJ5UAMb1YugFzAYCOu+QN4Ob2IcL8dHCSPVq7RIoB5IlwztF
+PqzCe8Cef3tLB1XAW3yfWBPFsCgUGm52NfGMW5opuwG8nWWfrSWU4fLBWmTiCQD6
+1/MggnOLHh+Ema4tZRBJOoDaUok7mA7GCUik8Hd98+Sq8TZZAD88TJ4sKEbPpjIM
+D8SRe3qLQkvdyHP+g4E/8pR4nnsea1Jz0fCistvgQHoA0+NXGcBCuzvmoUFsDgGE
+TRQMw3M4jckOjpXFU8vjoTm6eWZn5cKcmRZmX0Udf9uHGIMzPuMmp5/1LHk2N7Eq
+F32+xsDDYZXRXiQe7gBIilYDncM6P12aJ/x8r19dGiEAIoWXN1/jhLzMtJQnn4n2
+2wMEKNyM2PL4ScXZ4R/1AzNB/kT6+pyg/vnG53gG0FKogiuqO9U+5YmbxF82DcXJ
+crChq1eHUG6Jwdaj5tNtOZVBdYZhDgKjRqwN2gt+w8qswp5MpXKPwa7FtJZEps0N
+c8kIoXTmAbHm+vlKmYPgIkcR/ixwNKUF7sBMXbi2P3++kKG3hZ+VcrifCV80t26s
+X29Bo2e1Pd8QWq2NCwEsXR0Qql63AQHGsYSmHdYLdclPbNm7pZu+eODvRMwHD130
+8PWmipNAfiXlxrvRy3X1NzqC0UvYL+cqjBXkpkGF+Q17z7TUJt6cXL7UY/Lb6Imr
+9JBHP5wBcrL7eES2CTgCSC7N4GpKLNi4k+9S6MCIcV1uRbOD1+XR0O0lVTeZlGsk
+rRmfKfUKISQr2Ix2G0Cb5Ki9uHCktTd/zl9Mrr5B5sPrlrs1m5wkjq7u/rBsWOS6
+Hr4IEhpnBiyBNXgeNX6U5qJttswPF2dN5doafF0+cD2zpLJO9EHhiyTOCZyQT6uY
+EmkYIOnIhD+HQ3HXvnlnCEaJnysnsVtPOxrrPrHVnuPkOTl/52R9A9aTwfnGtWyg
+o1So4PFPYr3V5ZbSMhrz6uxur3q3hvZ9SKVGNjvvWVDmWYmO7Ui9klFUXO26nsf6
+UPjZL2Sc+a6uinnaUcoy6ICnrahAoE7fh3VkC3OJCdSSFOEcNglagv8XM2Y3T2Xy
+u5TyMToyvVv7cXQaB91sUDOsrQEwA1W4IJPhiH2It3b/ThgiYkkXQV0f8Y/meCwZ
+kGxJl3umNSOQnoRGsm3h2+P2LtPdudUwdYzXFhIhTD/Ptv5DgQPuK2hgDVc/d1jT
+60hXwEzT9FiduqVY9zCySc0uJfJEd/xRVwHqt0ZD0W85WlT/XgoKQidtAiEAsFI+
+tw2wZQZHcA6uy+W+FbPkcmj1z0B4qaWWz1jauNMCggQAbGIsFN9AnadBRd5wh9Mk
+GpDCIG6Z4xDAQVhdMN34e2MUxBMHBwwmD43ww7mJlBzAC8UtgdP50wcuDIejjWt2
+8Ym0JsHt8+1xFhk1Kx5g40ezmBrWk3UymPkzfIWZCUZHnyVZroweVdHZuF5qeYod
+CrmHy3zklge2WtZmDuhkL2ZhDkQo99bshOWkuUQHiufsOiTLeM49Iy3AF+nNTq7n
+MSA7BVJKd/cZbSS0HIb/4NsCRC1CzANTIliYuj3BztjnJ7oVIKi+2J+gpWZ268U5
+pl7kEjcjRyC3Uu0M85PZQaSPTd7FL0NoJnvdiRAKOqh/7AQniUf96CtMI81rHoTW
+CBf/P7T4hkUQpcQTmIacixKFOCXQwMN0GfADyl8neS7mwS0KJRgCNr0ogi9jh7ag
+tOkaNlg/5YyN1W05YFP5BP5OM9Xc2LxzTacN41lNli4gSt7zGHaM+LNOT+U5w6Rj
+wo+nCAdD7CXFYVuJoPCLbN9TRn6iniP8KIe+I4MfT7s25lNjs9d7G1v9kQhLni82
+NaYGI0v7bT7+YlH8damY63Dw6PiCBtEQEBSkg0z+rb0hJjgSpdXkWPtGlZRIIJWu
+EHnUbsBbH2mhuZ6cIPSqipAK7+XFIneJy0OJDvppiT4ypey925GSL8qytwrfzi8P
+kydZKQ8tIDRvXQj9Xgw+oXnHQQo9uOgzUDICB/lRnNb0/EAOtb6Js29dWpICRrdJ
+gNPzyYCWFN+zYVZE4ajul4zNwcULC3ACg9OcgYhbog8OzCnhpVVn//Sd5uDYN38v
+tA311gF/acfyssGueTgcc6EMqYcG/sQnHSSuAt2zRNom30/B9sSkyMe4ugwLjpSE
+i/qqYdMt1mmZMz3tpLfYi8Nl1/FGlDUKJqIoPZa9W/cIqwwlK4xDO00/2daMOtfg
+BDB0FPP3rC+fFmp1kesz7A9MpPZM6uwVL1KF+ZbytPkyxSgWK9Ky4TCmzbHf7eD3
+Z+aPB1PX440N0nr5mn9PDqGFhS74gqOp6cZjUBzvo2nGMlJLTVi/2DsoiYsa7KDR
+mvIw34IwTBN20BmESO0YUnJKLaBHwuJd7Y+5CsDiVoiMaAhl31rLwuk4GUicJTYN
++Zk1UPunTxEZm5geLZOiyOTpEi4AXSAvCdgwerK5b5uePz/jCikXluvn/bWcOEM3
+UGMOrw0UEdoLAGdWiIxDZviBZ1iT+a8plwfpO3L9ltyxPOCGYkEj1vyfBAlTV6tt
+1WTzXkVIS0G35OnnliT3BHZO1Xzbe31CJa6qees4DG6t9VqYZ+xSPFdcyTOmVkCi
+yhAjbYITLFBQZCvJINhD4ndh9JtSwIbNphp0ad/az8jAFsSK1OUshmCqf8kydVTv
+XAOCBAUAAoIEAF+p9otK4DCWmk1LE0iID0A0wwTiUiZjzphMY4s2wPwuP3eimyTt
+boiwLwZ3TT5VZmEiWsAxR8FoAmtpya+y4L0v7CN8wNTCVc1Bvkxr3IDY+wh1g7dT
+CSQtXEE0nkjxv6WBPp1ZfyAA6FWfPRJSckkKLU5ynJ9L1bNHKkYfi2R9BVxtGlmh
+NuMvuTDxnmVp2lI+BWDKPAzt/zifzQ5t+wSJ0uKhyIk3VfMHfwiQhStytsOaNWss
+WV+BpoIMpMtL3+MWmdJdS0lEGy0p54PlpR6K+4wq0HkRZakl6RDY7APGlZEmgk6F
+t+ulVWmeK9m7Lhc0FjpNLxvnL7hmWHsGlA9XVbrxlCL1hM2olCuD57VPgYtATwBt
+Fw71aNNpRjeXUDeTLMI2mOTrR74xZNKYiCp6Q3/dmww9XfNv4nBgZM9BQiVSSRWb
+/fOmuApACRvhbqaf5k+OM4VY83WAUQcJ8O9MncFZ+5jISZ2x9b//BlXodDMzwS1E
+s3EGNpIF6HUJqI/U9nE31nRXpTuXIBcbOlF7dcdysh46H4dBSR+Ich9xD/aLlw7g
+tjiKWEddfSBM9UwvVpitJGePpoFpT17bIoUgqUAcuaVv4N8YOkevj8261yK3m1kT
+NXLw467UUAuiyVaBFLcSoYZgeML9ENgYCBUGLVnQ8BxFlgrYLSX7Eal5l4m7FN7v
+gxkpSR7V2lTnhO86EajoGjyg0WqAP1KWDbOL+WeBa8J/i7l8LV1Roa5W8NIVxIcj
+YKbE35fiXySdg7DOHdXyeXLACeXaj/iQoVnPmXpU5xbWB722Xn+NZOB/OXfCgNt7
+sL72PXpodtrUJEUilS/CMu+g/0aOHlDN8xIQ9P6gTn8+Xrt8Z22GnL6rCm2Ap51G
+20+GcaSIvx3IaHEyN+3tXHoqeNV1LMFmEkUqYbZ7iQZ9vac7CvxKsUHPQjIYoSgQ
+2RKqZDnSsWuKGLio7OevM8EwThQeJ4VPbuGww/xmuyY7fn7RoNXZ/2HQS+MFq44G
+tI/IOVHdiu/nET69aWASu2P8/Wc+6NHGLf7/+1bF/x9Hr//r2lHbCsVNNDw4p+5g
+IQaEe2Zja1nUM86l7+DuN6YNh2MN+tNBW7dVW7+fthvrU34BANOhL1bqJ8UvKuav
+wPvmaZrWhnRReR0V3TRemjneE9EEmFoVLqOAcIEH7iJC67vcRilG8GaJwxifw8ud
+LEhfdU+3hDXElLbytbhMf+AIjAhJitVUyAt7R+C9WmhNW09DbrR74zSmzKgC2jut
+iBUyGgpBb72nBRBqwzMG8EoQJb4J9r5Rli6u49/vPQgt4I4mC5BTsitHLAFhVpng
+WeJznXOmjWhQE935s8DC4XY43509e/5gon8=
+-----END PUBLIC KEY-----
diff -pruN 1.4.0-1/test/engine/dsamax.key 2.5.0-0ubuntu1/test/engine/dsamax.key
--- 1.4.0-1/test/engine/dsamax.key	1970-01-01 00:00:00.000000000 +0000
+++ 2.5.0-0ubuntu1/test/engine/dsamax.key	2025-04-16 08:09:24.000000000 +0000
@@ -0,0 +1,83 @@
+-----BEGIN DSA PRIVATE KEY-----
+MIIO/QIBAAKCBOMAktKbrplsBjCew5j4joSsuBsQT/3RbBXLNzl4ucFO6gsKq020
+rVSUVDC097sR3v5gfO52WXVPRfBniZyV/Wv8eK49NCrxHTK2wj5x1TaocPCwyrGL
+oSdj5VaC2HR5TiiqLE37iBSEXd00ZE3fk9ucdct383p/iYg9CPH6KADASvEnZ0Cb
+B76/WiP4RjAm41zBAOHkyqSI3N8o67e9UCFk94G/wiSqilqlij2Kj/bD5Y7rLUEw
+KvT/sUsE1Bv105hLCfMU2zsV5CsTx5N01fw+eiB0NLHyL/k5EPPw52q/S6UQGCcJ
+xmc6ecPWed70jcscH5XDp7xlBLA/0pkq1u8henzJyUsMUOG/JkQB7dbow4dlVNhr
+IEDJv+V1wD2jm7YBpp9Oz3Q8PWiEY38+7uzysnQe/K6uHlJ1CXqPyihWkmpqYWtH
+NePNkydj4ZNLnJfKVqSinUdhJwzhF9erBt0/WCOF3W13iA+1awWjN180KwL6VIsN
+/EQ0+mll+oWVL5msySbFNnZfXakCwz6jt1+VV86Uoi79yvt9QkWJ+Ka0hF/9iCt7
+DlNH1hBBo9L9TF0UHyMapyMxzKrvNaYxTaVCjflspf9nM1QDtI0RGsV/03QKDxBg
+YkupV+DOf4IUUMv71sKLl5YcX34Nky/uO0TCkH298Ulb7C1hKTCb/iEzYHGlt3Sy
+2ehiEMRkjgHI1Tk1REsiOxWx63/jLCWenp7c4FtJOlrsW3GUwQl4haQpjoX5CUU2
+jKfX9lX1BTLvmctwZAy6+eOaMYUGxZ183+kDqiJbomSWp2NrNS3gQ9gNPbDkBFCO
+jqNhNt8dzVst+nvAzIM5wx9kRHikKjjQGr5WsE0ncp8R11CWz1LRPIKj+Ewe6kUW
++qF0v4dsxZcIekKkRwEkH0mcHuYO6g5fxUnnWBZUUP+inDjFaJ8NlBrxcKBgZz0d
+8537sKSxJYqTWIXWK6UMSVkKqSABAotGdZ8cY6Dv+22ptdvHnK6V2ryq47+xZajp
+c5bNkvBqCMtNaZOrMv9A1USCN4ej7LWoBmhNUMQd9uQDXlrjR7Rnr8+wO6i91e+I
+8yuOHFjOCFKNHka3wXMpeS4WgiH3JTzVhiTs+b81u+G8clrbNdOfLOyfbeRQHXLZ
+2DnrrtrWK/WveIgDYzg6Dgj4iJsFyYlVxlFoArGPfs8VhK0WzhPs6iWQYCKhiGoH
+yX2/awZoOFoYNXxilLE83m1yw8UM67TBUoAR0EPeXECe03dQEhorDwfacNIw+CvR
+gNQHrXTATAGUDL6VhAsdxY50zOdW6nEOKtOQLR2NRnKSTn4Sa4B23Z9tZo/v4UXV
+tpG1/Ch1vYy3OGhGSvY6wzcKAR2itnyrXaLoSY/GwwAvh8L70a5+JhOAELqkWmHK
+g/gmUB1CEhGuV4X87uCahWXD7faAkcj1lPKMEUB4ZqQs055wMd/FUB4T32iIzEU3
+7SRfMS5Og0jZ83PJGjrZP2HJLb8b/88AbtoIBevOeKJHtUNDg4MKQlLdpuAYH+5B
+bjsp9oj8ISvronNueThO3esgyba07E3QgCk6QWjgYZ9+5NP4uIuIqTSQz8K5CFaO
+T6h7pwetEEFszcYN5YPslFzW8CwYI/N/XlBcfkTK4GBeaYqca0S5SBGndvapNBCA
+MG410C998pIwA0djM+0CIQDbT+xFXLhaCXDLN3H/3cFfHwrJAysugTkIShrsBA09
+qwKCBOJfTR72Koj6uRzwunWH6w8VVmGVLWHQS2mvJ2EnuBaOhkEOUQRPr0pOYLcP
+Tgk3Lebbz8I2j81fiddo7+mxSp2g2qnp6cspO0oJJj1O6E1NZqj6lVHZux9aC2xg
+t9MIi754UzHNfLYwGwIYeGXVqpw7JLaYYqZdAvYdwam8KWNtW6tdicPLi7Bi4eU9
+pvPdmhyyXWky+7Q8QI0d2ABgZVHKpMZPoXnZ0bAGuQgChATE+u2XymhiORyXr6wk
+2qFt09lkq2vE3iFM3Wj2hphF4R5GLZX9aJznjJSUSR/9K4FrG34f0fQ2vjg1qeoz
+Vis4jAe9ISNtMGAjLS/hujqi3vhDbSAGbgOOk7GQClGnLsjIzp+BYhamUPEKhjwX
+roNIKJEss7YUxqNDLKb9mMESza8aLag8ZHKzh6oqTqwi5S9k3/cFizAFy3Tvs4oR
+qhzPUdhnPMtvsPLAaOhJIjl4EEntAMpSYW70aIisW/hZpJqIEbOBr4uQmQFqOpqs
+BfiY3zJq/APrw57H6tHoCtEVvZBadg2KrZjDVrETzQR1vU6L8tyiB5wLNj0fw+FO
+jeDdhsZHyvaWzk/1jFCl+thcNI8erz0Yarj/IQEw/FOHjqsJXfGN0mXKxp2chL4a
+haJJ9DIRG3poFASC4G1GZ5gV8eU/bT/pDY4DgQtNz35hoaVAY8X9LulMhRWLF1GV
+YNn2JMMS/WCJWL2KDxapReOsAY01RECEEd5/QUIUL3MAWysV5ckNCmr3h6CJvxSQ
++GC/b+G/KNofuu20ank80ut6ES0tDyZVMdu+6tLyyPY6DQDyKFMkAK08as4FbIFp
+GuNUIE351wSxlu05lYIsVw5hY0x3v9b5aGejV89lqp2V9PuWTFs/JqGeGE9bvb6+
+4G2cH8TsSTtx+7wzTAakEaH4yqSRT02GBfI2bwiF6GDSGAiv+raqIHiu9vdqeHBN
+SIHDGUVOKcssSnDQVQfH/c0OENDuxQNg+/mASckKK6uVs+B2NPuwCprib4en+LWr
+2M1YNMUZGmeiZduz8sbUDcZ7rcExKQdwiWQO0GNNBmug9WqtUhdXHwiDMy6/im99
+G1PUDrHwUb+Ica70+tSqnGr+A++z72smLYGGsjUHdgMm/xisrCG7yYu3+NJMzq6k
+48+zzIz/6YxTRe8DE/5ebLh6YF4DX7BIMp9z/fwvN/HH8UiR/yrYafvJSlveLVuU
+QwP/74c7mkyP+YHreOREIZQ/TAQ0Vskp5lJsic+Ks24Ymek3GRDC6/GUb5kC2zvR
+5kBeRn0cHX1Atjil0Ab1NQNviAoy9oCUvbShI6CC318mQzT3Z7mrzbhUbExKUWzE
+NhTIji120JCjLIivK+bf2eb0/V+8y42QitAz7tRTqn5feijSHMrlM1IP58kfGOgo
+YQEj0LFBf+WS8fNaMTQOxFw8q0E1xcG4qpsf1IwTjifAcq+qvsI0Mrme0BFksAoX
+vlo08StPR0Bblj0+Ghreh1i1c7r0sT3ykdHmEUZABhud0+XfoW8qzbH9djGcQJVD
+ltGmvEoTj0sbyjavbaNitje+0UVC+UGC0w19g1DI0shYpcvpJ55guX4Ksd6D7lQE
+eqWA/IzcCdlyvn7piv4VTRT9f0gAy2swPJD4LFxnKsqOjTE25fZfHfPTZUFcbC1G
+snEWScz1KwKCBOMAhzoWJ9oiA6D2CLIAHraRMOP7ZoQHzaeRIffIp9p6Ld5mFTFw
+KI+2uhd971zYWAVQO2PWz+m/kJjjeQrdmAYCUnEB69XkXlgKLLr3i0nD0NCE6jZB
+23rdv3iiXigZrL5Uuvx0WSwHu71XdIeKw+xWWyluUsWJRsffsOGOgV51LeV+dNjH
+Ntd2tJ2TNWqGBhLA1r6b8DO1x1hRG+/eepSxInz8+TEH355pqRme6auTBRKqsy2z
+fxwLjDzaXsgZV7nmhMVeecAfvAkR4aQugqj/ac3bqs/FdAAFf55WGiQWJL/pLwrc
+K4f0oiGGMU3tGSwUHp+rVsxI1gpSqv1MfX5JXKgRpw23mxMm8JPWIOi/xXzemb1l
+N+qYzsH0FJ8Xr86cXLoOlzFpHZNHEq3xNIeRhGAk1l8rKqUIduaOKeM9izBRtiLJ
+ZXckvVxvqPzwW/SPvzl+M3kC5dHlMr8idUVI7Hd4gHFQ11eekCER9j8HGQi+FjBk
+MF6eOqBuopVeHlUTgDnZ39G//h0ETa/DB0jHZw5IOHSAHGjJIGTELUTrO8/ONZYS
+4Tx9TkV2JDikNjD5i8CUOXC7rVXOiuA0P4nhnm17sy31PHIBrKxcncO4ny9go+81
+IsLRsZJMwlx2+9YaYbXL6nPiWBmgBQMb1Xqomtvcc079cqQdvME8MvPYfQewrmGG
+F4aXRQTfTe9/T+8GzGlergHd7kcnX5XC6Eg1f2MDw8zjyvWva+vO/9SNAqBeZuzL
+PaPSvEFJLMJ6AMA/+7BThmYmqZ+NN04XQZekd/6QVx5WDACCpJTVMY7O9UULk7f3
+60ad6zBqBvStS9eG3IXCWukxHfE0+D1fM0PYmX5feC0bI2rz+TSwXfUSIiNpl3at
+iIR7FcGvUpOcGJuKRX2Vq/FKqfW5tQKgQcfx4P0+qLwHoyxUsPIYBA1NrPNOAnSx
+F/K/RTIslQIbTtGq8TBRaLAKO0IYAUqKhWFDOte7mt6HOMCklSFGgsMLQG364w9y
+26CDkoHUu1S9yHE1lPL9QdSi9pize/ATfs8SuCKnYVlPPT+obSpSvBJs64+ChJHI
+8S8r4W8lkYDuSe4UVxjCp9339erx/Ttn2fGk+2A5kn85hTH61q3J3wo1dTML+3fa
+brYH0p1dD8OeaNa1cKs5iwJf1B8F/2laq53zs5+qUmbhmnbpZfg5ZorjtKzUxAeo
+1u5f/Ty8j9Kp1PXNgdtBWKHgMOXrCjm9VJTpWWZFoIf7l2M1yqAqgJTU8OJPn6qh
+MUIDca/EUzzbFGWRlALhA0ZF0s+QJwzdgUg7vS24Mih0hXcU/WBAe6F6V0oYrEK+
+fUGXgPb6kRuh1KaK2aYjwtGTTzuvDYQWqUDobBl4x4BPKQSzq1KZL+Y6RDFt6PNZ
+H1ZXjM2YJcxHTO3/cLa4S5jBdHCvi9vMy2A0N4QM4t6cdZxF77ErVSCNi2Q2fDLR
+0W4Ociaa0TUQLnX171KYpDCJSZn3lYN7uAQ9/kJar7judAhQ/lKKF/gXRhp9hUNw
+ukB1eUacI0xIzem6QEtMfwfSfiMO3b97yrHHUPxpYmWDkYBhItDKiIv4F5REwFVA
+Sbnqcyn/s8Bq07rL55lvjqDsQPeLF/n87jHmLb3NeqEGR1rMZ1zEDbxctQj5Kn4O
+/Tkk3D+Q5SVCEyrPM5ECIQDMTxlHVgocn3b4nBVbW91u9cVkpMzcOuSysDBygGsa
+UQ==
+-----END DSA PRIVATE KEY-----
diff -pruN 1.4.0-1/test/engine/dsamax.pl 2.5.0-0ubuntu1/test/engine/dsamax.pl
--- 1.4.0-1/test/engine/dsamax.pl	1970-01-01 00:00:00.000000000 +0000
+++ 2.5.0-0ubuntu1/test/engine/dsamax.pl	2025-04-16 08:09:24.000000000 +0000
@@ -0,0 +1,7 @@
+#!/usr/bin/env perl
+
+use strict;
+use warnings;
+use test;
+
+test::dsasignverify("max");
diff -pruN 1.4.0-1/test/engine/dsamax_pub.key 2.5.0-0ubuntu1/test/engine/dsamax_pub.key
--- 1.4.0-1/test/engine/dsamax_pub.key	1970-01-01 00:00:00.000000000 +0000
+++ 2.5.0-0ubuntu1/test/engine/dsamax_pub.key	2025-04-16 08:09:24.000000000 +0000
@@ -0,0 +1,82 @@
+-----BEGIN PUBLIC KEY-----
+MIIO7TCCCf0GByqGSM44BAEwggnwAoIE4wCS0puumWwGMJ7DmPiOhKy4GxBP/dFs
+Fcs3OXi5wU7qCwqrTbStVJRUMLT3uxHe/mB87nZZdU9F8GeJnJX9a/x4rj00KvEd
+MrbCPnHVNqhw8LDKsYuhJ2PlVoLYdHlOKKosTfuIFIRd3TRkTd+T25x1y3fzen+J
+iD0I8fooAMBK8SdnQJsHvr9aI/hGMCbjXMEA4eTKpIjc3yjrt71QIWT3gb/CJKqK
+WqWKPYqP9sPljustQTAq9P+xSwTUG/XTmEsJ8xTbOxXkKxPHk3TV/D56IHQ0sfIv
++TkQ8/Dnar9LpRAYJwnGZzp5w9Z53vSNyxwflcOnvGUEsD/SmSrW7yF6fMnJSwxQ
+4b8mRAHt1ujDh2VU2GsgQMm/5XXAPaObtgGmn07PdDw9aIRjfz7u7PKydB78rq4e
+UnUJeo/KKFaSampha0c1482TJ2Phk0ucl8pWpKKdR2EnDOEX16sG3T9YI4XdbXeI
+D7VrBaM3XzQrAvpUiw38RDT6aWX6hZUvmazJJsU2dl9dqQLDPqO3X5VXzpSiLv3K
++31CRYn4prSEX/2IK3sOU0fWEEGj0v1MXRQfIxqnIzHMqu81pjFNpUKN+Wyl/2cz
+VAO0jREaxX/TdAoPEGBiS6lX4M5/ghRQy/vWwouXlhxffg2TL+47RMKQfb3xSVvs
+LWEpMJv+ITNgcaW3dLLZ6GIQxGSOAcjVOTVESyI7FbHrf+MsJZ6entzgW0k6Wuxb
+cZTBCXiFpCmOhfkJRTaMp9f2VfUFMu+Zy3BkDLr545oxhQbFnXzf6QOqIluiZJan
+Y2s1LeBD2A09sOQEUI6Oo2E23x3NWy36e8DMgznDH2REeKQqONAavlawTSdynxHX
+UJbPUtE8gqP4TB7qRRb6oXS/h2zFlwh6QqRHASQfSZwe5g7qDl/FSedYFlRQ/6Kc
+OMVonw2UGvFwoGBnPR3znfuwpLElipNYhdYrpQxJWQqpIAECi0Z1nxxjoO/7bam1
+28ecrpXavKrjv7FlqOlzls2S8GoIy01pk6sy/0DVRII3h6PstagGaE1QxB325ANe
+WuNHtGevz7A7qL3V74jzK44cWM4IUo0eRrfBcyl5LhaCIfclPNWGJOz5vzW74bxy
+Wts1058s7J9t5FAdctnYOeuu2tYr9a94iANjODoOCPiImwXJiVXGUWgCsY9+zxWE
+rRbOE+zqJZBgIqGIagfJfb9rBmg4Whg1fGKUsTzebXLDxQzrtMFSgBHQQ95cQJ7T
+d1ASGisPB9pw0jD4K9GA1AetdMBMAZQMvpWECx3FjnTM51bqcQ4q05AtHY1GcpJO
+fhJrgHbdn21mj+/hRdW2kbX8KHW9jLc4aEZK9jrDNwoBHaK2fKtdouhJj8bDAC+H
+wvvRrn4mE4AQuqRaYcqD+CZQHUISEa5Xhfzu4JqFZcPt9oCRyPWU8owRQHhmpCzT
+nnAx38VQHhPfaIjMRTftJF8xLk6DSNnzc8kaOtk/Ycktvxv/zwBu2ggF6854oke1
+Q0ODgwpCUt2m4Bgf7kFuOyn2iPwhK+uic255OE7d6yDJtrTsTdCAKTpBaOBhn37k
+0/i4i4ipNJDPwrkIVo5PqHunB60QQWzNxg3lg+yUXNbwLBgj839eUFx+RMrgYF5p
+ipxrRLlIEad29qk0EIAwbjXQL33ykjADR2Mz7QIhANtP7EVcuFoJcMs3cf/dwV8f
+CskDKy6BOQhKGuwEDT2rAoIE4l9NHvYqiPq5HPC6dYfrDxVWYZUtYdBLaa8nYSe4
+Fo6GQQ5RBE+vSk5gtw9OCTct5tvPwjaPzV+J12jv6bFKnaDaqenpyyk7SgkmPU7o
+TU1mqPqVUdm7H1oLbGC30wiLvnhTMc18tjAbAhh4ZdWqnDsktphipl0C9h3Bqbwp
+Y21bq12Jw8uLsGLh5T2m892aHLJdaTL7tDxAjR3YAGBlUcqkxk+hednRsAa5CAKE
+BMT67ZfKaGI5HJevrCTaoW3T2WSra8TeIUzdaPaGmEXhHkYtlf1onOeMlJRJH/0r
+gWsbfh/R9Da+ODWp6jNWKziMB70hI20wYCMtL+G6OqLe+ENtIAZuA46TsZAKUacu
+yMjOn4FiFqZQ8QqGPBeug0gokSyzthTGo0Mspv2YwRLNrxotqDxkcrOHqipOrCLl
+L2Tf9wWLMAXLdO+zihGqHM9R2Gc8y2+w8sBo6EkiOXgQSe0AylJhbvRoiKxb+Fmk
+mogRs4Gvi5CZAWo6mqwF+JjfMmr8A+vDnsfq0egK0RW9kFp2DYqtmMNWsRPNBHW9
+Tovy3KIHnAs2PR/D4U6N4N2GxkfK9pbOT/WMUKX62Fw0jx6vPRhquP8hATD8U4eO
+qwld8Y3SZcrGnZyEvhqFokn0MhEbemgUBILgbUZnmBXx5T9tP+kNjgOBC03PfmGh
+pUBjxf0u6UyFFYsXUZVg2fYkwxL9YIlYvYoPFqlF46wBjTVEQIQR3n9BQhQvcwBb
+KxXlyQ0KaveHoIm/FJD4YL9v4b8o2h+67bRqeTzS63oRLS0PJlUx277q0vLI9joN
+APIoUyQArTxqzgVsgWka41QgTfnXBLGW7TmVgixXDmFjTHe/1vloZ6NXz2WqnZX0
++5ZMWz8moZ4YT1u9vr7gbZwfxOxJO3H7vDNMBqQRofjKpJFPTYYF8jZvCIXoYNIY
+CK/6tqogeK7292p4cE1IgcMZRU4pyyxKcNBVB8f9zQ4Q0O7FA2D7+YBJyQorq5Wz
+4HY0+7AKmuJvh6f4tavYzVg0xRkaZ6Jl27PyxtQNxnutwTEpB3CJZA7QY00Ga6D1
+aq1SF1cfCIMzLr+Kb30bU9QOsfBRv4hxrvT61Kqcav4D77PvayYtgYayNQd2Ayb/
+GKysIbvJi7f40kzOrqTjz7PMjP/pjFNF7wMT/l5suHpgXgNfsEgyn3P9/C838cfx
+SJH/Kthp+8lKW94tW5RDA//vhzuaTI/5get45EQhlD9MBDRWySnmUmyJz4qzbhiZ
+6TcZEMLr8ZRvmQLbO9HmQF5GfRwdfUC2OKXQBvU1A2+ICjL2gJS9tKEjoILfXyZD
+NPdnuavNuFRsTEpRbMQ2FMiOLXbQkKMsiK8r5t/Z5vT9X7zLjZCK0DPu1FOqfl96
+KNIcyuUzUg/nyR8Y6ChhASPQsUF/5ZLx81oxNA7EXDyrQTXFwbiqmx/UjBOOJ8By
+r6q+wjQyuZ7QEWSwChe+WjTxK09HQFuWPT4aGt6HWLVzuvSxPfKR0eYRRkAGG53T
+5d+hbyrNsf12MZxAlUOW0aa8ShOPSxvKNq9to2K2N77RRUL5QYLTDX2DUMjSyFil
+y+knnmC5fgqx3oPuVAR6pYD8jNwJ2XK+fumK/hVNFP1/SADLazA8kPgsXGcqyo6N
+MTbl9l8d89NlQVxsLUaycRZJzPUrA4IE6AACggTjAIc6FifaIgOg9giyAB62kTDj
++2aEB82nkSH3yKfaei3eZhUxcCiPtroXfe9c2FgFUDtj1s/pv5CY43kK3ZgGAlJx
+AevV5F5YCiy694tJw9DQhOo2Qdt63b94ol4oGay+VLr8dFksB7u9V3SHisPsVlsp
+blLFiUbH37DhjoFedS3lfnTYxzbXdrSdkzVqhgYSwNa+m/AztcdYURvv3nqUsSJ8
+/PkxB9+eaakZnumrkwUSqrMts38cC4w82l7IGVe55oTFXnnAH7wJEeGkLoKo/2nN
+26rPxXQABX+eVhokFiS/6S8K3CuH9KIhhjFN7RksFB6fq1bMSNYKUqr9TH1+SVyo
+EacNt5sTJvCT1iDov8V83pm9ZTfqmM7B9BSfF6/OnFy6DpcxaR2TRxKt8TSHkYRg
+JNZfKyqlCHbmjinjPYswUbYiyWV3JL1cb6j88Fv0j785fjN5AuXR5TK/InVFSOx3
+eIBxUNdXnpAhEfY/BxkIvhYwZDBenjqgbqKVXh5VE4A52d/Rv/4dBE2vwwdIx2cO
+SDh0gBxoySBkxC1E6zvPzjWWEuE8fU5FdiQ4pDYw+YvAlDlwu61VzorgND+J4Z5t
+e7Mt9TxyAaysXJ3DuJ8vYKPvNSLC0bGSTMJcdvvWGmG1y+pz4lgZoAUDG9V6qJrb
+3HNO/XKkHbzBPDLz2H0HsK5hhheGl0UE303vf0/vBsxpXq4B3e5HJ1+VwuhINX9j
+A8PM48r1r2vrzv/UjQKgXmbsyz2j0rxBSSzCegDAP/uwU4ZmJqmfjTdOF0GXpHf+
+kFceVgwAgqSU1TGOzvVFC5O39+tGneswagb0rUvXhtyFwlrpMR3xNPg9XzND2Jl+
+X3gtGyNq8/k0sF31EiIjaZd2rYiEexXBr1KTnBibikV9lavxSqn1ubUCoEHH8eD9
+Pqi8B6MsVLDyGAQNTazzTgJ0sRfyv0UyLJUCG07RqvEwUWiwCjtCGAFKioVhQzrX
+u5rehzjApJUhRoLDC0Bt+uMPctugg5KB1LtUvchxNZTy/UHUovaYs3vwE37PErgi
+p2FZTz0/qG0qUrwSbOuPgoSRyPEvK+FvJZGA7knuFFcYwqfd9/Xq8f07Z9nxpPtg
+OZJ/OYUx+tatyd8KNXUzC/t32m62B9KdXQ/DnmjWtXCrOYsCX9QfBf9pWqud87Of
+qlJm4Zp26WX4OWaK47Ss1MQHqNbuX/08vI/SqdT1zYHbQVih4DDl6wo5vVSU6Vlm
+RaCH+5djNcqgKoCU1PDiT5+qoTFCA3GvxFM82xRlkZQC4QNGRdLPkCcM3YFIO70t
+uDIodIV3FP1gQHuheldKGKxCvn1Bl4D2+pEbodSmitmmI8LRk087rw2EFqlA6GwZ
+eMeATykEs6tSmS/mOkQxbejzWR9WV4zNmCXMR0zt/3C2uEuYwXRwr4vbzMtgNDeE
+DOLenHWcRe+xK1UgjYtkNnwy0dFuDnImmtE1EC519e9SmKQwiUmZ95WDe7gEPf5C
+Wq+47nQIUP5Sihf4F0YafYVDcLpAdXlGnCNMSM3pukBLTH8H0n4jDt2/e8qxx1D8
+aWJlg5GAYSLQyoiL+BeURMBVQEm56nMp/7PAatO6y+eZb46g7ED3ixf5/O4x5i29
+zXqhBkdazGdcxA28XLUI+Sp+Dv05JNw/kOUlQhMqzzOR
+-----END PUBLIC KEY-----
diff -pruN 1.4.0-1/test/engine/dsaparam2k.key 2.5.0-0ubuntu1/test/engine/dsaparam2k.key
--- 1.4.0-1/test/engine/dsaparam2k.key	1970-01-01 00:00:00.000000000 +0000
+++ 2.5.0-0ubuntu1/test/engine/dsaparam2k.key	2025-04-16 08:09:24.000000000 +0000
@@ -0,0 +1,14 @@
+-----BEGIN DSA PARAMETERS-----
+MIICLQKCAQEA9PsXA/O3soomi8tuZOLnjLSH8lBkpfSvOsnrKjegF/wGafTpAIwx
+ucPyRvC3B03lI6IHH/pTH42/Lbkk46Qnm7nLOQKkBq5SOvx4DecZfewtNXvPZ4Kc
+qrFCs/xjR+qHJ10HntcHBW7yKzoNX7Q2TXj78FUSIQe0f4PCaiHJwMgzibcJUBfO
+xegjY38/MQkTGyaOEzFwTrcTCO6O5xokhTmqbPP3Zw2FOqEwcm7vnCXmUU+ffE5E
+b6HIpUYOy947A6hEHz4mbFAPx3/IqslKICegqmu6sVtWAVVPhn7BCbjvNFZTznuW
+3nkp7bGoeOG1q6ssM096OxLs9MZS7aGeOwIhAInYRUBxrHUZ3ji+E3MflNjwT7pW
+QV50gthrPe8k+Ov/AoIBAQCcqYFWlR3hVHtlLtewB85Wo/vbyq/NvilF+RyTq6Q0
+U0R+9+1E/wmoBvhRpiyYh75fDVD838rVq8FeCdk3drdZ0PlvnReLQN25AJeOI5uN
+YWIjAlPwhpsYUA9f/GaD6zJPldCv9xCHeB7KyJwl2L9bcUFs+S0S8IK3Gh2lguUR
+h4byBcPSKUmnZf0EELXe2tf/U+wNd0xJh/inRnFfZ6LgxY3KTun5Xv7efEQhCaXE
+aSfQDqEDaAfC1VVhJVF+jKTNaqwpwWWe+OWj83IseFOFOyRGz2YrlEFcPDMUbG20
+FocKan8RjKDAmo8zBD15Ieid9NceJzujetJF8VFGHVd4
+-----END DSA PARAMETERS-----
diff -pruN 1.4.0-1/test/engine/dsaparam4k.key 2.5.0-0ubuntu1/test/engine/dsaparam4k.key
--- 1.4.0-1/test/engine/dsaparam4k.key	1970-01-01 00:00:00.000000000 +0000
+++ 2.5.0-0ubuntu1/test/engine/dsaparam4k.key	2025-04-16 08:09:24.000000000 +0000
@@ -0,0 +1,25 @@
+-----BEGIN DSA PARAMETERS-----
+MIIELAKCAgEAyJscZddSVlbzJWUul/6UyYNMDQ5Tw1Hw7qCKGWmpAAZlASED5TPD
+60SgxOBMZK37oce07VBxe/pqXLdMEGN52Fo7ST1mil5/Rb4JUvexLX9NCyz7LBNg
+hIXQtb7826dK79Q6t5nXAHmUl/1WeHOIgwSDD9ECU6km/O10CPKrWlN187ob8lk/
+/zA0uU/J7ZN8jFei8d0+E+tSRRhDtNjJPXw9uHc+Vd2H8rfGh2riP0x7mH/h+32U
+hcPbKyRtzgweeCYbdS5rLaWCoKAawNg2QbpotFf9LFWqohnkHINpgHQuBCDAuriN
+euRX4yNR/SA1BXx/esKRxqnHs34qxSSZzHea1uIWi/IbmU0xFT02tWwZ2MKFpmfr
+ky3eJY/L6wSyIrrbRp42m6sV4R5jjTg8nTOl1d4N/JVe6ttXH4fx/VDc/3vLktiv
+69+dKCgsGrEo+vBKpqY/6CXuHFYSVZM+7NTMKGilsT59gP3Oc9I4ApF/Z/MuN9jv
+k3Uyz1uUDszbmVYjHhWg5g/O56G8wj+QH4XPfW4IS0AiaHkuo008xJDRcJbBVzbx
+z+gxZqL143OQG9ciXxw+imnbSNsyB+a5cpKOEwgoeU3lIRhe8eRtvWfzfT8T94LN
+8xU7w6tzoSk0JxLTdIBKHDST17jmato/SA4uwz31uaWA7eU3gv4u1DECIQDHoioD
+Wzjed2EIPznryUN1YsmeTbyn53t4Ud4o4DLOmQKCAgA104Ps34soiOkNr9e+e8tb
+kbOhix2Brr2OM2c0V5CGU6Oofjila1hNp0DfoN/jrPzZT/qeiyuIleqVjlpaV/f6
+PJn2FX9Amx3g8Yhy4ywP3PPU71Orbj+LiP56cHanvHEe941HKxUpXcYSHOskE6AQ
+gl/pz6leBqfnWUjAPQbO7jeYVhuaz8ovfke7G6gZNDpiVV8BLdJ9c4OF+83SimDJ
+CdFnRYrbtDXsYHCv2FK7bLDaujJEOqjNFjQX5/7OCSb7f17zJtM7BZkAZR89+N8K
+uOjQKVqzCFqGaNAB/7VPgzHjzAoi9YGNHoM2Jy+aY0kv1hBjkwdw9m/gnNZAREMo
+tXtVbxnV9+ppHpacA9eqHE1XIeIE7SN8D1snQAGhRmvlOUAuyhnDOnkBvvCXnoAm
+lprDX4P+WtBdGXFDKGkYz7LRZt6jT1GasgXdjscYlEUb/Y7Hklyn56tTlLEVICpZ
+qWt+mRB0gXrt5NDpq6rvq2Xv7A545H48dQ4Q8HisOkn6VoedhR6jMfXCM93SOx0l
+g20cmfYMp3H2OYW1z+j1K4Lgsqdf+RX1t1glvS+1vP/Mp/xqFvc0T8D7YYMYnjQj
+ZE8zgDKqlGeeXYKxzD9EOeVAzftS37E2A3TGSG0JOPN/c4jtO1EAIXJobRmPkTFa
+AbIF7OuseZPHf6QYGuelbg==
+-----END DSA PARAMETERS-----
diff -pruN 1.4.0-1/test/engine/dsaparam8k.key 2.5.0-0ubuntu1/test/engine/dsaparam8k.key
--- 1.4.0-1/test/engine/dsaparam8k.key	1970-01-01 00:00:00.000000000 +0000
+++ 2.5.0-0ubuntu1/test/engine/dsaparam8k.key	2025-04-16 08:09:24.000000000 +0000
@@ -0,0 +1,46 @@
+-----BEGIN DSA PARAMETERS-----
+MIIILAKCBAEA/buSBt58jvUKAG7eazsdnUEmS0JRrgBQj4fLGiFpe2gsDkxFnXiD
+a5NZQY+2Ui6GrfgB96rED73GH1lDl8FhHYkgc9vSgROc0opeGU760QpN5LCeVADG
+9WLoBcwGAjrvkDeDm9iHC/HRwkj1au0SKAeSJcM7RT6swnvAnn97SwdVwFt8n1gT
+xbAoFBpudjXxjFuaKbsBvJ1ln60llOHywVpk4gkA+tfzIIJzix4fhJmuLWUQSTqA
+2lKJO5gOxglIpPB3ffPkqvE2WQA/PEyeLChGz6YyDA/EkXt6i0JL3chz/oOBP/KU
+eJ57HmtSc9HworLb4EB6ANPjVxnAQrs75qFBbA4BhE0UDMNzOI3JDo6VxVPL46E5
+unlmZ+XCnJkWZl9FHX/bhxiDMz7jJqef9Sx5NjexKhd9vsbAw2GV0V4kHu4ASIpW
+A53DOj9dmif8fK9fXRohACKFlzdf44S8zLSUJ5+J9tsDBCjcjNjy+EnF2eEf9QMz
+Qf5E+vqcoP75xud4BtBSqIIrqjvVPuWJm8RfNg3FyXKwoatXh1BuicHWo+bTbTmV
+QXWGYQ4Co0asDdoLfsPKrMKeTKVyj8GuxbSWRKbNDXPJCKF05gGx5vr5SpmD4CJH
+Ef4scDSlBe7ATF24tj9/vpCht4WflXK4nwlfNLdurF9vQaNntT3fEFqtjQsBLF0d
+EKpetwEBxrGEph3WC3XJT2zZu6Wbvnjg70TMBw9d9PD1poqTQH4l5ca70ct19Tc6
+gtFL2C/nKowV5KZBhfkNe8+01CbenFy+1GPy2+iJq/SQRz+cAXKy+3hEtgk4Akgu
+zeBqSizYuJPvUujAiHFdbkWzg9fl0dDtJVU3mZRrJK0Znyn1CiEkK9iMdhtAm+So
+vbhwpLU3f85fTK6+QebD65a7NZucJI6u7v6wbFjkuh6+CBIaZwYsgTV4HjV+lOai
+bbbMDxdnTeXaGnxdPnA9s6SyTvRB4YskzgmckE+rmBJpGCDpyIQ/h0Nx1755ZwhG
+iZ8rJ7FbTzsa6z6x1Z7j5Dk5f+dkfQPWk8H5xrVsoKNUqODxT2K91eWW0jIa8+rs
+bq96t4b2fUilRjY771lQ5lmJju1IvZJRVFztup7H+lD42S9knPmurop52lHKMuiA
+p62oQKBO34d1ZAtziQnUkhThHDYJWoL/FzNmN09l8ruU8jE6Mr1b+3F0GgfdbFAz
+rK0BMANVuCCT4Yh9iLd2/04YImJJF0FdH/GP5ngsGZBsSZd7pjUjkJ6ERrJt4dvj
+9i7T3bnVMHWM1xYSIUw/z7b+Q4ED7itoYA1XP3dY0+tIV8BM0/RYnbqlWPcwsknN
+LiXyRHf8UVcB6rdGQ9FvOVpU/14KCkInbQIhALBSPrcNsGUGR3AOrsvlvhWz5HJo
+9c9AeKmlls9Y2rjTAoIEAGxiLBTfQJ2nQUXecIfTJBqQwiBumeMQwEFYXTDd+Htj
+FMQTBwcMJg+N8MO5iZQcwAvFLYHT+dMHLgyHo41rdvGJtCbB7fPtcRYZNSseYONH
+s5ga1pN1Mpj5M3yFmQlGR58lWa6MHlXR2bheanmKHQq5h8t85JYHtlrWZg7oZC9m
+YQ5EKPfW7ITlpLlEB4rn7Doky3jOPSMtwBfpzU6u5zEgOwVSSnf3GW0ktByG/+Db
+AkQtQswDUyJYmLo9wc7Y5ye6FSCovtifoKVmduvFOaZe5BI3I0cgt1LtDPOT2UGk
+j03exS9DaCZ73YkQCjqof+wEJ4lH/egrTCPNax6E1ggX/z+0+IZFEKXEE5iGnIsS
+hTgl0MDDdBnwA8pfJ3ku5sEtCiUYAja9KIIvY4e2oLTpGjZYP+WMjdVtOWBT+QT+
+TjPV3Ni8c02nDeNZTZYuIEre8xh2jPizTk/lOcOkY8KPpwgHQ+wlxWFbiaDwi2zf
+U0Z+op4j/CiHviODH0+7NuZTY7PXextb/ZEIS54vNjWmBiNL+20+/mJR/HWpmOtw
+8Oj4ggbREBAUpINM/q29ISY4EqXV5Fj7RpWUSCCVrhB51G7AWx9pobmenCD0qoqQ
+Cu/lxSJ3ictDiQ76aYk+MqXsvduRki/KsrcK384vD5MnWSkPLSA0b10I/V4MPqF5
+x0EKPbjoM1AyAgf5UZzW9PxADrW+ibNvXVqSAka3SYDT88mAlhTfs2FWROGo7peM
+zcHFCwtwAoPTnIGIW6IPDswp4aVVZ//0nebg2Dd/L7QN9dYBf2nH8rLBrnk4HHOh
+DKmHBv7EJx0krgLds0TaJt9PwfbEpMjHuLoMC46UhIv6qmHTLdZpmTM97aS32IvD
+ZdfxRpQ1CiaiKD2WvVv3CKsMJSuMQztNP9nWjDrX4AQwdBTz96wvnxZqdZHrM+wP
+TKT2TOrsFS9ShfmW8rT5MsUoFivSsuEwps2x3+3g92fmjwdT1+ONDdJ6+Zp/Tw6h
+hYUu+IKjqenGY1Ac76NpxjJSS01Yv9g7KImLGuyg0ZryMN+CMEwTdtAZhEjtGFJy
+Si2gR8LiXe2PuQrA4laIjGgIZd9ay8LpOBlInCU2DfmZNVD7p08RGZuYHi2Tosjk
+6RIuAF0gLwnYMHqyuW+bnj8/4wopF5br5/21nDhDN1BjDq8NFBHaCwBnVoiMQ2b4
+gWdYk/mvKZcH6Tty/ZbcsTzghmJBI9b8nwQJU1erbdVk815FSEtBt+Tp55Yk9wR2
+TtV823t9QiWuqnnrOAxurfVamGfsUjxXXMkzplZAosoQI22CEyxQUGQrySDYQ+J3
+YfSbUsCGzaYadGnf2s/IwBbEitTlLIZgqn/JMnVU71w=
+-----END DSA PARAMETERS-----
diff -pruN 1.4.0-1/test/engine/dsaparammax.key 2.5.0-0ubuntu1/test/engine/dsaparammax.key
--- 1.4.0-1/test/engine/dsaparammax.key	1970-01-01 00:00:00.000000000 +0000
+++ 2.5.0-0ubuntu1/test/engine/dsaparammax.key	2025-04-16 08:09:24.000000000 +0000
@@ -0,0 +1,56 @@
+-----BEGIN DSA PARAMETERS-----
+MIIJ8AKCBOMAktKbrplsBjCew5j4joSsuBsQT/3RbBXLNzl4ucFO6gsKq020rVSU
+VDC097sR3v5gfO52WXVPRfBniZyV/Wv8eK49NCrxHTK2wj5x1TaocPCwyrGLoSdj
+5VaC2HR5TiiqLE37iBSEXd00ZE3fk9ucdct383p/iYg9CPH6KADASvEnZ0CbB76/
+WiP4RjAm41zBAOHkyqSI3N8o67e9UCFk94G/wiSqilqlij2Kj/bD5Y7rLUEwKvT/
+sUsE1Bv105hLCfMU2zsV5CsTx5N01fw+eiB0NLHyL/k5EPPw52q/S6UQGCcJxmc6
+ecPWed70jcscH5XDp7xlBLA/0pkq1u8henzJyUsMUOG/JkQB7dbow4dlVNhrIEDJ
+v+V1wD2jm7YBpp9Oz3Q8PWiEY38+7uzysnQe/K6uHlJ1CXqPyihWkmpqYWtHNePN
+kydj4ZNLnJfKVqSinUdhJwzhF9erBt0/WCOF3W13iA+1awWjN180KwL6VIsN/EQ0
++mll+oWVL5msySbFNnZfXakCwz6jt1+VV86Uoi79yvt9QkWJ+Ka0hF/9iCt7DlNH
+1hBBo9L9TF0UHyMapyMxzKrvNaYxTaVCjflspf9nM1QDtI0RGsV/03QKDxBgYkup
+V+DOf4IUUMv71sKLl5YcX34Nky/uO0TCkH298Ulb7C1hKTCb/iEzYHGlt3Sy2ehi
+EMRkjgHI1Tk1REsiOxWx63/jLCWenp7c4FtJOlrsW3GUwQl4haQpjoX5CUU2jKfX
+9lX1BTLvmctwZAy6+eOaMYUGxZ183+kDqiJbomSWp2NrNS3gQ9gNPbDkBFCOjqNh
+Nt8dzVst+nvAzIM5wx9kRHikKjjQGr5WsE0ncp8R11CWz1LRPIKj+Ewe6kUW+qF0
+v4dsxZcIekKkRwEkH0mcHuYO6g5fxUnnWBZUUP+inDjFaJ8NlBrxcKBgZz0d8537
+sKSxJYqTWIXWK6UMSVkKqSABAotGdZ8cY6Dv+22ptdvHnK6V2ryq47+xZajpc5bN
+kvBqCMtNaZOrMv9A1USCN4ej7LWoBmhNUMQd9uQDXlrjR7Rnr8+wO6i91e+I8yuO
+HFjOCFKNHka3wXMpeS4WgiH3JTzVhiTs+b81u+G8clrbNdOfLOyfbeRQHXLZ2Dnr
+rtrWK/WveIgDYzg6Dgj4iJsFyYlVxlFoArGPfs8VhK0WzhPs6iWQYCKhiGoHyX2/
+awZoOFoYNXxilLE83m1yw8UM67TBUoAR0EPeXECe03dQEhorDwfacNIw+CvRgNQH
+rXTATAGUDL6VhAsdxY50zOdW6nEOKtOQLR2NRnKSTn4Sa4B23Z9tZo/v4UXVtpG1
+/Ch1vYy3OGhGSvY6wzcKAR2itnyrXaLoSY/GwwAvh8L70a5+JhOAELqkWmHKg/gm
+UB1CEhGuV4X87uCahWXD7faAkcj1lPKMEUB4ZqQs055wMd/FUB4T32iIzEU37SRf
+MS5Og0jZ83PJGjrZP2HJLb8b/88AbtoIBevOeKJHtUNDg4MKQlLdpuAYH+5Bbjsp
+9oj8ISvronNueThO3esgyba07E3QgCk6QWjgYZ9+5NP4uIuIqTSQz8K5CFaOT6h7
+pwetEEFszcYN5YPslFzW8CwYI/N/XlBcfkTK4GBeaYqca0S5SBGndvapNBCAMG41
+0C998pIwA0djM+0CIQDbT+xFXLhaCXDLN3H/3cFfHwrJAysugTkIShrsBA09qwKC
+BOJfTR72Koj6uRzwunWH6w8VVmGVLWHQS2mvJ2EnuBaOhkEOUQRPr0pOYLcPTgk3
+Lebbz8I2j81fiddo7+mxSp2g2qnp6cspO0oJJj1O6E1NZqj6lVHZux9aC2xgt9MI
+i754UzHNfLYwGwIYeGXVqpw7JLaYYqZdAvYdwam8KWNtW6tdicPLi7Bi4eU9pvPd
+mhyyXWky+7Q8QI0d2ABgZVHKpMZPoXnZ0bAGuQgChATE+u2XymhiORyXr6wk2qFt
+09lkq2vE3iFM3Wj2hphF4R5GLZX9aJznjJSUSR/9K4FrG34f0fQ2vjg1qeozVis4
+jAe9ISNtMGAjLS/hujqi3vhDbSAGbgOOk7GQClGnLsjIzp+BYhamUPEKhjwXroNI
+KJEss7YUxqNDLKb9mMESza8aLag8ZHKzh6oqTqwi5S9k3/cFizAFy3Tvs4oRqhzP
+UdhnPMtvsPLAaOhJIjl4EEntAMpSYW70aIisW/hZpJqIEbOBr4uQmQFqOpqsBfiY
+3zJq/APrw57H6tHoCtEVvZBadg2KrZjDVrETzQR1vU6L8tyiB5wLNj0fw+FOjeDd
+hsZHyvaWzk/1jFCl+thcNI8erz0Yarj/IQEw/FOHjqsJXfGN0mXKxp2chL4ahaJJ
+9DIRG3poFASC4G1GZ5gV8eU/bT/pDY4DgQtNz35hoaVAY8X9LulMhRWLF1GVYNn2
+JMMS/WCJWL2KDxapReOsAY01RECEEd5/QUIUL3MAWysV5ckNCmr3h6CJvxSQ+GC/
+b+G/KNofuu20ank80ut6ES0tDyZVMdu+6tLyyPY6DQDyKFMkAK08as4FbIFpGuNU
+IE351wSxlu05lYIsVw5hY0x3v9b5aGejV89lqp2V9PuWTFs/JqGeGE9bvb6+4G2c
+H8TsSTtx+7wzTAakEaH4yqSRT02GBfI2bwiF6GDSGAiv+raqIHiu9vdqeHBNSIHD
+GUVOKcssSnDQVQfH/c0OENDuxQNg+/mASckKK6uVs+B2NPuwCprib4en+LWr2M1Y
+NMUZGmeiZduz8sbUDcZ7rcExKQdwiWQO0GNNBmug9WqtUhdXHwiDMy6/im99G1PU
+DrHwUb+Ica70+tSqnGr+A++z72smLYGGsjUHdgMm/xisrCG7yYu3+NJMzq6k48+z
+zIz/6YxTRe8DE/5ebLh6YF4DX7BIMp9z/fwvN/HH8UiR/yrYafvJSlveLVuUQwP/
+74c7mkyP+YHreOREIZQ/TAQ0Vskp5lJsic+Ks24Ymek3GRDC6/GUb5kC2zvR5kBe
+Rn0cHX1Atjil0Ab1NQNviAoy9oCUvbShI6CC318mQzT3Z7mrzbhUbExKUWzENhTI
+ji120JCjLIivK+bf2eb0/V+8y42QitAz7tRTqn5feijSHMrlM1IP58kfGOgoYQEj
+0LFBf+WS8fNaMTQOxFw8q0E1xcG4qpsf1IwTjifAcq+qvsI0Mrme0BFksAoXvlo0
+8StPR0Bblj0+Ghreh1i1c7r0sT3ykdHmEUZABhud0+XfoW8qzbH9djGcQJVDltGm
+vEoTj0sbyjavbaNitje+0UVC+UGC0w19g1DI0shYpcvpJ55guX4Ksd6D7lQEeqWA
+/IzcCdlyvn7piv4VTRT9f0gAy2swPJD4LFxnKsqOjTE25fZfHfPTZUFcbC1GsnEW
+Scz1Kw==
+-----END DSA PARAMETERS-----
diff -pruN 1.4.0-1/test/engine/eckey.c 2.5.0-0ubuntu1/test/engine/eckey.c
--- 1.4.0-1/test/engine/eckey.c	1970-01-01 00:00:00.000000000 +0000
+++ 2.5.0-0ubuntu1/test/engine/eckey.c	2025-04-16 08:09:24.000000000 +0000
@@ -0,0 +1,140 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <openssl/conf.h>
+#include <openssl/ec.h>
+#include <openssl/engine.h>
+
+void setup(void)
+{
+    OPENSSL_load_builtin_modules();
+
+    ENGINE_load_builtin_engines();
+
+    /* CONF_MFLAGS_DEFAULT_SECTION introduced some time between 0.9.8b and
+       0.9.8e */
+#ifndef CONF_MFLAGS_DEFAULT_SECTION
+#define CONF_MFLAGS_DEFAULT_SECTION 0x0
+#endif
+
+    CONF_modules_load_file(NULL, NULL,
+                           CONF_MFLAGS_DEFAULT_SECTION|
+                           CONF_MFLAGS_IGNORE_MISSING_FILE);
+#if (OPENSSL_VERSION_NUMBER >= 0x10100000L) &&  \
+    !defined(LIBRESSL_VERSION_NUMBER)
+    /* OpenSSL 1.1.0+ takes care of initialization itself */
+#else
+    OpenSSL_add_all_algorithms();
+#endif
+}
+
+int check_eckey(int nid, const char *name, int error)
+{
+    int            ret = !error;
+    ECDSA_SIG     *sig = NULL;
+    EC_KEY        *eckey = NULL;
+    unsigned char  digest[20];
+    ENGINE        *engine = NULL;
+
+    memset(digest, 0, sizeof(digest));
+
+    engine = ENGINE_by_id("ibmca");
+    if (engine == NULL) {
+        fprintf(stderr, "ibmca engine not loaded\n");
+        goto out;
+    }
+    if (ENGINE_get_EC(engine) == NULL) {
+        fprintf(stderr, "ibmca does not support EC_KEY.  Skipping...\n");
+        exit(77);
+    }
+    eckey = EC_KEY_new_by_curve_name(nid);
+    if (eckey == NULL) {
+        /* curve not supported => test passed */
+        fprintf(stderr, "Curve %s not supported by OpenSSL\n", name);
+        ret = 1;
+        goto out;
+    }
+    if (EC_KEY_get0_engine(eckey) != engine) {
+        fprintf(stderr, "EC_KEY for %s does not use ibmca engine\n", name);
+        goto out;
+    }
+    if (!EC_KEY_generate_key(eckey)) {
+        /* error */
+        fprintf(stderr, "Failed to generate EC_KEY for %s\n", name);
+        goto out;
+    }
+    sig = ECDSA_do_sign(digest, sizeof(digest), eckey);
+    if (sig == NULL) {
+        if (error) 
+            fprintf(stderr, "Failed to sign with %s\n", name);
+        else
+            fprintf(stderr, "Assuming %s is not supported and skipping test\n",
+                    name);
+        goto out;
+    }
+    ret = ECDSA_do_verify(digest, sizeof(digest), sig, eckey);
+    if (ret == -1) {
+        /* error */
+        fprintf(stderr, "Failed to verify signature with %s\n", name);
+        goto out;
+    } else if (ret == 0) {
+        /* incorrect signature */
+        fprintf(stderr, "Signature incorrect with %s\n", name);
+        goto out;
+    } else {
+        /* signature ok */
+        ret = 1;
+    }
+ out:
+    if (engine)
+        ENGINE_free(engine);
+    if (sig)
+        ECDSA_SIG_free(sig);
+    if (eckey)
+        EC_KEY_free(eckey);
+    return ret;
+}
+
+int main(int argc, char **argv)
+{
+    static const struct testparams {
+        int         nid;
+        const char *name;
+        int         error;
+    } params[] = {
+                  {NID_X9_62_prime192v1, "NID_X9_62_prime192v1", 0},
+                  {NID_secp224r1,        "NID_secp224r1",        0},
+                  {NID_X9_62_prime256v1, "NID_X9_62_prime256v1", 1},
+                  {NID_secp384r1,        "NID_secp384r1",        1},
+                  {NID_secp521r1,        "NID_secp521r1",        1},
+                  {NID_brainpoolP160r1,  "NID_brainpoolP160r1",  0},
+                  {NID_brainpoolP192r1,  "NID_brainpoolP192r1",  0},
+                  {NID_brainpoolP224r1,  "NID_brainpoolP224r1",  0},
+                  {NID_brainpoolP256r1,  "NID_brainpoolP256r1",  0},
+                  {NID_brainpoolP320r1,  "NID_brainpoolP320r1",  0},
+                  {NID_brainpoolP384r1,  "NID_brainpoolP384r1",  0},
+                  {NID_brainpoolP512r1,  "NID_brainpoolP512r1",  0}
+    };
+            
+    int ret = 0, i;
+    /* First fix the environment */
+    char *testcnf = getenv("IBMCA_OPENSSL_TEST_CONF");
+
+    /* Do not overwrite a user-provided OPENSSL_CONF in the
+       environment.  This allows us to execute this test also on an
+       installation with a user-provided engine configuration. */
+    if (testcnf && setenv("OPENSSL_CONF", testcnf, 0)) {
+        fprintf(stderr, "Failed to set OPENSSL_CONF environment variable!\n");
+        return 77;
+    }
+    
+    setup();
+    for (i = 0; i < sizeof(params) / sizeof(struct testparams); ++i) {
+        if (!check_eckey(params[i].nid, params[i].name, params[i].error)) {
+            fprintf(stderr, "Failure for %s\n", params[i].name);
+            ret = 99;
+        }
+    }
+    return ret;
+}
diff -pruN 1.4.0-1/test/engine/enginectrl.c 2.5.0-0ubuntu1/test/engine/enginectrl.c
--- 1.4.0-1/test/engine/enginectrl.c	1970-01-01 00:00:00.000000000 +0000
+++ 2.5.0-0ubuntu1/test/engine/enginectrl.c	2025-04-16 08:09:24.000000000 +0000
@@ -0,0 +1,125 @@
+#include <dlfcn.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <openssl/conf.h>
+#include <openssl/ec.h>
+#include <openssl/engine.h>
+
+void setup(void)
+{
+    OPENSSL_load_builtin_modules();
+
+    ENGINE_load_builtin_engines();
+
+    /* CONF_MFLAGS_DEFAULT_SECTION introduced some time between 0.9.8b and
+       0.9.8e */
+#ifndef CONF_MFLAGS_DEFAULT_SECTION
+#define CONF_MFLAGS_DEFAULT_SECTION 0x0
+#endif
+
+    CONF_modules_load_file(NULL, NULL,
+                           CONF_MFLAGS_DEFAULT_SECTION|
+                           CONF_MFLAGS_IGNORE_MISSING_FILE);
+#if (OPENSSL_VERSION_NUMBER >= 0x10100000L) &&  \
+    !defined(LIBRESSL_VERSION_NUMBER)
+    /* OpenSSL 1.1.0+ takes care of initialization itself */
+#else
+    OpenSSL_add_all_algorithms();
+#endif
+}
+
+typedef void (*ica_cleanup_t) (void);
+
+int initwithlib(ENGINE *e, const char *lib, int checkexists, int expectedinitval)
+{
+    void *hdl;
+    void (*ica_cleanup)(void);
+
+    if (checkexists) {
+        hdl = dlopen(lib, RTLD_LAZY);
+        if (hdl == NULL) {
+            fprintf(stderr, "Skipping initialization with non-existent library \"%s\"\n", lib);
+            return 1;
+        }
+        *(void **)(&ica_cleanup) = dlsym(hdl, "ica_cleanup");
+        if (ica_cleanup != NULL)
+            ica_cleanup();
+        dlclose(hdl);
+    }
+    if (ENGINE_ctrl_cmd_string(e, "libica", lib, 0) != 1) {
+        fprintf(stderr, "\"libica\" ctrl failed to set \"%s\" on un-initialized ibmca!\n", lib);
+        return 0;
+    }
+    if (ENGINE_init(e) != expectedinitval) {
+        fprintf(stderr, "ibmca unexpted initialization result for libica=%s (expected: %d)!\n",
+                lib, expectedinitval);
+        return 0;
+    }
+    ENGINE_finish(e);
+    return 1;
+}
+
+int testctrl(void)
+{
+    ENGINE *engine;
+    int ret = 99, i;
+    static const struct testparams {
+        const char *lib;
+        int checkexists;
+        int expectedinitval;
+    } params[] = {
+                  {"doesnotexist",    0, 0},
+                  {"libica.so.3",     1, 1},
+                  {"libica-cex.so.3", 1, 1},
+                  {"libica.so.4",     1, 1},
+                  {"libica-cex.so.4", 1, 1}
+    };
+
+    engine = ENGINE_by_id("ibmca");
+    if (engine == NULL) {
+        fprintf(stderr, "ibmca engine not loaded!  Skipping...\n");
+        return 77;
+    }
+    if (!ENGINE_init(engine)) {
+        fprintf(stderr, "ibmca engine initialization failed!\n");
+        goto out;
+    }
+    /* Engine ctrl "libica" only works if engine is not initialized. */
+    if (ENGINE_ctrl_cmd_string(engine, "libica", "doesnotexist", 0) == 1) {
+        fprintf(stderr, "\"libica\" ctrl succeeded despite initialized ibmca!\n");
+        goto out;
+    }
+    ENGINE_finish(engine);
+    ret = 0;
+    for (i = 0; i < sizeof(params) / sizeof(struct testparams); ++i) {
+        if (!initwithlib(engine, params[i].lib, params[i].checkexists, params[i].expectedinitval))
+            ret = 99;
+    }
+    /* We have to restore the correct libica and init ibmca here to
+       restore the double free above.  This might leak resources, but
+       should be okay for a test. */
+    ENGINE_ctrl_cmd_string(engine, "libica", LIBICA_SHARED_LIB, 0);
+    ENGINE_init(engine);
+ out:
+    ENGINE_free(engine);
+    return ret;
+}
+
+int main(int argc, char **argv)
+{
+    /* First fix the environment */
+    char *testcnf = getenv("IBMCA_OPENSSL_TEST_NOINIT_CONF");
+
+    /* Do not overwrite a user-provided OPENSSL_CONF in the
+       environment.  This allows us to execute this test also on an
+       installation with a user-provided engine configuration. */
+    if (testcnf && setenv("OPENSSL_CONF", testcnf, 0)) {
+        fprintf(stderr, "Failed to set OPENSSL_CONF environment variable!\n");
+        return 77;
+    }
+    
+    setup();
+    return testctrl();
+}
diff -pruN 1.4.0-1/test/engine/loadtest-ec.c 2.5.0-0ubuntu1/test/engine/loadtest-ec.c
--- 1.4.0-1/test/engine/loadtest-ec.c	1970-01-01 00:00:00.000000000 +0000
+++ 2.5.0-0ubuntu1/test/engine/loadtest-ec.c	2025-04-16 08:09:24.000000000 +0000
@@ -0,0 +1,175 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <openssl/conf.h>
+#include <openssl/ec.h>
+#include <openssl/engine.h>
+#include <openssl/evp.h>
+#include <openssl/ssl.h>
+
+int setup()
+{
+    const SSL_METHOD *req_method;
+    SSL_CTX          *ctx;
+    EC_KEY           *eckey = NULL;
+    ENGINE           *ibmca;
+    ENGINE           *actual;
+    int               ret = 0;
+
+    /* Start code copy from libcurl 7.61.1 Curl_ossl_init function */
+    OPENSSL_load_builtin_modules();
+
+    /* MOD start */
+#ifdef HAVE_ENGINE_LOAD_BUILTIN_ENGINES
+    //ENGINE_load_builtin_engines();
+#endif
+    ENGINE_load_builtin_engines();
+    /* MOD end */
+
+    /* OPENSSL_config(NULL); is "strongly recommended" to use but unfortunately
+       that function makes an exit() call on wrongly formatted config files
+       which makes it hard to use in some situations. OPENSSL_config() itself
+       calls CONF_modules_load_file() and we use that instead and we ignore
+       its return code! */
+
+    /* CONF_MFLAGS_DEFAULT_SECTION introduced some time between 0.9.8b and
+       0.9.8e */
+#ifndef CONF_MFLAGS_DEFAULT_SECTION
+#define CONF_MFLAGS_DEFAULT_SECTION 0x0
+#endif
+
+    CONF_modules_load_file(NULL, NULL,
+                           CONF_MFLAGS_DEFAULT_SECTION|
+                           CONF_MFLAGS_IGNORE_MISSING_FILE);
+#if (OPENSSL_VERSION_NUMBER >= 0x10100000L) &&  \
+    !defined(LIBRESSL_VERSION_NUMBER)
+    /* OpenSSL 1.1.0+ takes care of initialization itself */
+#else
+    /* Lets get nice error messages */
+    SSL_load_error_strings();
+
+    /* Init the global ciphers and digests */
+    if(!SSLeay_add_ssl_algorithms())
+        return 0;
+
+    OpenSSL_add_all_algorithms();
+#endif
+    /* End code copy from libcurl 7.61.1 Curl_ossl_init function */
+
+    ibmca = ENGINE_by_id("ibmca");
+    if (ibmca == NULL) {
+        fprintf(stderr, "Failed to retrieve ibmca engine\n");
+        goto out;
+    }
+    
+    eckey = EC_KEY_new_by_curve_name(NID_secp384r1);
+    if (eckey == NULL) {
+        /* error */
+        fprintf(stderr, "Failed to create EC_KEY for secp384r1\n");
+        goto out;
+    }
+    actual = EC_KEY_get0_engine(eckey);
+    if (ibmca != actual) {
+        fprintf(stderr, "EC_KEY not associated with ibmca\n");
+        goto out;
+    }
+    
+    /* Start extraction from libcurl 7.61.1 ossl_connect_step1 */
+    req_method = TLS_client_method();
+    /* This initializes libssl which initializes libcrypto for the
+       second time. */
+    ctx = SSL_CTX_new(req_method);
+    SSL_CTX_free(ctx);
+    ret = 1;
+ out:
+    if (eckey)
+        EC_KEY_free(eckey);
+    return ret;
+}
+
+int check_globals()
+{
+    int            ret = 0;
+    ECDSA_SIG     *sig = NULL;
+    EC_KEY        *eckey = NULL;
+    unsigned char  digest[20];
+    ENGINE        *ibmca;
+    ENGINE        *actual;
+
+    memset(digest, 0, sizeof(digest));
+
+    ibmca = ENGINE_by_id("ibmca");
+    if (ibmca == NULL) {
+        fprintf(stderr, "Failed to retrieve ibmca engine\n");
+        goto out;
+    }
+    
+    eckey = EC_KEY_new_by_curve_name(NID_secp384r1);
+    if (eckey == NULL) {
+        /* error */
+        fprintf(stderr, "Failed to create EC_KEY for secp384r1\n");
+        goto out;
+    }
+    if (!EC_KEY_generate_key(eckey)) {
+        /* error */
+        fprintf(stderr, "Failed to generate EC_KEY\n");
+        goto out;
+    }
+
+    actual = EC_KEY_get0_engine(eckey);
+    if (ibmca != actual) {
+        fprintf(stderr, "EC_KEY not associated with ibmca\n");
+        goto out;
+    }
+    
+    sig = ECDSA_do_sign(digest, sizeof(digest), eckey);
+    if (sig == NULL) {
+        /* error */
+        fprintf(stderr, "Failed to sign\n");
+        goto out;
+    }
+    ret = ECDSA_do_verify(digest, sizeof(digest), sig, eckey);
+    if (ret == -1) {
+        /* error */
+        fprintf(stderr, "Failed to verify signature\n");
+        goto out;
+    } else if (ret == 0) {
+        /* incorrect signature */
+        fprintf(stderr, "Signature incorrect\n");
+        goto out;
+    } else {
+        /* signature ok */
+        ret = 1;
+    }
+ out:
+    if (sig)
+        ECDSA_SIG_free(sig);
+    if (eckey)
+        EC_KEY_free(eckey);
+    return ret;
+}
+
+int main(int argc, char **argv)
+{
+    /* First fix the environment */
+    char *testcnf = getenv("IBMCA_OPENSSL_TEST_CONF");
+
+    /* Do not overwrite a user-provided OPENSSL_CONF in the
+       environment.  This allows us to execute this test also on an
+       installation with a user-provided engine configuration. */
+    if (testcnf && setenv("OPENSSL_CONF", testcnf, 0)) {
+        fprintf(stderr, "Failed to set OPENSSL_CONF environment variable!\n");
+        return 77;
+    }
+    
+    if (!setup()) {
+        fprintf(stderr, "Setup failed!  Skipping...\n");
+        return 77;
+    }
+    if (!check_globals()) {
+        fprintf(stderr, "Check for global variables failed!\n");
+        return 99;
+    }
+    return 0;
+}
diff -pruN 1.4.0-1/test/engine/loadtest.c 2.5.0-0ubuntu1/test/engine/loadtest.c
--- 1.4.0-1/test/engine/loadtest.c	1970-01-01 00:00:00.000000000 +0000
+++ 2.5.0-0ubuntu1/test/engine/loadtest.c	2025-04-16 08:09:24.000000000 +0000
@@ -0,0 +1,132 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <openssl/conf.h>
+#include <openssl/ec.h>
+#include <openssl/engine.h>
+#include <openssl/evp.h>
+#include <openssl/ssl.h>
+
+int setup()
+{
+    const SSL_METHOD *req_method;
+    SSL_CTX *ctx;
+    ENGINE        *engine;
+    EVP_PKEY_CTX  *pctx = NULL;
+
+    /* Start code copy from libcurl 7.61.1 Curl_ossl_init function */
+    OPENSSL_load_builtin_modules();
+
+    /* MOD start */
+#ifdef HAVE_ENGINE_LOAD_BUILTIN_ENGINES
+    //ENGINE_load_builtin_engines();
+#endif
+    ENGINE_load_builtin_engines();
+    /* MOD end */
+
+    /* OPENSSL_config(NULL); is "strongly recommended" to use but unfortunately
+       that function makes an exit() call on wrongly formatted config files
+       which makes it hard to use in some situations. OPENSSL_config() itself
+       calls CONF_modules_load_file() and we use that instead and we ignore
+       its return code! */
+
+    /* CONF_MFLAGS_DEFAULT_SECTION introduced some time between 0.9.8b and
+       0.9.8e */
+#ifndef CONF_MFLAGS_DEFAULT_SECTION
+#define CONF_MFLAGS_DEFAULT_SECTION 0x0
+#endif
+
+    CONF_modules_load_file(NULL, NULL,
+                           CONF_MFLAGS_DEFAULT_SECTION|
+                           CONF_MFLAGS_IGNORE_MISSING_FILE);
+#if (OPENSSL_VERSION_NUMBER >= 0x10100000L) &&  \
+    !defined(LIBRESSL_VERSION_NUMBER)
+    /* OpenSSL 1.1.0+ takes care of initialization itself */
+#else
+    /* Lets get nice error messages */
+    SSL_load_error_strings();
+
+    /* Init the global ciphers and digests */
+    if(!SSLeay_add_ssl_algorithms())
+        return 0;
+
+    OpenSSL_add_all_algorithms();
+#endif
+    /* End code copy from libcurl 7.61.1 Curl_ossl_init function */
+
+    engine = ENGINE_by_id("ibmca");
+    pctx = EVP_PKEY_CTX_new_id(NID_X25519, engine);
+    if (pctx == NULL) {
+        return 0;
+    }
+    EVP_PKEY_CTX_free(pctx);
+    
+    /* Start extraction from libcurl 7.61.1 ossl_connect_step1 */
+    req_method = TLS_client_method();
+    /* This initializes libssl which initializes libcrypto for the
+       second time. */
+    ctx = SSL_CTX_new(req_method);
+    SSL_CTX_free(ctx);
+    return 1;
+}
+
+int check_globals()
+{
+    int            ret = 0;
+    EVP_PKEY      *eckey = NULL;
+    ENGINE        *engine;
+    EVP_PKEY_CTX  *pctx = NULL;
+
+    engine = ENGINE_by_id("ibmca");
+    if (engine == NULL) {
+        fprintf(stderr, "Failed to retrieve ibmca engine\n");
+        goto out;
+    }
+    pctx = EVP_PKEY_CTX_new_id(NID_X25519, engine);
+    if (pctx == NULL) {
+        fprintf(stderr, "Failed to create PKEY_CTX\n");
+        return 0;
+    }
+    if (EVP_PKEY_keygen_init(pctx) != 1 ||
+        EVP_PKEY_keygen(pctx, &eckey) != 1) {
+        fprintf(stderr, "keygen initialization failed\n");
+        goto out;
+    }
+    if (eckey == NULL) {
+        /* error */
+        fprintf(stderr, "Failed to create ec key for X25519\n");
+        goto out;
+    }
+    ret = 1;
+ out:
+    if (pctx)
+        EVP_PKEY_CTX_free(pctx);
+    if (eckey)
+        EVP_PKEY_free(eckey);
+    return ret;
+}
+
+int main(int argc, char **argv)
+{
+    /* First fix the environment */
+    char *testcnf = getenv("IBMCA_OPENSSL_TEST_CONF");
+
+    /* Do not overwrite a user-provided OPENSSL_CONF in the
+       environment.  This allows us to execute this test also on an
+       installation with a user-provided engine configuration. */
+    if (testcnf && setenv("OPENSSL_CONF", testcnf, 0)) {
+        fprintf(stderr, "Failed to set OPENSSL_CONF environment variable!\n");
+        return 77;
+    }
+    
+    if (!setup()) {
+        fprintf(stderr, "Setup failed!  Skipping...\n");
+        return 77;
+    }
+    if (!check_globals()) {
+        fprintf(stderr, "Check for global variables failed!\n");
+        return 99;
+    }
+    return 0;
+}
diff -pruN 1.4.0-1/test/engine/openssl-test-noinit.cnf 2.5.0-0ubuntu1/test/engine/openssl-test-noinit.cnf
--- 1.4.0-1/test/engine/openssl-test-noinit.cnf	1970-01-01 00:00:00.000000000 +0000
+++ 2.5.0-0ubuntu1/test/engine/openssl-test-noinit.cnf	2025-04-16 08:09:24.000000000 +0000
@@ -0,0 +1,20 @@
+openssl_conf = openssl_def
+
+[openssl_def]
+engines = engine_section
+
+[engine_section]
+ibmca = ibmca_section
+
+[ibmca_section]
+dynamic_path = $ENV::IBMCA_TEST_PATH
+engine_id = ibmca
+init = 0
+
+# OpenSSL <  1.1.0
+#   ALL  = RSA,DSA,DH,RAND,CIPHERS,DIGESTS,PKEY,ECDH,ECDSA
+#   PKEY = PKEY_CRYPTO,PKEY_ASN1
+# OpenSSL >= 1.1.0
+#   ALL  = RSA,DSA,DH,RAND,CIPHERS,DIGESTS,PKEY,EC
+#   PKEY = PKEY_CRYPTO,PKEY_ASN1
+default_algorithms = ALL
diff -pruN 1.4.0-1/test/engine/openssl-test.cnf 2.5.0-0ubuntu1/test/engine/openssl-test.cnf
--- 1.4.0-1/test/engine/openssl-test.cnf	1970-01-01 00:00:00.000000000 +0000
+++ 2.5.0-0ubuntu1/test/engine/openssl-test.cnf	2025-04-16 08:09:24.000000000 +0000
@@ -0,0 +1,20 @@
+openssl_conf = openssl_def
+
+[openssl_def]
+engines = engine_section
+
+[engine_section]
+ibmca = ibmca_section
+
+[ibmca_section]
+dynamic_path = $ENV::IBMCA_TEST_PATH
+engine_id = ibmca
+init = 1
+
+# OpenSSL <  1.1.0
+#   ALL  = RSA,DSA,DH,RAND,CIPHERS,DIGESTS,PKEY,ECDH,ECDSA
+#   PKEY = PKEY_CRYPTO,PKEY_ASN1
+# OpenSSL >= 1.1.0
+#   ALL  = RSA,DSA,DH,RAND,CIPHERS,DIGESTS,PKEY,EC
+#   PKEY = PKEY_CRYPTO,PKEY_ASN1
+default_algorithms = ALL
diff -pruN 1.4.0-1/test/engine/rsa16k.key 2.5.0-0ubuntu1/test/engine/rsa16k.key
--- 1.4.0-1/test/engine/rsa16k.key	1970-01-01 00:00:00.000000000 +0000
+++ 2.5.0-0ubuntu1/test/engine/rsa16k.key	2025-04-16 08:09:24.000000000 +0000
@@ -0,0 +1,195 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIkJwIBAAKCCAEAwIRvCSL4d3ZS4Wso9xNB11q5z+V95FjbBsqEJLq3FtgXSjWc
+HH7I7dGp0UmYSONReGhyzMPaJcms9ZSvt9dxUk3vP/wLYFZy/Oxwwtb29LWayRbi
+K19et0LSZPBUqoGPNpVICJ5nmmEdHA2KeYUIJ1Rv05P8F3/pNL22JLov2ycAGak/
+8Q8LdhE1UtAuZGxx3bDRHp6VUhS+ftWWYX0YpFb/xEBRKxzhZCkDPKo6phGSL6sW
+1VjTeWXi+awKLa2wUZI6y7kM8vf5TMFPhCc+3d6lkXS0sURy7622QFRIhOYFTlOM
+r7kGYdlYsLjXK9+WKRPbVUOmft33gdxITy3yKKVTGA0INvqnUYn9+aLRxrWdADtH
+iKQYBA0q74LwMQkvpzU0U1LqwJm0TvYH/KSz/TQb6Hlvz7SeMN0ICNbfwZbVuNv6
+Kc6h3P6/eJPLpDt9Gd/Sb2vY6tzN2lDLx6I0598isLIYYf9QT5JkLxHmGm8rE/gH
+5FT6OS6iL7+29bWvU7zXzBm2VogFPR6V/iTR9Q07UuPkzmaihCb6T4DU7SHGfiY0
+U1ZxN//0Q1+wOBS90grq1JgwEnLJZoONgdKHHfrJ1VPz6PmsuGBNF+YD+avGp82r
+4pVFysQhv4kxHhigNR2GdLJiIeac2Wol2DAnQl5AUqZK6NgfnTBu/n5IScNBYjbJ
+oHZ3QWtLy47OziFszepKjsl/hY+xNmdUWd4Fav57JP0ZbFeEuuW5ujVSA8Gex0Vc
+yAANbsRBkP2Klu1xpRtmNOmEitMFf3hYTUUQE6TdWnBqmvjw4ydkePd1pydqUMDm
++oaT2fzdzUqAj4YsjObR6Hz+yP42C33EeEOxwqokpEuN0mdG9dCu0KB6/ndvBc4+
+e2vtLjLY+x3YXkzPuQ6MwfzLt3VPSJDjinLojd8U27b+GS2bY3hfaACEfTZ533Is
+HY/PKbuu9Zws/UJcpaxUrrgYcfMkZMrNCP/Zb1MERSJQU0LU6f/4JMsQADOflknY
+gcOqBsGnWfQ41UGPYzedUaogAi3qkmEL8ZH86venIc2Z4bdp5kkG4sRADb+Jtxbt
+43eMqotY/woYd866TLknMc3v0hqxT737HAsh6xFKLuIQlyvH3mpXa2d0R9WoXuJY
+QtNb/3uOxtdPRHbGhkdCw71jPZPIR2Oe9kfF25WFvW5Wg7XJx/TXPYD6hSrs/nvZ
+ZofUA55AanWROYg0V3T6sn9Wsz09STmBPXF1ZPLtuBVjLnlycZLK8xtebXgGARtE
+6UJgg7OnDpF1P+xk5GSVxrlIDIbbMG+A26NV+0seCFku7hGS2ORQHYhObxcCqofg
+4x90/U8GkCFudmbu9PfxycNGrMfhjUS2urcmUDfEM3aRQq17Epjz6D4QwYlp4stn
+5lXH7wIkNdwqc1d5MJU245XUrq1N3yVZHK40+jN2GdW/RS3E/ga8PI12HvGTKli1
+PUpnVdZRQ5Aej4vfW9pxfb+LDwaV7r4hfGsucTVSLUErnmF6Ny5xr/pLoO6mKlpG
+X/R2sNGTfOy7JLDET0vAFPZJhbA/QxQY0DJCL5TNNW05E22f6cOpwEiEXQZmnoxs
+P+c+UxrJF+dnqjAAukfLMt9bnqmbMCCEYdrVrkLzwUeFYKpiDAsafSN6iKhC0GIq
+YG6XxiVEolzHdjoHKQu+eg1uOBh5yjvixtuEVLNbSD3FLbLU8nbR5Beu1FtLBlm+
+fX4t+c7mlgIL+zffYH1pj9zZPgxZMKeMZdkthuce4McScb8vS8kkZgGMh8Id0T3v
+KgcdH5qM0eRtxddhPP7+vSI73BkqySPhcq6PQOa2Df5IG4jyr4vBFWGqW03anZyS
+YX+gn2YXoVLyU+TqwZtxXMZvURh6sNprH8F1JdXWMKZrrqgcvb62738iCTFUeynb
+uG5Q1lpw/9zs2Bc0bfqM4UiGcOfUDdzaz5uY4mu/bPrTqcl/ltYFON9JeR5sS7+G
+LmQTOeTjTecTXaHwMls1QhOb0f/Wzo983M9CWgxRoy9ywhB/Ouab5N544daLS/kz
+smk+aFVS+OoqW1xLJHqWqU0IVoPkPqz1ztv46W/2Aoc3DPgqFErQH2L8xTE0EHx8
+m2Pl9knLLRIqE0kQ5GyoTrWWAElxdl6Sp5/H4ndU07+sjQpRkEcbacCzBBgZl2VB
+iJyzdv6QyvXD608ij4w5zcc6pN+LTSkb62tIwF2KDiaEgx+pT93sEeSw/L/fymdO
+/AxweORsOJob0oqUFwHa2Yz4JGvhodEJ/YxBO/N1694OXhpNJC4H1V0Xsq5gmpMH
++u2sY1Q0/ZikShrt92xFG1x4RVmbCPAP2bydeXlMemyfcFvUrQhbREwaMtNu4FIk
+XTIbFCL+/Jts0yWq8vIoKRsfA0UBV3Kumv64XLDLgjFyuALt7+d8f38hE2sAdAnW
+05iG+W+199Ga77akMkXoIUfsDEQ/EAlHu2BK8jAtO6sYg+e1kiPchMWI56ZSZyth
+8p3HOdGMhHrNDOCHDHFO5Vpa/HH5b2b04/WdWrdGm4LoXDUzo10eO5ElKNB1sL1j
+ciAIFOXwjkJwAtGh6yeAR15WbMRkdjxgt9p3EoTwSqxSErwilichdjBpEEGBZRAy
+WVE8ZmyLdEk+Nvc4YoCVE7hKGo6HzrLpneoovSkdKNij5COuQ8dM2WiSzfy7y1Rc
+vSVQTDwx3jxyEu31Y0llbKAyBQnafzgD4UUyJ2nM00ssHYkNcToDIIxEn2kCAwEA
+AQKCCAACDmgRNOpqfD0OY1KgsPf3EOUDL3RUyA4u2GLo8YWbHNvC0yfDtib3Q1IJ
+Bjw2KxdnrdXsEft2g9zxEpF96TMwqdxwVoTYlpPL18qsihdBLd9lXnUz+WTNmE0e
+/uIooBGjh5ypWufIYlkpP4vHGICpyuEW5XG/3oHjKlOTuNliAdyiaYhhTsPUkwJ6
+y/Zy5CWjNIxHQ3Jc2hPezvE4QVnOB4/bzMQRLdHKfBWoxDi0JWPmVnF7/eLkXQ47
+lHbXXg+QD3vJSUS2I6fqBfaPdsvZGtOx5gk4C38eyEA2F7BemwZbe9D434UN7oBg
+Atlpgo3cCnZBRuO0XzKRlITBM8BuZrWogBsiWzMGT+8Ch5auBKqUog11jCslf+6B
+eGiQXYTAyHSynyQawomGfmI/dJLnlXLm3MUw9sDpzLKVRZZGPxIxU0gswiRAdAgf
+hZSbQHdpm1ZoEkLS32DjgBFtJHJkN6HrOTvBcDSvZOTlC8/lYGKZaADNk4rIqZLZ
+57ZQkYw53L6BU5T6/6DusjDv8fy8HVQQ4pZDDh2WQ8RQrROZJM818bqFWVsEOhul
+zJPZRX9xiuRPOQDqcYdtLOKKDHGdBFy7hpYIWwJZRc4M/0vzkr4gdLXtSaLhM48Y
+uKJpq+GqL5KMFIuvyQLL37JfyMPilZciStxp+ET+LNJCrZ6gCxHbtrIYoKtNa5P3
+rKLQ2NKMsH/zXnuaNZqy9g+UliaAqg8uqfVnjtfAF0wQjRk/KQZFIpyHFlkOYdJB
+djPQwOCB3vUMg6ICyZY5clOfH6m4S/6BRMoetaH9CyKrmBkNnAIqsqTE8EMMBQNp
+ee49mjVHm/+J+CvQgcaaANHkZvSzG5Ygmx38rVW4sm3rlIs7xJLPARDxyIlUAP8I
+T51MBI4DcABY4hZa80nRgJqtk+Sf/039usqkAjSyr0p1levUWoyasmF+P1b7HyIT
+RYPFgLv0mQ6efyDjtrEg0nm2KuVrpvtYsbEh8ojLpwxDeFfHaMW/mTJS0IA0akUb
+VNx2iqi5apKnmKWW4F6tCZRTFDgbWmHSnpuSKZ9b8lO8gFQe0XjrF7eawmAiQjLF
+Jpp0TST4RNICWizBlb9nFqRj/Ep0dE7SiV+b/b+4xHICl6xKSCHwQYKn1BIbfHpd
+7VbPaGVeccxowtEYi0eMW6gLAWE/oy02hMMvzPVGyXqzEXmbioXVggUkbzb4guw1
+NuRCNQ46EOnMlkeayoCpE5Ap09hpJ58aZEAW8v0h8WtxhU1Q5gNKZ7wosajOP1D9
+ethBwoihV+GxqbD3UUluDwLVVb1JXdgIRsuVh2KGJookhpwv25MPtzvTsTZSrlFn
+oLPJV/gdbbiLayaKGehy1dUN0X77P5gp7WAsIj+3/XWjmwBmx6FC2eHuJeei41mC
+flGEm+N+jk1AMRFjHh0Fn3VUFbcfSQ2/WkHimJ8awlI47oB9fRSWO0wQRrtjiTC6
+PNEYVEk9KAtEPI7MAkhF9f1f2vKNO/QSKvmzK/bAmUZtx2+dMsggQU+nljmgPAhp
+aqRIn6DoJPZpFoQp4C4A9fRgaV4SNRWNPZq2iCj5TbRzBdAt8f7YNEpB9o6fWGeE
+BxOdboG0B6pyBn3offvnF6hzt6q1nVpXIyAXAvDcvszBfHpo1xf5uWLGjM14lnWV
+8iUacjpgEugefI8Z2sV1d673hvyPbDjldh1Ic0j/TdO9Yc6/j974QECa+2cPoA+P
+JSQK8SHbn5b+FFYeEZk3K0kxPCiwzllZTuCCH6ye/fcG1WJQ3gulrMonD4yPyuF6
+nZk6k3ml0IqJh7yKdf/eIcpVFKw0SfDt5BSBq8aJx8t827gHKstpnimYovD33Wpq
+vIs2c5cUFIblkPQsmqosLHCd2EJHlW8Vzj/f/aWg8BuUUwRGUKVACMUpiKVXhR+v
+KT4gqq35pQiMKbIYcsscz5M8dk2zVb9kPTSyGxTl4zeKwKxWjwcVXVsZYwF8vfEy
+gvmYpfvKDcmlq19YeX/p4GDT1d1aS4BYltE+UlTa2ITkGTAtmrr+gfPBPK+3kgnS
+OFcprlyHd8/a74rdN6hwktS2Po3CoAq8bKOXvW0AM1B4WVKVNGVxtA+EmjUVRstn
+rscrPI0NWFGHY6wyunFzuf1gkWEpxTdxez7TT02UyG3+5C94/10U4u8qt2zUGxOP
+hZ6lVCgb4IxT9Etmd6+e/kq5BDXPsJEI4T9KxMCneUqdg8Q5HlD0nFgWpa1O2f9I
+wfEmRO+45wgjWpgw6G0Xvlzche+y+laBdUzPZkCj/IYL+ehvfmz8KuNzjo06Vg3Z
+qn8hF5UXkhYslx8L8QSXJUF68y21x0Y7fnrGxE9j2MvOGFV/7ra14C9++f/KVbr6
+qvYh39J/wPac2LqoOOqE6/3CdnLuCnS+KSHqp+q05FSkK1Jh4OdP9o2zvOB+6p17
+X0VQbny1d9/V93GsvTSiGC4/vX0KvfsqTBrKC8mC/+DSQF2zMwW3qzKH2n8XqnBl
+NnUIJ5b9/TNbyuyvAUJNPpauLgm5y3eOU+2y9nmoxC3WypvOEAyAc2RN5bFO2ucr
+HOaYXy8FMmAdwmuqDQXh0pbWZroNcImQdQUunOTJ37TiV+z+aPprxdu++VwR+Wjz
+YbSkNuZBMNeJjZ9Mn7uq9roI934JHZFL04+Nvswl7t2ITIF/pNJCPFgFhw3XMtMQ
+/W0CyeIWboR40ULP5xosLas284y38poasWgJk7do7B+PhijY8QKCBAEA9yqpN3DR
+/bylS0BmmupuCBP1LTeS5ilXqdMHZupHLMF0cEmSOb7ZJxXYt7K7oeKCSyu6mbmq
+EltUDZ20nFYBI/okwe3YKP3CJLUUzjMHszHdpxrD81iZvFFiM+2npuXJD89LQ7HW
+FXEc+2zROoWOaZ5kXctGtTjg5a79TzJeyS6kpFdmFdlb4aEGl+uM/MOACWLTE2k7
+97KR1Aa8KDOCO+KJW8qEuH7MoS6LaRkrON7fn7jfjoqm5uydG4P8yDzluh3MDvcT
+yr/4x81CPxbRwNocV83f5kugrig+Z4ObcgmS+D3uuaAwr2iqnooqGfmosw4u5Pet
+rlNajUXmTLZceKmWlEa7Kv/hPWeCWdV+6kGk46OOJTnq5Lff1s83vRB9+lVa5Vpb
+NwE2L6JbOtwveLUUJCAtgOVQ+b/huq8UKo2zuT1Jljf20DtfHivjGjHDV489ovW4
+G5k6fn9TcodNxqchUf2E0r2Z1UO0bxADefv3UBlpJLZ/CwqnbgnNBbXxcpD6sn9k
+UgG+OjIc5dZnC/kWdLw5hocyGSmLOfAzvWa7JBKsmXYAIPZOI5gt28/gpo8M1PXY
+kOifQyLwhNH79gUfpxTU8AI214VtiAgp5YYHut4jDD1lMYAWrxBiT5g3gFufA7cr
+N3IzPkS1Fb+yiT2WtoGnK80gTgfgSlLq7ZKDf4e+Asq7lzR5M23P4LjcMsAeM7AB
+NCxsNQNitm0NXSXhi/BJIPfe8FuwxJVk+GSNk4nIsAJPUkHD+yagAjGz+Ni3gCle
+9Tjyj+eZUIATeTZR8iKDgfIRNprrZcnjXUhM8nU/i9gmb8Qaw2HSjrU5AAP3JPW2
+/JwB+c6pgPEIA5WQ+pJDpMGwtGVs6trat0a/wOrXJAxRa5oRu+SuHAts17bseSBB
+B5W7c92fhqxxQiZZNRaHf8vE5LPGhbBjp83Ry86zJL+h+aPbGp3SE+vQHpwWHOgU
+dN/ymDEXVB20quAqSZODcTx/UELiERnZlUGTril3GcTUetTyEETEw10d9ZUZbkQo
+L8O3W71PkTyHHmrq99CQGSbrNUI6s/CZyUG4ow4CBHD4/r6+U3TgRPIyIHou9fqB
+9+965z5pPlxAIOnB4EQ0zsISrAabSi+tzEOAfAUlgrcUDqVfdr/lajaaZd3X41K+
+1+PezOV3Fau9Az0JFUqcCYkei/w2RHaRXFok/Vfs1GkNeLJ0gERLGHeAOJw8/6v/
+VaiUlqY6kqZIcYDtKHaJHlCHbQuW+/rxTgsBUewSX85wZ3B/o2SY4SN50OVhStnI
++wEBkb+ZaVCjRhKaJXXpv3ZrGJ5H0gJ8c4uVJbLbBppoJpWPhOFubLgYyIv6yQhh
+PG7srRFTy1nniwKCBAEAx2XIliFZ2676xQ9G44NMLfAVbaD0aR2R3Uqn8CBhxzSl
+i2XStL3+qr0911vwwgGlMQ4/DMIAlyI14tFF2TMjFITLHfw2wnL8zsVaFBWIzd09
+XZtgoKo0h5rEw385lg4Te1evI47wpgdvCPOpZ34pDJd8UVwM21LbBIZYduDQLT36
+bWxSYLY8st4vVadG9a8dkVrnx4sJnCRllcLu0Z0C9KZjCbJ2IV5H7ZYAkurV9fxa
+4yS5X3ga6JJi5+k3rjwnmPGSrGd8Chok8NUrz/KXols9DRqybH7WfxfajEl9ZyQ9
+1f3XJeAp1ZHEf+4cxeXzZR8ifF7ZC0qFUzjl0jy4wYWZBA62noBfG02hTFmQWn1G
+CutuNtGlLb5Kx9RlNEca1JfpRf3wbk7BRWMHs0BYJ5NDhh9/jyIa5o1870maAw2U
+LMIoHBSlc/gkqWEgxCtm1Oxca//T9sgQuWkEDYhluuunmdz7MuH+16xfX4OYiRfe
+6ytM67Dvu/cLZMbjPG7OF7BHBNzXrUjdPKZPdIFFpj9ZuolxFBWmR2vsQIEQTpjq
+yfkJcMxdb0FeMTKG2sne3Q93VcyDxWNponPZGPiAUMMwUsmUqSQfDh7OwQQGioR7
++DiZiTEh5CO/FY+CSpE1QGOyjolvgZSlJMeZz+/AhiLVGOh3VH/Q5SOBKNgpqG1a
+o4jLU9KJ6SekGIM21q1VuQ45ZonVRHONo3OgOWLoedo3Kp2iFm2MbDQQt1OUFg2o
+RFHUk5/6mvXaFvMj3VG8+9i/GtQqYNbBjTnp+NfPZonyD6CcSzI1iqR3RJQD/n4W
+eYKLFB+nuxU2FhrmDUd/LnKLMztopXUVl0hEaPc4j6VQWu1dD+191gLcylnCZlKF
+DtHhOka0Di0rWDdFo1Qx1nMA7ZvwET0sUwV0wcnUH/vd8F5FWRxFdsA4aPhwbfAu
+tE3uU6X2u+5XlNnRStRrjeKUU0ZDXh8X/jBuvxVDkOqnzwNCiETb8m/jtHRQnudV
+OOOztXcXikSXqpNcvQqb2OodeIxDFfVDI1LAw//nQeiZ8TuNKPsxqXvKAZ693OBM
+EwPfKUfO7TpbpxHJpgg1cnnlejSgciE7TMZJ572i3k7K/j5Feo9VZd3PRzMXK/tK
+DyBtFl5ypiRV1HCwwBlH7XDiiCs7ShEkcIyG4Oy9xm6ZESWH037DEv9PMIy5807z
+ALeqEXPIXryrQP1XtKqqODZuvZqB0XvrcMCzRjtX6geCCwSm1aXlxWDa3hPCK+5x
+mqU0Hx6u5SfgeHrkSzbHJZlH+M4EWy8vjYTHTCHt41BNfRIG75c6IUkMuilK0GbD
+rZtoAZcv760P8MvpyfgL8+K/0ZkiyhBjE1nKqWATWwKCBAArXwUKgXWXvL6Ukdxv
+WqusJzRiltnkIWUMm8c0u+95Xwj7my1ZjK+2OBMrSOW75m9OmYHI79d0GSqKqR3E
+QSM6GO7SSNYhe0XSAyR/elzjiOCILh4sWWPAs9cznYRJuRCrAYkHe/FBGfQn+lRM
+nJr81ZpvCviBXFW6eBZjC0eurcyeOefj9Zw133e0uNSolfWlr/2x7kljkaYdOoFR
+3JgBwiKwRL09UqK9tdkovA3lkNZUxLAV5yaaZvK6nVKCII1/NzNTfO+wKDeTd+Uk
+BPERkjRfA/VCCAtOBCnPKDziqeNsRBHb6MwiO4+U+lGg9ToxEVARRZTofh/polZj
+J6zaT4foxPCa/wObLsqk0N3QZWx1tYF/7sXVFJ7mpr31Iyu3/bpRfKV85W2Itkij
+73c1CzFZlXvmnlazPIRzqb00KmFbI/IBthhNOd3bAYB8XDcW6j+6lLuVvDGTeOw/
+sJZpQXKLXJ0uDa8syRCkXH7/rMMc/xiAJLXuThRLLtUXJ3IS1HXpVIlaCoxC0gBY
+JGM4qqDSIyVFAfw8hhm0NP4fQiOclNpMo3QqVI4pmM6TP8r/7yIlTG6QHwGzWjIy
+XFaGOSfwvasP4pOVBgjOUgBoEjjRBaHIyA01vnA8j/dwWBmFII6qrD2dISD00EGm
+a1j81B3aMK4Q9ZGxn5gv8pTe01LX0g35l3GLet25tI9Bs66QCT5De6L6cMEcHFf6
+AvDRY5BMagGrE1O/gZdnJ3/sRljBhu/8Uqtwl7G4JHL3b5lRFVY92wSaqz+FGPLC
+OXcxUeLXRk8lQkDbABxGZELNX56A/SoFdAxqmFLJ7iRwfkReSFPZ3HQbp5SBLU55
+HdJ2LoyeK4RLKOHFBfcyzuyKNJP5WjweSzsOUp7CBcK3kRmsUKD2MD65AgE+t6kC
+jMfi/6+vrSBhwHPwXct8MhOKoocp6D9vN2FI2QPdi8wNStfeRy1qLR9J0vJS2mpp
+jDzjryo8dvIdsKv2fYYF0i9oDXXIneYUU4GlfDBtwmN0lKZpLZykyLj0iLdoFbQc
+nC5pnAFXMvXWNAoZ8mEyqgmSU0H++rpH5TsMGJR1OJESCB3brRUIfMjJpPBAG6eE
+FdP68onevJk3nr6ACTi+r9cjfMTS/mQyp8WRPFugDtRG6wX00hTMAv0SDR5PfgSD
+fPtR0igB4HG5CqKdlchAAz5GolrTSdZ4/2Ypw8bFqzYyOOX7CQ97HcdUVJyeQtUM
+XrlfkWwMKaCt/RuGev4ngUVOmIfGxFIFFciLniYHmeNpvjvl+Bo2dDjcMrT/yks1
+O70q0qDR6fr2aeUpEL0xkNYdwGSPlO7yHa4g9yWT/6rM0REoP8fl9t55KE3HoL6/
+dCs5AoIEAC0UvQn8VlWNtRKShqNvjuyf6ChWCIwvbEgM1fU7TP/9ziO7DK1ie05c
+5LtpBQDH4HI711NqFToWOPqj99fIcPj1oPqRiEvkNXYGNnxt9fUdPP+J3onUkXXL
+LwtIG1vqqMrtIyK18YCIE7mBZV6JrnwzT58RYbLAg9X9DckqwMZJyfGtWN6WtRh3
+lVoXLLrlMZkoZVi+/Zg4vmLipJ3tkrp/V8rcd0rKBxcXH+AXifJIcMAEEIkeHW9R
+kVz/ZGBXJ1iz3UZvSyL5kDqG55XnMhM5746j540SDCrF2Np4zwpfrj9QWbEtgifQ
+Ag4uZoDzoVjOdjyKMSYVXhlXttYWdmsCanr5XJ5vRZFltr8NTM64nfpgChYIxgCW
+kk+lnI/6N32zLsFtTUuTMK0gW6BSPtwLtmt/eZaBznUrrgBnspNYIZUQ/IKExHd4
+tck5tW/EpEYlfivItkKILwfMT2dD3EQrfUQTKxPw9m4KgbGVjgoIxJSEwKpU4Y6s
+pv3M3ZlPL/c5GoVl7Lk3zRUmQLaFd5rDneTHqDIzRtnE87ANvgMXHZY2YMUI32ME
+82fKFaUnmOR/16olR/F7rWlWKdI0dMZNqwI8owiuqvrFSldnMpdkpqaTHVtgKVps
+HoqgXIPBWrnJEJibSAOS+Ndc7VCO0wdAns8316raDv+PTGdQn/J9bs2kYtv7sMRu
++vGz0NUucUAakC23oekM+wvmW2Y+D3NlO6293+SUNfhMuS7JjaSCx/VSTu5lcAJS
+7VDc/dHO5hxGf8ZwC3jDdRUZ7+Ub3b9AFuMjm9KAcox67KArms+R8EDem9Ub5w8s
+F4Nd538/F+kHaqNVni4yT4RZiWX15urLJGS6X5z3Q8j78rEpJAOASqb0GqjTTWgb
+HD6gmHT51x6195LA+8DaBMhsZl5d5ECq8B2lIqan9qiHgvlwmfKxJ84KBIUS87q/
+gzubF2+EoSB+BqzhKOABxanph1BaXWFJPFFYWlV2xAeG9oNY/NVAVgs7p+iyGTdg
+lK2zDXeMC3o6TBlyFMTFI727uutVUxKapRteLGcEi9DWtPAy1Jg4edqeuHibzJPj
+6s7V/bI1Duhn2NMNTO0gyEhuWE01mboLNGfQ6mJwP419XZyqV2XRNYg/+4ngJqmb
+c3BJXhzAWnu7VuwUYh43y/ZKP0BhJfLUFHVE9/WAJliIMn42guAYjGYwIBo8BCQv
+IcTawlaNc1isLdNeOFWFX9CpMJ8DUbgOpH8bOSD6lf1PDYzCCNdn3HVrPmJ+zGnw
+3xY+Kpk6tEGy6wu8gE2hWG31hB2ZWjBXen15cun+Vd0qp9768Pg3E6yPgy9A4Eye
+4wi9AYlxNDqanGWl6rCmjc/VMDWlDBsCggQAJRHF6VDdN9eFtD8EYiaoCS1S6WLW
+8qQFfYR6Cy69HaSsCVeLGlLTLhi6XG/KR+fky7aUEN8rnjAvP5QAlNxejsVFU/f6
+VB9w+Owxj0ETV/TGciCUpV3sm3L3ke/0itOlZ6pGUqoWAlTPbPfU6bctUR1pHBLa
+RgRuux+Nrd8mVuwRadEHrvQQuh6KbqfVCK7ixoiwrjoYz3szzlszWECqi87tE8/4
+Z4PdPH0/uxh7xbf2ZiofPxlmnbHDrjDBOoniX4coNJmme/+ihFUadtfal5zvyHg3
+MMFzE7knlF8RoEAgYbaJ87LYA8bwRROrO/X6LSgfL8FCfGsTEBpBSiECidghGOME
+HiZHLIhAgvYC/wTOXdrutO4j2oWr2VxG115DjIbBPqxAuRSG6fw2LHMmvPk9oOZ7
+u79FvSBgiVO3xprLxKM43qXsITQ7P3TFQT4lOLzARciJfq944+iEsqaN9ZxOflEK
+IrfiENxuv+30O4DS6iXz6HhHPU+nXcbYGoIpkMNrJ64QDje0BVCqOtC2T3KvEB/K
+J/zOVAX2W06Pe8WWyaRL7FTiWXt6qZuE0fEFJqugOxZWg/lXNs9YQRiUtjWXKVYg
+eHxUDOc2dWiPNVY/D3xM5b9JhzrBLeNLnWYEXnB7krvgKzrusqpMTPNtH3dzasA0
+xVnmUYUgta2g9xfn30Vv/qGkWVhp15wMT7GuRndmPLForNTHHDd5zmG9ewLxPyjA
+UqORCvds4DaCmXCtdF7iwIgXM+5d2MJ5Z6P6FAPF+QYWDXXxo552LZogD2EQTW06
+orcxxnKOHAXD6Ixr4dRiVGqfVy/huodhr0IG4TxkhfuVS7pThVzSgz4FjHXW3T0A
+i5I8BjvlFl/hH+kRXBjOEYjvrPbm1rjUpUJ2FfZAlj6nXLTGKinGIWWrg1PEzLbE
+g2TT3C8nKxDSWA/qy+jgqB+sbJaPlv7eVgDTc+orN1pXGkkLtpks/Ec9r2vLpOje
+riYDjavn8Gcuf1D0o/IPIaxkYU9HJZiRYQhJRkXDTbpRBa7HjgnnDrxgBcPQai0B
+YHUnWa13GYZKLqaIuvqRJjCDx9W0zdjgFcyt8EaA3v5LQNc/DdeoTlS0VBa8eILi
+lZvM6BQqEj54HRSLtSD9WORtkYDlhF1QGCplB/4N04YP2Hdo/7OSeVspKP1kmQdW
+9jOVhIY2hxsknxHkpq7e2l42j74FxuNLy59699leH+iE2mmFhH524WY2YCMKgdgz
+z8jm9NyzbAWS2xprajdym+9/iIxitkteLjw4DeEz8qmUKKf7zh/ArT1FehJpbtE1
+rGjBtDmK51rM8DDnlxTUq17ZppveCTzIhh7MB9QdWhrrHUleVMbqqvEriw==
+-----END RSA PRIVATE KEY-----
diff -pruN 1.4.0-1/test/engine/rsa16k.pl 2.5.0-0ubuntu1/test/engine/rsa16k.pl
--- 1.4.0-1/test/engine/rsa16k.pl	1970-01-01 00:00:00.000000000 +0000
+++ 2.5.0-0ubuntu1/test/engine/rsa16k.pl	2025-04-16 08:09:24.000000000 +0000
@@ -0,0 +1,9 @@
+#!/usr/bin/env perl
+
+use strict;
+use warnings;
+use test;
+
+test::rsaencdec("16k", 2, 2037);
+test::rsasignverify("16k", 2, 2037);
+
diff -pruN 1.4.0-1/test/engine/rsa2k.key 2.5.0-0ubuntu1/test/engine/rsa2k.key
--- 1.4.0-1/test/engine/rsa2k.key	1970-01-01 00:00:00.000000000 +0000
+++ 2.5.0-0ubuntu1/test/engine/rsa2k.key	2025-04-16 08:09:24.000000000 +0000
@@ -0,0 +1,27 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIEpAIBAAKCAQEApvjwHFoxHNoLvRafqgbcMHXn/kUxe2VxwmkbMg3u9sYXjZ1I
+UoyKN4GIy6VqLdZDFGfbDL4ijXOh3ak0pGD/H65dbfaaK/+oggfIgpPWbtF+K5VO
+sXY/fkt+16IcKlxU9+Pzve9eDDFchKr6McVvZBTF0D41XxX3uf9SZkag4W6f0iEj
+UzOLOLRI1OeVS3Sjiz/qQYxXqcNqAu7AH1yrepCzjCflaSYDX85ivJqoqWsNsBcq
+iPG2FnkxMtqGBwZzhuldcnLnBG6XNvKEJnw5OkbtukhtE3Clh83E7fs3QN9ZkV1x
+wtQtPPOOVM/GehpqkhcQLXl+ePV7ZkXr8L1V8wIDAQABAoIBAC71LZnbp5pEnC6G
+cUrA+SXL0v3Ied0JL5AMPTC7iGvwU+loxVEp4ykvDciv8YeIjX7sokcYJeGAFpKf
+JoXgHX/MaswlIUCkesBu7e4DI7gwxHn/WI1CAyNCmh6Mob0sEF1Sx1awPdPSAgHx
+j8F1lBkiWmtxQ1NFe1DcxFCtJkufe5GaOpzX642++1XIzNdD20ueG2HEidPpLxla
+eQ7rw0Aisg/W92UMLGM5US1ZX7jhq79mwCiLcQfGf74Gd66/lPy+xlZAP/3boj2U
+BiV9vRl94pv+FJXK8Xli076CCKo3BTevIOJwZTsQp2uZXfPWp7qHVZ/vG4tZvlnj
+IwQWgjECgYEA0/WHfyrAmxvQWrVHvOGaEoUZUEpP4N1q1FL6rhNtRoTDx6qtPXyp
+sDFCFKqYF9UFPGaQP/bvz6QAUViifC+Kb0Zy/60dCeYeyvqF9beyCey4IrfXPX4T
+vWTRA1dSV7L6E/9hNybqwh5CVG+1aqyoAHO+ti7Innt5Y6q2l0mt6okCgYEAyap9
+OFTqjM5503j5fovt5KISAMrjPhu9M35057SdClFZDSAJpYVKpkctsnEJCY4OHA3d
+NHH7xFE6FgdUXQ00tfqoBUeSB8ITZ33Top8EJuWCvEKsEect8TO4XJ67jWvezpwM
+6pjIYSsjljO1GG9WDtHBuum/wg03u2ZBvpX0bZsCgYEAwEDQafQpR4du8RfEvfoT
+egDn+P+ufFPZdkTdw88zKud5fT2+bSsJ7xp+yagC1HsPiL2u0ZX15uzmrOIsX1/v
+vQa1gFtRH41vGMUrhLjd8EqaqkL+JHSbCwrK2rD1qz5XvpPBZfkSrogrudKpRtzc
+dayGq1C7/6QXcuOBQrA946ECgYEAlhvPZXTfubufNuffXnCtvPdeSzS5/WsMZB4G
+ysnH8vwmOvzT8V7L+AUXYLF+tpwEqKExPG5zpueHQwplo3XG/IX9NyDU8+1kIC6q
+r2uJ3aIMtDE0f4cCzX1gB6EnndbjXAsQGjdrP9iqfo0yiWOmPRWpDIYddEX+bAFr
+gn+AF20CgYB/oV4xXkWZ4j4jbHPBOLCH41S8eGWfrKDc/8o9uu91w6psKhFTqyLE
+go0fL/amg122kpGpCL6SHxLDPeeTCvInvaUfLx5LL4l3XgmJuq1kQ1GN3/qm5ObM
+kfVkgDK18CsQkQQxmWg2sgEnuACCXDCXPNwNQFgd0P6F8CjoL9JPsA==
+-----END RSA PRIVATE KEY-----
diff -pruN 1.4.0-1/test/engine/rsa2k.pl 2.5.0-0ubuntu1/test/engine/rsa2k.pl
--- 1.4.0-1/test/engine/rsa2k.pl	1970-01-01 00:00:00.000000000 +0000
+++ 2.5.0-0ubuntu1/test/engine/rsa2k.pl	2025-04-16 08:09:24.000000000 +0000
@@ -0,0 +1,8 @@
+#!/usr/bin/env perl
+
+use strict;
+use warnings;
+use test;
+
+test::rsaencdec("2k", 50, 245);
+test::rsasignverify("2k", 50, 245);
diff -pruN 1.4.0-1/test/engine/rsa4k.key 2.5.0-0ubuntu1/test/engine/rsa4k.key
--- 1.4.0-1/test/engine/rsa4k.key	1970-01-01 00:00:00.000000000 +0000
+++ 2.5.0-0ubuntu1/test/engine/rsa4k.key	2025-04-16 08:09:24.000000000 +0000
@@ -0,0 +1,51 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIJKAIBAAKCAgEAmyo9b6ms31MtIvgNYq8FHsv+hTrQrBCkOwBMyfkuXb6/c4tl
+NLzuY8rpqnq4ndfyciCmzYXSOfaxvpOcGVk7X4S3brj6bITRmh58J54//O0tq6Kz
+YilvOuRyKE5xG3OhYwlb7SqYf59B8l1+q/M8YfWMl44YfJxSR8gihMIt1jgkdRDh
+mAyFAEanNrgR4BqrA4tXy/4ncV2gte30MLn8HYUaJb68E9C3tTsytwkT8AR/0c9D
+809VGRUL9fhCIUWE8jBd4v6y6k4OU/GcHJWAFkOEzlE2wvaIqogQCV35Kxlo/kWV
+Js2nkvcda7GzIUDK2+xhegGimI5vNkAB6phoD2D3LzEUrqJBnqyn4gc+vwKdASgY
+/mg5jDTNGg299pFbzRlZ3tcUJJLxfeH7jPe6g1ARigZ1JRG3MTcN/1hzyYbXu7hT
+N1+qZlek0FDyHQVJC/T/V/ZAEDeZU9pxAs2N1odIX/fp24yyfoVkevccQBX48efv
+kDogWOPhDKZCQUkZb33WaG8x230sjFcYHv3wNUxG9FIjoN2Kt7jTZNY8lYnJJCeu
+eHVtrqDJqB2vjR/Tu18wRlf3IrVUWmZ41zGlVtXWzloRXxZlkSxlyd8WfNdLGP57
+QqFvLpr2OuwUMSt94s60jVVW1pScZ+vvB6NsDc96gfgUaR04hJIIkB8QUz0CAwEA
+AQKCAgAq8h789+50BVZ51kQGK0D0jCuO/n9hEF4UQnn+cOxnX/MnbM5/MUed8TyJ
+RdwxO+LLucPRIJfWGTSK9dn/nhXz0wzeJW6SeJR23rcoVAGL5K1+UtLpeWRi3XXH
+OCbQHUnu2llKJD7/G7op9byopPLyff8Ct36Jhs6zNW5wZjo/j1xv1lNKPTTWpbeE
+SqAP8fGhUoDjYh3Sn8JqeEjc760bEorxJYByz5L5I0BfLrEI/oXOMVqyTiPUhGHC
+G4taIB6y1bY2rJ4igaYvj7DQrIQ+JUPtwsawYYHkCTK/FPZw+mB3+BRXIBagc6r9
+8roMmx1Kj3tPRIKXTNz1G/Qr1TD/mkcnTF4Ms0gZ2kJRlSXA20+/S01eeQWMz8jv
+YVmtpDI7TXkP5uumfj9xM6tuPqu+VKusQZx4sBaCPJL26R5cCjO9rk1jGhTp6GqS
+rOzP+P7QUnjvGJr01wf9C0UvF96pTBd3sCzkzDLVQsqnfD1z6l+XxrmFxS0YakHO
+cpMpPhJaF7w6Gie3oM5At5wS3esQd4WMXIKSOuWrClgx3UXvvRz7V/dT+JWLOEIk
+8mk8K4md18jXZoLq4ECBGG4SV3ycKdoG4FbxVq7k4UIchumAX3NXn8vCvElVeiSo
+j3LNb2jkZBFKBOl2ndCbUROXje0jRb1uBE8IO6mzgLJhzt3plQKCAQEAzmBsF2/z
+MlOYG/aYfBqUyQYEW2vmNrh3JYrNJ773F7/aTglrGQnBVtFy2Fvc3z8Se9SBggEa
+Vns78A63ut+Odzisz93jjhiAt5eEcW1L98blW5rGAlzhdc6ZR0731KjHGCEAGsjS
+BXU8O4LEdFceiYUnLAQo1TAJdeWl98AfAOJvaemu4yzlchp5d9FtlhVTVrIHVugX
+GoCq8SH/auyuE21c87j1/YW1JX3cmI2o8rCa4UFotayaHK5UqCUA7Jp6Ps/Yg6U/
+jgHrxBUzP/fE4Fk2hHxXTiXCgV1b/Qrs/Y8pYuqH2J/aFkSFe19pSD3PyIVXoiND
+BxlpYtY80xTvYwKCAQEAwHl4ER0646IGIwCFbvsJ9hBEopv+EHNhcyfy2hkJeA76
+djwKAZfEqPQIHVS/b28bEjlEHRPiXBDdpo4ZTxAy+vZvV9pmlSai8xn/OzEHG/MC
+hY9lUatNU5HoaS9qxarMafalAnNKH433T23sjUkH+lhMF2zxOnlQL89i1mXag1Op
+drLgfnzB35NF5LoP3Z3odQ1iQvT9XvMaSUcB9OQJWPbuv728Kh7vKuqCcq8OzS8w
+OPdUwkDN2PimiVgS/bb93MlGcn00aVnX9X1YLyxj68VtStolkeuda0S++tgG4fTS
+89TGRWmNupqKHAKIpIgOXlZA9oHxZFLp3fn+CQTE3wKCAQEAwkjR2aqht7Is075t
+xP+9LBZHCc3lV4SHXkdyZcWIlsodeTOyqEqfwdzCEl9A6sZU31CqbzockmfHgHJK
+k1mcpC4tui7456+3lZ7Fp4auat/K0lc9q8HG2wIZGKGF/KS7ccK3d3+xAERi5N9x
+lNjjPbGls/Fig3iKn3wHp9otFccV/g5pfyKoCqhTy/HUCeOfplFJSY6LGX8h7ooE
+C6QvKMjfJe7tXbN/WoFHqDJMZ9TMqdzK96U7HF8eAYF8gkHXbcR730s1LrouO1V9
+/u2BYXd7NHReOIqhtumuDOU5QGeDHcUPCGdMh4QQaZnj4oeWUf7+BfzOQCOzK8Hm
+Vw0xYwKCAQBO27WgtWwiRYjkIFuLC9mZ8jJcuTLPxKYhizWJvAZQJTri5FhpLKhL
+XKhSdB1253uc7M+GZlYwyEw36DJgXU+12O/ne7jZ1RmgJn+W3dB+2AKpfAXT/y5Q
+VhB1wBKr5/2WUJJsvZWZ9g1kgrIPdOOK41BRrLXs8jUzUXG6S8LKWaWt1z35NN4t
+3uYc/0puzzqlcpVWv49rroiPi0M29ZMK2ykYcAE+9Kui1rAZkrWqxXGpdVRJvklY
+Yw/AZ9BoGOhL5D8zuy6VF1VO5EsUEMjjmWDc8SHBdlppz6G7nnS7cRZZBk0xtTx3
+PsflD/iyCHhymD5HgRfItdvRsZIuWcDbAoIBADk0nMaAqJTzoybcyk102k+oXwY8
+mVV0D1DCfrGeQr0Fa64XVyfqizuwDzIRy8+vQiBx+2dgOVxG2KLzMHKagxW+cBfm
+5zN5VELXAWB4qaKvIx64mxuM5QyO+EdTdqeGTcD6vanKGcGz6X0Fxsw2wqFHXpPX
+l1LFd+wTJsgkCX9C3XNWv5SH553WUvCnbZXZw1YwMD8H7N/+0yf/DQeRdNBOWIEV
+0/aSNxBP9TBOcegRFMOXy+0s5BXSDAlXC2rID6kDFXDKI/JET4FDWtiohiVaY4RN
+GMuRV0pcBLfuGtegGeTkqc7Gb0219m8lhGrDHLlH1CddqYweCOG1RaEoMyc=
+-----END RSA PRIVATE KEY-----
diff -pruN 1.4.0-1/test/engine/rsa4k.pl 2.5.0-0ubuntu1/test/engine/rsa4k.pl
--- 1.4.0-1/test/engine/rsa4k.pl	1970-01-01 00:00:00.000000000 +0000
+++ 2.5.0-0ubuntu1/test/engine/rsa4k.pl	2025-04-16 08:09:24.000000000 +0000
@@ -0,0 +1,9 @@
+#!/usr/bin/env perl
+
+use strict;
+use warnings;
+use test;
+
+test::rsaencdec("4k", 50, 501);
+test::rsasignverify("4k", 50, 501);
+
diff -pruN 1.4.0-1/test/engine/rsa8k.key 2.5.0-0ubuntu1/test/engine/rsa8k.key
--- 1.4.0-1/test/engine/rsa8k.key	1970-01-01 00:00:00.000000000 +0000
+++ 2.5.0-0ubuntu1/test/engine/rsa8k.key	2025-04-16 08:09:24.000000000 +0000
@@ -0,0 +1,99 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIISKAIBAAKCBAEAtfz48HrXiXbnyW55fPh6NSns/+eR5pqkbiW2lYKMslcDEuku
+dk0JORrEnwhB7PPdMlXIfCFIVRASBu8mku2a+Zl027D2LBDhgiaAcztMLAADW0Zl
+wtSyd0MWTfJD627h18sscKlFIxpN82sSv8EtSkSIwYbNyIqtgulwt2TZtfvGy18n
+ahAu7CYG0p+S1hJTdrd4YNnb5V5ZPgCBzRsqkhIxILCJBqi29G3Nr7pqp9PKB8ZM
+Nca6ir0SpBB1pBcyYiyfhPeKf6u1qamRbFRvY7qSIBxHhlzREqxoWm3EHZLMcvLb
+hOd+xS5dpoaskEQs5HqQBcVjeXeDBldYUYrQCufaXmycHYTvREM1Dk0qDU0mZ6x0
+4WH4YGL/yQJ5/fwq9FKxJFlhjlX+7eyiY4oD/prpOOXVSMhCjOr5bTnSXqEFCCx6
+fQlJDXnqiHkfs/hnjK60Wq74oE0uZgpMYM0Z4/uhGo7Fz76ycd/4+P0yBALhkjB7
+nEmR5Amesx0avFg8Oeb8DM28f+pJ3QNZRLPPAh7wd3v4Ja61vjE0AANCexobJa8H
+X4GEDqdEAQDgMRCOj665KGgKs59tSrh128VGykcj+OzJCz4qkKLxahiXQvFBahnx
+qi3dpHrJ8J48M1JXp6kgSHiZByBZwBnItvmqYGnulbNgXPmCJK3AZWNNK/U8M7VT
+cFc/nroXNRZZ4GwV0Bjp1y1e8KY+H8KihWA4yEskwYMPtYh0HXfZPgJHBzMUYnXH
+QRNIyGlYoS2zuAsQXXgFFFca5WMt0mIeAuSILb6GTq6CJsLZj9kJ+LqAJ69PZauO
+CstejmKQLqtA6KticJ9wPDBoS8w40Mf4U8KMAzdC+b/ZKBFxdaDgvOMnH+8nwUoZ
+7PaqjKI8c6hfgObsA4JKCu3EYPHbhsQxO3PPMXGnyJCvwp6s4Ym6Fz3B7pvshlf6
+nsyMA4nzlGzX/JXWGIdps2G5/WpYiz8nDEOls7SsbEXEIO0dTq2oW5vJ9dOyxrAn
+vyaRGPusBrzRq8zq09Ry2WjhAAdJ0W+48U6X6fK4uJ41BL5EgJ2yKLGiA6KAdUDO
+TZBf4VsZLbLAY66veKHGRztNM9+tXl1M6cWpYHlx0v26hSqcoGMjaLSDgq0smYRm
+V29Ukm7WhguQHJjphC4RqAt2ZdrVbqcf+PvZongcOrle3xJtbNoUKhjXu24Yp+he
+UaGwNTSPkHAf/Ac1iItnF62+12ai2uZxoA6H0H6sbLhApjUaXq9UJ11SzMroKMX2
+JXr2EAqC6KyjlbPE7T9GOn7E9e7zOrgOrdViG05RuI6aew4Nf7guT5afUoag0HkB
+e6GNhtC291r0iCyjcGDTmKu9mUIvGVIsZ9XcfwIDAQABAoIEADwZlfB88O8xuGw6
+Mp7FxD4IYX9Fb1ZznXfOtpR7b5F50McPAMwKttAH+gT3CiaBhnwX/kSYDTYiP4HQ
+urFtDG//cKsYrsNws7hylJr5azdgrbsX7YVBDsr5+cS2DB4VGTpVm5B9JhrolfOc
+Wa2/0wAiz/LxXMYsng6Mny/Be3Bw/H6vJuoM/CH6kOg/0JMnrC6ZeoSpjcgat2gV
+bMhaf12L6nN8OVRcpUbleDbfXlecRqz4nLRps7Mbq6tI0EDndDkduXSE91UgVLb6
+C891tu99bsJx9GD4CAedrjym4UxO7XvMnhgvBxuBK2ZSNWzTRuwRWby+KhK0GC4g
+7EbJi9j+JoxmW8wX2Zh2hW/6pbeM0fEM7/B6lTbnmsc8LSNmgpz7LgruBZym47CZ
+JCuxKY6MhBbJN/14PRoig0rB/KpJjMrdPXpLdbegmoaFUaXyk+fOzjrga7Usp/UE
+06JoOXOj5tlJayQnOvEGYUeA/mJ6vRm1Ly020po3XxSRuQGYNhN9J6Hm06h+q/P0
+JifdJbDynMEozJNNNjHgN+cFLJBQ18RDIDE3KXBZZsjaDC/H4vyqW/VvHpGhRcuW
+TT3N0Yn9onCTafVvi0kGtqkuA84UtGCgvsFPslGeR1BSQVlFiTMSVFn6kbbbxSf+
+m91paA29DhCcE2fA8mzAgLg66gJoxL4pnmnGeO6kSCMUIvI3LymwqJGfVrUGHZKd
+Y9Ua5TMzWmUZSCJ+Z76ZGro7cRmyKxgh7F1Tdwfwllu4rFDqVOly9OInoMmA+rO9
+yoe5AlxGG9jOav8ZD3W7Aa0tGRRDPg1+sYh8XL+rYRuW6EBHzUKs/My5iMxEsD+u
+R0GWIjMxdZCtB8ilq8Px4yJ/nfoz4E/6EytxO67se0z/odRIRl8EoTMb/ErxOW+8
+m2pk9oA2oEa1HWnG0WRjE/TOKwmodEr6+uk5tAN9luJtMSeSRpRYsKHaoAJJucwR
+dXR1NO4fOEO2D8fvW3QqG65G+pfrPoZwU4WmdKVHLxisWhdulFoENAyHSYDFSOMP
+HPatnxQXZPCkQgu9EN3w6d+x1iqllTi2FiJgZrcpzgHyWnLqaofCR0YCwyYYm++X
+0W8jFKI9ByDK9JmKezo67N5+2VNqtXr6zkpnlDGnXndx4Se5et2yqSmiKq7a1U1e
+eEQkV4fKju5eSycLCFq5CqvCnlx8cEsRRebvICgPgpG8cVfrj3nOfWprrBwzNAC4
+0dZkdmfIRa3c6p3r+Aq/+/UUgg6N6mqI7RpFDaL0QBwiomyc0iScmFZWnHomR3oE
+wHx0Yl5n2FxTlRc6GUionOA90xAPNs7JrTvzFow4mDTCpwdhIMV+P2Wwieq5/FCH
+4Qa8KUECggIBAONfLJ11WB3q4GpzjJWHZUDnTrCltebOTBjkB4KIyFc1FfKxhrHy
+ddTRqU6UPUtr/LpTY53XLWlvZmsAgVdrWSfNXWkLC2wtMAKN9KTMAMt0VRbipdoJ
+O07OZqCxSW27iG4+U6gqJXsXo4VEY+tGujoj+Kwnfi4P/qR0JoiYondOJ9A+CRfB
+QIvYGphXekNMKzKRJUERakoGpSESE/kip5uROjs4dnWkt9RzOFByBJ4KGSIg+lt1
+dMg840rceDhJBo9003QxYhSaJxN+hQE3JSbemrv8l6NKMsnFU6ozp51bOMlNsfvT
+YjTh2VGPclBfZGOmLuGa3symwMxGPLsEsSxomaE0wHgqa7jSPOjLe7RGpzHb2vJm
+JLD82etA24OSpszONlLA90L/tJyOlyLeAaHO82VWxL+f76GbsGOE18VFkLiycNYG
+ywDsG+mWrVd9mcei5KQJLP4QxfcrU9DbxkmakAq094+Xllu6CaYL4zMQDQ67hD+a
+jHHm260Rda+5EeFJKUKXNo8B8ov/1+sZX8/Ljm9bI2occPHXlKI9x6t9PXVSfdWE
+hEIA31+5DdCfE8Uoa2dCL7sWKaDBl9hSKxCPJlnr6EO5BiHvACxUz83JjzjxCPcc
+Y+RjlTnTrglzIA/gNz1FBwlZfjcAbqu40YiUds2n0ffiEWc3WoFXFiCfAoICAQDM
+5vVPwW36aFX7KV0gbUyB+4o3pvhbwPjHlHqqz0U7sZmZV4PwhLgoFB71XMDc1tjy
+NvGm1or9yfnsYcssPv14PnFgxnKlR2t1HV1fOLdzd92chjr6BS+mr0v57Nz6g2D8
+/SGR+iy6enh9S5P+4a8jIvo1gYA2EodBEAMvsbGMQ49ZugPxCbvTaoZmb3qCLMNr
+Xcvul4fkg3qpaOb2ppyVo3arsTPhp84N7OcerCqo+OsOomDOBx0a0HrzF8gzbq62
+UdY2cSV4GG0aNazwXtkjcZ7ubpwsPY1b9g7cJdQkOJfSMacIeLPUSYPrlMkxr0Ck
+JAgU5piTKSsmWE+zoPYn8abhrHjuKZFHojdX0iS4ZaDkLOizVNxmJCi+3fOMo9MA
+i3oz6Vltmlrg1XdsBH0kuddEy92rmYwSnlR4nKV1C558ACbSS7xMLJH9zHLJuTVm
+6Cqev1DL/olHFDeLeTx7IuI7FdBGgh9hRxZ14q4g+0NiRW93CqIqJ0qqvqHgnMHo
+JmPIRu00WRm+MFv83LqjK4w3uopTH4g31TcGMZmK+qQSHR58dkSg4hmKyqMGy/p5
+wbT9ZbwMw0fD5zMXbF/OH98qg0ibFeoWbb7hbDV2YmXwE37zQqozrOqNJE/Ipyde
+faKIl1r0+txstzeoKFaexdSaez4ayx9VY/JHnhtYIQKCAgBfFp6B+2EuodvYqRpS
+Lp2Z1yKBncvFCuGoRzUc0jd2wSy8oqHCKbQ9nLFBu50mvbKj+dvfVu4W+g/LEBsH
+hE1059344DWWuA8sAlId2XM7oxsyxva1iMbU3lztNzeOPK+B627xFS83m1s3kLJ4
+4stfJUwKMzoB6Y6FGE5FMhA/9N+Rp2AGHzvvQCFfNLZdDq3CKBF8dNwOuVi1y+mu
+d1/BH6tKuQJRkOGQZ99VRVnI/lAkhcYuGaENXOlxlWJxp5imxiToov5xv0Y4UOEu
+8PEIoG/1zl1cXNuAppWmyJOAq3XEOkoFFxIrK4jHI0abdkl1ozpX/3zN+a8HvxgG
+3p96AwS89mLH3nXOhZ4TfYXujt29bETUuwL9QcGhMS6ch+haHqN5WFoQGwBduQRc
+AJyOHw6Lgw4lVa3R2FqcRCI4zi8IDxhBafXcHOJPfkRgIpb9dpJIR84ZMsqX4/bZ
+khALGCjFUYl2895d3h/K4Y3S/RN45dMZBnlV6UZVysd9BdkEbqRixPp8NERLaGPU
+XnhDXs3ipCrbZamWeqIed9fZUDczqZwWf6UQNW01ix7L8LtD6POB65wqkco3AL+Q
+KEz6T+OqF467Q9XcuydHYvDFD1Zmaedl8OmVyk/OL0jJiuwNt6diaEvZDSRLvFKM
+Xm7Cv9Qvjc+x60l78RpYnSMT4QKCAgEAnC26c6UrD7+xMbSwkAPfWgABtTOmgEFR
+Hsa0zTcZPUu8mN3U6mIvABV+F2Uwd+OnDtAEVzeWT6VAwwUzWLFWf+2Qe8QnJIWQ
+iX7taaQQaInHuAO0Nlf3nyRkU8ZQFP5I0eybRuoaZ7Tr3AzcbF1XIgAKGPhxYkWz
+JEtxMkxN2WyxeiHH2VSi/p1mffOgvFIkTdT7mQuU//+bYj9cNEqAdapXzLxPoENI
+UuLssztldM/uUoRAJynS7FpWLK5G4bUOpFcyYOLTPXvYxS5+d+TDuzlie7iBf1MX
+SS3+25xV25nrVsKT32gpIbk7PTh2NlGvgq0YH944E4tAoYSUk4Uvuvh9S+wrbNTw
+MFqbtmuto4OLJAB7Ch23tH8BM6J3fg5syTCG38SQbnNFq7Ppx8KQJIsmYwDFIIPz
++UkHd7tmfhVyIm/3Jzmk96bNTFcmc02hP6PcwI1wp1yHBE+Lmtq4196Z85FH5EZQ
+ZtRJcWdrdCYwkVn9gQ3w1xKF3ilZt5ukTS/dxI0TRmBXt9iidNVFyRNrWBruTjdt
+m6j/vM/XidBLeKcLUrDO1JYiA+E6a/bUHzQfoTmHIW9DZzX1gngPekxezMeucs88
+UwYphSb2hRqxOlGR5Rc+Jg855IT17ypBXsuCNHyxTqNbmd35JUpB49lEmM8xYjjK
+1EpPJHmFvCECggIAPjb46/G3Gq6mRYbkZE0MKHRzq+Od4kEQAbpRV3z45/y1NjG3
+qyoTduiY9eVzX0UVUEmtI8wA6xfpejUdv+S9GEAqbTE/fKOObmmWUsRKkWNPHSWp
+S1WICuuWds1tNomYHqz6lUPAnLzpq5jU22Dr3/2XYuOIHv5aOEPAAz5tlnkLhj8Y
+am7OWQ0MVJIb181w4eq6ozHlIA2ThoYWMUZzgAwMzPGM+iZZTLnJCIhKU5+nhJaM
+bTw2aFPkLJikn7r8PGO+4nIiBhJnQ+HPl5k5zO06dnMPna4sXGHkI9oeOFHZBmV3
+5bqpKMh1yyukEaNLWRdQoJDV4dSCiv/0iV28WWC7VIX8hgQAxgxPkVkKSAJultaF
+1DH06rwe38U7WFaK3hPyXCINU4Bv6KyJyCCi/TA4UmWJSXfO9c9O/Y7Y1seEu3NK
+NliqoXAqRcve30sVqGFqUIkNFlVhR8PmbjQevzlXJW3dlG47+QZAmSDoZ3UqoCJe
+yzMWjL79Bv/T7t1tl2EsUYizPaOKfmxWI6SVJC5TKEMV8cDsF/FM23P4ImOOaeLZ
+IfsCcCZwabE99oQgT0h7N//gREC4omENlb/Ip7YNBVbb4u15zlSQ5zy2F3qLchJP
+5TaZeXJQyYTz2Sq/9blLMnQldszylqJTR6LUVQA4KQIMZtGn4ADPHy0ylec=
+-----END RSA PRIVATE KEY-----
diff -pruN 1.4.0-1/test/engine/rsa8k.pl 2.5.0-0ubuntu1/test/engine/rsa8k.pl
--- 1.4.0-1/test/engine/rsa8k.pl	1970-01-01 00:00:00.000000000 +0000
+++ 2.5.0-0ubuntu1/test/engine/rsa8k.pl	2025-04-16 08:09:24.000000000 +0000
@@ -0,0 +1,8 @@
+#!/usr/bin/env perl
+
+use strict;
+use warnings;
+use test;
+
+test::rsaencdec("8k", 10, 1013);
+test::rsasignverify("8k", 10, 1013);
diff -pruN 1.4.0-1/test/engine/test.pm 2.5.0-0ubuntu1/test/engine/test.pm
--- 1.4.0-1/test/engine/test.pm	1970-01-01 00:00:00.000000000 +0000
+++ 2.5.0-0ubuntu1/test/engine/test.pm	2025-04-16 08:09:24.000000000 +0000
@@ -0,0 +1,150 @@
+#!/usr/bin/env perl
+
+use strict;
+use warnings;
+
+use FindBin;
+
+package test;
+
+sub osslversion1 {
+    my $vstr = `openssl version -v`;
+
+    return $vstr =~ /OpenSSL 1\..*/;
+}
+
+sub osslversion3 {
+    my $vstr = `openssl version -v`;
+
+    return $vstr =~ /OpenSSL 3\..*/;
+}
+
+sub cipher {
+	my $tests = 50;
+	my $max_file_size = 1024;
+	my $eng = "OPENSSL_CONF=$ENV{IBMCA_OPENSSL_TEST_CONF}";
+	my @hex = ("a".."f", "0".."9");
+
+	my ($cipher,$keylen,$ivlen) = @_;
+
+	# skip if engine not loaded
+	exit(77) unless (`$eng openssl engine -c` =~ m/ibmca/);
+
+	for my $i (1..$tests) {
+		my $bytes = 1 + int(rand($max_file_size));
+		my $key = "";
+		$key .= $hex[rand(@hex)] for (1..$keylen);
+		my $iv = "";
+		if ($ivlen > 0) {
+			$iv .= $hex[rand(@hex)] for (1..$ivlen);
+			$iv = "-iv $iv";
+		}
+
+		# engine enc, no-engine dec
+		`openssl rand $bytes > ${cipher}.${i}.data.in`;
+		`$eng openssl $cipher -e -K $key $iv -in ${cipher}.${i}.data.in -out ${cipher}.${i}.data.enc`;
+		`openssl $cipher -d -K $key $iv -in ${cipher}.${i}.data.enc -out ${cipher}.${i}.data.dec`;
+		`cmp ${cipher}.${i}.data.in ${cipher}.${i}.data.dec`;
+		exit(99) if ($?);
+		`rm -f ${cipher}.${i}.data.in ${cipher}.${i}.data.enc ${cipher}.${i}.data.dec`;
+
+		# no-engine enc, engine dec
+		`openssl rand $bytes > ${cipher}.${i}.data.in`;
+		`openssl $cipher -e -K $key $iv -in ${cipher}.${i}.data.in -out ${cipher}.${i}.data.enc`;
+		`$eng openssl $cipher -d -K $key $iv -in ${cipher}.${i}.data.enc -out ${cipher}.${i}.data.dec`;
+		`cmp ${cipher}.${i}.data.in ${cipher}.${i}.data.dec`;
+		exit(99) if ($?);
+		`rm -f ${cipher}.${i}.data.in ${cipher}.${i}.data.enc ${cipher}.${i}.data.dec`;
+	}
+}
+
+sub rsaencdec {
+	my $eng = "OPENSSL_CONF=$ENV{IBMCA_OPENSSL_TEST_CONF}";
+	my @hex = ("a".."f", "0".."9");
+
+	my ($keylen, $tests, $max_file_size) = @_;
+
+	# skip if engine not loaded
+	exit(77) unless (`$eng openssl engine -c` =~ m/ibmca/);
+
+	for my $i (1..$tests) {
+		my $bytes = 1 + int(rand($max_file_size));
+		# engine enc, no-engine dec
+		`openssl rand $bytes > rsaencdec.${i}.${keylen}.data.in`;
+		`$eng openssl rsautl -encrypt -inkey $FindBin::Bin/rsa$keylen.key -in rsaencdec.${i}.${keylen}.data.in -out rsaencdec.${i}.${keylen}.data.out`;
+		`openssl rsautl -decrypt -inkey $FindBin::Bin/rsa$keylen.key -in rsaencdec.${i}.${keylen}.data.out -out rsaencdec.${i}.${keylen}.data.dec`;
+		`cmp rsaencdec.${i}.${keylen}.data.in rsaencdec.${i}.${keylen}.data.dec`;
+		exit(99) if ($?);
+		`rm -f rsaencdec.${i}.${keylen}.data.in rsaencdec.${i}.${keylen}.out rsaencdec.${i}.${keylen}.dec`;
+
+		# no-engine enc, engine dec
+		`openssl rand $bytes > rsaencdec.${i}.${keylen}.data.in`;
+		`openssl rsautl -encrypt -inkey $FindBin::Bin/rsa$keylen.key -in rsaencdec.${i}.${keylen}.data.in -out rsaencdec.${i}.${keylen}.data.out`;
+		`$eng openssl rsautl -decrypt -inkey $FindBin::Bin/rsa$keylen.key -in rsaencdec.${i}.${keylen}.data.out -out rsaencdec.${i}.${keylen}.data.dec`;
+		`cmp rsaencdec.${i}.${keylen}.data.in rsaencdec.${i}.${keylen}.data.dec`;
+		exit(99) if ($?);
+		`rm -f rsaencdec.${i}.${keylen}.data.in rsaencdec.${i}.${keylen}.out rsaencdec.${i}.${keylen}.dec`;
+	}
+}
+
+sub rsasignverify {
+	my $eng = "OPENSSL_CONF=$ENV{IBMCA_OPENSSL_TEST_CONF}";
+	my @hex = ("a".."f", "0".."9");
+
+	my ($keylen, $tests, $max_file_size) = @_;
+
+	# skip if engine not loaded
+	exit(77) unless (`$eng openssl engine -c` =~ m/ibmca/);
+
+	for my $i (1..$tests) {
+		my $bytes = 1 + int(rand($max_file_size));
+		my $key = "";
+		$key .= $hex[rand(@hex)] for (1..$keylen);
+		# engine sign, no-engine verify
+		`openssl rand $bytes > rsasignverify.${i}.${keylen}.data.in`;
+		`$eng openssl rsautl -sign -inkey $FindBin::Bin/rsa$keylen.key -in rsasignverify.${i}.${keylen}.data.in -out rsasignverify.${i}.${keylen}.data.out`;
+		`openssl rsautl -verify -inkey $FindBin::Bin/rsa$keylen.key -in rsasignverify.${i}.${keylen}.data.out -out rsasignverify.${i}.${keylen}.data.rec`;
+		`cmp rsasignverify.${i}.${keylen}.data.in rsasignverify.${i}.${keylen}.data.rec`;
+		exit(99) if ($?);
+		`rm -f rsasignverify.${i}.${keylen}.data.in rsasignverify.${i}.${keylen}.data.out rsasignverify.${i}.${keylen}.data.rec`;
+
+		# no-engine sign, engine verify
+		`openssl rand $bytes > rsasignverify.${i}.${keylen}.data.in`;
+		`openssl rsautl -sign -inkey $FindBin::Bin/rsa$keylen.key -in rsasignverify.${i}.${keylen}.data.in -out rsasignverify.${i}.${keylen}.data.out`;
+		`$eng openssl rsautl -verify -inkey $FindBin::Bin/rsa$keylen.key -in rsasignverify.${i}.${keylen}.data.out -out rsasignverify.${i}.${keylen}.data.rec`;
+		`cmp rsasignverify.${i}.${keylen}.data.in rsasignverify.${i}.${keylen}.data.rec`;
+		exit(99) if ($?);
+		`rm -f rsasignverify.${i}.${keylen}.data.in rsasignverify.${i}.${keylen}.data.out rsasignverify.${i}.${keylen}.data.rec`;
+	}
+}
+
+sub dsasignverify {
+	my $tests = 50;
+	my $max_file_size = 1024;
+	my $eng = "OPENSSL_CONF=$ENV{IBMCA_OPENSSL_TEST_CONF}";
+	my @hex = ("a".."f", "0".."9");
+
+	my ($keylen) = @_;
+
+	# skip if engine not loaded
+	exit(77) unless (`$eng openssl engine -c` =~ m/ibmca/);
+
+	for my $i (1..$tests) {
+		my $bytes = 1 + int(rand($max_file_size));
+		# engine sign, no-engine verify
+		`openssl rand $bytes > dsa.${i}.${keylen}.data.in`;
+		`$eng openssl dgst -sign $FindBin::Bin/dsa$keylen.key -out dsa.${i}.${keylen}.data.out dsa.${i}.${keylen}.data.in`;
+		`openssl dgst -verify $FindBin::Bin/dsa${keylen}_pub.key -signature dsa.${i}.${keylen}.data.out dsa.${i}.${keylen}.data.in`;
+		exit(99) if ($?);
+		`rm -f dsa.${i}.${keylen}.data.in dsa.${i}.${keylen}.data.out`;
+
+		# no-engine sign, engine verify
+		`openssl rand $bytes > dsa.${i}.${keylen}.data.in`;
+		`openssl dgst -sign $FindBin::Bin/dsa$keylen.key -out dsa.${i}.${keylen}.data.out dsa.${i}.${keylen}.data.in`;
+		`$eng openssl dgst -verify $FindBin::Bin/dsa${keylen}_pub.key -signature dsa.${i}.${keylen}.data.out dsa.${i}.${keylen}.data.in`;
+		exit(99) if ($?);
+		`rm -f dsa.${i}.${keylen}.data.in dsa.${i}.${keylen}.data.out`;
+	}
+}
+
+1;
diff -pruN 1.4.0-1/test/engine/threadtest.c 2.5.0-0ubuntu1/test/engine/threadtest.c
--- 1.4.0-1/test/engine/threadtest.c	1970-01-01 00:00:00.000000000 +0000
+++ 2.5.0-0ubuntu1/test/engine/threadtest.c	2025-04-16 08:09:24.000000000 +0000
@@ -0,0 +1,176 @@
+#define _GNU_SOURCE
+#include <getopt.h>
+#include <pthread.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <openssl/conf.h>
+#include <openssl/ec.h>
+#include <openssl/engine.h>
+#include <openssl/evp.h>
+#include <openssl/ssl.h>
+
+/* This is just a random number of threads to stimulate engine configuration. */
+#define DEFAULT_MAX_THREADS 20
+
+static int setup()
+{
+    ENGINE        *engine;
+    EVP_PKEY_CTX  *pctx = NULL;
+
+    OPENSSL_load_builtin_modules();
+
+    ENGINE_load_builtin_engines();
+
+    /* CONF_MFLAGS_DEFAULT_SECTION introduced some time between 0.9.8b and
+       0.9.8e */
+#ifndef CONF_MFLAGS_DEFAULT_SECTION
+#define CONF_MFLAGS_DEFAULT_SECTION 0x0
+#endif
+
+    CONF_modules_load_file(NULL, NULL,
+                           CONF_MFLAGS_DEFAULT_SECTION|
+                           CONF_MFLAGS_IGNORE_MISSING_FILE);
+
+#if (OPENSSL_VERSION_NUMBER >= 0x10100000L) &&  \
+    !defined(LIBRESSL_VERSION_NUMBER)
+    /* OpenSSL 1.1.0+ takes care of initialization itself */
+#else
+    OpenSSL_add_all_algorithms();
+#endif
+
+    engine = ENGINE_by_id("ibmca");
+    pctx = EVP_PKEY_CTX_new_id(NID_X25519, engine);
+    if (pctx == NULL) {
+        return 0;
+    }
+    EVP_PKEY_CTX_free(pctx);
+
+    return 1;
+}
+
+static int check_globals()
+{
+    int            ret = 0;
+    EVP_PKEY      *eckey = NULL;
+    ENGINE        *engine;
+    EVP_PKEY_CTX  *pctx = NULL;
+
+    engine = ENGINE_by_id("ibmca");
+    if (engine == NULL) {
+        fprintf(stderr, "Failed to retrieve ibmca engine\n");
+        goto out;
+    }
+    pctx = EVP_PKEY_CTX_new_id(NID_X25519, engine);
+    if (pctx == NULL) {
+        fprintf(stderr, "Failed to create PKEY_CTX\n");
+        return 0;
+    }
+    if (EVP_PKEY_keygen_init(pctx) != 1 ||
+        EVP_PKEY_keygen(pctx, &eckey) != 1) {
+        fprintf(stderr, "keygen initialization failed\n");
+        goto out;
+    }
+    if (eckey == NULL) {
+        /* error */
+        fprintf(stderr, "Failed to create ec key for X25519\n");
+        goto out;
+    }
+    ret = 1;
+ out:
+    if (pctx)
+        EVP_PKEY_CTX_free(pctx);
+    if (eckey)
+        EVP_PKEY_free(eckey);
+    return ret;
+}
+
+static void *threadfn(void *arg) {
+    unsigned long res = 0;
+    (void) arg;
+    if (check_globals() != 1) {
+        res = 1;
+    }
+    return (void *)res;
+}
+
+int main(int argc, char **argv)
+{
+    pthread_t *threads;
+    unsigned long int i, maxthreads = 0, errors = 0;
+    int c;
+    pthread_t me;
+    
+    /* First fix the environment */
+    char *testcnf = getenv("IBMCA_OPENSSL_TEST_CONF");
+
+    /* Do not overwrite a user-provided OPENSSL_CONF in the
+       environment.  This allows us to execute this test also on an
+       installation with a user-provided engine configuration. */
+    if (testcnf && setenv("OPENSSL_CONF", testcnf, 0)) {
+        fprintf(stderr, "Failed to set OPENSSL_CONF environment variable!\n");
+        return 77;
+    }
+
+    // arg parse
+    while (1) {
+        int option_index;
+        static struct option long_options[] =
+            {
+             {"threads", required_argument, 0, 't'},
+             {0,         0,                 0, 0  }
+            };
+
+        c = getopt_long(argc, argv, "t:", long_options, &option_index);
+        if (c == -1) {
+            break;
+        } else if (c == 't') {
+            maxthreads = strtoul(optarg, NULL, 0);
+        } else {
+            fprintf(stderr, "USAGE: %s [-t|--threads <num>]\n", argv[0]);
+            fprintf(stderr, "where\t<num> specifies the number of threads to use (default: 20)\n");
+            return 1;
+        }
+    }
+
+    if (maxthreads == 0)
+        maxthreads = DEFAULT_MAX_THREADS;
+    threads = calloc(sizeof(pthread_t), maxthreads);
+    if (threads == NULL) {
+        fprintf(stderr, "Thread array allocation failed!\n");
+        return 1;
+    }
+
+    if (setup() != 1) {
+        fprintf(stderr, "Failed to set up test.  Skipping...\n");
+        free(threads);
+        return 77;
+    }
+    
+    me = pthread_self();
+    // Start threads
+    for (i = 0; i < maxthreads; ++i) {
+        int s = pthread_create(&threads[i], NULL, &threadfn, NULL);
+        if (s != 0) {
+            fprintf(stderr, "Failed to create thread %lu: %s\n", i, strerror(s));
+            threads[i] = me;
+        }
+    }
+    // Now join threads
+    for (i = 0; i < maxthreads; ++i) {
+        if (!pthread_equal(threads[i], me)) {
+            void *retval;
+            int s = pthread_join(threads[i], &retval);
+            if (s != 0) {
+                fprintf(stderr, "Failed to join thread %lu: %s\n", i, strerror(s));
+            } else if ((unsigned long)retval != 0) {
+                fprintf(stderr, "Error in thread %lu\n", i);
+                ++errors;
+            }
+        }
+    }
+    free(threads);
+    return errors ? 99 : 0;
+}
diff -pruN 1.4.0-1/test/provider/Makefile.am 2.5.0-0ubuntu1/test/provider/Makefile.am
--- 1.4.0-1/test/provider/Makefile.am	1970-01-01 00:00:00.000000000 +0000
+++ 2.5.0-0ubuntu1/test/provider/Makefile.am	2025-04-16 08:09:24.000000000 +0000
@@ -0,0 +1,62 @@
+TESTS_PERL = \
+tls.pl		\
+ecsign.pl	\
+ecderive.pl	\
+dhderive.pl	\
+rsa512.pl	\
+rsa1k.pl	\
+rsa2k.pl	\
+rsa4k.pl	\
+rsa8k.pl
+#rsa16k.pl
+
+if OPENSSL_IMPLICIT_REJECTION
+TESTS_PERL += rsaimplrej.pl
+endif
+
+TESTS = \
+	rsakey		\
+	eckey		\
+	dhkey		\
+	threadtest	\
+	${TESTS_PERL}
+
+check_PROGRAMS = rsakey eckey dhkey threadtest
+
+dhkey_SOURCES = dhkey.c
+dhkey_LDADD = -lcrypto -ldl
+if PROVIDER_FULL_LIBICA
+dhkey_CFLAGS = -DLIBICA_NAME=\"libica.so.@libicaversion@\"
+else
+dhkey_CFLAGS = -DLIBICA_NAME=\"libica-cex.so.@libicaversion@\"
+endif
+
+eckey_SOURCES = eckey.c
+eckey_LDADD = -lcrypto -ldl
+if PROVIDER_FULL_LIBICA
+eckey_CFLAGS = -DLIBICA_NAME=\"libica.so.@libicaversion@\"
+else
+eckey_CFLAGS = -DLIBICA_NAME=\"libica-cex.so.@libicaversion@\"
+endif
+
+rsakey_SOURCES = rsakey.c
+rsakey_LDADD = -lcrypto -ldl
+if PROVIDER_FULL_LIBICA
+rsakey_CFLAGS = -DLIBICA_NAME=\"libica.so.@libicaversion@\"
+else
+rsakey_CFLAGS = -DLIBICA_NAME=\"libica-cex.so.@libicaversion@\"
+endif
+
+threadtest_SOURCES = threadtest.c
+threadtest_LDADD = -lcrypto -lpthread
+
+AM_TESTS_ENVIRONMENT = export IBMCA_TEST_PATH=${top_builddir}/src/provider/.libs/ IBMCA_OPENSSL_TEST_CONF=${srcdir}/openssl-test.cnf PERL5LIB=${srcdir};
+EXTRA_DIST = ${TESTS_PERL} test.pm openssl-test.cnf server-cert-rsa.pem	\
+	server-key-rsa.pem server-cert-ec.pem server-key-ec.pem		\
+	rsa-implrej-bad-empty-in.bin rsa-implrej-bad-empty-out.bin	\
+	rsa-implrej-bad-max-in.bin rsa-implrej-bad-max-out.bin		\
+	rsa-implrej-bad-prf-in.bin rsa-implrej-bad-prf-out.bin		\
+	rsa-implrej-good-in.bin rsa-implrej-good-out.bin		\
+	rsa-implrej-key.pem rsa16k.pl
+
+CLEANFILES = *.out *.dec *.pub *.key
diff -pruN 1.4.0-1/test/provider/dhderive.pl 2.5.0-0ubuntu1/test/provider/dhderive.pl
--- 1.4.0-1/test/provider/dhderive.pl	1970-01-01 00:00:00.000000000 +0000
+++ 2.5.0-0ubuntu1/test/provider/dhderive.pl	2025-04-16 08:09:24.000000000 +0000
@@ -0,0 +1,45 @@
+#!/usr/bin/env perl
+
+#
+# Copyright [2021-2022] International Business Machines Corp.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+use strict;
+use warnings;
+use test;
+
+test::dhderive("ffdhe2048", 2);
+test::dhderivekdf("ffdhe2048", 2, 200, "X942KDF-ASN1", "SHA-256", "id-aes256-wrap");
+test::dhderive("ffdhe3072", 2);
+test::dhderivekdf("ffdhe3072", 2, 200, "X942KDF-ASN1", "SHA-256", "id-aes256-wrap");
+test::dhderive("ffdhe4096", 2);
+test::dhderivekdf("ffdhe4096", 2, 200, "X942KDF-ASN1", "SHA-256", "id-aes256-wrap");
+test::dhderive("ffdhe6144", 2);
+test::dhderivekdf("ffdhe6144", 2, 200, "X942KDF-ASN1", "SHA-256", "id-aes256-wrap");
+test::dhderive("ffdhe8192", 2);
+test::dhderivekdf("ffdhe8192", 2, 200, "X942KDF-ASN1", "SHA-256", "id-aes256-wrap");
+test::dhderive("modp_1536", 2);
+test::dhderivekdf("modp_1536", 2, 200, "X942KDF-ASN1", "SHA-256", "id-aes256-wrap");
+test::dhderive("modp_2048", 2);
+test::dhderivekdf("modp_2048", 2, 200, "X942KDF-ASN1", "SHA-256", "id-aes256-wrap");
+test::dhderive("modp_3072", 2);
+test::dhderivekdf("modp_3072", 2, 200, "X942KDF-ASN1", "SHA-256", "id-aes256-wrap");
+test::dhderive("modp_4096", 2);
+test::dhderivekdf("modp_4096", 2, 200, "X942KDF-ASN1", "SHA-256", "id-aes256-wrap");
+test::dhderive("modp_6144", 2);
+test::dhderivekdf("modp_6144", 2, 200, "X942KDF-ASN1", "SHA-256", "id-aes256-wrap");
+test::dhderive("modp_8192", 2);
+test::dhderivekdf("modp_8192", 2, 200, "X942KDF-ASN1", "SHA-256", "id-aes256-wrap");
+
diff -pruN 1.4.0-1/test/provider/dhkey.c 2.5.0-0ubuntu1/test/provider/dhkey.c
--- 1.4.0-1/test/provider/dhkey.c	1970-01-01 00:00:00.000000000 +0000
+++ 2.5.0-0ubuntu1/test/provider/dhkey.c	2025-04-16 08:09:24.000000000 +0000
@@ -0,0 +1,398 @@
+/*
+ * Copyright [2021-2022] International Business Machines Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <dlfcn.h>
+
+#include <openssl/conf.h>
+#include <openssl/evp.h>
+#include <openssl/dh.h>
+#include <openssl/obj_mac.h>
+#include <openssl/provider.h>
+#include <openssl/core_names.h>
+#include <openssl/err.h>
+
+#include <ica_api.h>
+
+#define UNUSED(var)                             ((void)(var))
+
+static void setup(void)
+{
+    OPENSSL_load_builtin_modules();
+
+    CONF_modules_load_file(NULL, NULL,
+                           CONF_MFLAGS_DEFAULT_SECTION|
+                           CONF_MFLAGS_IGNORE_MISSING_FILE);
+}
+
+static int check_provider(EVP_PKEY_CTX *ctx, const char *expected_provider)
+{
+    const OSSL_PROVIDER *provider;
+    const char *provname;
+
+    if (expected_provider == NULL)
+        expected_provider = "default";
+
+    provider = EVP_PKEY_CTX_get0_provider(ctx);
+    if (provider == NULL) {
+        fprintf(stderr, "Context is not a provider-context\n");
+        return 0;
+    }
+
+    provname = OSSL_PROVIDER_get0_name(provider);
+    if (strcmp(provname, expected_provider) != 0) {
+        fprintf(stderr, "Context is not using the %s provider, but '%s'\n",
+                expected_provider, provname);
+        return 0;
+    }
+
+    return 1;
+}
+
+static int generate_key(const char* provider, const char *algo, int nid,
+                        const char *name, const OSSL_PARAM *params,
+                        EVP_PKEY *template, EVP_PKEY **dh_pkey)
+{
+    char props[200];
+    EVP_PKEY_CTX *ctx = NULL;
+    int ok = 0;
+
+    sprintf(props, "%sprovider=%s", provider != NULL ? "?" : "",
+            provider != NULL ? provider : "default");
+
+    if (template != NULL) {
+        ctx = EVP_PKEY_CTX_new_from_pkey(NULL, template, props);
+        if (ctx == NULL) {
+            fprintf(stderr, "EVP_PKEY_CTX_new_from_pkey failed\n");
+            goto out;
+        }
+    } else {
+        ctx = EVP_PKEY_CTX_new_from_name(NULL, algo, props);
+        if (ctx == NULL) {
+            fprintf(stderr, "EVP_PKEY_CTX_new_from_name failed\n");
+            goto out;
+        }
+    }
+
+    if (EVP_PKEY_keygen_init(ctx) <= 0) {
+        fprintf(stderr, "EVP_PKEY_keygen_init failed\n");
+        goto out;
+    }
+
+    if (!check_provider(ctx, provider))
+        goto out;
+
+    if (template == NULL) {
+        if (EVP_PKEY_CTX_set_dh_nid(ctx, nid) <= 0) {
+            fprintf(stderr, "EVP_PKEY_CTX_set_dh_nid failed\n");
+            goto out;
+        }
+    }
+
+    if (params != NULL) {
+        if (EVP_PKEY_CTX_set_params(ctx, params) != 1) {
+            fprintf(stderr, "EVP_PKEY_CTX_set_params failed\n");
+            goto out;
+        }
+    }
+
+    if (EVP_PKEY_keygen(ctx, dh_pkey) <= 0) {
+        if (ERR_GET_REASON(ERR_peek_last_error()) == 7) {
+            /* group not supported => test passed */
+            fprintf(stderr, "Group %s not supported by OpenSSL\n", name);
+            ok = 1;
+        } else {
+            fprintf(stderr, "EVP_PKEY_keygen failed\n");
+        }
+        goto out;
+    }
+
+    ok = 1;
+
+out:
+    if (ctx != NULL)
+        EVP_PKEY_CTX_free(ctx);
+
+    return ok;
+}
+
+static int derive_key(const char* provider, EVP_PKEY *dh_pkey,
+                      EVP_PKEY *peer_pkey, int kdf, const char *kdf_md,
+                      int kdf_nid, size_t kdf_outlen,
+                      unsigned char *derived_key, size_t *derived_key_len)
+{
+    char props[200];
+    EVP_PKEY_CTX *ctx = NULL;
+    int ok = 0;
+
+    sprintf(props, "%sprovider=%s", provider != NULL ? "?" : "",
+            provider != NULL ? provider : "default");
+
+    ctx = EVP_PKEY_CTX_new_from_pkey(NULL, dh_pkey, props);
+    if (ctx == NULL) {
+        fprintf(stderr, "EVP_PKEY_CTX_new_from_pkey failed\n");
+        goto out;
+    }
+
+    if (EVP_PKEY_derive_init(ctx) <= 0) {
+        fprintf(stderr, "EVP_PKEY_derive_init failed\n");
+        goto out;
+    }
+
+    if (!check_provider(ctx, provider))
+        goto out;
+
+    if (kdf != 0 && kdf_md != NULL && kdf_nid != 0 && kdf_outlen != 0) {
+        if (EVP_PKEY_CTX_set_dh_kdf_type(ctx, kdf) != 1) {
+            fprintf(stderr, "EVP_PKEY_CTX_set_dh_kdf_type failed\n");
+            goto out;
+        }
+
+        if (EVP_PKEY_CTX_set_dh_kdf_md(ctx, EVP_get_digestbyname(kdf_md)) != 1) {
+            fprintf(stderr, "EVP_PKEY_CTX_set_dh_kdf_md failed\n");
+            goto out;
+        }
+
+        if (EVP_PKEY_CTX_set0_dh_kdf_oid(ctx, OBJ_nid2obj(kdf_nid)) != 1) {
+            fprintf(stderr, "EVP_PKEY_CTX_set0_dh_kdf_oid failed\n");
+            goto out;
+        }
+
+        if (EVP_PKEY_CTX_set_dh_kdf_outlen(ctx, kdf_outlen) != 1) {
+            fprintf(stderr, "EVP_PKEY_CTX_set_dh_kdf_outlen failed\n");
+            goto out;
+        }
+    }
+
+    if (EVP_PKEY_derive_set_peer_ex(ctx, peer_pkey, 1) != 1) {
+        fprintf(stderr, "EVP_PKEY_derive_set_peer_ex failed\n");
+        goto out;
+    }
+
+    if (EVP_PKEY_derive(ctx, derived_key, derived_key_len) <= 0) {
+        fprintf(stderr, "EVP_PKEY_derive failed\n");
+        goto out;
+    }
+
+    ok = 1;
+
+out:
+    if (ctx != NULL)
+        EVP_PKEY_CTX_free(ctx);
+
+    return ok;
+}
+
+static int check_dhkey(int nid, const char *name, const char *algo)
+{
+    int            ok = 0;
+    EVP_PKEY      *dh_pkey = NULL;
+    EVP_PKEY      *peer_pkey = NULL;
+    size_t         keylen1, keylen2;
+    unsigned char  keybuf1[1024], keybuf2[1024];
+
+    /* Keygen with IBMCA provider */
+    if (!generate_key("ibmca", algo, nid, name, NULL, NULL, &dh_pkey))
+        goto out;
+    if (dh_pkey == NULL) {
+        ok = 1; /* Group not supported, skip */
+        goto out;
+    }
+
+    /* Keygen with IBMCA provider (using dh_pkey as template) */
+    if (!generate_key("ibmca", algo, nid, name, NULL, dh_pkey, &peer_pkey))
+        goto out;
+
+    /* Derive with IBMCA provider (no KDF) */
+    keylen1 = sizeof(keybuf1);
+    if (!derive_key("ibmca", dh_pkey, peer_pkey, 0, NULL, 0, 0,
+                    keybuf1, &keylen1))
+        goto out;
+
+    /* Derive with default provider (no KDF) */
+    keylen2 = sizeof(keybuf2);
+    if (!derive_key(NULL, dh_pkey, peer_pkey, 0, NULL, 0, 0,
+                    keybuf2, &keylen2))
+        goto out;
+
+    if (keylen1 != keylen2 || memcmp(keybuf1, keybuf2, keylen1) != 0) {
+        fprintf(stderr, "Derived keys are not equal\n");
+        goto out;
+    }
+
+    /* Derive with IBMCA provider (X9_63 KDF) */
+    keylen1 = sizeof(keybuf1);
+    if (!derive_key("ibmca", dh_pkey, peer_pkey,
+                    EVP_PKEY_DH_KDF_X9_42, "SHA256", NID_id_aes256_wrap,
+                    sizeof(keybuf1), keybuf1, &keylen1))
+        goto out;
+
+    /* Derive with default provider (X9_63 KDF) */
+    keylen2 = sizeof(keybuf2);
+    if (!derive_key(NULL, dh_pkey, peer_pkey,
+                    EVP_PKEY_DH_KDF_X9_42, "SHA256", NID_id_aes256_wrap,
+                    sizeof(keybuf2), keybuf2, &keylen2))
+        goto out;
+
+    if (keylen1 != keylen2 || memcmp(keybuf1, keybuf2, keylen1) != 0) {
+        fprintf(stderr, "Derived keys are not equal\n");
+        goto out;
+    }
+
+    ok = 1;
+
+ out:
+    if (peer_pkey)
+       EVP_PKEY_free(peer_pkey);
+    if (dh_pkey)
+       EVP_PKEY_free(dh_pkey);
+
+    ERR_print_errors_fp(stderr);
+
+    return ok;
+}
+
+static const unsigned int required_ica_mechs[] = { RSA_ME };
+static const unsigned int required_ica_mechs_len =
+                        sizeof(required_ica_mechs) / sizeof(unsigned int);
+
+typedef unsigned int (*ica_get_functionlist_t)(libica_func_list_element *,
+                                               unsigned int *);
+
+static int check_libica()
+{
+    unsigned int mech_len, i, k, found = 0;
+    libica_func_list_element *mech_list = NULL;
+    void *ibmca_dso;
+    ica_get_functionlist_t p_ica_get_functionlist;
+    int rc;
+
+    ibmca_dso = dlopen(LIBICA_NAME, RTLD_NOW);
+    if (ibmca_dso == NULL) {
+        fprintf(stderr, "Failed to load libica '%s'!\n", LIBICA_NAME);
+        return 77;
+    }
+
+    p_ica_get_functionlist =
+            (ica_get_functionlist_t)dlsym(ibmca_dso, "ica_get_functionlist");
+    if (p_ica_get_functionlist == NULL) {
+        fprintf(stderr, "Failed to get ica_get_functionlist from '%s'!\n",
+                LIBICA_NAME);
+        return 77;
+    }
+
+    rc = p_ica_get_functionlist(NULL, &mech_len);
+    if (rc != 0) {
+        fprintf(stderr, "Failed to get function list from libica!\n");
+        return 77;
+    }
+
+    mech_list = calloc(sizeof(libica_func_list_element), mech_len);
+    if (mech_list == NULL) {
+        fprintf(stderr, "Failed to allocate memory for function list!\n");
+        return 77;
+    }
+
+    rc = p_ica_get_functionlist(mech_list, &mech_len);
+    if (rc != 0) {
+        fprintf(stderr, "Failed to get function list from libica!\n");
+        free(mech_list);
+        return 77;
+    }
+
+    for (i = 0; i < mech_len; i++) {
+        for (k = 0; k < required_ica_mechs_len; k++) {
+            if (mech_list[i].mech_mode_id == required_ica_mechs[k]) {
+                if (mech_list[i].flags &
+                    (ICA_FLAG_SW | ICA_FLAG_SHW | ICA_FLAG_DHW))
+                    found++;
+            }
+        }
+    }
+
+    free(mech_list);
+
+    if (found < required_ica_mechs_len) {
+        fprintf(stderr,
+               "Libica does not support the required algorithms, skipping.\n");
+        return 77;
+    }
+
+    return 0;
+}
+
+int main(int argc, char **argv)
+{
+    static const struct testparams {
+        int         nid;
+        const char *name;
+    } params[] = {
+                {NID_ffdhe2048,        "NID_ffdhe2048"},
+                {NID_ffdhe3072,        "NID_ffdhe3072"},
+                {NID_ffdhe4096,        "NID_ffdhe4096"},
+                {NID_ffdhe6144,        "NID_ffdhe6144"},
+                {NID_ffdhe8192,        "NID_ffdhe8192"},
+                {NID_modp_1536,        "NID_modp_1536"},
+                {NID_modp_2048,        "NID_modp_2048"},
+                {NID_modp_3072,        "NID_modp_3072"},
+                {NID_modp_4096,        "NID_modp_4096"},
+                {NID_modp_6144,        "NID_modp_6144"},
+                {NID_modp_8192,        "NID_modp_8192"},
+    };
+
+    UNUSED(argc);
+    UNUSED(argv);
+
+    int ret = 0, i;
+    /* First fix the environment */
+    char *testcnf = getenv("IBMCA_OPENSSL_TEST_CONF");
+    char *testpath = getenv("IBMCA_TEST_PATH");
+
+    /* Do not overwrite a user-provided OPENSSL_CONF in the
+       environment.  This allows us to execute this test also on an
+       installation with a user-provided engine configuration. */
+    if (testcnf && setenv("OPENSSL_CONF", testcnf, 0)) {
+        fprintf(stderr, "Failed to set OPENSSL_CONF environment variable!\n");
+        return 77;
+    }
+    
+    if (testpath && setenv("OPENSSL_MODULES", testpath, 0)) {
+        fprintf(stderr, "Failed to set OPENSSL_MODULES environment variable!\n");
+        return 77;
+    }
+
+    OPENSSL_init_crypto(OPENSSL_INIT_LOAD_CONFIG, NULL);
+
+    ret = check_libica();
+    if (ret != 0)
+        return ret;
+
+    setup();
+    for (i = 0; i < (int)(sizeof(params) / sizeof(struct testparams)); ++i) {
+        if (!check_dhkey(params[i].nid, params[i].name, "DH")) {
+            fprintf(stderr, "Failure for %s (DH)\n", params[i].name);
+            ret = 99;
+        }
+        if (!check_dhkey(params[i].nid, params[i].name, "DHX")) {
+            fprintf(stderr, "Failure for %s (DHX)\n", params[i].name);
+            ret = 99;
+        }
+    }
+    return ret;
+}
diff -pruN 1.4.0-1/test/provider/ecderive.pl 2.5.0-0ubuntu1/test/provider/ecderive.pl
--- 1.4.0-1/test/provider/ecderive.pl	1970-01-01 00:00:00.000000000 +0000
+++ 2.5.0-0ubuntu1/test/provider/ecderive.pl	2025-04-16 08:09:24.000000000 +0000
@@ -0,0 +1,47 @@
+#!/usr/bin/env perl
+
+#
+# Copyright [2021-2022] International Business Machines Corp.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+use strict;
+use warnings;
+use test;
+
+test::ecderive("prime192v1", 2);
+test::ecderivekdf("prime192v1", 2, 200, "X963KDF", "SHA-256");
+test::ecderive("secp224r1", 2);
+test::ecderivekdf("secp224r1", 2, 200, "X963KDF", "SHA-256");
+test::ecderive("prime256v1", 2);
+test::ecderivekdf("prime256v1", 2, 200, "X963KDF", "SHA-256");
+test::ecderive("secp384r1", 2);
+test::ecderivekdf("secp384r1", 2, 200, "X963KDF", "SHA-256");
+test::ecderive("secp521r1", 2);
+test::ecderivekdf("secp521r1", 2, 200, "X963KDF", "SHA-256");
+test::ecderive("brainpoolP160r1", 2);
+test::ecderivekdf("brainpoolP160r1", 2, 200, "X963KDF", "SHA-256");
+test::ecderive("brainpoolP192r1", 2);
+test::ecderivekdf("brainpoolP192r1", 2, 200, "X963KDF", "SHA-256");
+test::ecderive("brainpoolP224r1", 2);
+test::ecderivekdf("brainpoolP224r1", 2, 200, "X963KDF", "SHA-256");
+test::ecderive("brainpoolP256r1", 2);
+test::ecderivekdf("brainpoolP256r1", 2, 200, "X963KDF", "SHA-256");
+test::ecderive("brainpoolP320r1", 2);
+test::ecderivekdf("brainpoolP320r1", 2, 200, "X963KDF", "SHA-256");
+test::ecderive("brainpoolP384r1", 2);
+test::ecderivekdf("brainpoolP384r1", 2, 200, "X963KDF", "SHA-256");
+test::ecderive("brainpoolP512r1", 2);
+test::ecderivekdf("brainpoolP512r1", 2, 200, "X963KDF", "SHA-256");
+
diff -pruN 1.4.0-1/test/provider/eckey.c 2.5.0-0ubuntu1/test/provider/eckey.c
--- 1.4.0-1/test/provider/eckey.c	1970-01-01 00:00:00.000000000 +0000
+++ 2.5.0-0ubuntu1/test/provider/eckey.c	2025-04-16 08:09:24.000000000 +0000
@@ -0,0 +1,1154 @@
+/*
+ * Copyright [2021-2022] International Business Machines Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <dlfcn.h>
+
+#include <openssl/conf.h>
+#include <openssl/evp.h>
+#include <openssl/ec.h>
+#include <openssl/obj_mac.h>
+#include <openssl/provider.h>
+#include <openssl/core_names.h>
+#include <openssl/err.h>
+
+#include <ica_api.h>
+
+#define UNUSED(var)                             ((void)(var))
+
+static void setup(void)
+{
+    OPENSSL_load_builtin_modules();
+
+    CONF_modules_load_file(NULL, NULL,
+                           CONF_MFLAGS_DEFAULT_SECTION|
+                           CONF_MFLAGS_IGNORE_MISSING_FILE);
+}
+
+static int check_provider(EVP_PKEY_CTX *ctx, const char *expected_provider)
+{
+    const OSSL_PROVIDER *provider;
+    const char *provname;
+
+    if (expected_provider == NULL)
+        expected_provider = "default";
+
+    provider = EVP_PKEY_CTX_get0_provider(ctx);
+    if (provider == NULL) {
+        fprintf(stderr, "Context is not a provider-context\n");
+        return 0;
+    }
+
+    provname = OSSL_PROVIDER_get0_name(provider);
+    if (strcmp(provname, expected_provider) != 0) {
+        fprintf(stderr, "Context is not using the %s provider, but '%s'\n",
+                expected_provider, provname);
+        return 0;
+    }
+
+    return 1;
+}
+
+static int generate_key(const char* provider, int nid, const char *curvename,
+                        const OSSL_PARAM *params, EVP_PKEY *template,
+                        EVP_PKEY **ec_pkey)
+{
+    char props[200];
+    EVP_PKEY_CTX *ctx = NULL;
+    int ok = 0;
+
+    sprintf(props, "%sprovider=%s", provider != NULL ? "?" : "",
+            provider != NULL ? provider : "default");
+
+    if (template != NULL) {
+        ctx = EVP_PKEY_CTX_new_from_pkey(NULL, template, props);
+        if (ctx == NULL) {
+            fprintf(stderr, "EVP_PKEY_CTX_new_from_pkey failed\n");
+            goto out;
+        }
+    } else {
+        ctx = EVP_PKEY_CTX_new_from_name(NULL, "EC", props);
+        if (ctx == NULL) {
+            fprintf(stderr, "EVP_PKEY_CTX_new_from_name failed\n");
+            goto out;
+        }
+    }
+
+    if (EVP_PKEY_keygen_init(ctx) <= 0) {
+        fprintf(stderr, "EVP_PKEY_keygen_init failed\n");
+        goto out;
+    }
+
+    if (!check_provider(ctx, provider))
+        goto out;
+
+    if (template == NULL) {
+        if (EVP_PKEY_CTX_set_ec_paramgen_curve_nid(ctx, nid) <= 0) {
+            if (ERR_GET_REASON(ERR_peek_last_error()) == 7) {
+                /* curve not supported => test passed */
+                fprintf(stderr, "Curve %s not supported by OpenSSL\n", curvename);
+                ok = 1;
+            } else {
+                fprintf(stderr, "EVP_PKEY_CTX_set_ec_paramgen_curve_nid failed\n");
+            }
+            goto out;
+        }
+    }
+
+    if (params != NULL) {
+        if (EVP_PKEY_CTX_set_params(ctx, params) != 1) {
+            fprintf(stderr, "EVP_PKEY_CTX_set_params failed\n");
+            goto out;
+        }
+    }
+
+    if (EVP_PKEY_keygen(ctx, ec_pkey) <= 0) {
+        if (ERR_GET_REASON(ERR_peek_last_error()) == 7) {
+            /* curve not supported => test passed */
+            fprintf(stderr, "Curve %s not supported by OpenSSL\n", curvename);
+            ok = 1;
+        } else {
+            fprintf(stderr, "EVP_PKEY_keygen failed\n");
+        }
+        goto out;
+    }
+
+    ok = 1;
+
+out:
+    if (ctx != NULL)
+        EVP_PKEY_CTX_free(ctx);
+
+    return ok;
+}
+
+static int sign_single(const char* provider, EVP_PKEY *ec_pkey,
+                       const unsigned char *tbs, size_t tbs_len,
+                       unsigned char *sig, size_t *sig_len)
+{
+    char props[200];
+    EVP_PKEY_CTX *ctx = NULL;
+    int ok = 0;
+
+    sprintf(props, "%sprovider=%s", provider != NULL ? "?" : "",
+            provider != NULL ? provider : "default");
+
+    ctx = EVP_PKEY_CTX_new_from_pkey(NULL, ec_pkey, props);
+    if (ctx == NULL) {
+        fprintf(stderr, "EVP_PKEY_CTX_new_from_pkey failed\n");
+        goto out;
+    }
+
+    if (EVP_PKEY_sign_init(ctx) <= 0) {
+        fprintf(stderr, "EVP_PKEY_sign_init failed\n");
+        goto out;
+    }
+
+    if (!check_provider(ctx, provider))
+        goto out;
+
+    if (EVP_PKEY_sign(ctx, sig, sig_len, tbs, tbs_len) <= 0) {
+        fprintf(stderr, "EVP_PKEY_sign failed\n");
+        goto out;
+    }
+
+    ok = 1;
+
+out:
+    if (ctx != NULL)
+        EVP_PKEY_CTX_free(ctx);
+
+    return ok;
+}
+
+static int verify_single(const char* provider, const char *curvename,
+                         EVP_PKEY *ec_pkey, const unsigned char *tbs,
+                         size_t tbs_len, const unsigned char *sig,
+                         size_t sig_len)
+{
+    char props[200];
+    EVP_PKEY_CTX *ctx = NULL;
+    int ok = 0;
+
+    sprintf(props, "%sprovider=%s", provider != NULL ? "?" : "",
+            provider != NULL ? provider : "default");
+
+    ctx = EVP_PKEY_CTX_new_from_pkey(NULL, ec_pkey, props);
+    if (ctx == NULL) {
+        fprintf(stderr, "EVP_PKEY_CTX_new_from_pkey failed\n");
+        goto out;
+    }
+
+    if (EVP_PKEY_verify_init(ctx) <= 0) {
+        fprintf(stderr, "EVP_PKEY_verify_init failed\n");
+        goto out;
+    }
+
+    if (!check_provider(ctx, provider))
+        goto out;
+
+    ok = EVP_PKEY_verify(ctx, sig, sig_len, tbs, tbs_len);
+    if (ok == -1) {
+        /* error */
+        fprintf(stderr, "Failed to verify signature with %s (%s provider)\n",
+                curvename, provider != NULL ? provider : "default");
+        ok = 0;
+        goto out;
+    } else if (ok == 0) {
+        /* incorrect signature */
+        fprintf(stderr, "Signature incorrect with %s (%s provider)\n",
+                curvename, provider != NULL ? provider : "default");
+        goto out;
+    } else {
+        /* signature ok */
+        printf("Signature correct with %s (%s provider)\n", curvename,
+                provider != NULL ? provider : "default");
+        ok = 1;
+    }
+
+out:
+    if (ctx != NULL)
+        EVP_PKEY_CTX_free(ctx);
+
+    return ok;
+}
+
+static int sign_digest(const char* provider, EVP_PKEY *ec_pkey,
+                       const char *md_name, const OSSL_PARAM *params,
+                       const unsigned char *tbs, size_t tbs_len,
+                       unsigned char *sig, size_t *sig_len)
+{
+    char props[200];
+    EVP_MD_CTX *md_ctx = NULL;
+    EVP_PKEY_CTX *ctx = NULL;
+    int ok = 0;
+
+    sprintf(props, "%sprovider=%s", provider != NULL ? "?" : "",
+            provider != NULL ? provider : "default");
+
+    md_ctx = EVP_MD_CTX_new();
+    if (md_ctx == NULL) {
+        fprintf(stderr, "EVP_MD_CTX_new failed\n");
+        goto out;
+    }
+
+    if (EVP_DigestSignInit_ex(md_ctx, &ctx, md_name, NULL,
+                              props, ec_pkey, NULL) == 0) {
+        fprintf(stderr, "EVP_DigestSignInit_ex failed\n");
+        goto out;
+    }
+
+    if (!check_provider(ctx, provider))
+        goto out;
+
+    if (params != NULL) {
+        if (EVP_PKEY_CTX_set_params(ctx, params) == 0) {
+            fprintf(stderr, "EVP_PKEY_CTX_set_params failed\n");
+            goto out;
+        }
+    }
+
+    if (EVP_DigestSignUpdate(md_ctx, tbs, tbs_len) <= 0) {
+        fprintf(stderr, "EVP_DigestSignUpdate (1) failed\n");
+        goto out;
+    }
+
+    if (EVP_DigestSignUpdate(md_ctx, tbs, tbs_len) <= 0) {
+        fprintf(stderr, "EVP_DigestSignUpdate (2) failed\n");
+        goto out;
+    }
+
+    if (EVP_DigestSignFinal(md_ctx, sig, sig_len) <= 0) {
+        fprintf(stderr, "EVP_DigestSignFinal failed\n");
+        goto out;
+    }
+
+    ok = 1;
+
+out:
+    if (md_ctx != NULL)
+        EVP_MD_CTX_free(md_ctx);
+
+    return ok;
+}
+
+static int verify_digest(const char* provider, const char *curvename,
+                         EVP_PKEY *ec_pkey, const char *md_name,
+                         const unsigned char *tbs, size_t tbs_len,
+                         unsigned char *sig, size_t sig_len)
+{
+    char props[200];
+    EVP_MD_CTX *md_ctx = NULL;
+    EVP_PKEY_CTX *ctx = NULL;
+    int ok = 0;
+
+    sprintf(props, "%sprovider=%s", provider != NULL ? "?" : "",
+            provider != NULL ? provider : "default");
+
+    md_ctx = EVP_MD_CTX_new();
+    if (md_ctx == NULL) {
+        fprintf(stderr, "EVP_MD_CTX_new failed\n");
+        goto out;
+    }
+
+    if (EVP_DigestVerifyInit_ex(md_ctx, &ctx, md_name, NULL,
+                                props, ec_pkey, NULL) == 0) {
+        fprintf(stderr, "EVP_DigestVerifyInit_ex failed\n");
+        goto out;
+    }
+
+    if (!check_provider(ctx, provider))
+        goto out;
+
+    if (EVP_DigestVerifyUpdate(md_ctx, tbs, tbs_len) <= 0) {
+        fprintf(stderr, "EVP_DigestVerifyUpdate (1) failed\n");
+        goto out;
+    }
+
+    if (EVP_DigestVerifyUpdate(md_ctx, tbs, tbs_len) <= 0) {
+        fprintf(stderr, "EVP_DigestVerifyUpdate (2) failed\n");
+        goto out;
+    }
+
+    ok = EVP_DigestVerifyFinal(md_ctx, sig, sig_len);
+    if (ok == -1) {
+        /* error */
+        fprintf(stderr, "Failed to digest-verify signature with %s (%s provider)\n",
+                curvename, provider != NULL ? provider : "default");
+        ok = 0;
+        goto out;
+    } else if (ok == 0) {
+        /* incorrect signature */
+        fprintf(stderr, "Digest-Signature incorrect with %s (%s provider)\n",
+                curvename, provider != NULL ? provider : "default");
+        goto out;
+    } else {
+        /* signature ok */
+        printf("Digest-Signature correct with %s (%s provider)\n", curvename,
+                provider != NULL ? provider : "default");
+        ok = 1;
+    }
+
+out:
+    if (md_ctx != NULL)
+        EVP_MD_CTX_free(md_ctx);
+
+    return ok;
+}
+
+#ifdef EVP_PKEY_OP_SIGNMSG
+static int sign_message(const char* provider, EVP_PKEY *ec_pkey,
+                        const char *alg_name, const unsigned char *tbs,
+                        size_t tbs_len, unsigned char *sig, size_t *sig_len)
+{
+    char props[200];
+    EVP_PKEY_CTX *ctx = NULL;
+    EVP_SIGNATURE *alg = NULL;
+    int ok = 0;
+
+    sprintf(props, "%sprovider=%s", provider != NULL ? "?" : "",
+            provider != NULL ? provider : "default");
+
+    ctx = EVP_PKEY_CTX_new_from_pkey(NULL, ec_pkey, props);
+    if (ctx == NULL) {
+        fprintf(stderr, "EVP_PKEY_CTX_new_from_pkey failed\n");
+        goto out;
+    }
+
+    alg = EVP_SIGNATURE_fetch(NULL, alg_name, props);
+    if (alg == NULL) {
+        fprintf(stderr, "EVP_SIGNATURE_fetch for %s failed\n", alg_name);
+        goto out;
+    }
+
+    if (EVP_PKEY_sign_message_init(ctx, alg, NULL) <= 0) {
+        fprintf(stderr, "EVP_PKEY_sign_message_init failed\n");
+        goto out;
+    }
+
+    if (!check_provider(ctx, provider))
+        goto out;
+
+    if (EVP_PKEY_sign_message_update(ctx, tbs, tbs_len) <= 0) {
+        fprintf(stderr, "EVP_PKEY_sign_message_update (1) failed\n");
+        ctx = NULL;
+        goto out;
+    }
+
+    if (EVP_PKEY_sign_message_update(ctx, tbs, tbs_len) <= 0) {
+        fprintf(stderr, "EVP_PKEY_sign_message_update (2) failed\n");
+        goto out;
+    }
+
+    if (EVP_PKEY_sign_message_final(ctx, sig, sig_len) <= 0) {
+        fprintf(stderr, "EVP_PKEY_sign_message_final failed\n");
+        goto out;
+    }
+
+    ok = 1;
+
+out:
+    if (ctx != NULL)
+        EVP_PKEY_CTX_free(ctx);
+    if (alg != NULL)
+        EVP_SIGNATURE_free(alg);
+
+    return ok;
+}
+
+static int verify_message(const char* provider, const char *curvename,
+                          EVP_PKEY *ec_pkey, const char *alg_name,
+                          const unsigned char *tbs, size_t tbs_len,
+                          unsigned char *sig, size_t sig_len)
+{
+    char props[200];
+    EVP_PKEY_CTX *ctx = NULL;
+    EVP_SIGNATURE *alg = NULL;
+    int ok = 0;
+
+    sprintf(props, "%sprovider=%s", provider != NULL ? "?" : "",
+            provider != NULL ? provider : "default");
+
+    ctx = EVP_PKEY_CTX_new_from_pkey(NULL, ec_pkey, props);
+    if (ctx == NULL) {
+        fprintf(stderr, "EVP_PKEY_CTX_new_from_pkey failed\n");
+        goto out;
+    }
+
+    alg = EVP_SIGNATURE_fetch(NULL, alg_name, props);
+    if (alg == NULL) {
+        fprintf(stderr, "EVP_SIGNATURE_fetch for %s failed\n", alg_name);
+        goto out;
+    }
+
+    if (EVP_PKEY_verify_message_init(ctx, alg, NULL) <= 0) {
+        fprintf(stderr, "EVP_PKEY_verify_message_init failed\n");
+        goto out;
+    }
+
+    if (!check_provider(ctx, provider))
+        goto out;
+
+
+    if (EVP_PKEY_CTX_set_signature(ctx, sig, sig_len) != 1) {
+        fprintf(stderr, "EVP_PKEY_CTX_set_signature failed\n");
+        goto out;
+    }
+
+    if (EVP_PKEY_verify_message_update(ctx, tbs, tbs_len) <= 0) {
+        fprintf(stderr, "EVP_PKEY_verify_message_update (1) failed\n");
+        goto out;
+    }
+
+    if (EVP_PKEY_verify_message_update(ctx, tbs, tbs_len) <= 0) {
+        fprintf(stderr, "EVP_PKEY_verify_message_update (2) failed\n");
+        goto out;
+    }
+
+    ok = EVP_PKEY_verify_message_final(ctx);
+    if (ok == -1) {
+        /* error */
+        fprintf(stderr, "Failed to verify-message signature with %s (%s provider)\n",
+                curvename, provider != NULL ? provider : "default");
+        ok = 0;
+        goto out;
+    } else if (ok == 0) {
+        /* incorrect signature */
+        fprintf(stderr, "Message-Signature incorrect with %s (%s provider)\n",
+                curvename, provider != NULL ? provider : "default");
+        goto out;
+    } else {
+        /* signature ok */
+        printf("Message-Signature correct with %s (%s provider)\n", curvename,
+                provider != NULL ? provider : "default");
+        ok = 1;
+    }
+
+out:
+    if (ctx != NULL)
+        EVP_PKEY_CTX_free(ctx);
+    if (alg != NULL)
+        EVP_SIGNATURE_free(alg);
+
+    return ok;
+}
+
+static int sign_message_single(const char* provider, EVP_PKEY *ec_pkey,
+                               const char *alg_name, const unsigned char *tbs,
+                               size_t tbs_len, unsigned char *sig,
+                               size_t *sig_len)
+{
+    char props[200];
+    EVP_PKEY_CTX *ctx = NULL;
+    EVP_SIGNATURE *alg = NULL;
+    int ok = 0;
+
+    sprintf(props, "%sprovider=%s", provider != NULL ? "?" : "",
+            provider != NULL ? provider : "default");
+
+    ctx = EVP_PKEY_CTX_new_from_pkey(NULL, ec_pkey, props);
+    if (ctx == NULL) {
+        fprintf(stderr, "EVP_PKEY_CTX_new_from_pkey failed\n");
+        goto out;
+    }
+
+    alg = EVP_SIGNATURE_fetch(NULL, alg_name, props);
+    if (alg == NULL) {
+        fprintf(stderr, "EVP_SIGNATURE_fetch for %s failed\n", alg_name);
+        goto out;
+    }
+
+    if (EVP_PKEY_sign_message_init(ctx, alg, NULL) <= 0) {
+        fprintf(stderr, "EVP_PKEY_sign_message_init failed\n");
+        goto out;
+    }
+
+    if (!check_provider(ctx, provider))
+        goto out;
+
+    if (EVP_PKEY_sign(ctx, sig, sig_len, tbs, tbs_len) <= 0) {
+        fprintf(stderr, "EVP_PKEY_sign failed\n");
+        goto out;
+    }
+
+    ok = 1;
+
+out:
+    if (ctx != NULL)
+        EVP_PKEY_CTX_free(ctx);
+    if (alg != NULL)
+        EVP_SIGNATURE_free(alg);
+
+    return ok;
+}
+
+static int verify_message_single(const char* provider, const char *curvename,
+                                 EVP_PKEY *ec_pkey, const char *alg_name,
+                                 const unsigned char *tbs, size_t tbs_len,
+                                 unsigned char *sig, size_t sig_len)
+{
+    char props[200];
+    EVP_PKEY_CTX *ctx = NULL;
+    EVP_SIGNATURE *alg = NULL;
+    int ok = 0;
+
+    sprintf(props, "%sprovider=%s", provider != NULL ? "?" : "",
+            provider != NULL ? provider : "default");
+
+    ctx = EVP_PKEY_CTX_new_from_pkey(NULL, ec_pkey, props);
+    if (ctx == NULL) {
+        fprintf(stderr, "EVP_PKEY_CTX_new_from_pkey failed\n");
+        goto out;
+    }
+
+    alg = EVP_SIGNATURE_fetch(NULL, alg_name, props);
+    if (alg == NULL) {
+        fprintf(stderr, "EVP_SIGNATURE_fetch for %s failed\n", alg_name);
+        goto out;
+    }
+
+    if (EVP_PKEY_verify_message_init(ctx, alg, NULL) <= 0) {
+        fprintf(stderr, "EVP_PKEY_verify_message_init failed\n");
+        goto out;
+    }
+
+    if (!check_provider(ctx, provider))
+        goto out;
+
+
+    ok = EVP_PKEY_verify(ctx, sig, sig_len, tbs, tbs_len);
+    if (ok == -1) {
+        /* error */
+        fprintf(stderr, "Failed to verify-message-single signature with %s (%s provider)\n",
+                curvename, provider != NULL ? provider : "default");
+        ok = 0;
+        goto out;
+    } else if (ok == 0) {
+        /* incorrect signature */
+        fprintf(stderr, "Message-Signature single incorrect with %s (%s provider)\n",
+                curvename, provider != NULL ? provider : "default");
+        goto out;
+    } else {
+        /* signature ok */
+        printf("Message-Signature single correct with %s (%s provider)\n", curvename,
+                provider != NULL ? provider : "default");
+        ok = 1;
+    }
+
+out:
+    if (ctx != NULL)
+        EVP_PKEY_CTX_free(ctx);
+    if (alg != NULL)
+        EVP_SIGNATURE_free(alg);
+
+    return ok;
+}
+
+static int sign_message_prehashed(const char* provider, EVP_PKEY *ec_pkey,
+                                  const char *alg_name,
+                                  const unsigned char *tbs, size_t tbs_len,
+                                  unsigned char *sig, size_t *sig_len)
+{
+    char props[200];
+    EVP_PKEY_CTX *ctx = NULL;
+    EVP_SIGNATURE *alg = NULL;
+    int ok = 0;
+
+    sprintf(props, "%sprovider=%s", provider != NULL ? "?" : "",
+            provider != NULL ? provider : "default");
+
+    ctx = EVP_PKEY_CTX_new_from_pkey(NULL, ec_pkey, props);
+    if (ctx == NULL) {
+        fprintf(stderr, "EVP_PKEY_CTX_new_from_pkey failed\n");
+        goto out;
+    }
+
+    alg = EVP_SIGNATURE_fetch(NULL, alg_name, props);
+    if (alg == NULL) {
+        fprintf(stderr, "EVP_SIGNATURE_fetch for %s failed\n", alg_name);
+        goto out;
+    }
+
+    if (EVP_PKEY_sign_init_ex2(ctx, alg, NULL) <= 0) {
+        fprintf(stderr, "EVP_PKEY_sign_init_ex2 failed\n");
+        goto out;
+    }
+
+    if (!check_provider(ctx, provider))
+        goto out;
+
+    if (EVP_PKEY_sign(ctx, sig, sig_len, tbs, tbs_len) <= 0) {
+        fprintf(stderr, "EVP_PKEY_sign failed\n");
+        goto out;
+    }
+
+    ok = 1;
+
+out:
+    if (ctx != NULL)
+        EVP_PKEY_CTX_free(ctx);
+    if (alg != NULL)
+        EVP_SIGNATURE_free(alg);
+
+    return ok;
+}
+
+static int verify_message_prehashed(const char* provider, const char *curvename,
+                                    EVP_PKEY *ec_pkey, const char *alg_name,
+                                    const unsigned char *tbs, size_t tbs_len,
+                                    unsigned char *sig, size_t sig_len)
+{
+    char props[200];
+    EVP_PKEY_CTX *ctx = NULL;
+    EVP_SIGNATURE *alg = NULL;
+    int ok = 0;
+
+    sprintf(props, "%sprovider=%s", provider != NULL ? "?" : "",
+            provider != NULL ? provider : "default");
+
+    ctx = EVP_PKEY_CTX_new_from_pkey(NULL, ec_pkey, props);
+    if (ctx == NULL) {
+        fprintf(stderr, "EVP_PKEY_CTX_new_from_pkey failed\n");
+        goto out;
+    }
+
+    alg = EVP_SIGNATURE_fetch(NULL, alg_name, props);
+    if (alg == NULL) {
+        fprintf(stderr, "EVP_SIGNATURE_fetch for %s failed\n", alg_name);
+        goto out;
+    }
+
+    if (EVP_PKEY_verify_init_ex2(ctx, alg, NULL) <= 0) {
+        fprintf(stderr, "EVP_PKEY_verify_init_ex2 failed\n");
+        goto out;
+    }
+
+    if (!check_provider(ctx, provider))
+        goto out;
+
+
+    ok = EVP_PKEY_verify(ctx, sig, sig_len, tbs, tbs_len);
+    if (ok == -1) {
+        /* error */
+        fprintf(stderr, "Failed to verify-message-prehashed signature with %s (%s provider)\n",
+                curvename, provider != NULL ? provider : "default");
+        ok = 0;
+        goto out;
+    } else if (ok == 0) {
+        /* incorrect signature */
+        fprintf(stderr, "Message-Signature prehashed incorrect with %s (%s provider)\n",
+                curvename, provider != NULL ? provider : "default");
+        goto out;
+    } else {
+        /* signature ok */
+        printf("Message-Signature prehashed correct with %s (%s provider)\n", curvename,
+                provider != NULL ? provider : "default");
+        ok = 1;
+    }
+
+out:
+    if (ctx != NULL)
+        EVP_PKEY_CTX_free(ctx);
+    if (alg != NULL)
+        EVP_SIGNATURE_free(alg);
+
+    return ok;
+}
+#endif
+
+static int derive_key(const char* provider, EVP_PKEY *ec_pkey,
+                      EVP_PKEY *peer_pkey, int kdf, const char *kdf_md,
+                      size_t kdf_outlen, unsigned char *derived_key,
+                      size_t *derived_key_len)
+{
+    char props[200];
+    EVP_PKEY_CTX *ctx = NULL;
+    int ok = 0;
+
+    sprintf(props, "%sprovider=%s", provider != NULL ? "?" : "",
+            provider != NULL ? provider : "default");
+
+    ctx = EVP_PKEY_CTX_new_from_pkey(NULL, ec_pkey, props);
+    if (ctx == NULL) {
+        fprintf(stderr, "EVP_PKEY_CTX_new_from_pkey failed\n");
+        goto out;
+    }
+
+    if (EVP_PKEY_derive_init(ctx) <= 0) {
+        fprintf(stderr, "EVP_PKEY_derive_init failed\n");
+        goto out;
+    }
+
+    if (!check_provider(ctx, provider))
+        goto out;
+
+    if (kdf != 0 && kdf_md != NULL && kdf_outlen != 0) {
+        if (EVP_PKEY_CTX_set_ecdh_kdf_type(ctx, kdf) != 1) {
+            fprintf(stderr, "EVP_PKEY_CTX_set_ecdh_kdf_type failed\n");
+            goto out;
+        }
+
+        if (EVP_PKEY_CTX_set_ecdh_kdf_md(ctx,
+                                         EVP_get_digestbyname(kdf_md)) != 1) {
+            fprintf(stderr, "EVP_PKEY_CTX_set_ecdh_kdf_md failed\n");
+            goto out;
+        }
+
+        if (EVP_PKEY_CTX_set_ecdh_kdf_outlen(ctx, kdf_outlen) != 1) {
+            fprintf(stderr, "EVP_PKEY_CTX_set_ecdh_kdf_outlen failed\n");
+            goto out;
+        }
+    }
+
+    if (EVP_PKEY_derive_set_peer_ex(ctx, peer_pkey, 1) != 1) {
+        fprintf(stderr, "EVP_PKEY_derive_set_peer_ex failed\n");
+        goto out;
+    }
+
+    if (EVP_PKEY_derive(ctx, derived_key, derived_key_len) <= 0) {
+        fprintf(stderr, "EVP_PKEY_derive failed\n");
+        goto out;
+    }
+
+    ok = 1;
+
+out:
+    if (ctx != NULL)
+        EVP_PKEY_CTX_free(ctx);
+
+    return ok;
+}
+
+static int check_eckey(int nid, const char *curvename)
+{
+    int            ok = 0;
+    size_t         siglen;
+    unsigned char  sigbuf[1024];
+    EVP_PKEY      *ec_pkey = NULL;
+    EVP_PKEY      *peer_pkey = NULL;
+    size_t         keylen1, keylen2;
+    unsigned char  keybuf1[512], keybuf2[512];
+    unsigned char  digest[32];
+#ifdef OSSL_SIGNATURE_PARAM_NONCE_TYPE
+    size_t         siglen2;
+    unsigned char  sigbuf2[1024];
+    OSSL_PARAM params[2];
+    unsigned int   nonce_type;
+#endif
+#ifdef OSSL_PKEY_PARAM_DHKEM_IKM
+    EVP_PKEY      *ec_pkey1 = NULL, *ec_pkey2 = NULL;
+    const char     dhkem_ikm[100] = { 0 };
+#endif
+
+    memset(digest, 0, sizeof(digest));
+
+    /* Keygen with IBMCA provider */
+    if (!generate_key("ibmca", nid, curvename, NULL, NULL, &ec_pkey))
+        goto out;
+    if (ec_pkey == NULL) {
+        ok = 1; /* Curve not supported, skip */
+        goto out;
+    }
+
+#ifdef OSSL_PKEY_PARAM_DHKEM_IKM
+    /* Test DHKEM keygen */
+    switch (nid) {
+    case NID_X9_62_prime256v1:
+    case NID_secp384r1:
+    case NID_secp521r1:
+        params[0] = OSSL_PARAM_construct_octet_string(OSSL_PKEY_PARAM_DHKEM_IKM,
+                                                      (void *)dhkem_ikm,
+                                                      sizeof(dhkem_ikm));
+        params[1] = OSSL_PARAM_construct_end();
+
+        /* Keygen using DHKEM with IBMCA provider */
+        if (!generate_key("ibmca", nid, curvename, params, NULL, &ec_pkey1))
+            goto out;
+
+        /* Keygen using DHKEM with default provider */
+        if (!generate_key(NULL, nid, curvename, params, NULL, &ec_pkey2))
+            goto out;
+
+        /* Compare key from IBMCA with key from default provider */
+        if (!EVP_PKEY_eq(ec_pkey1, ec_pkey2)) {
+            fprintf(stderr, "EC keys generated via DHKEM do not match\n");
+            ok = 1;
+            goto out;
+        }
+
+        EVP_PKEY_free(ec_pkey1);
+        ec_pkey1 = NULL;
+        EVP_PKEY_free(ec_pkey2);
+        ec_pkey2 = NULL;
+        break;
+    }
+#endif
+
+    /* Sign with IBMCA provider */
+    siglen = sizeof(sigbuf);
+    if (!sign_single("ibmca", ec_pkey, digest, sizeof(digest),
+                     sigbuf, &siglen))
+        goto out;
+
+    /* Verify with default provider */
+    if (!verify_single(NULL, curvename, ec_pkey,
+                       digest, sizeof(digest), sigbuf, siglen))
+        goto out;
+
+    /* Verify with IBMCA provider */
+    if (!verify_single("ibmca", curvename, ec_pkey,
+                       digest, sizeof(digest), sigbuf, siglen))
+        goto out;
+
+
+    /* Digest-Sign with IBMCA provider */
+    siglen = sizeof(sigbuf);
+    if (!sign_digest("ibmca", ec_pkey, "SHA256", NULL,
+                     digest, sizeof(digest), sigbuf, &siglen))
+        goto out;
+
+    /* Digest-Verify with default provider */
+    if (!verify_digest(NULL, curvename, ec_pkey, "SHA256",
+                       digest, sizeof(digest), sigbuf, siglen))
+        goto out;
+
+    /* Digest-Verify with IBMCA provider */
+    if (!verify_digest("ibmca", curvename, ec_pkey, "SHA256",
+                       digest, sizeof(digest), sigbuf, siglen))
+        goto out;
+
+    /* Digest-Sign with default provider */
+    siglen = sizeof(sigbuf);
+    if (!sign_digest(NULL, ec_pkey, "SHA256", NULL,
+                     digest, sizeof(digest), sigbuf, &siglen))
+        goto out;
+
+    /* Digest-Verify with default provider */
+    if (!verify_digest(NULL, curvename, ec_pkey, "SHA256",
+                       digest, sizeof(digest), sigbuf, siglen))
+        goto out;
+
+    /* Digest-Verify with IBMCA provider */
+    if (!verify_digest("ibmca", curvename, ec_pkey, "SHA256",
+                       digest, sizeof(digest), sigbuf, siglen))
+        goto out;
+
+#ifdef OSSL_SIGNATURE_PARAM_NONCE_TYPE
+    nonce_type = 1;
+    params[0] = OSSL_PARAM_construct_uint(OSSL_SIGNATURE_PARAM_NONCE_TYPE,
+                                          &nonce_type);
+    params[1] = OSSL_PARAM_construct_end();
+
+    /* Digest-Sign using deterministic signature with default provider */
+    siglen = sizeof(sigbuf);
+    if (!sign_digest(NULL, ec_pkey, "SHA256", params,
+                     digest, sizeof(digest), sigbuf, &siglen))
+        goto out;
+
+    /* Digest-Sign using deterministic signature with IBMCA provider */
+    siglen2 = sizeof(sigbuf2);
+    if (!sign_digest("ibmca", ec_pkey, "SHA256", params,
+                     digest, sizeof(digest), sigbuf2, &siglen2))
+        goto out;
+
+    if (siglen != siglen2 ||
+        memcmp(sigbuf, sigbuf2, siglen) != 0) {
+        fprintf(stderr, "Deterministic signatures do not match\n");
+        ok = 0;
+        goto out;
+    } else {
+        printf("Deterministic signature is correct\n");
+    }
+#endif
+
+#ifdef EVP_PKEY_OP_SIGNMSG
+    /* SignMessage with IBMCA provider */
+    siglen = sizeof(sigbuf);
+    if (!sign_message("ibmca", ec_pkey, "ECDSA-SHA256",
+                      digest, sizeof(digest), sigbuf, &siglen))
+        goto out;
+
+    /* VerifyMessage with default provider */
+    if (!verify_message(NULL, curvename, ec_pkey, "ECDSA-SHA256",
+                        digest, sizeof(digest), sigbuf, siglen))
+        goto out;
+
+    /* VerifyMessage with IBMCA provider */
+    if (!verify_message("ibmca", curvename, ec_pkey, "ECDSA-SHA256",
+                        digest, sizeof(digest), sigbuf, siglen))
+        goto out;
+
+
+    /* SignMessage one-shot with IBMCA provider */
+    siglen = sizeof(sigbuf);
+    if (!sign_message_single("ibmca", ec_pkey, "ECDSA-SHA256",
+                             digest, sizeof(digest), sigbuf, &siglen))
+        goto out;
+
+    /* VerifyMessage one-shot with default provider */
+    if (!verify_message_single(NULL, curvename, ec_pkey, "ECDSA-SHA256",
+                               digest, sizeof(digest), sigbuf, siglen))
+        goto out;
+
+    /* VerifyMessage one-shot with IBMCA provider */
+    if (!verify_message_single("ibmca", curvename, ec_pkey, "ECDSA-SHA256",
+                               digest, sizeof(digest), sigbuf, siglen))
+        goto out;
+
+
+    /* Sign pre-hashed message with IBMCA provider */
+    siglen = sizeof(sigbuf);
+    if (!sign_message_prehashed("ibmca", ec_pkey, "ECDSA-SHA256",
+                                digest, sizeof(digest), sigbuf, &siglen))
+        goto out;
+
+    /* Verify pre-hashed message with default provider */
+    if (!verify_message_prehashed(NULL, curvename, ec_pkey, "ECDSA-SHA256",
+                                  digest, sizeof(digest), sigbuf, siglen))
+        goto out;
+
+    /* Verify pre-hashed message with IBMCA provider */
+    if (!verify_message_prehashed("ibmca", curvename, ec_pkey, "ECDSA-SHA256",
+                                  digest, sizeof(digest), sigbuf, siglen))
+        goto out;
+#endif
+
+    /* Keygen with IBMCA provider (using ec_pkey as template) */
+    if (!generate_key("ibmca", nid, curvename, NULL, ec_pkey, &peer_pkey))
+        goto out;
+
+    /* Derive with IBMCA provider (no KDF) */
+    keylen1 = sizeof(keybuf1);
+    if (!derive_key("ibmca", ec_pkey, peer_pkey, 0, NULL, 0, keybuf1, &keylen1))
+        goto out;
+
+    /* Derive with default provider (no KDF) */
+    keylen2 = sizeof(keybuf2);
+    if (!derive_key(NULL, ec_pkey, peer_pkey, 0, NULL, 0, keybuf2, &keylen2))
+        goto out;
+
+    if (keylen1 != keylen2 || memcmp(keybuf1, keybuf2, keylen1) != 0) {
+        fprintf(stderr, "Derived keys are not equal\n");
+        goto out;
+    }
+
+    /* Derive with IBMCA provider (X9_63 KDF) */
+    keylen1 = sizeof(keybuf1);
+    if (!derive_key("ibmca", ec_pkey, peer_pkey,
+                    EVP_PKEY_ECDH_KDF_X9_63, "SHA256", keylen1,
+                    keybuf1, &keylen1))
+        goto out;
+
+    /* Derive with default provider (X9_63 KDF) */
+    keylen2 = sizeof(keybuf2);
+    if (!derive_key(NULL, ec_pkey, peer_pkey,
+                    EVP_PKEY_ECDH_KDF_X9_63, "SHA256", keylen2,
+                    keybuf2, &keylen2))
+        goto out;
+
+    if (keylen1 != keylen2 || memcmp(keybuf1, keybuf2, keylen1) != 0) {
+        fprintf(stderr, "Derived keys are not equal\n");
+        goto out;
+    }
+
+    ok = 1;
+
+ out:
+    if (peer_pkey)
+       EVP_PKEY_free(peer_pkey);
+    if (ec_pkey)
+       EVP_PKEY_free(ec_pkey);
+#ifdef OSSL_PKEY_PARAM_DHKEM_IKM
+    if (ec_pkey1)
+       EVP_PKEY_free(ec_pkey1);
+    if (ec_pkey2)
+       EVP_PKEY_free(ec_pkey2);
+#endif
+
+    ERR_print_errors_fp(stderr);
+
+    return ok;
+}
+
+static const unsigned int required_ica_mechs[] = { EC_DH, EC_DSA_SIGN,
+                                                   EC_DSA_VERIFY, EC_KGEN, };
+static const unsigned int required_ica_mechs_len =
+                        sizeof(required_ica_mechs) / sizeof(unsigned int);
+
+typedef unsigned int (*ica_get_functionlist_t)(libica_func_list_element *,
+                                               unsigned int *);
+
+static int check_libica()
+{
+    unsigned int mech_len, i, k, found = 0;
+    libica_func_list_element *mech_list = NULL;
+    void *ibmca_dso;
+    ica_get_functionlist_t p_ica_get_functionlist;
+    int rc;
+
+    ibmca_dso = dlopen(LIBICA_NAME, RTLD_NOW);
+    if (ibmca_dso == NULL) {
+        fprintf(stderr, "Failed to load libica '%s'!\n", LIBICA_NAME);
+        return 77;
+    }
+
+    p_ica_get_functionlist =
+            (ica_get_functionlist_t)dlsym(ibmca_dso, "ica_get_functionlist");
+    if (p_ica_get_functionlist == NULL) {
+        fprintf(stderr, "Failed to get ica_get_functionlist from '%s'!\n",
+                LIBICA_NAME);
+        return 77;
+    }
+
+    rc = p_ica_get_functionlist(NULL, &mech_len);
+    if (rc != 0) {
+        fprintf(stderr, "Failed to get function list from libica!\n");
+        return 77;
+    }
+
+    mech_list = calloc(sizeof(libica_func_list_element), mech_len);
+    if (mech_list == NULL) {
+        fprintf(stderr, "Failed to allocate memory for function list!\n");
+        return 77;
+    }
+
+    rc = p_ica_get_functionlist(mech_list, &mech_len);
+    if (rc != 0) {
+        fprintf(stderr, "Failed to get function list from libica!\n");
+        free(mech_list);
+        return 77;
+    }
+
+    for (i = 0; i < mech_len; i++) {
+        for (k = 0; k < required_ica_mechs_len; k++) {
+            if (mech_list[i].mech_mode_id == required_ica_mechs[k]) {
+                if (mech_list[i].flags &
+                    (ICA_FLAG_SW | ICA_FLAG_SHW | ICA_FLAG_DHW))
+                    found++;
+            }
+        }
+    }
+
+    free(mech_list);
+
+    if (found < required_ica_mechs_len) {
+        fprintf(stderr,
+               "Libica does not support the required algorithms, skipping.\n");
+        return 77;
+    }
+
+    return 0;
+}
+
+int main(int argc, char **argv)
+{
+    static const struct testparams {
+        int         nid;
+        const char *name;
+    } params[] = {
+                {NID_X9_62_prime192v1, "NID_X9_62_prime192v1"},
+                {NID_secp224r1,        "NID_secp224r1"},
+                {NID_X9_62_prime256v1, "NID_X9_62_prime256v1"},
+                {NID_secp384r1,        "NID_secp384r1"},
+                {NID_secp521r1,        "NID_secp521r1"},
+                {NID_brainpoolP160r1,  "NID_brainpoolP160r1"},
+                {NID_brainpoolP192r1,  "NID_brainpoolP192r1"},
+                {NID_brainpoolP224r1,  "NID_brainpoolP224r1"},
+                {NID_brainpoolP256r1,  "NID_brainpoolP256r1"},
+                {NID_brainpoolP320r1,  "NID_brainpoolP320r1"},
+                {NID_brainpoolP384r1,  "NID_brainpoolP384r1"},
+                {NID_brainpoolP512r1,  "NID_brainpoolP512r1"}
+    };
+
+    UNUSED(argc);
+    UNUSED(argv);
+
+    int ret = 0, i;
+    /* First fix the environment */
+    char *testcnf = getenv("IBMCA_OPENSSL_TEST_CONF");
+    char *testpath = getenv("IBMCA_TEST_PATH");
+
+    /* Do not overwrite a user-provided OPENSSL_CONF in the
+       environment.  This allows us to execute this test also on an
+       installation with a user-provided engine configuration. */
+    if (testcnf && setenv("OPENSSL_CONF", testcnf, 0)) {
+        fprintf(stderr, "Failed to set OPENSSL_CONF environment variable!\n");
+        return 77;
+    }
+    
+    if (testpath && setenv("OPENSSL_MODULES", testpath, 0)) {
+        fprintf(stderr, "Failed to set OPENSSL_MODULES environment variable!\n");
+        return 77;
+    }
+
+    OPENSSL_init_crypto(OPENSSL_INIT_LOAD_CONFIG, NULL);
+
+    ret = check_libica();
+    if (ret != 0)
+        return ret;
+
+    setup();
+    for (i = 0; i < (int)(sizeof(params) / sizeof(struct testparams)); ++i) {
+        if (!check_eckey(params[i].nid, params[i].name)) {
+            fprintf(stderr, "Failure for %s\n", params[i].name);
+            ret = 99;
+        }
+    }
+    return ret;
+}
diff -pruN 1.4.0-1/test/provider/ecsign.pl 2.5.0-0ubuntu1/test/provider/ecsign.pl
--- 1.4.0-1/test/provider/ecsign.pl	1970-01-01 00:00:00.000000000 +0000
+++ 2.5.0-0ubuntu1/test/provider/ecsign.pl	2025-04-16 08:09:24.000000000 +0000
@@ -0,0 +1,35 @@
+#!/usr/bin/env perl
+
+#
+# Copyright [2021-2022] International Business Machines Corp.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+use strict;
+use warnings;
+use test;
+
+test::ecsignverify("prime192v1", 8, 200, "SHA-256");
+test::ecsignverify("secp224r1", 8, 200, "SHA-256");
+test::ecsignverify("prime256v1", 8, 200, "SHA-256");
+test::ecsignverify("secp384r1", 8, 200, "SHA-256");
+test::ecsignverify("secp521r1", 8, 200, "SHA-256");
+test::ecsignverify("brainpoolP160r1", 8, 200, "SHA-256");
+test::ecsignverify("brainpoolP192r1", 8, 200, "SHA-256");
+test::ecsignverify("brainpoolP224r1", 8, 200, "SHA-256");
+test::ecsignverify("brainpoolP256r1", 8, 200, "SHA-256");
+test::ecsignverify("brainpoolP320r1", 8, 200, "SHA-256");
+test::ecsignverify("brainpoolP384r1", 8, 200, "SHA-256");
+test::ecsignverify("brainpoolP512r1", 8, 200, "SHA-256");
+
diff -pruN 1.4.0-1/test/provider/openssl-test.cnf 2.5.0-0ubuntu1/test/provider/openssl-test.cnf
--- 1.4.0-1/test/provider/openssl-test.cnf	1970-01-01 00:00:00.000000000 +0000
+++ 2.5.0-0ubuntu1/test/provider/openssl-test.cnf	2025-04-16 08:09:24.000000000 +0000
@@ -0,0 +1,26 @@
+openssl_conf = openssl_def
+
+[openssl_def]
+providers = provider_sect
+alg_section = evp_properties
+
+[provider_sect]
+default = default_sect
+ibmca_provider = ibmca_sect
+
+[default_sect]
+activate = 1
+
+[ibmca_sect]
+identity = ibmca
+module = ibmca-provider.so
+activate = 1
+#debug = yes
+#debug-path = /dir/to/debug/directory
+#fips=yes
+#algorithms = RSA,EC,DH
+algorithms = ALL
+#fallback-properties = provider=default
+
+[evp_properties]
+default_properties = ?provider=ibmca
Binary files 1.4.0-1/test/provider/rsa-implrej-bad-empty-in.bin and 2.5.0-0ubuntu1/test/provider/rsa-implrej-bad-empty-in.bin differ
Binary files 1.4.0-1/test/provider/rsa-implrej-bad-max-in.bin and 2.5.0-0ubuntu1/test/provider/rsa-implrej-bad-max-in.bin differ
diff -pruN 1.4.0-1/test/provider/rsa-implrej-bad-max-out.bin 2.5.0-0ubuntu1/test/provider/rsa-implrej-bad-max-out.bin
--- 1.4.0-1/test/provider/rsa-implrej-bad-max-out.bin	1970-01-01 00:00:00.000000000 +0000
+++ 2.5.0-0ubuntu1/test/provider/rsa-implrej-bad-max-out.bin	2025-04-16 08:09:24.000000000 +0000
@@ -0,0 +1,2 @@
+"ØP{žëà’²O`-Å»yÁkØÛòg±ÒùÂä½}%’ÏS!àó5WVY#Ç:Ôðœ"¾¨‘H>`1p(³ª&‘Éy91×á_@gæ9y³'QeŽ÷iaé|ùÎó'‹1Ó„;‚ÂQÂ0TÈö„0æ1ªÖ>pâ[ÍŽûTÉ.ÆÓ±¢øæNï÷Ó„•°üPÉq8¯K
+g¡Äâ{{„93.ß¨`þ®e<Ö¦(¬U•÷çCä,h"4‡	%îª§vÏ.ã½¦Ÿg?ø·É\—™ç£¾¥çä¡ÃYw/¶±ÆæÅfþ0Ã
\ No newline at end of file
Binary files 1.4.0-1/test/provider/rsa-implrej-bad-prf-in.bin and 2.5.0-0ubuntu1/test/provider/rsa-implrej-bad-prf-in.bin differ
diff -pruN 1.4.0-1/test/provider/rsa-implrej-bad-prf-out.bin 2.5.0-0ubuntu1/test/provider/rsa-implrej-bad-prf-out.bin
--- 1.4.0-1/test/provider/rsa-implrej-bad-prf-out.bin	1970-01-01 00:00:00.000000000 +0000
+++ 2.5.0-0ubuntu1/test/provider/rsa-implrej-bad-prf-out.bin	2025-04-16 08:09:24.000000000 +0000
@@ -0,0 +1 @@
+›
\ No newline at end of file
Binary files 1.4.0-1/test/provider/rsa-implrej-good-in.bin and 2.5.0-0ubuntu1/test/provider/rsa-implrej-good-in.bin differ
diff -pruN 1.4.0-1/test/provider/rsa-implrej-good-out.bin 2.5.0-0ubuntu1/test/provider/rsa-implrej-good-out.bin
--- 1.4.0-1/test/provider/rsa-implrej-good-out.bin	1970-01-01 00:00:00.000000000 +0000
+++ 2.5.0-0ubuntu1/test/provider/rsa-implrej-good-out.bin	2025-04-16 08:09:24.000000000 +0000
@@ -0,0 +1 @@
+lorem ipsum dolor sit amet
\ No newline at end of file
diff -pruN 1.4.0-1/test/provider/rsa-implrej-key.pem 2.5.0-0ubuntu1/test/provider/rsa-implrej-key.pem
--- 1.4.0-1/test/provider/rsa-implrej-key.pem	1970-01-01 00:00:00.000000000 +0000
+++ 2.5.0-0ubuntu1/test/provider/rsa-implrej-key.pem	2025-04-16 08:09:24.000000000 +0000
@@ -0,0 +1,36 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIEowIBAAKCAQEAyMyDlxQJjaVsqiNkD5PciZfBY3KWj8Gwxt9RE8HJTosh5IrS
+KX5lQZARtObY9ec7G3iyV0ADIdHva2AtTsjOjRQclJBetK0wZjmkkgZTS25/JgdC
+Ppff/RM8iNchOZ3vvH6WzNy9fzquH+iScSv7SSmBfVEWZkQKH6y3ogj16hZZEK3Y
+o/LUlyAjYMy2MgJPDQcWnBkY8xb3lLFDrvVOyHUipMApePlomYC/+/ZJwwfoGBm/
++IQJY41IvZS+FStZ/2SfoL1inQ/6GBPDq/S1a9PC6lRl3/oUWJKSqdiiStJr5+4F
+EHQbY4LUPIPVv6QKRmE9BivkRVF9vK8MtOGnaQIDAQABAoIBABRVAQ4PLVh2Y6Zm
+pv8czbvw7dgQBkbQKgI5IpCJksStOeVWWSlybvZQjDpxFY7wtv91HTnQdYC7LS8G
+MhBELQYD/1DbvXs1/iybsZpHoa+FpMJJAeAsqLWLeRmyDt8yqs+/Ua20vEthubfp
+aMqk1XD3DvGNgGMiiJPkfUOe/KeTJZvPLNEIo9hojN8HjnrHmZafIznSwfUiuWlo
+RimpM7quwmgWJeq4T05W9ER+nYj7mhmc9xAj4OJXsURBszyE07xnyoAx0mEmGBA6
+egpAhEJi912IkM1hblH5A1SI/W4Jnej/bWWk/xGCVIB8n1jS+7qLoVHcjGi+NJyX
+eiBOBMECgYEA+PWta6gokxvqRZuKP23AQdI0gkCcJXHpY/MfdIYColY3GziD7UWe
+z5cFJkWe3RbgVSL1pF2UdRsuwtrycsf4gWpSwA0YCAFxY02omdeXMiL1G5N2MFSG
+lqn32MJKWUl8HvzUVc+5fuhtK200lyszL9owPwSZm062tcwLsz53Yd0CgYEAznou
+O0mpC5YzChLcaCvfvfuujdbcA7YUeu+9V1dD8PbaTYYjUGG3Gv2crS00Al5WrIaw
+93Q+s14ay8ojeJVCRGW3Bu0iF15XGMjHC2cD6o9rUQ+UW+SOWja7PDyRcytYnfwF
+1y2AkDGURSvaITSGR+xylD8RqEbmL66+jrU2sP0CgYB2/hXxiuI5zfHfa0RcpLxr
+uWjXiMIZM6T13NKAAz1nEgYswIpt8gTB+9C+RjB0Q+bdSmRWN1Qp1OA4yiVvrxyb
+3pHGsXt2+BmV+RxIy768e/DjSUwINZ5OjNalh9e5bWIh/X4PtcVXXwgu5XdpeYBx
+sru0oyI4FRtHMUu2VHkDEQKBgQCZiEiwVUmaEAnLx9KUs2sf/fICDm5zZAU+lN4a
+AA3JNAWH9+JydvaM32CNdTtjN3sDtvQITSwCfEs4lgpiM7qe2XOLdvEOp1vkVgeL
+9wH2fMaz8/3BhuZDNsdrNy6AkQ7ICwrcwj0C+5rhBIaigkgHW06n5W3fzziC5FFW
+FHGikQKBgGQ790ZCn32DZnoGUwITR++/wF5jUfghqd67YODszeUAWtnp7DHlWPfp
+LCkyjnRWnXzvfHTKvCs1XtQBoaCRS048uwZITlgZYFEWntFMqi76bqBE4FTSYUTM
+FinFUBBVigThM/RLfCRNrCW/kTxXuJDuSfVIJZzWNAT+9oWdz5da
+-----END RSA PRIVATE KEY-----
+-----BEGIN PUBLIC KEY-----
+MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAyMyDlxQJjaVsqiNkD5Pc
+iZfBY3KWj8Gwxt9RE8HJTosh5IrSKX5lQZARtObY9ec7G3iyV0ADIdHva2AtTsjO
+jRQclJBetK0wZjmkkgZTS25/JgdCPpff/RM8iNchOZ3vvH6WzNy9fzquH+iScSv7
+SSmBfVEWZkQKH6y3ogj16hZZEK3Yo/LUlyAjYMy2MgJPDQcWnBkY8xb3lLFDrvVO
+yHUipMApePlomYC/+/ZJwwfoGBm/+IQJY41IvZS+FStZ/2SfoL1inQ/6GBPDq/S1
+a9PC6lRl3/oUWJKSqdiiStJr5+4FEHQbY4LUPIPVv6QKRmE9BivkRVF9vK8MtOGn
+aQIDAQAB
+-----END PUBLIC KEY-----
diff -pruN 1.4.0-1/test/provider/rsa16k.pl 2.5.0-0ubuntu1/test/provider/rsa16k.pl
--- 1.4.0-1/test/provider/rsa16k.pl	1970-01-01 00:00:00.000000000 +0000
+++ 2.5.0-0ubuntu1/test/provider/rsa16k.pl	2025-04-16 08:09:24.000000000 +0000
@@ -0,0 +1,28 @@
+#!/usr/bin/env perl
+
+#
+# Copyright [2021-2022] International Business Machines Corp.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+use strict;
+use warnings;
+use test;
+
+test::rsaencdec("16384", 2, 2037);
+test::rsaoaepencdec("16384", 2, 200, "SHA-256");
+test::rsasignverify("16384", 2, 64);
+test::rsapsssignverify("16384", 2, 100, "SHA-256", 25);
+test::rsax931signverify("16384", 2, 100, "SHA-256");
+
diff -pruN 1.4.0-1/test/provider/rsa1k.pl 2.5.0-0ubuntu1/test/provider/rsa1k.pl
--- 1.4.0-1/test/provider/rsa1k.pl	1970-01-01 00:00:00.000000000 +0000
+++ 2.5.0-0ubuntu1/test/provider/rsa1k.pl	2025-04-16 08:09:24.000000000 +0000
@@ -0,0 +1,27 @@
+#!/usr/bin/env perl
+
+#
+# Copyright [2021-2022] International Business Machines Corp.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+use strict;
+use warnings;
+use test;
+
+test::rsaencdec("1024", 10, 64);
+test::rsaoaepencdec("1024", 10, 60, "SHA-256");
+test::rsasignverify("1024", 10, 64);
+test::rsapsssignverify("1024", 10, 100, "SHA-256", 25);
+test::rsax931signverify("1024", 10, 100, "SHA-256");
diff -pruN 1.4.0-1/test/provider/rsa2k.pl 2.5.0-0ubuntu1/test/provider/rsa2k.pl
--- 1.4.0-1/test/provider/rsa2k.pl	1970-01-01 00:00:00.000000000 +0000
+++ 2.5.0-0ubuntu1/test/provider/rsa2k.pl	2025-04-16 08:09:24.000000000 +0000
@@ -0,0 +1,27 @@
+#!/usr/bin/env perl
+
+#
+# Copyright [2021-2022] International Business Machines Corp.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+use strict;
+use warnings;
+use test;
+
+test::rsaencdec("2048", 10, 245);
+test::rsaoaepencdec("2048", 10, 180, "SHA-256");
+test::rsasignverify("2048", 10, 64);
+test::rsapsssignverify("2048", 10, 100, "SHA-256", 25);
+test::rsax931signverify("2048", 10, 100, "SHA-256");
diff -pruN 1.4.0-1/test/provider/rsa4k.pl 2.5.0-0ubuntu1/test/provider/rsa4k.pl
--- 1.4.0-1/test/provider/rsa4k.pl	1970-01-01 00:00:00.000000000 +0000
+++ 2.5.0-0ubuntu1/test/provider/rsa4k.pl	2025-04-16 08:09:24.000000000 +0000
@@ -0,0 +1,28 @@
+#!/usr/bin/env perl
+
+#
+# Copyright [2021-2022] International Business Machines Corp.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+use strict;
+use warnings;
+use test;
+
+test::rsaencdec("4096", 10, 501);
+test::rsaoaepencdec("4096", 10, 200, "SHA-256");
+test::rsasignverify("4096", 10, 64);
+test::rsapsssignverify("4096", 10, 100, "SHA-256", 25);
+test::rsax931signverify("4096", 10, 100, "SHA-256");
+
diff -pruN 1.4.0-1/test/provider/rsa512.pl 2.5.0-0ubuntu1/test/provider/rsa512.pl
--- 1.4.0-1/test/provider/rsa512.pl	1970-01-01 00:00:00.000000000 +0000
+++ 2.5.0-0ubuntu1/test/provider/rsa512.pl	2025-04-16 08:09:24.000000000 +0000
@@ -0,0 +1,27 @@
+#!/usr/bin/env perl
+
+#
+# Copyright [2021-2022] International Business Machines Corp.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+use strict;
+use warnings;
+use test;
+
+test::rsaencdec("512", 10, 20);
+test::rsaoaepencdec("512", 10, 10, "SHA-1");
+test::rsasignverify("512", 10, 20);
+test::rsapsssignverify("512", 10, 100, "SHA-256", 25);
+test::rsax931signverify("512", 10, 100, "SHA-256");
diff -pruN 1.4.0-1/test/provider/rsa8k.pl 2.5.0-0ubuntu1/test/provider/rsa8k.pl
--- 1.4.0-1/test/provider/rsa8k.pl	1970-01-01 00:00:00.000000000 +0000
+++ 2.5.0-0ubuntu1/test/provider/rsa8k.pl	2025-04-16 08:09:24.000000000 +0000
@@ -0,0 +1,27 @@
+#!/usr/bin/env perl
+
+#
+# Copyright [2021-2022] International Business Machines Corp.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+use strict;
+use warnings;
+use test;
+
+test::rsaencdec("8192", 2, 1013);
+test::rsaoaepencdec("8192", 2, 200, "SHA-256");
+test::rsasignverify("8192", 2, 64);
+test::rsapsssignverify("8192", 2, 100, "SHA-256", 25);
+test::rsax931signverify("8192", 2, 100, "SHA-256");
diff -pruN 1.4.0-1/test/provider/rsaimplrej.pl 2.5.0-0ubuntu1/test/provider/rsaimplrej.pl
--- 1.4.0-1/test/provider/rsaimplrej.pl	1970-01-01 00:00:00.000000000 +0000
+++ 2.5.0-0ubuntu1/test/provider/rsaimplrej.pl	2025-04-16 08:09:24.000000000 +0000
@@ -0,0 +1,33 @@
+#!/usr/bin/env perl
+
+#
+# Copyright [2023-2023] International Business Machines Corp.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+use strict;
+use warnings;
+use test;
+use FindBin;
+
+# RSA implicit rejection - random positive test case
+test::rsaimplrej("$FindBin::Bin/rsa-implrej-key.pem", "$FindBin::Bin/rsa-implrej-good-in.bin", "$FindBin::Bin/rsa-implrej-good-out.bin");
+# RSA implicit rejection - random negative test case decrypting to empty
+test::rsaimplrej("$FindBin::Bin/rsa-implrej-key.pem", "$FindBin::Bin/rsa-implrej-bad-empty-in.bin", "$FindBin::Bin/rsa-implrej-bad-empty-out.bin");
+# RSA implicit rejection - invalid decrypting to max length message
+test::rsaimplrej("$FindBin::Bin/rsa-implrej-key.pem", "$FindBin::Bin/rsa-implrej-bad-max-in.bin", "$FindBin::Bin/rsa-implrej-bad-max-out.bin");
+# RSA implicit rejection - invalid decrypting to message with length specified by second to last value from PRF
+test::rsaimplrej("$FindBin::Bin/rsa-implrej-key.pem", "$FindBin::Bin/rsa-implrej-bad-prf-in.bin", "$FindBin::Bin/rsa-implrej-bad-prf-out.bin");
+
+
diff -pruN 1.4.0-1/test/provider/rsakey.c 2.5.0-0ubuntu1/test/provider/rsakey.c
--- 1.4.0-1/test/provider/rsakey.c	1970-01-01 00:00:00.000000000 +0000
+++ 2.5.0-0ubuntu1/test/provider/rsakey.c	2025-04-16 08:09:24.000000000 +0000
@@ -0,0 +1,1126 @@
+/*
+ * Copyright [2021-2022] International Business Machines Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <dlfcn.h>
+
+#include <openssl/conf.h>
+#include <openssl/evp.h>
+#include <openssl/rsa.h>
+#include <openssl/obj_mac.h>
+#include <openssl/provider.h>
+#include <openssl/err.h>
+#include <openssl/core_names.h>
+
+#include <ica_api.h>
+
+#define UNUSED(var)                             ((void)(var))
+
+static void setup(void)
+{
+    OPENSSL_load_builtin_modules();
+
+    CONF_modules_load_file(NULL, NULL,
+                           CONF_MFLAGS_DEFAULT_SECTION|
+                           CONF_MFLAGS_IGNORE_MISSING_FILE);
+}
+
+static int check_provider(EVP_PKEY_CTX *ctx, const char *expected_provider)
+{
+    const OSSL_PROVIDER *provider;
+    const char *provname;
+
+    if (expected_provider == NULL)
+        expected_provider = "default";
+
+    provider = EVP_PKEY_CTX_get0_provider(ctx);
+    if (provider == NULL) {
+        fprintf(stderr, "Context is not a provider-context\n");
+        return 0;
+    }
+
+    provname = OSSL_PROVIDER_get0_name(provider);
+    if (strcmp(provname, expected_provider) != 0) {
+        fprintf(stderr, "Context is not using the %s provider, but '%s'\n",
+                expected_provider, provname);
+        return 0;
+    }
+
+    return 1;
+}
+
+static int set_rsa_pss_keygen_params(EVP_PKEY_CTX *ctx, const char *pss_md,
+                                     const char *pss_mgf1_md, int pss_saltlen)
+{
+    if (pss_md != NULL) {
+        if (EVP_PKEY_CTX_set_rsa_pss_keygen_md_name(ctx, pss_md, NULL) <= 0) {
+            fprintf(stderr, "EVP_PKEY_CTX_set_rsa_pss_keygen_md_name failed\n");
+            return 0;
+        }
+    }
+
+    if (pss_mgf1_md != NULL) {
+        if (EVP_PKEY_CTX_set_rsa_pss_keygen_mgf1_md_name(ctx, pss_mgf1_md) <= 0) {
+            fprintf(stderr, "EVP_PKEY_CTX_set_rsa_pss_keygen_mgf1_md_name failed\n");
+            return 0;
+        }
+    }
+
+    if (pss_saltlen != 0) {
+        if (EVP_PKEY_CTX_set_rsa_pss_keygen_saltlen(ctx, pss_saltlen) <= 0) {
+            fprintf(stderr, "EVP_PKEY_CTX_set_rsa_pss_keygen_saltlen failed\n");
+            return 0;
+        }
+    }
+
+    return 1;
+}
+
+static int set_rsa_pss_params(EVP_PKEY_CTX *ctx, int padding,
+                              const char *pss_mgf1_md, int pss_saltlen)
+{
+    if (padding != 0) {
+        if (EVP_PKEY_CTX_set_rsa_padding(ctx, padding) <= 0) {
+            fprintf(stderr, "EVP_PKEY_CTX_set_rsa_padding failed\n");
+            return 0;
+        }
+    }
+
+    if (pss_mgf1_md != NULL) {
+        if (EVP_PKEY_CTX_set_rsa_mgf1_md_name(ctx, pss_mgf1_md, NULL) <= 0) {
+            fprintf(stderr, "EVP_PKEY_CTX_set_rsa_mgf1_md_name failed\n");
+            return 0;
+        }
+    }
+
+    if (pss_saltlen != 0) {
+        if (EVP_PKEY_CTX_set_rsa_pss_saltlen(ctx, pss_saltlen) <= 0) {
+            fprintf(stderr, "EVP_PKEY_CTX_set_rsa_pss_saltlen failed\n");
+            return 0;
+        }
+    }
+
+    return 1;
+}
+
+static int generate_key(const char* provider, const char *algo, int bits,
+                        const char *pss_md, const char *pss_mgf1_md,
+                        int pss_saltlen, const OSSL_PARAM *params,
+                        EVP_PKEY **rsa_pkey)
+{
+    char props[200];
+    EVP_PKEY_CTX *ctx = NULL;
+    int ok = 0;
+
+    sprintf(props, "%sprovider=%s", provider != NULL ? "?" : "",
+            provider != NULL ? provider : "default");
+
+    ctx = EVP_PKEY_CTX_new_from_name(NULL, algo, props);
+    if (ctx == NULL) {
+        fprintf(stderr, "EVP_PKEY_CTX_new_from_name failed\n");
+        goto out;
+    }
+
+    if (EVP_PKEY_keygen_init(ctx) <= 0) {
+        fprintf(stderr, "EVP_PKEY_keygen_init failed\n");
+        goto out;
+    }
+
+    if (!check_provider(ctx, provider))
+        goto out;
+
+    if (EVP_PKEY_CTX_set_rsa_keygen_bits(ctx, bits) <= 0) {
+        fprintf(stderr, "EVP_PKEY_CTX_set_rsa_keygen_bits failed\n");
+        goto out;
+    }
+
+    if (!set_rsa_pss_keygen_params(ctx, pss_md, pss_mgf1_md, pss_saltlen))
+        goto out;
+
+    if (params != NULL) {
+        if (EVP_PKEY_CTX_set_params(ctx, params) != 1) {
+            fprintf(stderr, "EVP_PKEY_CTX_set_params failed\n");
+            goto out;
+        }
+    }
+
+    if (EVP_PKEY_keygen(ctx, rsa_pkey) <= 0) {
+        fprintf(stderr, "EVP_PKEY_keygen failed\n");
+        goto out;
+    }
+
+    ok = 1;
+
+out:
+    if (ctx != NULL)
+        EVP_PKEY_CTX_free(ctx);
+
+    return ok;
+}
+
+static int sign_single(const char* provider, EVP_PKEY *rsa_pkey,
+                       int pss_padding, const char *pss_mgf1_md,
+                       int pss_saltlen, const unsigned char *tbs,
+                       size_t tbs_len, unsigned char *sig, size_t *sig_len)
+{
+    char props[200];
+    EVP_PKEY_CTX *ctx = NULL;
+    int ok = 0;
+
+    sprintf(props, "%sprovider=%s", provider != NULL ? "?" : "",
+            provider != NULL ? provider : "default");
+
+    ctx = EVP_PKEY_CTX_new_from_pkey(NULL, rsa_pkey, props);
+    if (ctx == NULL) {
+        fprintf(stderr, "EVP_PKEY_CTX_new_from_pkey failed\n");
+        goto out;
+    }
+
+    if (EVP_PKEY_sign_init(ctx) <= 0) {
+        fprintf(stderr, "EVP_PKEY_sign_init failed\n");
+        goto out;
+    }
+
+    if (!check_provider(ctx, provider))
+        goto out;
+
+    if (!set_rsa_pss_params(ctx, pss_padding, pss_mgf1_md, pss_saltlen))
+        goto out;
+
+    if (EVP_PKEY_sign(ctx, sig, sig_len, tbs, tbs_len) <= 0) {
+        fprintf(stderr, "EVP_PKEY_sign failed\n");
+        goto out;
+    }
+
+    ok = 1;
+
+out:
+    if (ctx != NULL)
+        EVP_PKEY_CTX_free(ctx);
+
+    return ok;
+}
+
+static int verify_single(const char* provider, const char *algo,
+                         EVP_PKEY *rsa_pkey, int pss_padding,
+                         const char *pss_mgf1_md, int pss_saltlen,
+                         const unsigned char *tbs, size_t tbs_len,
+                         const unsigned char *sig, size_t sig_len)
+{
+    char props[200];
+    EVP_PKEY_CTX *ctx = NULL;
+    int ok = 0;
+
+    sprintf(props, "%sprovider=%s", provider != NULL ? "?" : "",
+            provider != NULL ? provider : "default");
+
+    ctx = EVP_PKEY_CTX_new_from_pkey(NULL, rsa_pkey, props);
+    if (ctx == NULL) {
+        fprintf(stderr, "EVP_PKEY_CTX_new_from_pkey failed\n");
+        goto out;
+    }
+
+    if (EVP_PKEY_verify_init(ctx) <= 0) {
+        fprintf(stderr, "EVP_PKEY_verify_init failed\n");
+        goto out;
+    }
+
+    if (!check_provider(ctx, provider))
+        goto out;
+
+    if (!set_rsa_pss_params(ctx, pss_padding, pss_mgf1_md, pss_saltlen))
+        goto out;
+
+    ok = EVP_PKEY_verify(ctx, sig, sig_len, tbs, tbs_len);
+    if (ok == -1) {
+        /* error */
+        fprintf(stderr, "Failed to verify signature with %s (%s provider)\n",
+                algo, provider != NULL ? provider : "default");
+        ok = 0;
+        goto out;
+    } else if (ok == 0) {
+        /* incorrect signature */
+        fprintf(stderr, "Signature incorrect with %s (%s provider)\n",
+                algo, provider != NULL ? provider : "default");
+        goto out;
+    } else {
+        /* signature ok */
+        printf("Signature correct with %s (%s provider)\n", algo,
+                provider != NULL ? provider : "default");
+        ok = 1;
+    }
+
+out:
+    if (ctx != NULL)
+        EVP_PKEY_CTX_free(ctx);
+
+    return ok;
+}
+
+static int sign_digest(const char* provider, EVP_PKEY *rsa_pkey,
+                       const char *md_name, int pss_padding,
+                       const char *pss_mgf1_md, int pss_saltlen,
+                       const unsigned char *tbs, size_t tbs_len,
+                       unsigned char *sig, size_t *sig_len)
+{
+    char props[200];
+    EVP_MD_CTX *md_ctx = NULL;
+    EVP_PKEY_CTX *ctx = NULL;
+    int ok = 0;
+
+    sprintf(props, "%sprovider=%s", provider != NULL ? "?" : "",
+            provider != NULL ? provider : "default");
+
+    md_ctx = EVP_MD_CTX_new();
+    if (md_ctx == NULL) {
+        fprintf(stderr, "EVP_MD_CTX_new failed\n");
+        goto out;
+    }
+
+    if (EVP_DigestSignInit_ex(md_ctx, &ctx, md_name, NULL,
+                              props, rsa_pkey, NULL) == 0) {
+        fprintf(stderr, "EVP_DigestSignInit_ex failed\n");
+        goto out;
+    }
+
+    if (!check_provider(ctx, provider))
+        goto out;
+
+    if (!set_rsa_pss_params(ctx, pss_padding, pss_mgf1_md, pss_saltlen))
+        goto out;
+
+    if (EVP_DigestSignUpdate(md_ctx, tbs, tbs_len) <= 0) {
+        fprintf(stderr, "EVP_DigestSignUpdate (1) failed\n");
+        goto out;
+    }
+
+    if (EVP_DigestSignUpdate(md_ctx, tbs, tbs_len) <= 0) {
+        fprintf(stderr, "EVP_DigestSignUpdate (2) failed\n");
+        goto out;
+    }
+
+    if (EVP_DigestSignFinal(md_ctx, sig, sig_len) <= 0) {
+        fprintf(stderr, "EVP_DigestSignFinal failed\n");
+        goto out;
+    }
+
+    ok = 1;
+
+out:
+    if (md_ctx != NULL)
+        EVP_MD_CTX_free(md_ctx);
+
+    return ok;
+}
+
+static int verify_digest(const char* provider, const char *algo,
+                         EVP_PKEY *rsa_pkey, const char *md_name,
+                         int pss_padding, const char *pss_mgf1_md,
+                         int pss_saltlen, const unsigned char *tbs,
+                         size_t tbs_len, unsigned char *sig, size_t sig_len)
+{
+    char props[200];
+    EVP_MD_CTX *md_ctx = NULL;
+    EVP_PKEY_CTX *ctx = NULL;
+    int ok = 0;
+
+    sprintf(props, "%sprovider=%s", provider != NULL ? "?" : "",
+            provider != NULL ? provider : "default");
+
+    md_ctx = EVP_MD_CTX_new();
+    if (md_ctx == NULL) {
+        fprintf(stderr, "EVP_MD_CTX_new failed\n");
+        goto out;
+    }
+
+    if (EVP_DigestVerifyInit_ex(md_ctx, &ctx, md_name, NULL,
+                                props, rsa_pkey, NULL) == 0) {
+        fprintf(stderr, "EVP_DigestVerifyInit_ex failed\n");
+        goto out;
+    }
+
+    if (!check_provider(ctx, provider))
+        goto out;
+
+    if (!set_rsa_pss_params(ctx, pss_padding, pss_mgf1_md, pss_saltlen))
+        goto out;
+
+    if (EVP_DigestVerifyUpdate(md_ctx, tbs, tbs_len) <= 0) {
+        fprintf(stderr, "EVP_DigestVerifyUpdate (1) failed\n");
+        goto out;
+    }
+
+    if (EVP_DigestVerifyUpdate(md_ctx, tbs, tbs_len) <= 0) {
+        fprintf(stderr, "EVP_DigestVerifyUpdate (2) failed\n");
+        goto out;
+    }
+
+    ok = EVP_DigestVerifyFinal(md_ctx, sig, sig_len);
+    if (ok == -1) {
+        /* error */
+        fprintf(stderr, "Failed to digest-verify signature with %s (%s provider)\n",
+                algo, provider != NULL ? provider : "default");
+        ok = 0;
+        goto out;
+    } else if (ok == 0) {
+        /* incorrect signature */
+        fprintf(stderr, "Digest-Signature incorrect with %s (%s provider)\n",
+                algo, provider != NULL ? provider : "default");
+        goto out;
+    } else {
+        /* signature ok */
+        printf("Digest-Signature correct with %s (%s provider)\n", algo,
+                provider != NULL ? provider : "default");
+        ok = 1;
+    }
+
+out:
+    if (md_ctx != NULL)
+        EVP_MD_CTX_free(md_ctx);
+
+    return ok;
+}
+
+#ifdef EVP_PKEY_OP_SIGNMSG
+static int sign_message(const char* provider, EVP_PKEY *rsa_pkey,
+                        const char *alg_name, const unsigned char *tbs,
+                        size_t tbs_len, unsigned char *sig, size_t *sig_len)
+{
+    char props[200];
+    EVP_PKEY_CTX *ctx = NULL;
+    EVP_SIGNATURE *alg = NULL;
+    int ok = 0;
+
+    sprintf(props, "%sprovider=%s", provider != NULL ? "?" : "",
+            provider != NULL ? provider : "default");
+
+    ctx = EVP_PKEY_CTX_new_from_pkey(NULL, rsa_pkey, props);
+    if (ctx == NULL) {
+        fprintf(stderr, "EVP_PKEY_CTX_new_from_pkey failed\n");
+        goto out;
+    }
+
+    alg = EVP_SIGNATURE_fetch(NULL, alg_name, props);
+    if (alg == NULL) {
+        fprintf(stderr, "EVP_SIGNATURE_fetch for %s failed\n", alg_name);
+        goto out;
+    }
+
+    if (EVP_PKEY_sign_message_init(ctx, alg, NULL) <= 0) {
+        fprintf(stderr, "EVP_PKEY_sign_message_init failed\n");
+        goto out;
+    }
+
+    if (!check_provider(ctx, provider))
+        goto out;
+
+    if (EVP_PKEY_sign_message_update(ctx, tbs, tbs_len) <= 0) {
+        fprintf(stderr, "EVP_PKEY_sign_message_update (1) failed\n");
+        goto out;
+    }
+
+    if (EVP_PKEY_sign_message_update(ctx, tbs, tbs_len) <= 0) {
+        fprintf(stderr, "EVP_PKEY_sign_message_update (2) failed\n");
+        goto out;
+    }
+
+    if (EVP_PKEY_sign_message_final(ctx, sig, sig_len) <= 0) {
+        fprintf(stderr, "EVP_PKEY_sign_message_final failed\n");
+        goto out;
+    }
+
+    ok = 1;
+
+out:
+    if (ctx != NULL)
+        EVP_PKEY_CTX_free(ctx);
+    if (alg != NULL)
+        EVP_SIGNATURE_free(alg);
+
+    return ok;
+}
+
+static int verify_message(const char* provider, const char *algo,
+                          EVP_PKEY *rsa_pkey, const char *alg_name,
+                          const unsigned char *tbs, size_t tbs_len,
+                          unsigned char *sig, size_t sig_len)
+{
+    char props[200];
+    EVP_PKEY_CTX *ctx = NULL;
+    EVP_SIGNATURE *alg = NULL;
+    int ok = 0;
+
+    sprintf(props, "%sprovider=%s", provider != NULL ? "?" : "",
+            provider != NULL ? provider : "default");
+
+    ctx = EVP_PKEY_CTX_new_from_pkey(NULL, rsa_pkey, props);
+    if (ctx == NULL) {
+        fprintf(stderr, "EVP_PKEY_CTX_new_from_pkey failed\n");
+        goto out;
+    }
+
+    alg = EVP_SIGNATURE_fetch(NULL, alg_name, props);
+    if (alg == NULL) {
+        fprintf(stderr, "EVP_SIGNATURE_fetch for %s failed\n", alg_name);
+        goto out;
+    }
+
+    if (EVP_PKEY_verify_message_init(ctx, alg, NULL) <= 0) {
+        fprintf(stderr, "EVP_PKEY_verify_message_init failed\n");
+        goto out;
+    }
+
+    if (!check_provider(ctx, provider))
+        goto out;
+
+    if (EVP_PKEY_CTX_set_signature(ctx, sig, sig_len) != 1) {
+        fprintf(stderr, "EVP_PKEY_CTX_set_signature failed\n");
+        goto out;
+    }
+
+    if (EVP_PKEY_verify_message_update(ctx, tbs, tbs_len) <= 0) {
+        fprintf(stderr, "EVP_PKEY_verify_message_update (1) failed\n");
+        goto out;
+    }
+
+    if (EVP_PKEY_verify_message_update(ctx, tbs, tbs_len) <= 0) {
+        fprintf(stderr, "EVP_PKEY_verify_message_update (2) failed\n");
+        goto out;
+    }
+
+    ok = EVP_PKEY_verify_message_final(ctx);
+    if (ok == -1) {
+        /* error */
+        fprintf(stderr, "Failed to verify-message signature with %s (%s provider)\n",
+                algo, provider != NULL ? provider : "default");
+        ok = 0;
+        goto out;
+    } else if (ok == 0) {
+        /* incorrect signature */
+        fprintf(stderr, "Message-Signature incorrect with %s (%s provider)\n",
+                algo, provider != NULL ? provider : "default");
+        goto out;
+    } else {
+        /* signature ok */
+        printf("Message-Signature correct with %s (%s provider)\n", algo,
+                provider != NULL ? provider : "default");
+        ok = 1;
+    }
+
+out:
+    if (ctx != NULL)
+        EVP_PKEY_CTX_free(ctx);
+    if (alg != NULL)
+        EVP_SIGNATURE_free(alg);
+
+    return ok;
+}
+
+static int sign_message_single(const char* provider, EVP_PKEY *rsa_pkey,
+                               const char *alg_name, const unsigned char *tbs,
+                               size_t tbs_len, unsigned char *sig,
+                               size_t *sig_len)
+{
+    char props[200];
+    EVP_PKEY_CTX *ctx = NULL;
+    EVP_SIGNATURE *alg = NULL;
+    int ok = 0;
+
+    sprintf(props, "%sprovider=%s", provider != NULL ? "?" : "",
+            provider != NULL ? provider : "default");
+
+    ctx = EVP_PKEY_CTX_new_from_pkey(NULL, rsa_pkey, props);
+    if (ctx == NULL) {
+        fprintf(stderr, "EVP_PKEY_CTX_new_from_pkey failed\n");
+        goto out;
+    }
+
+    alg = EVP_SIGNATURE_fetch(NULL, alg_name, props);
+    if (alg == NULL) {
+        fprintf(stderr, "EVP_SIGNATURE_fetch for %s failed\n", alg_name);
+        goto out;
+    }
+
+    if (EVP_PKEY_sign_message_init(ctx, alg, NULL) <= 0) {
+        fprintf(stderr, "EVP_PKEY_sign_message_init failed\n");
+        goto out;
+    }
+
+    if (!check_provider(ctx, provider))
+        goto out;
+
+    if (EVP_PKEY_sign(ctx, sig, sig_len, tbs, tbs_len) <= 0) {
+        fprintf(stderr, "EVP_PKEY_sign failed\n");
+        goto out;
+    }
+
+    ok = 1;
+
+out:
+    if (ctx != NULL)
+        EVP_PKEY_CTX_free(ctx);
+    if (alg != NULL)
+        EVP_SIGNATURE_free(alg);
+
+    return ok;
+}
+
+static int verify_message_single(const char* provider, const char *algo,
+                                 EVP_PKEY *rsa_pkey, const char *alg_name,
+                                 const unsigned char *tbs, size_t tbs_len,
+                                 unsigned char *sig, size_t sig_len)
+{
+    char props[200];
+    EVP_PKEY_CTX *ctx = NULL;
+    EVP_SIGNATURE *alg = NULL;
+    int ok = 0;
+
+    sprintf(props, "%sprovider=%s", provider != NULL ? "?" : "",
+            provider != NULL ? provider : "default");
+
+    ctx = EVP_PKEY_CTX_new_from_pkey(NULL, rsa_pkey, props);
+    if (ctx == NULL) {
+        fprintf(stderr, "EVP_PKEY_CTX_new_from_pkey failed\n");
+        goto out;
+    }
+
+    alg = EVP_SIGNATURE_fetch(NULL, alg_name, props);
+    if (alg == NULL) {
+        fprintf(stderr, "EVP_SIGNATURE_fetch for %s failed\n", alg_name);
+        goto out;
+    }
+
+    if (EVP_PKEY_verify_message_init(ctx, alg, NULL) <= 0) {
+        fprintf(stderr, "EVP_PKEY_verify_message_init failed\n");
+        goto out;
+    }
+
+    if (!check_provider(ctx, provider))
+        goto out;
+
+
+    ok = EVP_PKEY_verify(ctx, sig, sig_len, tbs, tbs_len);
+    if (ok == -1) {
+        /* error */
+        fprintf(stderr, "Failed to verify-message-single signature with %s (%s provider)\n",
+                algo, provider != NULL ? provider : "default");
+        ok = 0;
+        goto out;
+    } else if (ok == 0) {
+        /* incorrect signature */
+        fprintf(stderr, "Message-Signature single incorrect with %s (%s provider)\n",
+                algo, provider != NULL ? provider : "default");
+        goto out;
+    } else {
+        /* signature ok */
+        printf("Message-Signature single correct with %s (%s provider)\n", algo,
+                provider != NULL ? provider : "default");
+        ok = 1;
+    }
+
+out:
+    if (ctx != NULL)
+        EVP_PKEY_CTX_free(ctx);
+    if (alg != NULL)
+        EVP_SIGNATURE_free(alg);
+
+    return ok;
+}
+
+static int sign_message_prehashed(const char* provider, EVP_PKEY *rsa_pkey,
+                                  const char *alg_name,
+                                  const unsigned char *tbs, size_t tbs_len,
+                                  unsigned char *sig, size_t *sig_len)
+{
+    char props[200];
+    EVP_PKEY_CTX *ctx = NULL;
+    EVP_SIGNATURE *alg = NULL;
+    int ok = 0;
+
+    sprintf(props, "%sprovider=%s", provider != NULL ? "?" : "",
+            provider != NULL ? provider : "default");
+
+    ctx = EVP_PKEY_CTX_new_from_pkey(NULL, rsa_pkey, props);
+    if (ctx == NULL) {
+        fprintf(stderr, "EVP_PKEY_CTX_new_from_pkey failed\n");
+        goto out;
+    }
+
+    alg = EVP_SIGNATURE_fetch(NULL, alg_name, props);
+    if (alg == NULL) {
+        fprintf(stderr, "EVP_SIGNATURE_fetch for %s failed\n", alg_name);
+        goto out;
+    }
+
+    if (EVP_PKEY_sign_init_ex2(ctx, alg, NULL) <= 0) {
+        fprintf(stderr, "EVP_PKEY_sign_init_ex2 failed\n");
+        goto out;
+    }
+
+    if (!check_provider(ctx, provider))
+        goto out;
+
+    if (EVP_PKEY_sign(ctx, sig, sig_len, tbs, tbs_len) <= 0) {
+        fprintf(stderr, "EVP_PKEY_sign failed\n");
+        goto out;
+    }
+
+    ok = 1;
+
+out:
+    if (ctx != NULL)
+        EVP_PKEY_CTX_free(ctx);
+    if (alg != NULL)
+        EVP_SIGNATURE_free(alg);
+
+    return ok;
+}
+
+static int verify_message_prehashed(const char* provider, const char *algo,
+                                    EVP_PKEY *rsa_pkey, const char *alg_name,
+                                    const unsigned char *tbs, size_t tbs_len,
+                                    unsigned char *sig, size_t sig_len)
+{
+    char props[200];
+    EVP_PKEY_CTX *ctx = NULL;
+    EVP_SIGNATURE *alg = NULL;
+    int ok = 0;
+
+    sprintf(props, "%sprovider=%s", provider != NULL ? "?" : "",
+            provider != NULL ? provider : "default");
+
+    ctx = EVP_PKEY_CTX_new_from_pkey(NULL, rsa_pkey, props);
+    if (ctx == NULL) {
+        fprintf(stderr, "EVP_PKEY_CTX_new_from_pkey failed\n");
+        goto out;
+    }
+
+    alg = EVP_SIGNATURE_fetch(NULL, alg_name, props);
+    if (alg == NULL) {
+        fprintf(stderr, "EVP_SIGNATURE_fetch for %s failed\n", alg_name);
+        goto out;
+    }
+
+    if (EVP_PKEY_verify_init_ex2(ctx, alg, NULL) <= 0) {
+        fprintf(stderr, "EVP_PKEY_verify_init_ex2 failed\n");
+        goto out;
+    }
+
+    if (!check_provider(ctx, provider))
+        goto out;
+
+
+    ok = EVP_PKEY_verify(ctx, sig, sig_len, tbs, tbs_len);
+    if (ok == -1) {
+        /* error */
+        fprintf(stderr, "Failed to verify-message-prehashed signature with %s (%s provider)\n",
+                algo, provider != NULL ? provider : "default");
+        ok = 0;
+        goto out;
+    } else if (ok == 0) {
+        /* incorrect signature */
+        fprintf(stderr, "Message-Signature prehashed incorrect with %s (%s provider)\n",
+                algo, provider != NULL ? provider : "default");
+        goto out;
+    } else {
+        /* signature ok */
+        printf("Message-Signature prehashed correct with %s (%s provider)\n", algo,
+                provider != NULL ? provider : "default");
+        ok = 1;
+    }
+
+out:
+    if (ctx != NULL)
+        EVP_PKEY_CTX_free(ctx);
+    if (alg != NULL)
+        EVP_SIGNATURE_free(alg);
+
+    return ok;
+}
+#endif
+
+#ifdef OSSL_PKEY_PARAM_RSA_DERIVE_FROM_PQ
+static int export_import_key_from_pq(const char* provider, EVP_PKEY *rsa_pkey)
+{
+    char props[200];
+    EVP_PKEY_CTX *ctx = NULL;
+    OSSL_PARAM *export_params = NULL;
+    OSSL_PARAM import_params[7];
+    OSSL_PARAM *export_params2 = NULL;
+    EVP_PKEY *rsa_pkey2 = NULL;
+    int ok = 0, i, k, derive_from_pq = 1;
+
+    sprintf(props, "%sprovider=%s", provider != NULL ? "?" : "",
+            provider != NULL ? provider : "default");
+
+    ctx = EVP_PKEY_CTX_new_from_name(NULL, "RSA", props);
+    if (ctx == NULL) {
+        fprintf(stderr, "EVP_PKEY_CTX_new_from_name failed\n");
+        goto out;
+    }
+
+    if (EVP_PKEY_todata(rsa_pkey, EVP_PKEY_KEYPAIR, &export_params) != 1) {
+        fprintf(stderr, "EVP_PKEY_todata failed\n");
+        goto out;
+    }
+
+    for (i = 0, k = 0; export_params[i].key != NULL && k < 5; i++) {
+        if (strcmp(export_params[i].key, OSSL_PKEY_PARAM_RSA_N) == 0 ||
+            strcmp(export_params[i].key, OSSL_PKEY_PARAM_RSA_E) == 0 ||
+            strcmp(export_params[i].key, OSSL_PKEY_PARAM_RSA_D) == 0 ||
+            strcmp(export_params[i].key, OSSL_PKEY_PARAM_RSA_FACTOR1) == 0 ||
+            strcmp(export_params[i].key, OSSL_PKEY_PARAM_RSA_FACTOR2) == 0) {
+            import_params[k] = export_params[i];
+            k++;
+        }
+    }
+    import_params[k++] =
+            OSSL_PARAM_construct_int(OSSL_PKEY_PARAM_RSA_DERIVE_FROM_PQ,
+                                     &derive_from_pq);
+    import_params[k++] = OSSL_PARAM_construct_end();
+
+    if (EVP_PKEY_fromdata_init(ctx) <= 0) {
+        fprintf(stderr, "EVP_PKEY_fromdata_init failed\n");
+        goto out;
+    }
+
+    if (EVP_PKEY_fromdata(ctx, &rsa_pkey2, EVP_PKEY_KEYPAIR,
+                          import_params) != 1) {
+        fprintf(stderr, "EVP_PKEY_fromdata failed\n");
+        goto out;
+    }
+
+    /*
+     * Export the new key again to compare the key components. EVP_PKEY_eq()
+     * would only compare the public key components, but not the private
+     * components.
+     */
+    if (EVP_PKEY_todata(rsa_pkey2, EVP_PKEY_KEYPAIR, &export_params2) != 1) {
+        fprintf(stderr, "EVP_PKEY_todata failed\n");
+        goto out;
+    }
+
+    ok = 1;
+
+    for (i = 0; export_params[i].key != NULL; i++) {
+        for (k = 0; export_params2[k].key != NULL; k++) {
+            if (strcmp(export_params[i].key, export_params2[k].key) == 0) {
+                if (export_params[i].data_size != export_params2[k].data_size ||
+                    memcmp(export_params[i].data, export_params2[k].data,
+                           export_params[i].data_size) != 0) {
+                    fprintf(stderr, "Key component '%s' is different\n",
+                            export_params[i].key);
+                    ok = 0;
+                }
+                break;
+            }
+        }
+    }
+
+out:
+    if (ctx != NULL)
+        EVP_PKEY_CTX_free(ctx);
+    if (export_params != NULL)
+        OSSL_PARAM_free(export_params);
+    if (export_params2 != NULL)
+        OSSL_PARAM_free(export_params2);
+    if (rsa_pkey2 != NULL)
+        EVP_PKEY_free(rsa_pkey2);
+
+    return ok;
+}
+#endif
+
+static int check_rsakey(int bits, const char *algo, const char *name)
+{
+    int            ok = 0;
+    size_t         siglen;
+    unsigned char  sigbuf[1024];
+    EVP_PKEY       *rsa_pkey = NULL;
+    unsigned char  digest[32];
+    const char *pss_md = NULL;
+    const char *pss_mgf1_md = NULL;
+    int pss_saltlen = 0;
+    int pss_padding = 0;
+
+    memset(digest, 0, sizeof(digest));
+
+    if (strcmp(algo, "RSA-PSS") == 0) {
+        pss_md = "SHA256";
+        pss_mgf1_md = "SHA256";
+        pss_saltlen = 24;
+        pss_padding = RSA_PKCS1_PSS_PADDING;
+    }
+
+    /* Keygen with IBMCA provider */
+    if (!generate_key("ibmca", algo, bits, pss_md, pss_mgf1_md, pss_saltlen,
+                      NULL, &rsa_pkey))
+        goto out;
+
+    /* Sign with IBMCA provider */
+    siglen = sizeof(sigbuf);
+    if (!sign_single("ibmca", rsa_pkey, pss_padding, pss_mgf1_md, pss_saltlen,
+                     digest, sizeof(digest), sigbuf, &siglen))
+        goto out;
+
+    /* Verify with default provider */
+    if (!verify_single(NULL, name, rsa_pkey, pss_padding, pss_mgf1_md, pss_saltlen,
+                     digest, sizeof(digest), sigbuf, siglen))
+        goto out;
+
+
+    /* Verify with IBMCA provider */
+    if (!verify_single("ibmca", name, rsa_pkey, pss_padding, pss_mgf1_md, pss_saltlen,
+                     digest, sizeof(digest), sigbuf, siglen))
+        goto out;
+
+    /* Digest-Sign with IBMCA provider */
+    siglen = sizeof(sigbuf);
+    if (!sign_digest("ibmca", rsa_pkey, "SHA256",
+                     pss_padding, pss_mgf1_md, pss_saltlen,
+                     digest, sizeof(digest), sigbuf, &siglen))
+        goto out;
+
+    /* Digest-Verify with default provider */
+    if (!verify_digest(NULL, name, rsa_pkey, "SHA256",
+                       pss_padding, pss_mgf1_md, pss_saltlen,
+                       digest, sizeof(digest), sigbuf, siglen))
+        goto out;
+
+    /* Digest-Verify with IBMCA provider */
+    if (!verify_digest("ibmca", name, rsa_pkey, "SHA256",
+                       pss_padding, pss_mgf1_md, pss_saltlen,
+                       digest, sizeof(digest), sigbuf, siglen))
+        goto out;
+
+    /* Digest-Sign with default provider */
+    siglen = sizeof(sigbuf);
+    if (!sign_digest(NULL, rsa_pkey, "SHA256",
+                     pss_padding, pss_mgf1_md, pss_saltlen,
+                     digest, sizeof(digest), sigbuf, &siglen))
+        goto out;
+
+    /* Digest-Verify with default provider */
+    if (!verify_digest(NULL, name, rsa_pkey, "SHA256",
+                       pss_padding, pss_mgf1_md, pss_saltlen,
+                       digest, sizeof(digest), sigbuf, siglen))
+        goto out;
+
+    /* Digest-Verify with IBMCA provider */
+    if (!verify_digest("ibmca", name, rsa_pkey, "SHA256",
+                       pss_padding, pss_mgf1_md, pss_saltlen,
+                       digest, sizeof(digest), sigbuf, siglen))
+        goto out;
+
+#ifdef EVP_PKEY_OP_SIGNMSG
+    if (strcmp(algo, "RSA-PSS") == 0)
+        goto skip;
+
+    /* SignMessage with IBMCA provider */
+    siglen = sizeof(sigbuf);
+    if (!sign_message("ibmca", rsa_pkey, "RSA-SHA256",
+                      digest, sizeof(digest), sigbuf, &siglen))
+        goto out;
+
+    /* VerifyMessage with default provider */
+    if (!verify_message(NULL, name, rsa_pkey, "RSA-SHA256",
+                        digest, sizeof(digest), sigbuf, siglen))
+        goto out;
+
+    /* VerifyMessage with IBMCA provider */
+    if (!verify_message("ibmca", name, rsa_pkey, "RSA-SHA256",
+                        digest, sizeof(digest), sigbuf, siglen))
+        goto out;
+
+    /* SignMessage one-shot with IBMCA provider */
+    siglen = sizeof(sigbuf);
+    if (!sign_message_single("ibmca", rsa_pkey, "RSA-SHA256",
+                             digest, sizeof(digest), sigbuf, &siglen))
+        goto out;
+
+    /* VerifyMessage one-shot with default provider */
+    if (!verify_message_single(NULL, name, rsa_pkey, "RSA-SHA256",
+                               digest, sizeof(digest), sigbuf, siglen))
+        goto out;
+
+    /* VerifyMessage one-shot with IBMCA provider */
+    if (!verify_message_single("ibmca", name, rsa_pkey, "RSA-SHA256",
+                               digest, sizeof(digest), sigbuf, siglen))
+        goto out;
+
+    /* Sign pre-hashed message with IBMCA provider */
+    siglen = sizeof(sigbuf);
+    if (!sign_message_prehashed("ibmca", rsa_pkey, "RSA-SHA256",
+                                digest, sizeof(digest), sigbuf, &siglen))
+        goto out;
+
+    /* Verify pre-hashed message with default provider */
+    if (!verify_message_prehashed(NULL, name, rsa_pkey, "RSA-SHA256",
+                                  digest, sizeof(digest), sigbuf, siglen))
+        goto out;
+
+    /* Verify pre-hashed message with IBMCA provider */
+    if (!verify_message_prehashed("ibmca", name, rsa_pkey, "RSA-SHA256",
+                                  digest, sizeof(digest), sigbuf, siglen))
+        goto out;
+
+skip:
+#endif
+
+#ifdef OSSL_PKEY_PARAM_RSA_DERIVE_FROM_PQ
+    if (!export_import_key_from_pq("ibmca", rsa_pkey))
+        goto out;
+
+    if (!export_import_key_from_pq(NULL, rsa_pkey))
+        goto out;
+#endif
+
+    ok = 1;
+
+ out:
+    if (rsa_pkey)
+       EVP_PKEY_free(rsa_pkey);
+
+    ERR_print_errors_fp(stderr);
+
+    return ok;
+}
+
+static const unsigned int required_ica_mechs[] = { RSA_ME,  RSA_CRT };
+static const unsigned int required_ica_mechs_len =
+                        sizeof(required_ica_mechs) / sizeof(unsigned int);
+
+typedef unsigned int (*ica_get_functionlist_t)(libica_func_list_element *,
+                                               unsigned int *);
+
+static int check_libica()
+{
+    unsigned int mech_len, i, k, found = 0;
+    libica_func_list_element *mech_list = NULL;
+    void *ibmca_dso;
+    ica_get_functionlist_t p_ica_get_functionlist;
+    int rc;
+
+    ibmca_dso = dlopen(LIBICA_NAME, RTLD_NOW);
+    if (ibmca_dso == NULL) {
+        fprintf(stderr, "Failed to load libica '%s'!\n", LIBICA_NAME);
+        return 77;
+    }
+
+    p_ica_get_functionlist =
+            (ica_get_functionlist_t)dlsym(ibmca_dso, "ica_get_functionlist");
+    if (p_ica_get_functionlist == NULL) {
+        fprintf(stderr, "Failed to get ica_get_functionlist from '%s'!\n",
+                LIBICA_NAME);
+        return 77;
+    }
+
+    rc = p_ica_get_functionlist(NULL, &mech_len);
+    if (rc != 0) {
+        fprintf(stderr, "Failed to get function list from libica!\n");
+        return 77;
+    }
+
+    mech_list = calloc(sizeof(libica_func_list_element), mech_len);
+    if (mech_list == NULL) {
+        fprintf(stderr, "Failed to allocate memory for function list!\n");
+        return 77;
+    }
+
+    rc = p_ica_get_functionlist(mech_list, &mech_len);
+    if (rc != 0) {
+        fprintf(stderr, "Failed to get function list from libica!\n");
+        free(mech_list);
+        return 77;
+    }
+
+    for (i = 0; i < mech_len; i++) {
+        for (k = 0; k < required_ica_mechs_len; k++) {
+            if (mech_list[i].mech_mode_id == required_ica_mechs[k]) {
+                if (mech_list[i].flags &
+                    (ICA_FLAG_SW | ICA_FLAG_SHW | ICA_FLAG_DHW))
+                    found++;
+            }
+        }
+    }
+
+    free(mech_list);
+
+    if (found < required_ica_mechs_len) {
+        fprintf(stderr,
+               "Libica does not support the required algorithms, skipping.\n");
+        return 77;
+    }
+
+    return 0;
+}
+
+int main(int argc, char **argv)
+{
+    static const struct testparams {
+        int         bits;
+        const char *algo;
+        const char *name;
+    } params[] = {
+                {512, "RSA", "RSA-512"},
+                {1024, "RSA", "RSA-1024"},
+                {2048, "RSA", "RSA-2048"},
+                {4096, "RSA", "RSA-4096"},
+                {512, "RSA-PSS", "RSA-PSS-512"},
+                {1024, "RSA-PSS", "RSA-PSS-1024"},
+                {2048, "RSA-PSS", "RSA-PSS-2048"},
+                {4096, "RSA-PSS", "RSA-PSS-4096"},
+    };
+
+    UNUSED(argc);
+    UNUSED(argv);
+
+    int ret = 0, i;
+    /* First fix the environment */
+    char *testcnf = getenv("IBMCA_OPENSSL_TEST_CONF");
+    char *testpath = getenv("IBMCA_TEST_PATH");
+
+    /* Do not overwrite a user-provided OPENSSL_CONF in the
+       environment.  This allows us to execute this test also on an
+       installation with a user-provided engine configuration. */
+    if (testcnf && setenv("OPENSSL_CONF", testcnf, 0)) {
+        fprintf(stderr, "Failed to set OPENSSL_CONF environment variable!\n");
+        return 77;
+    }
+    
+    if (testpath && setenv("OPENSSL_MODULES", testpath, 0)) {
+        fprintf(stderr, "Failed to set OPENSSL_MODULES environment variable!\n");
+        return 77;
+    }
+
+    OPENSSL_init_crypto(OPENSSL_INIT_LOAD_CONFIG, NULL);
+
+    ret = check_libica();
+    if (ret != 0)
+        return ret;
+
+    setup();
+    for (i = 0; i < (int)(sizeof(params) / sizeof(struct testparams)); ++i) {
+        if (!check_rsakey(params[i].bits, params[i].algo, params[i].name)) {
+            fprintf(stderr, "Failure for %s\n", params[i].name);
+            ret = 99;
+        }
+    }
+    return ret;
+}
diff -pruN 1.4.0-1/test/provider/server-cert-ec.pem 2.5.0-0ubuntu1/test/provider/server-cert-ec.pem
--- 1.4.0-1/test/provider/server-cert-ec.pem	1970-01-01 00:00:00.000000000 +0000
+++ 2.5.0-0ubuntu1/test/provider/server-cert-ec.pem	2025-04-16 08:09:24.000000000 +0000
@@ -0,0 +1,17 @@
+-----BEGIN CERTIFICATE-----
+MIICtjCCAlugAwIBAgIUZKDDTgiMEBhW4cW1fDdYk8Zp4RMwCgYIKoZIzj0EAwIw
+fzELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAk5ZMREwDwYDVQQHDAhOZXcgWW9yazEV
+MBMGA1UECgwMRXhhbXBsZSwgTExDMRgwFgYDVQQDDA9FeGFtcGxlIENvbXBhbnkx
+HzAdBgkqhkiG9w0BCQEWEHRlc3RAZXhhbXBsZS5jb20wHhcNMjIwMTEyMDkwNTE4
+WhcNMjQxMDA4MDkwNTE4WjB/MQswCQYDVQQGEwJVUzELMAkGA1UECAwCTlkxETAP
+BgNVBAcMCE5ldyBZb3JrMRUwEwYDVQQKDAxFeGFtcGxlLCBMTEMxGDAWBgNVBAMM
+D0V4YW1wbGUgQ29tcGFueTEfMB0GCSqGSIb3DQEJARYQdGVzdEBleGFtcGxlLmNv
+bTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABGZd6YEJHN/KruZK9+R+5KOhVMvD
+3MTbi7dDuPBTnBGpyS151/LvH6QMAtq08uMvePGIXYG4G0kwVoL7nXEGomKjgbQw
+gbEwHQYDVR0OBBYEFDTzrZFKewJscQkcECACxyU4rcHHMAkGA1UdEwQCMAAwCwYD
+VR0PBAQDAgWgMEoGA1UdEQRDMEGCC2V4YW1wbGUuY29tgg93d3cuZXhhbXBsZS5j
+b22CEG1haWwuZXhhbXBsZS5jb22CD2Z0cC5leGFtcGxlLmNvbTAsBglghkgBhvhC
+AQ0EHxYdT3BlblNTTCBHZW5lcmF0ZWQgQ2VydGlmaWNhdGUwCgYIKoZIzj0EAwID
+SQAwRgIhAMYwfXPr7QWZp2uZ6PeYy7kUSr9OmC7bRGLDWVi5Y8XWAiEA91Hh8UOa
+KCOo8mbFyeRrrgFMt+82P3baa9pzKhl+KF0=
+-----END CERTIFICATE-----
diff -pruN 1.4.0-1/test/provider/server-cert-rsa.pem 2.5.0-0ubuntu1/test/provider/server-cert-rsa.pem
--- 1.4.0-1/test/provider/server-cert-rsa.pem	1970-01-01 00:00:00.000000000 +0000
+++ 2.5.0-0ubuntu1/test/provider/server-cert-rsa.pem	2025-04-16 08:09:24.000000000 +0000
@@ -0,0 +1,25 @@
+-----BEGIN CERTIFICATE-----
+MIIEQTCCAymgAwIBAgIUEb+BhAFigO+vYunYfkVYVoCpVkcwDQYJKoZIhvcNAQEL
+BQAwfzELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAk5ZMREwDwYDVQQHDAhOZXcgWW9y
+azEVMBMGA1UECgwMRXhhbXBsZSwgTExDMRgwFgYDVQQDDA9FeGFtcGxlIENvbXBh
+bnkxHzAdBgkqhkiG9w0BCQEWEHRlc3RAZXhhbXBsZS5jb20wHhcNMjIwMTEyMDg0
+MjM2WhcNMjQxMDA4MDg0MjM2WjB/MQswCQYDVQQGEwJVUzELMAkGA1UECAwCTlkx
+ETAPBgNVBAcMCE5ldyBZb3JrMRUwEwYDVQQKDAxFeGFtcGxlLCBMTEMxGDAWBgNV
+BAMMD0V4YW1wbGUgQ29tcGFueTEfMB0GCSqGSIb3DQEJARYQdGVzdEBleGFtcGxl
+LmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMeMOu0OlFMmDD35
+HYS4SHY3U7Z2NlYhhx7UIuBVLJ5sXleynXtCXBSKic4/jYV8wvGPxAlxTpA1JgrO
+SCvSPkw/6hcC3UjvdXaFBYm0dYNSkaSzU5mK7jB1oj1RmOBoojyXm09Wou6kW4yb
+Rg/z7vP2tbICa8EV4EiRmI8YM0BWaHOyAQiUMQRSKmrmQX8oTyKPeGS/Au+8md2S
+nxF1XHFDiK1+5vpE22DA7F0ErkSbPuLssuthni+W0xWPNMFBlV6pt9gdEsSYlkGr
+YpMIB6oevWGxMQa3+79f2rxjKz4B3b/xlU/V+ANeB8C3MEx7XTWE71TM68pPILRI
+9fXN6mMCAwEAAaOBtDCBsTAdBgNVHQ4EFgQUjyFE+3GAhb9y5p/qY38tVO4uMiow
+CQYDVR0TBAIwADALBgNVHQ8EBAMCBaAwSgYDVR0RBEMwQYILZXhhbXBsZS5jb22C
+D3d3dy5leGFtcGxlLmNvbYIQbWFpbC5leGFtcGxlLmNvbYIPZnRwLmV4YW1wbGUu
+Y29tMCwGCWCGSAGG+EIBDQQfFh1PcGVuU1NMIEdlbmVyYXRlZCBDZXJ0aWZpY2F0
+ZTANBgkqhkiG9w0BAQsFAAOCAQEAGua+EFwg8RUK80AA2uJzT/KAsqn0uLTX2kCs
+ca/tkOT7ou2jsJDoAZnpCQYGUgCgroEcmVexesUjFcOUjv4xZJuiXqIKhP7d5vqQ
+NIZOOwx56PqqEcwADRkeHEsEGbOSQS02Ts0+D4pMVwfYrvMmsLfwC+X0szQTaLu8
+SMFgSaUg89sC+qpCsEsS+R06NEfzEnz3fgT+F5uKpBMGwMPel6LpVLsMNyidEnAW
+L4hPXZKUSY4jbKUxfS9b9vpsZbGvBuU+evMe8XABAjTcxLbSDWxzQ4VdQzIhRx6d
+V/kWkUl6pZg1hL9393UbNjSEILbdLtsdc/t3S/yFh5d03F40BA==
+-----END CERTIFICATE-----
diff -pruN 1.4.0-1/test/provider/server-key-ec.pem 2.5.0-0ubuntu1/test/provider/server-key-ec.pem
--- 1.4.0-1/test/provider/server-key-ec.pem	1970-01-01 00:00:00.000000000 +0000
+++ 2.5.0-0ubuntu1/test/provider/server-key-ec.pem	2025-04-16 08:09:24.000000000 +0000
@@ -0,0 +1,5 @@
+-----BEGIN PRIVATE KEY-----
+MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgrsX231aJljHPR1Xf
+135Xz+Ll0f86PE0es2bfiI4oRxOhRANCAARmXemBCRzfyq7mSvfkfuSjoVTLw9zE
+24u3Q7jwU5wRqcktedfy7x+kDALatPLjL3jxiF2BuBtJMFaC+51xBqJi
+-----END PRIVATE KEY-----
diff -pruN 1.4.0-1/test/provider/server-key-rsa.pem 2.5.0-0ubuntu1/test/provider/server-key-rsa.pem
--- 1.4.0-1/test/provider/server-key-rsa.pem	1970-01-01 00:00:00.000000000 +0000
+++ 2.5.0-0ubuntu1/test/provider/server-key-rsa.pem	2025-04-16 08:09:24.000000000 +0000
@@ -0,0 +1,28 @@
+-----BEGIN PRIVATE KEY-----
+MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDHjDrtDpRTJgw9
++R2EuEh2N1O2djZWIYce1CLgVSyebF5Xsp17QlwUionOP42FfMLxj8QJcU6QNSYK
+zkgr0j5MP+oXAt1I73V2hQWJtHWDUpGks1OZiu4wdaI9UZjgaKI8l5tPVqLupFuM
+m0YP8+7z9rWyAmvBFeBIkZiPGDNAVmhzsgEIlDEEUipq5kF/KE8ij3hkvwLvvJnd
+kp8RdVxxQ4itfub6RNtgwOxdBK5Emz7i7LLrYZ4vltMVjzTBQZVeqbfYHRLEmJZB
+q2KTCAeqHr1hsTEGt/u/X9q8Yys+Ad2/8ZVP1fgDXgfAtzBMe101hO9UzOvKTyC0
+SPX1zepjAgMBAAECggEBAJMyxmUIVDHh5zXwBe5ZYlqSBZabLQnsQZhkNDX3nqpe
+lllq0PCTywj8CRuzldnaZpN60cmFY8bM7fsan/JzbLEilLPU0Rd0TNnY1nT2QZlV
+10n/XrPs4DevDrbc8kDX7pVz4IVuC1KuuznFcLFj5+jfHLjrQEF2ubPOcxNbbrMN
+vRXK3AyXaapPCXlwzhIivRDX9p63Bfi16B6ckdHHdRw4Qlb3CajexSw7dH8JUlUS
+w2S4cTMm10VPrFNce0IdXqSTIK770a9z4EyYSbnybF+O12alr/GTL/niG/Iut4g8
+PpGiywn58l0kqtti6N49e6U4DKqdDaYIRvy9HUaZF0ECgYEA5I3G30swteDTFozG
+kyCjYLw3yqIXWxn042zdhsfcXgbuolrZjXCUrSOIwV42UkJ1RzssrdCe6cBemqlx
+LJpeb+LAJh8vVUrz65WoBcMDsXVgcD51/ztMPyadzwrJ8BIiJG8uBVP6pCj4Wfiu
+UjIBk+hQEeHspSfvi+IvCO5JvtUCgYEA34K/MQkoZM3gfhyY4mr4P2vdjIMqR0qD
+H1mCja8tcJtROMP1tY0IWTjEmP+/6AbdsYwREk2C5Kn7lxZ0dbXRI1sHffhShLD7
+eEy8SUV3vhy03CBS1y8VpZEUSKyAOlRlN1tY88QUny3xOQdLZb5t9QGGyQQAuYGu
+GIsUPRNt0FcCgYAg8u+AsW5MSiUkUxctjr7+6yN/6u5DC/Lp9n/ZNmbmh0lPzpke
+cuR8MWs9tT9PjJUrt7QxOqouOLVqxpKyQ27p4l6hIE3KlgukIuceLYcSxkEo7VhF
+e176m66UcpG6MmMJrZ2M1xaDJATps5gt8VeY4xzn9xIOnTtDh1AQkYnAIQKBgHM9
+OFKSBC+lLoAXQoRK3t/kP4B5CE1lj6GURwnCLk2G5yO7dW473vgRmtu/0TRShe9K
+5mpnaHt5YOyPeVBPhBsUjhJW/ETJ834dIl8s4AY3StDMIaos7p5E5Q5rUlnAtccK
+5BwbFv7TczISHr/ApXTTaqkt1SnQQPEKlE7BO7dVAoGBAIK68ZrPFeQBwv7NWPXw
+4iAt6p9NMdLJoBdS3zOjP1vKUGuR97B1diQeMmAsoQxxXJt0TABiDF3YJLE+bm65
+sb1b7k8C8g9TN0ZcgIBTK1IBrI3SY4zDCzFoww5bdzs5eDaKs1KYmsd4Zou6trmU
+Zm/9Qsaoe59t6BaPwAVwzasz
+-----END PRIVATE KEY-----
diff -pruN 1.4.0-1/test/provider/test.pm 2.5.0-0ubuntu1/test/provider/test.pm
--- 1.4.0-1/test/provider/test.pm	1970-01-01 00:00:00.000000000 +0000
+++ 2.5.0-0ubuntu1/test/provider/test.pm	2025-04-16 08:09:24.000000000 +0000
@@ -0,0 +1,374 @@
+#!/usr/bin/env perl
+
+#
+# Copyright [2021-2022] International Business Machines Corp.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+use strict;
+use warnings;
+
+package test;
+
+sub rsaencdec {
+    my $prov = "OPENSSL_CONF=$ENV{IBMCA_OPENSSL_TEST_CONF} OPENSSL_MODULES=$ENV{IBMCA_TEST_PATH}";
+
+    my ($keylen, $tests, $max_file_size) = @_;
+
+    `$prov openssl list -providers | grep "name: ibmca"`;
+    exit(99) if ($?);
+
+    `$prov openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:$keylen -out rsaencdec$keylen.key`;
+    `$prov openssl rsa -in rsaencdec$keylen.key -check -pubout -out rsaencdec$keylen.pub`;
+    exit(99) if ($?);
+
+    for my $i (1..$tests) {
+        my $bytes = 1 + int(rand($max_file_size));
+        # provider enc, no-provider dec
+        `openssl rand $bytes > rsaencdec.${i}.${keylen}.data.in`;
+        `$prov openssl pkeyutl -encrypt -pubin -inkey rsaencdec$keylen.pub -in rsaencdec.${i}.${keylen}.data.in -out rsaencdec.${i}.${keylen}.data.out`;
+        `openssl pkeyutl -decrypt -inkey rsaencdec$keylen.key -in rsaencdec.${i}.${keylen}.data.out -out rsaencdec.${i}.${keylen}.data.dec`;
+        `cmp rsaencdec.${i}.${keylen}.data.in rsaencdec.${i}.${keylen}.data.dec`;
+        exit(99) if ($?);
+        `rm -f rsaencdec.${i}.${keylen}.data.in rsaencdec.${i}.${keylen}.data.out rsaencdec.${i}.${keylen}.data.dec`;
+
+        # no-provider enc, provider dec
+        `openssl rand $bytes > rsaencdec.${i}.${keylen}.data.in`;
+        `openssl pkeyutl -encrypt -pubin -inkey rsaencdec$keylen.pub -in rsaencdec.${i}.${keylen}.data.in -out rsaencdec.${i}.${keylen}.data.out`;
+        `$prov openssl pkeyutl -decrypt -inkey rsaencdec$keylen.key -in rsaencdec.${i}.${keylen}.data.out -out rsaencdec.${i}.${keylen}.data.dec`;
+        `cmp rsaencdec.${i}.${keylen}.data.in rsaencdec.${i}.${keylen}.data.dec`;
+        exit(99) if ($?);
+        `rm -f rsaencdec.${i}.${keylen}.data.in rsaencdec.${i}.${keylen}.data.out rsaencdec.${i}.${keylen}.data.dec`;
+    }
+
+    `rm -f rsaencdec$keylen.key rsaencdec$keylen.pub`;
+}
+
+sub rsaoaepencdec {
+    my $prov = "OPENSSL_CONF=$ENV{IBMCA_OPENSSL_TEST_CONF} OPENSSL_MODULES=$ENV{IBMCA_TEST_PATH}";
+
+    my ($keylen, $tests, $max_file_size, $md) = @_;
+
+    `$prov openssl list -providers | grep "name: ibmca"`;
+    exit(99) if ($?);
+
+    `$prov openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:$keylen -out rsaoaepencdec$keylen.key`;
+    `$prov openssl rsa -in rsaoaepencdec$keylen.key -check -pubout -out rsaoaepencdec$keylen.pub`;
+    exit(99) if ($?);
+
+    for my $i (1..$tests) {
+        my $bytes = 1 + int(rand($max_file_size));
+        # provider enc, no-provider dec
+        `openssl rand $bytes > rsaoaepencdec.${i}.${keylen}.data.in`;
+        `$prov openssl pkeyutl -encrypt -pkeyopt rsa_padding_mode:oaep -pkeyopt rsa_oaep_md:$md -pubin -inkey rsaoaepencdec$keylen.pub -in rsaoaepencdec.${i}.${keylen}.data.in -out rsaoaepencdec.${i}.${keylen}.data.out`;
+        `openssl pkeyutl -decrypt -pkeyopt rsa_padding_mode:oaep -pkeyopt rsa_oaep_md:$md -inkey rsaoaepencdec$keylen.key -in rsaoaepencdec.${i}.${keylen}.data.out -out rsaoaepencdec.${i}.${keylen}.data.dec`;
+        `cmp rsaoaepencdec.${i}.${keylen}.data.in rsaoaepencdec.${i}.${keylen}.data.dec`;
+        exit(99) if ($?);
+        `rm -f rsaoaepencdec.${i}.${keylen}.data.in rsaoaepencdec.${i}.${keylen}.data.out rsaoaepencdec.${i}.${keylen}.data.dec`;
+
+        # no-provider enc, provider dec
+        `openssl rand $bytes > rsaoaepencdec.${i}.${keylen}.data.in`;
+        `openssl pkeyutl -encrypt -pkeyopt rsa_padding_mode:oaep -pkeyopt rsa_oaep_md:$md -pubin -inkey rsaoaepencdec$keylen.pub -in rsaoaepencdec.${i}.${keylen}.data.in -out rsaoaepencdec.${i}.${keylen}.data.out`;
+        `$prov openssl pkeyutl -decrypt -pkeyopt rsa_padding_mode:oaep -pkeyopt rsa_oaep_md:$md -inkey rsaoaepencdec$keylen.key -in rsaoaepencdec.${i}.${keylen}.data.out -out rsaoaepencdec.${i}.${keylen}.data.dec`;
+        `cmp rsaoaepencdec.${i}.${keylen}.data.in rsaoaepencdec.${i}.${keylen}.data.dec`;
+        exit(99) if ($?);
+        `rm -f rsaoaepencdec.${i}.${keylen}.data.in rsaoaepencdec.${i}.${keylen}.data.out rsaoaepencdec.${i}.${keylen}.data.dec`;
+    }
+
+    `rm -f rsa$keylen.key rsa$keylen.pub`;
+}
+
+sub rsasignverify {
+    my $prov = "OPENSSL_CONF=$ENV{IBMCA_OPENSSL_TEST_CONF} OPENSSL_MODULES=$ENV{IBMCA_TEST_PATH}";
+
+    my ($keylen, $tests, $input_size) = @_;
+
+    `$prov openssl list -providers | grep "name: ibmca"`;
+    exit(99) if ($?);
+
+    `$prov openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:$keylen -out rsasignverify$keylen.key`;
+    `$prov openssl rsa -in rsasignverify$keylen.key -check -pubout -out rsasignverify$keylen.pub`;
+    exit(99) if ($?);
+
+    for my $i (1..$tests) {
+        my $bytes = 1 + int(rand($input_size));
+        # provider sign, no-provider verify
+        `openssl rand $bytes > rsasignverify.${i}.${keylen}.data.in`;
+        `$prov openssl pkeyutl -sign -inkey rsasignverify$keylen.key -in rsasignverify.${i}.${keylen}.data.in -out rsasignverify.${i}.${keylen}.data.out`;
+        `openssl pkeyutl -verifyrecover -pubin -inkey rsasignverify$keylen.pub -in rsasignverify.${i}.${keylen}.data.out -out rsasignverify.${i}.${keylen}.data.rec`;
+        `cmp rsasignverify.${i}.${keylen}.data.in rsasignverify.${i}.${keylen}.data.rec`;
+        exit(99) if ($?);
+        `rm -f rsasignverify.${i}.${keylen}.data.in rsasignverify.${i}.${keylen}.data.out rsasignverify.${i}.${keylen}.data.rec`;
+
+        # no-provider sign, provider verify
+        `openssl rand $bytes > rsasignverify.${i}.${keylen}.data.in`;
+        `openssl pkeyutl -sign -inkey rsasignverify$keylen.key -in rsasignverify.${i}.${keylen}.data.in -out rsasignverify.${i}.${keylen}.data.out`;
+        `$prov openssl pkeyutl -verifyrecover -pubin -inkey rsasignverify$keylen.pub -in rsasignverify.${i}.${keylen}.data.out -out rsasignverify.${i}.${keylen}.data.rec`;
+        `cmp rsasignverify.${i}.${keylen}.data.in rsasignverify.${i}.${keylen}.data.rec`;
+        exit(99) if ($?);
+        `rm -f rsasignverify.${i}.${keylen}.data.in rsasignverify.${i}.${keylen}.data.out rsasignverify.${i}.${keylen}.data.rec`;
+    }
+
+    `rm -f rsasignverify$keylen.key rsasignverify$keylen.pub`;
+}
+
+sub rsapsssignverify {
+    my $prov = "OPENSSL_CONF=$ENV{IBMCA_OPENSSL_TEST_CONF} OPENSSL_MODULES=$ENV{IBMCA_TEST_PATH}";
+
+    my ($keylen, $tests, $input_size, $md, $saltlen) = @_;
+
+    `$prov openssl list -providers | grep "name: ibmca"`;
+    exit(99) if ($?);
+
+    `$prov openssl genpkey -algorithm RSA-PSS -pkeyopt rsa_keygen_bits:$keylen -pkeyopt rsa_pss_keygen_md:$md -pkeyopt rsa_pss_keygen_mgf1_md:$md -pkeyopt rsa_pss_keygen_saltlen:$saltlen -out rsapss$keylen.key`;
+    # bug in OpenSSL 3.0: `$prov openssl rsa -in rsapss$keylen.key -check -pubout -out rsapss$keylen.pub`;
+    exit(99) if ($?);
+
+    for my $i (1..$tests) {
+        my $bytes = 1 + int(rand($input_size));
+        # provider sign, no-provider verify
+        `openssl rand $bytes > rsapsssignverify.${i}.${keylen}.data.in`;
+        `$prov openssl pkeyutl -sign -digest $md -pkeyopt rsa_padding_mode:pss -pkeyopt rsa_pss_saltlen:$saltlen -pkeyopt rsa_mgf1_md:$md -inkey rsapss$keylen.key -rawin -in rsapsssignverify.${i}.${keylen}.data.in -out rsapsssignverify.${i}.${keylen}.data.out`;
+        # use pub key: `openssl pkeyutl -verify -digest $md -pkeyopt rsa_padding_mode:pss -pkeyopt rsa_pss_saltlen:$saltlen -pkeyopt rsa_mgf1_md:$md -pubin -inkey rsapss$keylen.pub -rawin -in rsapsssignverify.${i}.${keylen}.data.in -sigfile rsapsssignverify.${i}.${keylen}.data.out`;
+        `openssl pkeyutl -verify -digest $md -pkeyopt rsa_padding_mode:pss -pkeyopt rsa_pss_saltlen:$saltlen -pkeyopt rsa_mgf1_md:$md -inkey rsapss$keylen.key -rawin -in rsapsssignverify.${i}.${keylen}.data.in -sigfile rsapsssignverify.${i}.${keylen}.data.out`;
+        exit(99) if ($?);
+        `rm -f rsapsssignverify.${i}.${keylen}.data.in rsapsssignverify.${i}.${keylen}.data.out`;
+
+        # no-provider sign, provider verify
+        `openssl rand $bytes > rsapsssignverify.${i}.${keylen}.data.in`;
+        `openssl pkeyutl -sign -digest $md -pkeyopt rsa_padding_mode:pss -pkeyopt rsa_pss_saltlen:$saltlen -pkeyopt rsa_mgf1_md:$md -inkey rsapss$keylen.key -rawin -in rsapsssignverify.${i}.${keylen}.data.in -out rsapsssignverify.${i}.${keylen}.data.out`;
+        # use pub key: `$prov openssl pkeyutl -verify -digest $md -pkeyopt rsa_padding_mode:pss -pkeyopt rsa_pss_saltlen:$saltlen -pkeyopt rsa_mgf1_md:$md -pubin -inkey rsapss$keylen.pub -rawin -in rsapsssignverify.${i}.${keylen}.data.in -sigfile rsapsssignverify.${i}.${keylen}.data.out`;
+        `$prov openssl pkeyutl -verify -digest $md -pkeyopt rsa_padding_mode:pss -pkeyopt rsa_pss_saltlen:$saltlen -pkeyopt rsa_mgf1_md:$md -inkey rsapss$keylen.key -rawin -in rsapsssignverify.${i}.${keylen}.data.in -sigfile rsapsssignverify.${i}.${keylen}.data.out`;
+        exit(99) if ($?);
+        `rm -f rsapsssignverify.${i}.${keylen}.data.in rsapsssignverify.${i}.${keylen}.data.out`;
+    }
+
+    `rm -f rsapss$keylen.key rsapss$keylen.pub`;
+}
+
+sub rsax931signverify {
+    my $prov = "OPENSSL_CONF=$ENV{IBMCA_OPENSSL_TEST_CONF} OPENSSL_MODULES=$ENV{IBMCA_TEST_PATH}";
+
+    my ($keylen, $tests, $input_size, $md) = @_;
+
+    `$prov openssl list -providers | grep "name: ibmca"`;
+    exit(99) if ($?);
+
+    `$prov openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:$keylen -out rsax931$keylen.key`;
+    `$prov openssl rsa -in rsax931$keylen.key -check -pubout -out rsax931$keylen.pub`;
+    exit(99) if ($?);
+
+    for my $i (1..$tests) {
+        my $bytes = 1 + int(rand($input_size));
+        # provider sign, no-provider verify
+        `openssl rand $bytes > rsax931signverify.${i}.${keylen}.data.in`;
+        `$prov openssl pkeyutl -sign -digest $md -pkeyopt rsa_padding_mode:x931 -inkey rsax931$keylen.key -rawin -in rsax931signverify.${i}.${keylen}.data.in -out rsax931signverify.${i}.${keylen}.data.out`;
+        `openssl pkeyutl -verify -digest $md -pkeyopt rsa_padding_mode:x931 -pubin -inkey rsax931$keylen.pub -rawin -in rsax931signverify.${i}.${keylen}.data.in -sigfile rsax931signverify.${i}.${keylen}.data.out`;
+        exit(99) if ($?);
+        `rm -f rsax931signverify.${i}.${keylen}.data.in rsax931signverify.${i}.${keylen}.data.out`;
+
+        # no-provider sign, provider verify
+        `openssl rand $bytes > rsax931signverify.${i}.${keylen}.data.in`;
+        `openssl pkeyutl -sign -digest $md -pkeyopt rsa_padding_mode:x931 -inkey rsax931$keylen.key -rawin -in rsax931signverify.${i}.${keylen}.data.in -out rsax931signverify.${i}.${keylen}.data.out`;
+        `$prov openssl pkeyutl -verify -digest $md -pkeyopt rsa_padding_mode:x931 -pubin -inkey rsax931$keylen.pub -rawin -in rsax931signverify.${i}.${keylen}.data.in -sigfile rsax931signverify.${i}.${keylen}.data.out`;
+        exit(99) if ($?);
+        `rm -f rsax931signverify.${i}.${keylen}.data.in rsax931signverify.${i}.${keylen}.data.out`;
+    }
+
+    `rm -f rsax931$keylen.key rsax931$keylen.pub`;
+}
+
+sub ecsignverify {
+    my $prov = "OPENSSL_CONF=$ENV{IBMCA_OPENSSL_TEST_CONF} OPENSSL_MODULES=$ENV{IBMCA_TEST_PATH}";
+
+    my ($curve, $tests, $input_size, $md) = @_;
+
+    `$prov openssl list -providers | grep "name: ibmca"`;
+    exit(99) if ($?);
+
+    # skip if OpenSSL does not support the curve
+    `openssl ecparam -list_curves | grep $curve`;
+    return if ($?);
+
+    `$prov openssl genpkey -algorithm EC -pkeyopt ec_paramgen_curve:$curve -out ecsignverify$curve.key`;
+    `$prov openssl ec -in ecsignverify$curve.key -check -pubout -out ecsignverify$curve.pub`;
+    exit(99) if ($?);
+
+    for my $i (1..$tests) {
+        my $bytes = 1 + int(rand($input_size));
+        # provider sign, no-provider verify
+        `openssl rand $bytes > ecsignverify.${i}.${curve}.data.in`;
+        `$prov openssl pkeyutl -sign -digest $md -inkey ecsignverify$curve.key -rawin -in ecsignverify.${i}.${curve}.data.in -out ecsignverify.${i}.${curve}.data.out`;
+        `openssl pkeyutl -verify -digest $md -pubin -inkey ecsignverify$curve.pub -rawin -in ecsignverify.${i}.${curve}.data.in -sigfile ecsignverify.${i}.${curve}.data.out`;
+        exit(99) if ($?);
+        `rm -f ecsignverify.${i}.${curve}.data.in ecsignverify.${i}.${curve}.data.out`;
+
+        # no-provider sign, provider verify
+        `openssl rand $bytes > ecsignverify.${i}.${curve}.data.in`;
+        `openssl pkeyutl -sign -digest $md -inkey ecsignverify$curve.key -rawin -in ecsignverify.${i}.${curve}.data.in -out ecsignverify.${i}.${curve}.data.out`;
+        `$prov openssl pkeyutl -verify -digest $md -pubin -inkey ecsignverify$curve.pub -rawin -in ecsignverify.${i}.${curve}.data.in -sigfile ecsignverify.${i}.${curve}.data.out`;
+        exit(99) if ($?);
+        `rm -f ecsignverify.${i}.${curve}.data.in ecsignverify.${i}.${curve}.data.out`;
+    }
+
+    `rm -f ec$curve.key ec$curve.pub`;
+}
+
+sub ecderive {
+    my $prov = "OPENSSL_CONF=$ENV{IBMCA_OPENSSL_TEST_CONF} OPENSSL_MODULES=$ENV{IBMCA_TEST_PATH}";
+
+    my ($curve, $tests) = @_;
+
+    `$prov openssl list -providers | grep "name: ibmca"`;
+    exit(99) if ($?);
+
+    # skip if OpenSSL does not support the curve
+    `openssl ecparam -list_curves | grep $curve`;
+    return if ($?);
+
+    `$prov openssl genpkey -algorithm EC -pkeyopt ec_paramgen_curve:$curve -out ec$curve.key`;
+    `$prov openssl genpkey -algorithm EC -pkeyopt ec_paramgen_curve:$curve -out peer$curve.key`;
+    `$prov openssl ec -in peer$curve.key -check -pubout -out peer$curve.pub`;
+    exit(99) if ($?);
+
+    for my $i (1..$tests) {
+        `$prov openssl pkeyutl -derive -inkey ec$curve.key -peerkey peer$curve.pub -out ecderive.${i}.${curve}.data.out1`;
+        `openssl pkeyutl -derive -inkey ec$curve.key -peerkey peer$curve.pub -out ecderive.${i}.${curve}.data.out2`;
+        `cmp ecderive.${i}.${curve}.data.out1 ecderive.${i}.${curve}.data.out2`;
+        exit(99) if ($?);
+        `rm -f ecderive.${i}.${curve}.data.out1 ecderive.${i}.${curve}.data.out2`;
+    }
+
+    `rm -f ec$curve.key peer$curve.key peer$curve.pub`;
+}
+
+sub ecderivekdf {
+    my $prov = "OPENSSL_CONF=$ENV{IBMCA_OPENSSL_TEST_CONF} OPENSSL_MODULES=$ENV{IBMCA_TEST_PATH}";
+
+    my ($curve, $tests, $outlen, $kdf, $md) = @_;
+
+    `$prov openssl list -providers | grep "name: ibmca"`;
+    exit(99) if ($?);
+
+    # skip if OpenSSL does not support the curve
+    `openssl ecparam -list_curves | grep $curve`;
+    return if ($?);
+
+    `$prov openssl genpkey -algorithm EC -pkeyopt ec_paramgen_curve:$curve -out ec$curve.key`;
+    `$prov openssl genpkey -algorithm EC -pkeyopt ec_paramgen_curve:$curve -out peer$curve.key`;
+    `$prov openssl ec -in peer$curve.key -check -pubout -out peer$curve.pub`;
+    exit(99) if ($?);
+
+    for my $i (1..$tests) {
+        `$prov openssl pkeyutl -derive -inkey ec$curve.key -peerkey peer$curve.pub -pkeyopt kdf-type:$kdf -pkeyopt kdf-outlen:$outlen -pkeyopt kdf-digest:$md -out ecderive.${i}.${curve}.data.out1`;
+        `openssl pkeyutl -derive -inkey ec$curve.key -peerkey peer$curve.pub -pkeyopt kdf-type:$kdf -pkeyopt kdf-outlen:$outlen -pkeyopt kdf-digest:$md -out ecderive.${i}.${curve}.data.out2`;
+        `cmp ecderive.${i}.${curve}.data.out1 ecderive.${i}.${curve}.data.out2`;
+        exit(99) if ($?);
+        `rm -f ecderive.${i}.${curve}.data.out1 ecderive.${i}.${curve}.data.out2`;
+    }
+
+    `rm -f ec$curve.key peer$curve.key peer$curve.pub`;
+}
+
+sub dhderive {
+    my $prov = "OPENSSL_CONF=$ENV{IBMCA_OPENSSL_TEST_CONF} OPENSSL_MODULES=$ENV{IBMCA_TEST_PATH}";
+
+    my ($group, $tests) = @_;
+    `$prov openssl list -providers | grep "name: ibmca"`;
+    exit(99) if ($?);
+
+    `$prov openssl genpkey -algorithm DH -pkeyopt group:$group -out dhderive$group.key`;
+    `$prov openssl genpkey -algorithm DH -pkeyopt group:$group -out peerderive$group.key`;
+    `$prov openssl pkey -in peerderive$group.key -check -pubout -out peerderive$group.pub`;
+    exit(99) if ($?);
+
+    for my $i (1..$tests) {
+        `$prov openssl pkeyutl -derive -inkey dhderive$group.key -peerkey peerderive$group.pub -out dhderive.${i}.${group}.data.out1`;
+        `openssl pkeyutl -derive -inkey dhderive$group.key -peerkey peerderive$group.pub -out dhderive.${i}.${group}.data.out2`;
+        `cmp dhderive.${i}.${group}.data.out1 dhderive.${i}.${group}.data.out2`;
+        exit(99) if ($?);
+        `rm -f dhderive.${i}.${group}.data.out1 dhderive.${i}.${group}.data.out2`;
+    }
+
+    `rm -f dhderive$group.key peerderive$group.key peerderive$group.pub`;
+}
+
+sub dhderivekdf {
+    my $prov = "OPENSSL_CONF=$ENV{IBMCA_OPENSSL_TEST_CONF} OPENSSL_MODULES=$ENV{IBMCA_TEST_PATH}";
+
+    my ($group, $tests, $outlen, $kdf, $md, $cekalg) = @_;
+
+    `$prov openssl list -providers | grep "name: ibmca"`;
+    exit(99) if ($?);
+
+    `$prov openssl genpkey -algorithm DH -pkeyopt group:$group -out dhderivekdf$group.key`;
+    `$prov openssl genpkey -algorithm DH -pkeyopt group:$group -out peerderivekdf$group.key`;
+    `$prov openssl pkey -in peerderivekdf$group.key -check -pubout -out peerderivekdf$group.pub`;
+    exit(99) if ($?);
+
+
+    for my $i (1..$tests) {
+        `$prov openssl pkeyutl -derive -inkey dhderivekdf$group.key -peerkey peerderivekdf$group.pub -pkeyopt kdf-type:$kdf -pkeyopt kdf-outlen:$outlen -pkeyopt kdf-digest:$md -pkeyopt cekalg:$cekalg -out dhderivekdf.${i}.${group}.data.out1`;
+        `openssl pkeyutl -derive -inkey dhderivekdf$group.key -peerkey peerderivekdf$group.pub -pkeyopt kdf-type:$kdf -pkeyopt kdf-outlen:$outlen -pkeyopt kdf-digest:$md  -pkeyopt cekalg:$cekalg -out dhderivekdf.${i}.${group}.data.out2`;
+        `cmp dhderivekdf.${i}.${group}.data.out1 dhderivekdf.${i}.${group}.data.out2`;
+        exit(99) if ($?);
+        `rm -f dhderivekdf.${i}.${group}.data.out1 dhderivekdf.${i}.${group}.data.out2`;
+    }
+
+    `rm -f dhderivekdf$group.key peerderivekdf$group.key peerderivekdf$group.pub`;
+}
+
+sub tls {
+    my $prov = "OPENSSL_CONF=$ENV{IBMCA_OPENSSL_TEST_CONF} OPENSSL_MODULES=$ENV{IBMCA_TEST_PATH}";
+
+    my ($port, $privkey, $cert, $cipher, $ciphersuites, $opts) = @_;
+    my ($pid, $ret);
+
+    `$prov openssl list -providers | grep "name: ibmca"`;
+    exit(99) if ($?);
+
+    if ($pid = fork) {
+	sleep 1;
+	`echo "Hello World" | $prov openssl s_client -connect localhost:$port -cipher $cipher -ciphersuites $ciphersuites $opts`;
+	$ret = $?;
+	sleep 1;
+	kill 15, $pid;
+	waitpid $pid, 0;
+    } else {
+	exec "$prov openssl s_server -accept $port -naccept 1 -brief -cert $cert -key $privkey -cipher $cipher -ciphersuites $ciphersuites $opts 1>server-$port.out 2>&1";
+    }
+    exit(99) if ($ret);
+
+   `rm -f server-$port.out`;
+}
+
+sub rsaimplrej {
+    my $prov = "OPENSSL_CONF=$ENV{IBMCA_OPENSSL_TEST_CONF} OPENSSL_MODULES=$ENV{IBMCA_TEST_PATH}";
+
+    my ($key, $in, $out) = @_;
+
+    `$prov openssl list -providers | grep "name: ibmca"`;
+    exit(99) if ($?);
+
+    `$prov openssl pkeyutl -decrypt -inkey $key -in $in -out rsaimplrej.out`;
+    exit(99) if ($?);
+    `cmp $out rsaimplrej.out`;
+    exit(99) if ($?);
+    `rm -f rsaimplrej.out`;
+}
+
+`bash -c unset OPENSSL_CONF`;
+`bash -c unset OPENSSL_MODULES`;
+
+1;
diff -pruN 1.4.0-1/test/provider/threadtest.c 2.5.0-0ubuntu1/test/provider/threadtest.c
--- 1.4.0-1/test/provider/threadtest.c	1970-01-01 00:00:00.000000000 +0000
+++ 2.5.0-0ubuntu1/test/provider/threadtest.c	2025-04-16 08:09:24.000000000 +0000
@@ -0,0 +1,232 @@
+/*
+ * Copyright [2021-2022] International Business Machines Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#define _GNU_SOURCE
+#include <getopt.h>
+#include <pthread.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <openssl/conf.h>
+#include <openssl/evp.h>
+#include <openssl/ec.h>
+#include <openssl/obj_mac.h>
+#include <openssl/provider.h>
+#include <openssl/err.h>
+
+/* This is just a random number of threads to stimulate provider configuration. */
+#define DEFAULT_MAX_THREADS 20
+
+static void setup(void)
+{
+    OPENSSL_load_builtin_modules();
+
+    CONF_modules_load_file(NULL, NULL,
+                           CONF_MFLAGS_DEFAULT_SECTION|
+                           CONF_MFLAGS_IGNORE_MISSING_FILE);
+}
+
+static int do_sign_verify(EVP_PKEY *eckey)
+{
+    int            ret = 0, i;
+    EVP_PKEY_CTX  *pctx = NULL;
+    size_t         siglen;
+    unsigned char  sigbuf[1024];
+    unsigned char  digest[32];
+
+    memset(digest, 0, sizeof(digest));
+
+    for (i = 0; i < 100; i++) {
+
+        /* Sign with IBMCA provider */
+        pctx = EVP_PKEY_CTX_new_from_pkey(NULL, eckey, "?provider=ibmca");
+        if (pctx == NULL) {
+            fprintf(stderr, "EVP_PKEY_CTX_new_from_pkey failed\n");
+            goto out;
+        }
+
+        if (EVP_PKEY_sign_init(pctx) <= 0) {
+            fprintf(stderr, "EVP_PKEY_sign_init failed\n");
+            goto out;
+        }
+
+        siglen = sizeof(sigbuf);
+        if (EVP_PKEY_sign(pctx, sigbuf, &siglen, digest, sizeof(digest)) <= 0) {
+            fprintf(stderr, "EVP_PKEY_sign failed\n");
+            goto out;
+        }
+
+        EVP_PKEY_CTX_free(pctx);
+
+        /* Verify with IBMCA provider */
+        pctx = EVP_PKEY_CTX_new_from_pkey(NULL, eckey, "?provider=ibmca");
+        if (pctx == NULL) {
+            fprintf(stderr, "EVP_PKEY_CTX_new_from_pkey failed\n");
+            goto out;
+        }
+
+        if (EVP_PKEY_verify_init(pctx) <= 0) {
+            fprintf(stderr, "EVP_PKEY_verify_init failed\n");
+            goto out;
+        }
+
+        ret = EVP_PKEY_verify(pctx, sigbuf, siglen, digest, sizeof(digest));
+        if (ret == -1) {
+            /* error */
+            fprintf(stderr, "Failed to verify signature\n");
+            ret = 0;
+        } else if (ret == 0) {
+            /* incorrect signature */
+            fprintf(stderr, "Signature incorrect\n");
+            ret = 0;
+        } else {
+            /* signature ok */
+            ret = 1;
+        }
+
+        EVP_PKEY_CTX_free(pctx);
+        pctx = NULL;
+    }
+
+ out:
+    if (pctx)
+        EVP_PKEY_CTX_free(pctx);
+
+    ERR_print_errors_fp(stderr);
+    return ret;
+}
+
+static void *threadfn(void *arg) {
+    unsigned long res = 0;
+    EVP_PKEY *eckey = arg;
+    if (do_sign_verify(eckey) != 1) {
+        res = 1;
+    }
+    return (void *)res;
+}
+
+int main(int argc, char **argv)
+{
+    pthread_t *threads;
+    unsigned long int i, maxthreads = 0, errors = 0;
+    int c;
+    pthread_t me;
+    EVP_PKEY *eckey = NULL;
+    EVP_PKEY_CTX *pctx = NULL;
+
+    /* First fix the environment */
+    char *testcnf = getenv("IBMCA_OPENSSL_TEST_CONF");
+    char *testpath = getenv("IBMCA_TEST_PATH");
+
+    /* Do not overwrite a user-provided OPENSSL_CONF in the
+       environment.  This allows us to execute this test also on an
+       installation with a user-provided engine configuration. */
+    if (testcnf && setenv("OPENSSL_CONF", testcnf, 0)) {
+        fprintf(stderr, "Failed to set OPENSSL_CONF environment variable!\n");
+        return 77;
+    }
+
+    if (testpath && setenv("OPENSSL_MODULES", testpath, 0)) {
+        fprintf(stderr, "Failed to set OPENSSL_MODULES environment variable!\n");
+        return 77;
+    }
+
+    // arg parse
+    while (1) {
+        int option_index;
+        static struct option long_options[] = {
+            { "threads", required_argument, 0, 't'},
+            { 0,         0,                 0, 0  }
+        };
+
+        c = getopt_long(argc, argv, "t:", long_options, &option_index);
+        if (c == -1) {
+            break;
+        } else if (c == 't') {
+            maxthreads = strtoul(optarg, NULL, 0);
+        } else {
+            fprintf(stderr, "USAGE: %s [-t|--threads <num>]\n", argv[0]);
+            fprintf(stderr, "where\t<num> specifies the number of threads to use (default: 20)\n");
+            return 1;
+        }
+    }
+
+    setup();
+
+    pctx = EVP_PKEY_CTX_new_from_name(NULL, "EC", "?provider=ibmca");
+    if (pctx == NULL) {
+        fprintf(stderr, "Failed to create PKEY_CTX\n");
+        ERR_print_errors_fp(stderr);
+        return 1;
+    }
+    /*
+     * Generate an EC key with a curve that libica does not support, so that
+     * it uses SW-fallbacks, and thus stresses the fallback pkey cache
+     */
+    if (EVP_PKEY_keygen_init(pctx) != 1 ||
+        EVP_PKEY_CTX_set_ec_paramgen_curve_nid(pctx, NID_secp256k1) != 1 ||
+        EVP_PKEY_keygen(pctx, &eckey) != 1) {
+        fprintf(stderr, "keygen initialization failed\n");
+        EVP_PKEY_CTX_free(pctx);
+        ERR_print_errors_fp(stderr);
+        return 1;
+    }
+    if (eckey == NULL) {
+        /* error */
+        fprintf(stderr, "Failed to create ec key for NID_secp256k1\n");
+        EVP_PKEY_CTX_free(pctx);
+        ERR_print_errors_fp(stderr);
+        return 1;
+    }
+    EVP_PKEY_CTX_free(pctx);
+
+    if (maxthreads == 0)
+        maxthreads = DEFAULT_MAX_THREADS;
+    threads = calloc(sizeof(pthread_t), maxthreads);
+    if (threads == NULL) {
+        fprintf(stderr, "Thread array allocation failed!\n");
+        return 1;
+    }
+
+    me = pthread_self();
+    // Start threads
+    for (i = 0; i < maxthreads; ++i) {
+        int s = pthread_create(&threads[i], NULL, &threadfn, eckey);
+        if (s != 0) {
+            fprintf(stderr, "Failed to create thread %lu: %s\n", i, strerror(s));
+            threads[i] = me;
+        }
+    }
+    // Now join threads
+    for (i = 0; i < maxthreads; ++i) {
+        if (!pthread_equal(threads[i], me)) {
+            void *retval;
+            int s = pthread_join(threads[i], &retval);
+            if (s != 0) {
+                fprintf(stderr, "Failed to join thread %lu: %s\n", i, strerror(s));
+            } else if ((unsigned long)retval != 0) {
+                fprintf(stderr, "Error in thread %lu\n", i);
+                ++errors;
+            }
+        }
+    }
+    free(threads);
+    EVP_PKEY_free(eckey);
+    return errors ? 99 : 0;
+}
diff -pruN 1.4.0-1/test/provider/tls.pl 2.5.0-0ubuntu1/test/provider/tls.pl
--- 1.4.0-1/test/provider/tls.pl	1970-01-01 00:00:00.000000000 +0000
+++ 2.5.0-0ubuntu1/test/provider/tls.pl	2025-04-16 08:09:24.000000000 +0000
@@ -0,0 +1,36 @@
+#!/usr/bin/env perl
+
+#
+# Copyright [2021-2022] International Business Machines Corp.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+use strict;
+use warnings;
+use test;
+use FindBin;
+
+# TLS 1.3 with RSA signatures
+test::tls(10001, "$FindBin::Bin/server-key-rsa.pem", "$FindBin::Bin/server-cert-rsa.pem", "ALL", "TLS_AES_256_GCM_SHA384", "-tls1_3");
+# TLS 1.3 with EC signatures
+test::tls(10002, "$FindBin::Bin/server-key-ec.pem", "$FindBin::Bin/server-cert-ec.pem", "ALL", "TLS_AES_256_GCM_SHA384", "-tls1_3");
+# TLS 1.2 with RSA signatures and ECDH key exchange
+test::tls(10003, "$FindBin::Bin/server-key-rsa.pem", "$FindBin::Bin/server-cert-rsa.pem", "ECDHE-RSA-AES256-GCM-SHA384", "\"\"", "-no_tls1_3");
+# TLS 1.2 with ECDSA signatures and ECDH key exchange
+test::tls(10004, "$FindBin::Bin/server-key-ec.pem", "$FindBin::Bin/server-cert-ec.pem", "ECDHE-ECDSA-AES256-GCM-SHA384", "\"\"", "-no_tls1_3");
+# TLS 1.2 with RSA signatures and DH key exchange
+test::tls(10005, "$FindBin::Bin/server-key-rsa.pem", "$FindBin::Bin/server-cert-rsa.pem", "DHE-RSA-AES256-GCM-SHA384", "\"\"", "-no_tls1_3");
+# TLS 1.2 with RSA signatures and RSA key exchange
+test::tls(10006, "$FindBin::Bin/server-key-rsa.pem", "$FindBin::Bin/server-cert-rsa.pem", "AES256-GCM-SHA384", "\"\"", "-no_tls1_3");
+
