diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..1230c18
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1 @@
+SOURCES/opencryptoki-3.16.0.tar.gz
diff --git a/.opencryptoki.metadata b/.opencryptoki.metadata
new file mode 100644
index 0000000..9c6b848
--- /dev/null
+++ b/.opencryptoki.metadata
@@ -0,0 +1 @@
+e5d8cf8df446a9bdcb3658a8f191f5a31d3a751e SOURCES/opencryptoki-3.16.0.tar.gz
diff --git a/SOURCES/opencryptoki-3.11.0-group.patch b/SOURCES/opencryptoki-3.11.0-group.patch
new file mode 100644
index 0000000..e88b391
--- /dev/null
+++ b/SOURCES/opencryptoki-3.11.0-group.patch
@@ -0,0 +1,31 @@
+diff -up opencryptoki-3.11.0/usr/lib/api/shrd_mem.c.in.me opencryptoki-3.11.0/usr/lib/api/shrd_mem.c.in
+--- opencryptoki-3.11.0/usr/lib/api/shrd_mem.c.in.me	2019-01-31 10:42:23.325797012 +0100
++++ opencryptoki-3.11.0/usr/lib/api/shrd_mem.c.in	2019-01-31 10:52:17.585191667 +0100
+@@ -55,9 +55,11 @@ void *attach_shared_memory()
+     int shmid;
+     char *shmp;
+     struct stat statbuf;
++#if 0
+     struct group *grp;
+     struct passwd *pw, *epw;
+     uid_t uid, euid;
++#endif
+ 
+ #if !(MMAP)
+     // Really should fstat the tok_path, since it will be the actual
+@@ -69,6 +71,7 @@ void *attach_shared_memory()
+         return NULL;
+     }
+ 
++#if 0
+     uid = getuid();
+     euid = geteuid();
+     // only check group membership if not root user
+@@ -102,6 +105,7 @@ void *attach_shared_memory()
+             return NULL;
+         }
+     }
++#endif
+ 
+     Anchor->shm_tok = ftok(TOK_PATH, 'b');
+ 
diff --git a/SOURCES/opencryptoki-3.11.0-lockdir.patch b/SOURCES/opencryptoki-3.11.0-lockdir.patch
new file mode 100644
index 0000000..936a654
--- /dev/null
+++ b/SOURCES/opencryptoki-3.11.0-lockdir.patch
@@ -0,0 +1,12 @@
+diff -up opencryptoki-3.11.0/configure.ac.me opencryptoki-3.11.0/configure.ac
+--- opencryptoki-3.11.0/configure.ac.me	2019-01-30 17:10:19.660952694 +0100
++++ opencryptoki-3.11.0/configure.ac	2019-01-30 17:13:54.150089964 +0100
+@@ -62,7 +62,7 @@ AC_SUBST([OPENLDAP_LIBS])
+ 
+ dnl Define custom variables
+ 
+-lockdir=$localstatedir/lock/opencryptoki
++lockdir=/run/lock/opencryptoki
+ AC_SUBST(lockdir)
+ 
+ logdir=$localstatedir/log/opencryptoki
diff --git a/SOURCES/opencryptoki-3.16.0-19f56d12b302b87e1dacf613cc61a063ad209d15.patch b/SOURCES/opencryptoki-3.16.0-19f56d12b302b87e1dacf613cc61a063ad209d15.patch
new file mode 100644
index 0000000..e7872d6
--- /dev/null
+++ b/SOURCES/opencryptoki-3.16.0-19f56d12b302b87e1dacf613cc61a063ad209d15.patch
@@ -0,0 +1,136 @@
+commit 19f56d12b302b87e1dacf613cc61a063ad209d15
+Author: Ingo Franzki <ifranzki@linux.ibm.com>
+Date:   Fri Feb 12 15:57:20 2021 +0100
+
+    Fix compile warning when compiling pkcsslotd with -DDEV and/or -DTHREADED
+    
+    Signed-off-by: Ingo Franzki <ifranzki@linux.ibm.com>
+
+diff --git a/usr/sbin/pkcsslotd/garbage_linux.c b/usr/sbin/pkcsslotd/garbage_linux.c
+index d4878c3b..a4dd9713 100644
+--- a/usr/sbin/pkcsslotd/garbage_linux.c
++++ b/usr/sbin/pkcsslotd/garbage_linux.c
+@@ -15,6 +15,7 @@
+ #include <string.h>
+ #include <sys/types.h>
+ #include <fcntl.h>
++#include <stdlib.h>
+ 
+ #include "log.h"
+ #include "slotmgr.h"
+@@ -80,8 +81,8 @@ BOOL StartGCThread(Slot_Mgr_Shr_t *MemPtr)
+ #ifdef DEV
+     // Only development builds
+     LogLog("StartGCThread: garbage collection thread started as ID "
+-           "%d (%#x) by ID %d (%#x)",
+-           GCThread, GCThread, pthread_self(), pthread_self());
++           "%lu by ID %lu",
++           GCThread, pthread_self());
+ #endif
+ 
+     return TRUE;
+@@ -115,8 +116,8 @@ BOOL StopGCThread(void *Ptr)
+         return FALSE;
+     }
+ 
+-    DbgLog(DL0, "StopGCThread: tid %d is stopping the garbage collection "
+-           "thread (tid %d)",
++    DbgLog(DL0, "StopGCThread: tid %lu is stopping the garbage collection "
++           "thread (tid %lu)",
+            pthread_self(), GCThread);
+ 
+     /* Cause the GC thread to be cancelled */
+@@ -245,7 +246,7 @@ void GCCancel(void *Ptr)
+     UNUSED(Ptr);
+ 
+     /* Yeah, yeah.  Doesn't do anything, but I had plans */
+-    DbgLog(DL3, "GCCancel: tid: %d running cleanup routine", pthread_self());
++    DbgLog(DL3, "GCCancel: tid: %lu running cleanup routine", pthread_self());
+ 
+     return;
+ }
+@@ -268,7 +269,7 @@ BOOL CheckForGarbage(Slot_Mgr_Shr_t *MemPtr)
+ 
+     ASSERT(MemPtr != NULL_PTR);
+ #ifdef DEV
+-    DbgLog(DL5, "Thread %d is checking for garbage", pthread_self());
++    DbgLog(DL5, "Thread %lu is checking for garbage", pthread_self());
+ #endif                          /* DEV */
+ 
+ 
+@@ -326,9 +327,9 @@ BOOL CheckForGarbage(Slot_Mgr_Shr_t *MemPtr)
+                 if (*pProcSessions > 0) {
+ 
+ #ifdef DEV
+-                    DbgLog(DL2, "GC: Invalid pid (%d) is holding %d sessions "
++                    DbgLog(DL2, "GC: Invalid pid (%d) is holding %u sessions "
+                            "open on slot %d.  Global session count for this "
+-                           "slot is %d",
++                           "slot is %u",
+                            pProc->proc_id, *pProcSessions, SlotIndex,
+                            *pGlobalSessions);
+ #endif                          /* DEV */
+@@ -338,9 +339,9 @@ BOOL CheckForGarbage(Slot_Mgr_Shr_t *MemPtr)
+                         WarnLog("Garbage Collection: Illegal values in table "
+                                 "for defunct process");
+                         DbgLog(DL0, "Garbage collection: A process "
+-                               "( Index: %d, pid: %d ) showed %d sessions "
+-                               "open on slot %s, but the global count for this "
+-                               "slot is only %d",
++                               "( Index: %d, pid: %d ) showed %u sessions "
++                               "open on slot %d, but the global count for this "
++                               "slot is only %u",
+                                ProcIndex, pProc->proc_id, *pProcSessions,
+                                SlotIndex, *pGlobalSessions);
+ #endif                          /* DEV */
+@@ -395,14 +396,8 @@ int Stat2Proc(int pid, proc_t *p)
+     char fbuf[800];         // about 40 fields, 64-bit decimal is about 20 chars
+     char *tmp;
+     int fd, num;
+-    //  FILE *fp;
+-
+-    //  sprintf(buf, "%s/%d/stat", PROC_BASE, pid);
+-    //  if( (fp = fopen(buf, "r")) == NULL )
+-    //    return FALSE;
+ 
+     sprintf(fbuf, "%s/%d/stat", PROC_BASE, pid);
+-    printf("Buff = %s \n", fbuf);
+     fflush(stdout);
+     if ((fd = open(fbuf, O_RDONLY, 0)) == -1)
+         return FALSE;
+diff --git a/usr/sbin/pkcsslotd/log.c b/usr/sbin/pkcsslotd/log.c
+index 0214f952..0394cc7d 100644
+--- a/usr/sbin/pkcsslotd/log.c
++++ b/usr/sbin/pkcsslotd/log.c
+@@ -463,8 +463,8 @@ BOOL PKCS_Log(pLogHandle phLog, char *fmt, va_list ap)
+ #endif                          /* DEV */
+ 
+         if (WriteNow) {
+-            fprintf(stderr, "%s[%d.%d]: %s\n", pInfo->Descrip, getpid(),
+-                    (int) pthread_self(), buf);
++            fprintf(stderr, "%s[%d.%lu]: %s\n", pInfo->Descrip, getpid(),
++                    pthread_self(), buf);
+         }
+     }
+ 
+@@ -482,7 +482,7 @@ BOOL PKCS_Log(pLogHandle phLog, char *fmt, va_list ap)
+             GetCurrentTimeString(timebuf);
+ 
+             /* Date/Time stamp, descrip, Error message */
+-            fprintf(fd, "%s %s[%d.%d]: ", timebuf, pInfo->Descrip, getpid(),
++            fprintf(fd, "%s %s[%d.%lu]: ", timebuf, pInfo->Descrip, getpid(),
+                     pthread_self());
+             fprintf(fd, "%s\n", buf);
+             fflush(fd);
+diff --git a/usr/sbin/pkcsslotd/slotmgr.c b/usr/sbin/pkcsslotd/slotmgr.c
+index 94288f13..efbfe8fd 100644
+--- a/usr/sbin/pkcsslotd/slotmgr.c
++++ b/usr/sbin/pkcsslotd/slotmgr.c
+@@ -660,7 +660,6 @@ int main(int argc, char *argv[], char *envp[])
+      */
+ 
+ #if !defined(NOGARBAGE)
+-    printf("Start garbage \n");
+     /* start garbage collection thread */
+     if (!StartGCThread(shmp)) {
+         term_socket_server();
diff --git a/SOURCES/opencryptoki-3.16.0-1fdd0e4497b0078e73e0004e3492db647c7c458b.patch b/SOURCES/opencryptoki-3.16.0-1fdd0e4497b0078e73e0004e3492db647c7c458b.patch
new file mode 100644
index 0000000..264f27d
--- /dev/null
+++ b/SOURCES/opencryptoki-3.16.0-1fdd0e4497b0078e73e0004e3492db647c7c458b.patch
@@ -0,0 +1,2304 @@
+commit 1fdd0e4497b0078e73e0004e3492db647c7c458b
+Author: Ingo Franzki <ifranzki@linux.ibm.com>
+Date:   Wed Feb 24 15:47:05 2021 +0100
+
+    EP11: Handle APQN events to update APQN version, CP infos and target
+    
+    Signed-off-by: Ingo Franzki <ifranzki@linux.ibm.com>
+
+diff --git a/usr/lib/ep11_stdll/ep11_specific.c b/usr/lib/ep11_stdll/ep11_specific.c
+index 52f95d7a..728fedd5 100644
+--- a/usr/lib/ep11_stdll/ep11_specific.c
++++ b/usr/lib/ep11_stdll/ep11_specific.c
+@@ -38,6 +38,7 @@
+ #include "ock_syslog.h"
+ #include "ec_defs.h"
+ #include "p11util.h"
++#include "events.h"
+ 
+ #include <sys/types.h>
+ #include <sys/stat.h>
+@@ -197,19 +198,27 @@ typedef struct {
+ 
+ #define MAX_RETRY_COUNT 100
+ 
+-#define RETRY_START             do {                                     \
++#define RETRY_START(rc, tokdata)                                         \
++                                do {                                     \
+                                     int retry_count;                     \
++                                    ep11_target_info_t* target_info =    \
++                                             get_target_info((tokdata)); \
++                                    if (target_info == NULL)             \
++                                        (rc) = CKR_FUNCTION_FAILED;      \
+                                     for(retry_count = 0;                 \
++                                        target_info != NULL &&           \
+                                         retry_count < MAX_RETRY_COUNT;   \
+                                         retry_count ++) {
+ 
+ #define RETRY_END(rc, tokdata, session)  if ((rc) != CKR_SESSION_CLOSED) \
+                                              break;                      \
+                                          (rc) = ep11tok_relogin_session( \
+-                                                      tokdata, session); \
++                                                  (tokdata), (session)); \
+                                          if ((rc) != CKR_OK)             \
+                                              break;                      \
+                                     }                                    \
++                                    put_target_info((tokdata),           \
++                                                    target_info);        \
+                                 } while (0);
+ 
+ #define CKF_EP11_HELPER_SESSION      0x80000000
+@@ -248,7 +257,6 @@ typedef struct ep11_card_version {
+ } ep11_card_version_t;
+ 
+ static CK_RV ep11tok_get_ep11_library_version(CK_VERSION *lib_version);
+-static CK_RV ep11tok_get_ep11_version(STDLL_TokData_t *tokdata);
+ static void free_card_versions(ep11_card_version_t *card_version);
+ static int check_card_version(STDLL_TokData_t *tokdata, CK_ULONG card_type,
+                               const CK_VERSION *ep11_lib_version,
+@@ -476,16 +484,23 @@ static CK_RV handle_all_ep11_cards(ep11_target_t * ep11_targets,
+ #define PKEY_MODE_ENABLE4NONEXTR    2
+ 
+ typedef struct {
++    volatile unsigned long ref_count;
+     target_t target;
++    ep11_card_version_t *card_versions;
++    CK_ULONG used_firmware_API_version;
++    unsigned char control_points[XCP_CP_BYTES];
++    size_t control_points_len;
++    size_t max_control_point_index;
++    CK_CHAR serialNumber[16];
++} ep11_target_info_t;
++
++typedef struct {
+     ep11_target_t target_list;
+     CK_BYTE raw2key_wrap_blob[MAX_BLOBSIZE];
+     size_t raw2key_wrap_blob_l;
+     int cka_sensitive_default_true;
+     char cp_filter_config_filename[PATH_MAX];
+     cp_config_t *cp_config;
+-    unsigned char control_points[XCP_CP_BYTES];
+-    size_t control_points_len;
+-    size_t max_control_point_index;
+     int strict_mode;
+     int vhsm_mode;
+     int optimize_single_ops;
+@@ -497,12 +512,14 @@ typedef struct {
+     char digest_libica_path[PATH_MAX];
+     libica_t libica;
+     CK_VERSION ep11_lib_version;
+-    ep11_card_version_t *card_versions;
+-    CK_ULONG used_firmware_API_version;
+-    CK_CHAR serialNumber[16];
++    volatile ep11_target_info_t *target_info;
++    pthread_rwlock_t target_rwlock;
+ } ep11_private_data_t;
+ 
+-static CK_RV ep11tok_setup_target(STDLL_TokData_t *tokdata);
++static ep11_target_info_t *get_target_info(STDLL_TokData_t *tokdata);
++static void put_target_info(STDLL_TokData_t *tokdata,
++                            ep11_target_info_t *target_info);
++static CK_RV refresh_target_info(STDLL_TokData_t *tokdata);
+ 
+ static CK_RV get_ep11_target_for_apqn(uint_32 adapter, uint_32 domain,
+                                       target_t *target, uint64_t flags);
+@@ -704,8 +721,13 @@ static CK_RV ep11tok_pkey_get_firmware_mk_vp(STDLL_TokData_t *tokdata)
+     CK_BYTE blob[MAX_BLOBSIZE];
+     size_t blobsize = sizeof(blob);
+     CK_ATTRIBUTE *pkey_attr = NULL, *blob_attr=NULL;
++    ep11_target_info_t* target_info;
+     CK_RV ret;
+ 
++    target_info = get_target_info(tokdata);
++    if (target_info == NULL)
++        return CKR_FUNCTION_FAILED;
++
+     /* Check if CPACF_WRAP mech supported */
+     if (ep11tok_is_mechanism_supported(tokdata, CKM_IBM_CPACF_WRAP) != CKR_OK) {
+         TRACE_INFO("CKM_IBM_CPACF_WRAP not supported on this system.\n");
+@@ -717,7 +739,7 @@ static CK_RV ep11tok_pkey_get_firmware_mk_vp(STDLL_TokData_t *tokdata)
+ 
+     /* Create an AES testkey with CKA_IBM_PROTKEY_EXTRACTABLE */
+     ret = dll_m_GenerateKey(&mech, tmpl, tmpl_len, NULL, 0,
+-                            blob, &blobsize, csum, &csum_l, ep11_data->target);
++                            blob, &blobsize, csum, &csum_l, target_info->target);
+     if (ret != CKR_OK) {
+         TRACE_ERROR("dll_m_GenerateKey failed with rc=0x%lx\n",ret);
+         goto done;
+@@ -749,6 +771,8 @@ done:
+     if (blob_attr)
+         free(blob_attr);
+ 
++    put_target_info(tokdata, target_info);
++
+     return ret;
+ }
+ 
+@@ -1337,7 +1361,7 @@ static CK_RV ab_unwrap_update_template(STDLL_TokData_t * tokdata,
+                                        OBJECT *obj,
+                                        CK_KEY_TYPE keytype)
+ {
+-    ep11_private_data_t *ep11_data = tokdata->private_data;
++    ep11_target_info_t* target_info;
+     CK_RV rc;
+     CK_BBOOL trusted, encrypt, decrypt, wrap, unwrap, sign, sign_recover,
+              verify, verify_recover, derive, extractable, local,
+@@ -1367,9 +1391,16 @@ static CK_RV ab_unwrap_update_template(STDLL_TokData_t * tokdata,
+     CK_ATTRIBUTE *attr;
+     CK_BBOOL cktrue = TRUE;
+ 
++    target_info = get_target_info(tokdata);
++    if (target_info == NULL)
++        return CKR_FUNCTION_FAILED;
++
+     rc = dll_m_GetAttributeValue(blob, blob_len, attrs,
+                                  sizeof(attrs) / sizeof(CK_ATTRIBUTE),
+-                                 ep11_data->target);
++                                 target_info->target);
++
++    put_target_info(tokdata, target_info);
++
+     if (rc != CKR_OK) {
+         TRACE_ERROR("Retrieving attributes from AB unwrapped key failed, rc=0x%lx\n",
+                     rc);
+@@ -2117,10 +2148,10 @@ static CK_RV rawkey_2_blob(STDLL_TokData_t * tokdata, SESSION * sess,
+      * calls the ep11 lib (which in turns sends the request to the card),
+      * all m_ function are ep11 functions
+      */
+-    RETRY_START
++    RETRY_START(rc, tokdata)
+         rc = dll_m_EncryptSingle(ep11_data->raw2key_wrap_blob,
+                                  ep11_data->raw2key_wrap_blob_l, &mech, key,
+-                                 ksize, cipher, &clen, ep11_data->target);
++                                 ksize, cipher, &clen, target_info->target);
+     RETRY_END(rc, tokdata, sess)
+ 
+     if (rc != CKR_OK) {
+@@ -2146,12 +2177,12 @@ static CK_RV rawkey_2_blob(STDLL_TokData_t * tokdata, SESSION * sess,
+     /* the encrypted key is decrypted and a blob is build,
+      * card accepts only blobs as keys
+      */
+-    RETRY_START
++    RETRY_START(rc, tokdata)
+         rc = dll_m_UnwrapKey(cipher, clen, ep11_data->raw2key_wrap_blob,
+                              ep11_data->raw2key_wrap_blob_l, NULL, ~0,
+                              ep11_pin_blob, ep11_pin_blob_len, &mech,
+                              new_p_attrs, new_attrs_len, blob, blen, csum,
+-                             &cslen, ep11_data->target);
++                             &cslen, target_info->target);
+     RETRY_END(rc, tokdata, sess)
+ 
+     if (rc != CKR_OK) {
+@@ -2194,14 +2225,20 @@ rawkey_2_blob_end:
+ CK_RV token_specific_rng(STDLL_TokData_t * tokdata, CK_BYTE * output,
+                          CK_ULONG bytes)
+ {
+-    ep11_private_data_t *ep11_data = tokdata->private_data;
++    ep11_target_info_t* target_info;
++
++    target_info = get_target_info(tokdata);
++    if (target_info == NULL)
++        return CKR_FUNCTION_FAILED;
+ 
+-    CK_RV rc = dll_m_GenerateRandom(output, bytes, ep11_data->target);
++    CK_RV rc = dll_m_GenerateRandom(output, bytes, target_info->target);
+     if (rc != CKR_OK) {
+         rc = ep11_error_to_pkcs11_error(rc, NULL);
+         TRACE_ERROR("%s output=%p bytes=%lu rc=0x%lx\n",
+                     __func__, (void *)output, bytes, rc);
+     }
++
++    put_target_info(tokdata, target_info);
+     return rc;
+ }
+ 
+@@ -2215,6 +2252,7 @@ static CK_RV make_wrapblob(STDLL_TokData_t * tokdata, CK_ATTRIBUTE * tmpl_in,
+ {
+     ep11_private_data_t *ep11_data = tokdata->private_data;
+     CK_MECHANISM mech = { CKM_AES_KEY_GEN, NULL_PTR, 0 };
++    ep11_target_info_t* target_info;
+     CK_BYTE csum[MAX_CSUMSIZE];
+     size_t csum_l = sizeof(csum);
+     CK_RV rc;
+@@ -2225,11 +2263,15 @@ static CK_RV make_wrapblob(STDLL_TokData_t * tokdata, CK_ATTRIBUTE * tmpl_in,
+         return CKR_OK;
+     }
+ 
++    target_info = get_target_info(tokdata);
++    if (target_info == NULL)
++        return CKR_FUNCTION_FAILED;
++
+     ep11_data->raw2key_wrap_blob_l = sizeof(ep11_data->raw2key_wrap_blob);
+     rc = dll_m_GenerateKey(&mech, tmpl_in, tmpl_len, NULL, 0,
+                            ep11_data->raw2key_wrap_blob,
+                            &ep11_data->raw2key_wrap_blob_l, csum, &csum_l,
+-                           ep11_data->target);
++                           target_info->target);
+ 
+ 
+     if (rc != CKR_OK) {
+@@ -2240,6 +2282,7 @@ static CK_RV make_wrapblob(STDLL_TokData_t * tokdata, CK_ATTRIBUTE * tmpl_in,
+                    __func__, ep11_data->raw2key_wrap_blob_l, rc);
+     }
+ 
++    put_target_info(tokdata, target_info);
+     return rc;
+ }
+ 
+@@ -2479,6 +2522,14 @@ CK_RV ep11tok_init(STDLL_TokData_t * tokdata, CK_SLOT_ID SlotNumber,
+     if (ep11_data == NULL)
+         return CKR_HOST_MEMORY;
+ 
++    if (pthread_rwlock_init(&ep11_data->target_rwlock, NULL) != 0) {
++        TRACE_DEVEL("Target Lock init failed.\n");
++        OCK_SYSLOG(LOG_ERR, "%s: Failed to initialize the target lock\n",
++                   __func__);
++        rc = CKR_CANT_LOCK;
++        goto error;
++    }
++
+     tokdata->private_data = ep11_data;
+ 
+     /* read ep11 specific config file with user specified
+@@ -2513,13 +2564,28 @@ CK_RV ep11tok_init(STDLL_TokData_t * tokdata, CK_SLOT_ID SlotNumber,
+     }
+ #endif
+ 
+-    rc = ep11tok_get_ep11_version(tokdata);
+-    if (rc != CKR_OK)
++    rc = ep11tok_get_ep11_library_version(&ep11_data->ep11_lib_version);
++    if (rc != CKR_OK) {
++        TRACE_ERROR("%s Failed to get the Ep11 library version "
++                    "(ep11tok_get_ep11_library_version rc=0x%lx)\n", __func__,
++                    rc);
++        OCK_SYSLOG(LOG_ERR, "%s: Failed to get the EP11 library version "
++                   "rc=0x%lx\n", __func__, rc);
+         goto error;
++    }
+ 
+-    rc = ep11tok_setup_target(tokdata);
+-    if (rc != CKR_OK)
++    TRACE_INFO("%s Host library version: %d.%d\n", __func__,
++               ep11_data->ep11_lib_version.major,
++               ep11_data->ep11_lib_version.minor);
++
++    rc = refresh_target_info(tokdata);
++    if (rc != CKR_OK) {
++        TRACE_ERROR("%s Failed to get the target info (refresh_target_info "
++                    "rc=0x%lx)\n", __func__, rc);
++        OCK_SYSLOG(LOG_ERR, "%s: Failed to get the target info rc=0x%lx\n",
++                   __func__, rc);
+         goto error;
++    }
+ 
+     if (ep11_data->digest_libica) {
+         rc = ep11tok_load_libica(tokdata);
+@@ -2530,18 +2596,6 @@ CK_RV ep11tok_init(STDLL_TokData_t * tokdata, CK_SLOT_ID SlotNumber,
+     ep11_data->msa_level = get_msa_level();
+     TRACE_INFO("MSA level = %i\n", ep11_data->msa_level);
+ 
+-    ep11_data->control_points_len = sizeof(ep11_data->control_points);
+-    rc = get_control_points(tokdata, ep11_data->control_points,
+-                            &ep11_data->control_points_len,
+-                            &ep11_data->max_control_point_index);
+-    if (rc != CKR_OK) {
+-        TRACE_ERROR("%s Failed to get the control points (get_control_points "
+-                    "rc=0x%lx)\n", __func__, rc);
+-        OCK_SYSLOG(LOG_ERR, "%s: Failed to get the control points rc=0x%lx\n",
+-                   __func__, rc);
+-        goto error;
+-    }
+-
+     /* create an AES key needed for importing keys
+      * (encrypt by wrap_key and m_UnwrapKey by wrap key)
+      */
+@@ -2600,10 +2654,14 @@ CK_RV ep11tok_final(STDLL_TokData_t * tokdata)
+     TRACE_INFO("ep11 %s running\n", __func__);
+ 
+     if (ep11_data != NULL) {
+-        if (dll_m_rm_module != NULL)
+-            dll_m_rm_module(NULL, ep11_data->target);
++        if (ep11_data->target_info != NULL) {
++            if (dll_m_rm_module != NULL)
++                dll_m_rm_module(NULL, ep11_data->target_info->target);
++            free_card_versions(ep11_data->target_info->card_versions);
++            free((void* )ep11_data->target_info);
++        }
++        pthread_rwlock_destroy(&ep11_data->target_rwlock);
+         free_cp_config(ep11_data->cp_config);
+-        free_card_versions(ep11_data->card_versions);
+         free(ep11_data);
+         tokdata->private_data = NULL;
+     }
+@@ -2619,7 +2677,6 @@ static CK_RV make_maced_spki(STDLL_TokData_t *tokdata, SESSION * sess,
+                              CK_BYTE *spki, CK_ULONG spki_len,
+                              CK_BYTE *maced_spki, CK_ULONG *maced_spki_len)
+ {
+-    ep11_private_data_t *ep11_data = tokdata->private_data;
+     unsigned char *ep11_pin_blob = NULL;
+     CK_ULONG ep11_pin_blob_len = 0;
+     ep11_session_t *ep11_session = (ep11_session_t *) sess->private_data;
+@@ -2712,11 +2769,11 @@ static CK_RV make_maced_spki(STDLL_TokData_t *tokdata, SESSION * sess,
+     ep11_get_pin_blob(ep11_session, object_is_session_object(pub_key_obj),
+                       &ep11_pin_blob, &ep11_pin_blob_len);
+ 
+-    RETRY_START
++    RETRY_START(rc, tokdata)
+         rc = dll_m_UnwrapKey(spki, spki_len, NULL, 0, NULL, 0,
+                              ep11_pin_blob, ep11_pin_blob_len, &mech,
+                              p_attrs, attrs_len, maced_spki, maced_spki_len,
+-                             csum, &cslen, ep11_data->target);
++                             csum, &cslen, target_info->target);
+     RETRY_END(rc, tokdata, sess)
+ 
+     if (rc != CKR_OK) {
+@@ -2870,11 +2927,11 @@ static CK_RV import_RSA_key(STDLL_TokData_t * tokdata, SESSION * sess,
+         }
+ 
+         /* encrypt */
+-        RETRY_START
++        RETRY_START(rc, tokdata)
+             rc = dll_m_EncryptSingle(ep11_data->raw2key_wrap_blob,
+                                      ep11_data->raw2key_wrap_blob_l, &mech_w,
+                                      data, data_len, cipher, &cipher_l,
+-                                     ep11_data->target);
++                                     target_info->target);
+         RETRY_END(rc, tokdata, sess)
+ 
+         TRACE_INFO("%s wrapping wrap key rc=0x%lx cipher_l=0x%lx\n",
+@@ -2901,12 +2958,12 @@ static CK_RV import_RSA_key(STDLL_TokData_t * tokdata, SESSION * sess,
+         /* calls the card, it decrypts the private RSA key,
+          * reads its BER format and builds a blob.
+          */
+-        RETRY_START
++        RETRY_START(rc, tokdata)
+             rc = dll_m_UnwrapKey(cipher, cipher_l, ep11_data->raw2key_wrap_blob,
+                                  ep11_data->raw2key_wrap_blob_l, NULL, ~0,
+                                  ep11_pin_blob, ep11_pin_blob_len, &mech_w,
+                                  new_p_attrs, new_attrs_len, blob, blob_size,
+-                                 csum, &cslen, ep11_data->target);
++                                 csum, &cslen, target_info->target);
+         RETRY_END(rc, tokdata, sess)
+ 
+         if (rc != CKR_OK) {
+@@ -3101,11 +3158,11 @@ static CK_RV import_EC_key(STDLL_TokData_t * tokdata, SESSION * sess,
+         }
+ 
+         /* encrypt */
+-        RETRY_START
++        RETRY_START(rc, tokdata)
+             rc = dll_m_EncryptSingle(ep11_data->raw2key_wrap_blob,
+                                      ep11_data->raw2key_wrap_blob_l,
+                                      &mech_w, data, data_len,
+-                                     cipher, &cipher_l, ep11_data->target);
++                                     cipher, &cipher_l, target_info->target);
+         RETRY_END(rc, tokdata, sess)
+ 
+         TRACE_INFO("%s wrapping wrap key rc=0x%lx cipher_l=0x%lx\n",
+@@ -3133,14 +3190,14 @@ static CK_RV import_EC_key(STDLL_TokData_t * tokdata, SESSION * sess,
+         /* calls the card, it decrypts the private EC key,
+          * reads its BER format and builds a blob.
+          */
+-        RETRY_START
++        RETRY_START(rc, tokdata)
+             rc = dll_m_UnwrapKey(cipher, cipher_l,
+                                  ep11_data->raw2key_wrap_blob,
+                                  ep11_data->raw2key_wrap_blob_l, NULL, ~0,
+                                  ep11_pin_blob,
+                                  ep11_pin_blob_len, &mech_w,
+                                  new_p_attrs, new_attrs_len, blob,
+-                                 blob_size, csum, &cslen, ep11_data->target);
++                                 blob_size, csum, &cslen, target_info->target);
+         RETRY_END(rc, tokdata, sess)
+ 
+         if (rc != CKR_OK) {
+@@ -3295,11 +3352,11 @@ static CK_RV import_DSA_key(STDLL_TokData_t * tokdata, SESSION * sess,
+         }
+ 
+         /* encrypt */
+-        RETRY_START
++        RETRY_START(rc, tokdata)
+             rc = dll_m_EncryptSingle(ep11_data->raw2key_wrap_blob,
+                                      ep11_data->raw2key_wrap_blob_l,
+                                      &mech_w, data, data_len,
+-                                     cipher, &cipher_l, ep11_data->target);
++                                     cipher, &cipher_l, target_info->target);
+         RETRY_END(rc, tokdata, sess)
+ 
+ 
+@@ -3327,14 +3384,14 @@ static CK_RV import_DSA_key(STDLL_TokData_t * tokdata, SESSION * sess,
+         /* calls the card, it decrypts the private EC key,
+          * reads its BER format and builds a blob.
+          */
+-        RETRY_START
++        RETRY_START(rc, tokdata)
+             rc = dll_m_UnwrapKey(cipher, cipher_l,
+                                  ep11_data->raw2key_wrap_blob,
+                                  ep11_data->raw2key_wrap_blob_l, NULL, ~0,
+                                  ep11_pin_blob,
+                                  ep11_pin_blob_len, &mech_w,
+                                  new_p_attrs, new_attrs_len, blob,
+-                                 blob_size, csum, &cslen, ep11_data->target);
++                                 blob_size, csum, &cslen, target_info->target);
+         RETRY_END(rc, tokdata, sess)
+ 
+         if (rc != CKR_OK) {
+@@ -3478,11 +3535,11 @@ static CK_RV import_DH_key(STDLL_TokData_t * tokdata, SESSION * sess,
+         }
+ 
+         /* encrypt */
+-        RETRY_START
++        RETRY_START(rc, tokdata)
+             rc = dll_m_EncryptSingle(ep11_data->raw2key_wrap_blob,
+                                      ep11_data->raw2key_wrap_blob_l,
+                                      &mech_w, data, data_len,
+-                                     cipher, &cipher_l, ep11_data->target);
++                                     cipher, &cipher_l, target_info->target);
+         RETRY_END(rc, tokdata, sess)
+ 
+         TRACE_INFO("%s wrapping wrap key rc=0x%lx cipher_l=0x%lx\n",
+@@ -3509,14 +3566,14 @@ static CK_RV import_DH_key(STDLL_TokData_t * tokdata, SESSION * sess,
+         /* calls the card, it decrypts the private EC key,
+          * reads its BER format and builds a blob.
+          */
+-        RETRY_START
++        RETRY_START(rc, tokdata)
+             rc = dll_m_UnwrapKey(cipher, cipher_l,
+                                  ep11_data->raw2key_wrap_blob,
+                                  ep11_data->raw2key_wrap_blob_l, NULL, ~0,
+                                  ep11_pin_blob,
+                                  ep11_pin_blob_len, &mech_w,
+                                  new_p_attrs, new_attrs_len, blob,
+-                                 blob_size, csum, &cslen, ep11_data->target);
++                                 blob_size, csum, &cslen, target_info->target);
+         RETRY_END(rc, tokdata, sess)
+ 
+         if (rc != CKR_OK) {
+@@ -3666,11 +3723,11 @@ static CK_RV import_IBM_Dilithium_key(STDLL_TokData_t * tokdata, SESSION * sess,
+         }
+ 
+         /* encrypt */
+-        RETRY_START
++        RETRY_START(rc, tokdata)
+             rc = dll_m_EncryptSingle(ep11_data->raw2key_wrap_blob,
+                                      ep11_data->raw2key_wrap_blob_l,
+                                      &mech_w, data, data_len,
+-                                     cipher, &cipher_l, ep11_data->target);
++                                     cipher, &cipher_l, target_info->target);
+         RETRY_END(rc, tokdata, sess)
+ 
+         TRACE_INFO("%s wrapping wrap key rc=0x%lx cipher_l=0x%lx\n",
+@@ -3699,14 +3756,14 @@ static CK_RV import_IBM_Dilithium_key(STDLL_TokData_t * tokdata, SESSION * sess,
+         /* calls the card, it decrypts the private Dilithium key,
+          * reads its BER format and builds a blob.
+          */
+-        RETRY_START
++        RETRY_START(rc, tokdata)
+             rc = dll_m_UnwrapKey(cipher, cipher_l,
+                                  ep11_data->raw2key_wrap_blob,
+                                  ep11_data->raw2key_wrap_blob_l, NULL, ~0,
+                                  ep11_pin_blob,
+                                  ep11_pin_blob_len, &mech_w,
+                                  new_p_attrs, new_attrs_len, blob,
+-                                 blob_size, csum, &cslen, ep11_data->target);
++                                 blob_size, csum, &cslen, target_info->target);
+         RETRY_END(rc, tokdata, sess)
+ 
+         if (rc != CKR_OK) {
+@@ -3884,7 +3941,6 @@ CK_RV ep11tok_generate_key(STDLL_TokData_t * tokdata, SESSION * session,
+                            CK_MECHANISM_PTR mech, CK_ATTRIBUTE_PTR attrs,
+                            CK_ULONG attrs_len, CK_OBJECT_HANDLE_PTR handle)
+ {
+-    ep11_private_data_t *ep11_data = tokdata->private_data;
+     CK_BYTE blob[MAX_BLOBSIZE];
+     size_t blobsize = sizeof(blob);
+     CK_BYTE csum[MAX_CSUMSIZE];
+@@ -3936,10 +3992,10 @@ CK_RV ep11tok_generate_key(STDLL_TokData_t * tokdata, SESSION * session,
+     ep11_get_pin_blob(ep11_session, ep11_is_session_object(attrs, attrs_len),
+                       &ep11_pin_blob, &ep11_pin_blob_len);
+ 
+-    RETRY_START
++    RETRY_START(rc, tokdata)
+         rc = dll_m_GenerateKey(mech, new_attrs2, new_attrs2_len, ep11_pin_blob,
+                                ep11_pin_blob_len, blob, &blobsize,
+-                               csum, &csum_len, ep11_data->target);
++                               csum, &csum_len, target_info->target);
+     RETRY_END(rc, tokdata, session)
+     if (rc != CKR_OK) {
+         rc = ep11_error_to_pkcs11_error(rc, session);
+@@ -4354,6 +4410,7 @@ CK_RV token_specific_sha_init(STDLL_TokData_t * tokdata, DIGEST_CONTEXT * c,
+     size_t state_len = MAX(MAX_DIGEST_STATE_BYTES, sizeof(libica_sha_context_t));
+     CK_BYTE *state;
+     libica_sha_context_t *libica_ctx;
++    ep11_target_info_t* target_info;
+ 
+     state = calloc(state_len, 1); /* freed by dig_mgr.c */
+     if (!state) {
+@@ -4361,15 +4418,21 @@ CK_RV token_specific_sha_init(STDLL_TokData_t * tokdata, DIGEST_CONTEXT * c,
+         return CKR_HOST_MEMORY;
+     }
+ 
++    target_info = get_target_info(tokdata);
++    if (target_info == NULL)
++        return CKR_FUNCTION_FAILED;
++
+     if (ep11tok_libica_digest_available(ep11_data, mech->mechanism)) {
+         libica_ctx = (libica_sha_context_t *)state;
+         state_len = sizeof(libica_sha_context_t);
+         libica_ctx->first = CK_TRUE;
+         rc = get_sha_block_size(mech->mechanism, &libica_ctx->block_size);
+     } else {
+-        rc = dll_m_DigestInit(state, &state_len, mech, ep11_data->target);
++        rc = dll_m_DigestInit(state, &state_len, mech, target_info->target);
+     }
+ 
++    put_target_info(tokdata, target_info);
++
+     if (rc != CKR_OK) {
+         rc = ep11_error_to_pkcs11_error(rc, NULL);
+         TRACE_ERROR("%s rc=0x%lx\n", __func__, rc);
+@@ -4399,6 +4462,11 @@ CK_RV token_specific_sha(STDLL_TokData_t * tokdata, DIGEST_CONTEXT * c,
+ {
+     ep11_private_data_t *ep11_data = tokdata->private_data;
+     CK_RV rc;
++    ep11_target_info_t* target_info;
++
++    target_info = get_target_info(tokdata);
++    if (target_info == NULL)
++        return CKR_FUNCTION_FAILED;
+ 
+     if (ep11tok_libica_digest_available(ep11_data, c->mech.mechanism)) {
+         rc = ep11tok_libica_digest(ep11_data, c->mech.mechanism,
+@@ -4408,7 +4476,7 @@ CK_RV token_specific_sha(STDLL_TokData_t * tokdata, DIGEST_CONTEXT * c,
+                                    SHA_MSG_PART_ONLY);
+     } else {
+         rc = dll_m_Digest(c->context, c->context_len, in_data, in_data_len,
+-                          out_data, out_data_len, ep11_data->target);
++                          out_data, out_data_len, target_info->target);
+     }
+ 
+     if (rc != CKR_OK) {
+@@ -4417,6 +4485,8 @@ CK_RV token_specific_sha(STDLL_TokData_t * tokdata, DIGEST_CONTEXT * c,
+     } else {
+         TRACE_INFO("%s rc=0x%lx\n", __func__, rc);
+     }
++
++    put_target_info(tokdata, target_info);
+     return rc;
+ }
+ 
+@@ -4430,6 +4500,11 @@ CK_RV token_specific_sha_update(STDLL_TokData_t * tokdata, DIGEST_CONTEXT * c,
+     CK_ULONG out_len = sizeof(temp_out);
+     CK_ULONG len;
+     CK_RV rc = CKR_OK;
++    ep11_target_info_t* target_info;
++
++    target_info = get_target_info(tokdata);
++    if (target_info == NULL)
++        return CKR_FUNCTION_FAILED;
+ 
+     if (ep11tok_libica_digest_available(ep11_data, c->mech.mechanism)) {
+         if (libica_ctx->offset > 0 || in_data_len < libica_ctx->block_size) {
+@@ -4479,7 +4554,7 @@ CK_RV token_specific_sha_update(STDLL_TokData_t * tokdata, DIGEST_CONTEXT * c,
+         }
+     } else {
+         rc = dll_m_DigestUpdate(c->context, c->context_len,
+-                                in_data, in_data_len, ep11_data->target);
++                                in_data, in_data_len, target_info->target);
+     }
+ 
+ out:
+@@ -4489,6 +4564,8 @@ out:
+     } else {
+         TRACE_INFO("%s rc=0x%lx\n", __func__, rc);
+     }
++
++    put_target_info(tokdata, target_info);
+     return rc;
+ }
+ 
+@@ -4499,6 +4576,11 @@ CK_RV token_specific_sha_final(STDLL_TokData_t * tokdata, DIGEST_CONTEXT * c,
+     ep11_private_data_t *ep11_data = tokdata->private_data;
+     libica_sha_context_t *libica_ctx = (libica_sha_context_t *)c->context;
+     CK_RV rc;
++    ep11_target_info_t* target_info;
++
++    target_info = get_target_info(tokdata);
++    if (target_info == NULL)
++        return CKR_FUNCTION_FAILED;
+ 
+     if (ep11tok_libica_digest_available(ep11_data, c->mech.mechanism)) {
+         rc = ep11tok_libica_digest(ep11_data, c->mech.mechanism,
+@@ -4510,7 +4592,7 @@ CK_RV token_specific_sha_final(STDLL_TokData_t * tokdata, DIGEST_CONTEXT * c,
+                                         SHA_MSG_PART_FINAL);
+     } else {
+         rc = dll_m_DigestFinal(c->context, c->context_len,
+-                               out_data, out_data_len, ep11_data->target);
++                               out_data, out_data_len, target_info->target);
+     }
+ 
+     if (rc != CKR_OK) {
+@@ -4520,6 +4602,7 @@ CK_RV token_specific_sha_final(STDLL_TokData_t * tokdata, DIGEST_CONTEXT * c,
+         TRACE_INFO("%s rc=0x%lx\n", __func__, rc);
+     }
+ 
++    put_target_info(tokdata, target_info);
+     return rc;
+ }
+ 
+@@ -4528,7 +4611,6 @@ CK_RV token_specific_rsa_sign(STDLL_TokData_t *tokdata, SESSION *session,
+                               CK_BYTE *out_data, CK_ULONG *out_data_len,
+                               OBJECT *key_obj)
+ {
+-    ep11_private_data_t *ep11_data = tokdata->private_data;
+     CK_RV rc;
+     size_t keyblobsize = 0;
+     CK_BYTE *keyblob;
+@@ -4544,9 +4626,9 @@ CK_RV token_specific_rsa_sign(STDLL_TokData_t *tokdata, SESSION *session,
+     mech.pParameter = NULL;
+     mech.ulParameterLen = 0;
+ 
+-    RETRY_START
++    RETRY_START(rc, tokdata)
+     rc = dll_m_SignSingle(keyblob, keyblobsize, &mech, in_data, in_data_len,
+-                          out_data, out_data_len, ep11_data->target);
++                          out_data, out_data_len, target_info->target);
+     RETRY_END(rc, tokdata, session)
+     if (rc != CKR_OK) {
+         rc = ep11_error_to_pkcs11_error(rc, session);
+@@ -4563,7 +4645,6 @@ CK_RV token_specific_rsa_verify(STDLL_TokData_t *tokdata, SESSION *session,
+                                 CK_BYTE *signature, CK_ULONG sig_len,
+                                 OBJECT *key_obj)
+ {
+-    ep11_private_data_t *ep11_data = tokdata->private_data;
+     CK_RV rc;
+     CK_BYTE *spki;
+     size_t spki_len = 0;
+@@ -4579,9 +4660,9 @@ CK_RV token_specific_rsa_verify(STDLL_TokData_t *tokdata, SESSION *session,
+     mech.pParameter = NULL;
+     mech.ulParameterLen = 0;
+ 
+-    RETRY_START
++    RETRY_START(rc, tokdata)
+     rc = dll_m_VerifySingle(spki, spki_len, &mech, in_data, in_data_len,
+-                            signature, sig_len, ep11_data->target);
++                            signature, sig_len, target_info->target);
+     RETRY_END(rc, tokdata, session)
+     if (rc != CKR_OK) {
+         rc = ep11_error_to_pkcs11_error(rc, session);
+@@ -4598,7 +4679,6 @@ CK_RV token_specific_rsa_pss_sign(STDLL_TokData_t *tokdata, SESSION *session,
+                                   CK_BYTE *in_data, CK_ULONG in_data_len,
+                                   CK_BYTE *sig, CK_ULONG *sig_len)
+ {
+-    ep11_private_data_t *ep11_data = tokdata->private_data;
+     CK_RV rc;
+     size_t keyblobsize = 0;
+     CK_BYTE *keyblob;
+@@ -4616,9 +4696,9 @@ CK_RV token_specific_rsa_pss_sign(STDLL_TokData_t *tokdata, SESSION *session,
+     mech.ulParameterLen = ctx->mech.ulParameterLen;
+     mech.pParameter = ctx->mech.pParameter;
+ 
+-    RETRY_START
++    RETRY_START(rc, tokdata)
+     rc = dll_m_SignSingle(keyblob, keyblobsize, &mech, in_data, in_data_len,
+-                          sig, sig_len, ep11_data->target);
++                          sig, sig_len, target_info->target);
+     RETRY_END(rc, tokdata, session)
+     if (rc != CKR_OK) {
+         rc = ep11_error_to_pkcs11_error(rc, session);
+@@ -4638,7 +4718,6 @@ CK_RV token_specific_rsa_pss_verify(STDLL_TokData_t *tokdata, SESSION *session,
+                                     CK_BYTE *in_data, CK_ULONG in_data_len,
+                                     CK_BYTE *signature, CK_ULONG sig_len)
+ {
+-    ep11_private_data_t *ep11_data = tokdata->private_data;
+     CK_RV rc;
+     CK_BYTE *spki;
+     size_t spki_len = 0;
+@@ -4656,9 +4735,9 @@ CK_RV token_specific_rsa_pss_verify(STDLL_TokData_t *tokdata, SESSION *session,
+     mech.ulParameterLen = ctx->mech.ulParameterLen;
+     mech.pParameter = ctx->mech.pParameter;
+ 
+-    RETRY_START
++    RETRY_START(rc, tokdata)
+     rc = dll_m_VerifySingle(spki, spki_len, &mech, in_data, in_data_len,
+-                            signature, sig_len, ep11_data->target);
++                            signature, sig_len, target_info->target);
+     RETRY_END(rc, tokdata, session)
+     if (rc != CKR_OK) {
+         rc = ep11_error_to_pkcs11_error(rc, session);
+@@ -4678,7 +4757,6 @@ CK_RV token_specific_ec_sign(STDLL_TokData_t *tokdata, SESSION  *session,
+                              CK_BYTE *out_data, CK_ULONG *out_data_len,
+                              OBJECT *key_obj )
+ {
+-    ep11_private_data_t *ep11_data = tokdata->private_data;
+     SIGN_VERIFY_CONTEXT *ctx = &(session->sign_ctx);
+     CK_RV rc;
+     size_t keyblobsize = 0;
+@@ -4707,9 +4785,9 @@ CK_RV token_specific_ec_sign(STDLL_TokData_t *tokdata, SESSION  *session,
+     mech.pParameter = NULL;
+     mech.ulParameterLen = 0;
+ 
+-    RETRY_START
++    RETRY_START(rc, tokdata)
+     rc = dll_m_SignSingle(keyblob, keyblobsize, &mech, in_data, in_data_len,
+-                          out_data, out_data_len, ep11_data->target);
++                          out_data, out_data_len, target_info->target);
+     RETRY_END(rc, tokdata, session)
+     if (rc != CKR_OK) {
+         rc = ep11_error_to_pkcs11_error(rc, session);
+@@ -4728,7 +4806,6 @@ CK_RV token_specific_ec_verify(STDLL_TokData_t *tokdata, SESSION  *session,
+                                CK_BYTE *out_data, CK_ULONG out_data_len,
+                                OBJECT *key_obj )
+ {
+-    ep11_private_data_t *ep11_data = tokdata->private_data;
+     SIGN_VERIFY_CONTEXT *ctx = &(session->verify_ctx);
+     CK_RV rc;
+     CK_BYTE *spki;
+@@ -4757,9 +4834,9 @@ CK_RV token_specific_ec_verify(STDLL_TokData_t *tokdata, SESSION  *session,
+     mech.pParameter = NULL;
+     mech.ulParameterLen = 0;
+ 
+-    RETRY_START
++    RETRY_START(rc, tokdata)
+     rc = dll_m_VerifySingle(spki, spki_len, &mech, in_data, in_data_len,
+-                            out_data, out_data_len, ep11_data->target);
++                            out_data, out_data_len, target_info->target);
+     RETRY_END(rc, tokdata, session)
+     if (rc != CKR_OK) {
+         rc = ep11_error_to_pkcs11_error(rc, session);
+@@ -4784,7 +4861,6 @@ CK_RV token_specific_reencrypt_single(STDLL_TokData_t *tokdata,
+                                       CK_BYTE *in_data, CK_ULONG in_data_len,
+                                       CK_BYTE *out_data, CK_ULONG *out_data_len)
+ {
+-    ep11_private_data_t *ep11_data = tokdata->private_data;
+     CK_RV rc;
+     CK_BYTE *decr_key, *encr_key;
+     size_t decr_key_len = 0, encr_key_len = 0;
+@@ -4813,10 +4889,10 @@ CK_RV token_specific_reencrypt_single(STDLL_TokData_t *tokdata,
+         return rc;
+     }
+ 
+-    RETRY_START
++    RETRY_START(rc, tokdata)
+     rc = dll_m_ReencryptSingle(decr_key, decr_key_len, encr_key, encr_key_len,
+                                decr_mech, encr_mech, in_data, in_data_len,
+-                               out_data, out_data_len, ep11_data->target);
++                               out_data, out_data_len, target_info->target);
+     RETRY_END(rc, tokdata, session)
+     if (rc != CKR_OK) {
+         rc = ep11_error_to_pkcs11_error(rc, session);
+@@ -4892,7 +4968,6 @@ CK_RV ep11tok_derive_key(STDLL_TokData_t * tokdata, SESSION * session,
+                          CK_OBJECT_HANDLE_PTR handle, CK_ATTRIBUTE_PTR attrs,
+                          CK_ULONG attrs_len)
+ {
+-    ep11_private_data_t *ep11_data = tokdata->private_data;
+     CK_RV rc;
+     CK_BYTE *keyblob;
+     size_t keyblobsize;
+@@ -4920,6 +4995,8 @@ CK_RV ep11tok_derive_key(STDLL_TokData_t * tokdata, SESSION * session,
+     CK_ULONG privlen;
+     int curve_type;
+     CK_BBOOL allocated = FALSE;
++    ep11_target_info_t* target_info;
++    CK_ULONG used_firmware_API_version;
+ 
+     memset(newblob, 0, sizeof(newblob));
+ 
+@@ -5009,7 +5086,15 @@ CK_RV ep11tok_derive_key(STDLL_TokData_t * tokdata, SESSION * session,
+          * then we can pass the mechanism parameters as-is, otherwise we still
+          * need to use the old way.
+          */
+-        if (ep11_data->used_firmware_API_version <= 2) {
++        target_info = get_target_info(tokdata);
++        if (target_info == NULL)
++            return CKR_FUNCTION_FAILED;
++
++        used_firmware_API_version = target_info->used_firmware_API_version;
++
++        put_target_info(tokdata, target_info);
++
++        if (used_firmware_API_version <= 2) {
+             if (ecdh1_parms->kdf != CKD_NULL) {
+                 TRACE_ERROR("%s KDF for CKM_ECDH1_DERIVE not supported: %lu\n",
+                             __func__, ecdh1_parms->kdf);
+@@ -5133,11 +5218,11 @@ CK_RV ep11tok_derive_key(STDLL_TokData_t * tokdata, SESSION * session,
+     ep11_get_pin_blob(ep11_session, ep11_is_session_object(attrs, attrs_len),
+                       &ep11_pin_blob, &ep11_pin_blob_len);
+ 
+-    RETRY_START
++    RETRY_START(rc, tokdata)
+         rc =
+         dll_m_DeriveKey(mech, new_attrs2, new_attrs2_len, keyblob, keyblobsize,
+                         NULL, 0, ep11_pin_blob, ep11_pin_blob_len, newblob,
+-                        &newblobsize, csum, &cslen, ep11_data->target);
++                        &newblobsize, csum, &cslen, target_info->target);
+     RETRY_END(rc, tokdata, session)
+ 
+     if (rc != CKR_OK) {
+@@ -5231,7 +5316,6 @@ static CK_RV dh_generate_keypair(STDLL_TokData_t * tokdata,
+                                  CK_ULONG ulPrivateKeyAttributeCount,
+                                  CK_SESSION_HANDLE h)
+ {
+-    ep11_private_data_t *ep11_data = tokdata->private_data;
+     CK_RV rc;
+     CK_BYTE publblob[MAX_BLOBSIZE];
+     size_t publblobsize = sizeof(publblob);
+@@ -5418,13 +5502,13 @@ static CK_RV dh_generate_keypair(STDLL_TokData_t * tokdata,
+                                                  ulPrivateKeyAttributeCount)),
+                       &ep11_pin_blob, &ep11_pin_blob_len);
+ 
+-    RETRY_START
++    RETRY_START(rc, tokdata)
+         rc = dll_m_GenerateKeyPair(pMechanism,
+                                    new_publ_attrs, new_publ_attrs_len,
+                                    new_priv_attrs, new_priv_attrs_len,
+                                    ep11_pin_blob, ep11_pin_blob_len,
+                                    privblob, &privblobsize,
+-                                   publblob, &publblobsize, ep11_data->target);
++                                   publblob, &publblobsize, target_info->target);
+     RETRY_END(rc, tokdata, sess)
+ 
+     if (rc != CKR_OK) {
+@@ -5543,7 +5627,6 @@ static CK_RV dsa_generate_keypair(STDLL_TokData_t * tokdata,
+                                   CK_ULONG ulPrivateKeyAttributeCount,
+                                   CK_SESSION_HANDLE h)
+ {
+-    ep11_private_data_t *ep11_data = tokdata->private_data;
+     CK_RV rc;
+     CK_BYTE publblob[MAX_BLOBSIZE];
+     size_t publblobsize = sizeof(publblob);
+@@ -5752,13 +5835,13 @@ static CK_RV dsa_generate_keypair(STDLL_TokData_t * tokdata,
+                                                  ulPrivateKeyAttributeCount)),
+                       &ep11_pin_blob, &ep11_pin_blob_len);
+ 
+-    RETRY_START
++    RETRY_START(rc, tokdata)
+         rc = dll_m_GenerateKeyPair(pMechanism,
+                                    new_publ_attrs2, new_publ_attrs2_len,
+                                    new_priv_attrs2, new_priv_attrs2_len,
+                                    ep11_pin_blob, ep11_pin_blob_len, privblob,
+                                    &privblobsize, publblob, &publblobsize,
+-                                   ep11_data->target);
++                                   target_info->target);
+     RETRY_END(rc, tokdata, sess)
+ 
+     if (rc != CKR_OK) {
+@@ -5866,7 +5949,6 @@ static CK_RV rsa_ec_generate_keypair(STDLL_TokData_t * tokdata,
+                                      CK_ULONG ulPrivateKeyAttributeCount,
+                                      CK_SESSION_HANDLE h)
+ {
+-    ep11_private_data_t *ep11_data = tokdata->private_data;
+     CK_RV rc;
+     CK_ATTRIBUTE *attr = NULL;
+     CK_ATTRIBUTE *n_attr = NULL;
+@@ -5967,13 +6049,13 @@ static CK_RV rsa_ec_generate_keypair(STDLL_TokData_t * tokdata,
+                                                  ulPrivateKeyAttributeCount)),
+                       &ep11_pin_blob, &ep11_pin_blob_len);
+ 
+-    RETRY_START
++    RETRY_START(rc, tokdata)
+         rc = dll_m_GenerateKeyPair(pMechanism,
+                                    new_publ_attrs2, new_publ_attrs2_len,
+                                    new_priv_attrs2, new_priv_attrs2_len,
+                                    ep11_pin_blob, ep11_pin_blob_len,
+                                    privkey_blob, &privkey_blob_len, spki,
+-                                   &spki_len, ep11_data->target);
++                                   &spki_len, target_info->target);
+     RETRY_END(rc, tokdata, sess)
+     if (rc != CKR_OK) {
+         rc = ep11_error_to_pkcs11_error(rc, sess);
+@@ -6225,7 +6307,6 @@ static CK_RV ibm_dilithium_generate_keypair(STDLL_TokData_t * tokdata,
+                                      CK_ULONG ulPrivateKeyAttributeCount,
+                                      CK_SESSION_HANDLE h)
+ {
+-    ep11_private_data_t *ep11_data = tokdata->private_data;
+     CK_RV rc;
+     CK_ATTRIBUTE *attr = NULL;
+     CK_BYTE privkey_blob[MAX_BLOBSIZE];
+@@ -6308,13 +6389,13 @@ static CK_RV ibm_dilithium_generate_keypair(STDLL_TokData_t * tokdata,
+                                                  ulPrivateKeyAttributeCount)),
+                       &ep11_pin_blob, &ep11_pin_blob_len);
+ 
+-    RETRY_START
++    RETRY_START(rc, tokdata)
+         rc = dll_m_GenerateKeyPair(pMechanism,
+                                    new_publ_attrs2, new_publ_attrs2_len,
+                                    new_priv_attrs2, new_priv_attrs2_len,
+                                    ep11_pin_blob, ep11_pin_blob_len,
+                                    privkey_blob, &privkey_blob_len, spki,
+-                                   &spki_len, ep11_data->target);
++                                   &spki_len, target_info->target);
+     RETRY_END(rc, tokdata, sess)
+     if (rc != CKR_OK) {
+         rc = ep11_error_to_pkcs11_error(rc, sess);
+@@ -6914,7 +6995,6 @@ CK_RV ep11tok_sign_init(STDLL_TokData_t * tokdata, SESSION * session,
+                         CK_MECHANISM * mech, CK_BBOOL recover_mode,
+                         CK_OBJECT_HANDLE key)
+ {
+-    ep11_private_data_t *ep11_data = tokdata->private_data;
+     CK_RV rc;
+     size_t keyblobsize = 0;
+     CK_BYTE *keyblob;
+@@ -6980,9 +7060,9 @@ CK_RV ep11tok_sign_init(STDLL_TokData_t * tokdata, SESSION * session,
+         goto done;
+     }
+ 
+-    RETRY_START
++    RETRY_START(rc, tokdata)
+         rc = dll_m_SignInit(ep11_sign_state, &ep11_sign_state_l,
+-                            mech, keyblob, keyblobsize, ep11_data->target);
++                            mech, keyblob, keyblobsize, target_info->target);
+     RETRY_END(rc, tokdata, session)
+ 
+     if (rc != CKR_OK) {
+@@ -7017,7 +7097,6 @@ CK_RV ep11tok_sign(STDLL_TokData_t * tokdata, SESSION * session,
+                    CK_ULONG in_data_len, CK_BYTE * signature,
+                    CK_ULONG * sig_len)
+ {
+-    ep11_private_data_t *ep11_data = tokdata->private_data;
+     CK_RV rc;
+     SIGN_VERIFY_CONTEXT *ctx = &session->sign_ctx;
+     size_t keyblobsize = 0;
+@@ -7049,9 +7128,9 @@ CK_RV ep11tok_sign(STDLL_TokData_t * tokdata, SESSION * session,
+         goto done; /* no ep11 fallback possible */
+     }
+ 
+-    RETRY_START
++    RETRY_START(rc, tokdata)
+         rc = dll_m_Sign(ctx->context, ctx->context_len, in_data, in_data_len,
+-                        signature, sig_len, ep11_data->target);
++                        signature, sig_len, target_info->target);
+     RETRY_END(rc, tokdata, session)
+ 
+     if (rc != CKR_OK) {
+@@ -7073,7 +7152,6 @@ done:
+ CK_RV ep11tok_sign_update(STDLL_TokData_t * tokdata, SESSION * session,
+                           CK_BYTE * in_data, CK_ULONG in_data_len)
+ {
+-    ep11_private_data_t *ep11_data = tokdata->private_data;
+     CK_RV rc;
+     SIGN_VERIFY_CONTEXT *ctx = &session->sign_ctx;
+     size_t keyblobsize = 0;
+@@ -7095,9 +7173,9 @@ CK_RV ep11tok_sign_update(STDLL_TokData_t * tokdata, SESSION * session,
+         return rc;
+     }
+ 
+-    RETRY_START
++    RETRY_START(rc, tokdata)
+         rc = dll_m_SignUpdate(ctx->context, ctx->context_len, in_data,
+-                              in_data_len, ep11_data->target);
++                              in_data_len, target_info->target);
+     RETRY_END(rc, tokdata, session)
+ 
+     if (rc != CKR_OK) {
+@@ -7120,7 +7198,6 @@ CK_RV ep11tok_sign_final(STDLL_TokData_t * tokdata, SESSION * session,
+                          CK_BBOOL length_only, CK_BYTE * signature,
+                          CK_ULONG * sig_len)
+ {
+-    ep11_private_data_t *ep11_data = tokdata->private_data;
+     CK_RV rc;
+     SIGN_VERIFY_CONTEXT *ctx = &session->sign_ctx;
+     size_t keyblobsize = 0;
+@@ -7139,9 +7216,9 @@ CK_RV ep11tok_sign_final(STDLL_TokData_t * tokdata, SESSION * session,
+         return rc;
+     }
+ 
+-    RETRY_START
++    RETRY_START(rc, tokdata)
+         rc = dll_m_SignFinal(ctx->context, ctx->context_len, signature, sig_len,
+-                             ep11_data->target);
++                             target_info->target);
+     RETRY_END(rc, tokdata, session)
+ 
+     if (rc != CKR_OK) {
+@@ -7169,7 +7246,6 @@ CK_RV ep11tok_sign_single(STDLL_TokData_t *tokdata, SESSION *session,
+     size_t keyblobsize = 0;
+     CK_BYTE *keyblob;
+     OBJECT *key_obj = NULL;
+-    ep11_private_data_t *ep11_data = tokdata->private_data;
+ 
+     UNUSED(length_only);
+ 
+@@ -7186,9 +7262,9 @@ CK_RV ep11tok_sign_single(STDLL_TokData_t *tokdata, SESSION *session,
+         goto done;
+     }
+ 
+-    RETRY_START
++    RETRY_START(rc, tokdata)
+     rc = dll_m_SignSingle(keyblob, keyblobsize, mech, in_data, in_data_len,
+-                          signature, sig_len, ep11_data->target);
++                          signature, sig_len, target_info->target);
+     RETRY_END(rc, tokdata, session)
+     if (rc != CKR_OK) {
+         rc = ep11_error_to_pkcs11_error(rc, session);
+@@ -7209,7 +7285,6 @@ CK_RV ep11tok_verify_init(STDLL_TokData_t * tokdata, SESSION * session,
+                           CK_MECHANISM * mech, CK_BBOOL recover_mode,
+                           CK_OBJECT_HANDLE key)
+ {
+-    ep11_private_data_t *ep11_data = tokdata->private_data;
+     CK_RV rc;
+     CK_BYTE *spki;
+     size_t spki_len = 0;
+@@ -7285,9 +7360,9 @@ CK_RV ep11tok_verify_init(STDLL_TokData_t * tokdata, SESSION * session,
+         goto done;
+     }
+ 
+-    RETRY_START
++    RETRY_START(rc, tokdata)
+         rc = dll_m_VerifyInit(ep11_sign_state, &ep11_sign_state_l, mech,
+-                              spki, spki_len, ep11_data->target);
++                              spki, spki_len, target_info->target);
+     RETRY_END(rc, tokdata, session)
+ 
+     if (rc != CKR_OK) {
+@@ -7320,7 +7395,6 @@ CK_RV ep11tok_verify(STDLL_TokData_t * tokdata, SESSION * session,
+                      CK_BYTE * in_data, CK_ULONG in_data_len,
+                      CK_BYTE * signature, CK_ULONG sig_len)
+ {
+-    ep11_private_data_t *ep11_data = tokdata->private_data;
+     CK_RV rc;
+     SIGN_VERIFY_CONTEXT *ctx = &session->verify_ctx;
+     size_t keyblobsize = 0;
+@@ -7353,9 +7427,9 @@ CK_RV ep11tok_verify(STDLL_TokData_t * tokdata, SESSION * session,
+         goto done; /* no ep11 fallback possible */
+     }
+ 
+-    RETRY_START
++    RETRY_START(rc, tokdata)
+         rc = dll_m_Verify(ctx->context, ctx->context_len, in_data, in_data_len,
+-                          signature, sig_len, ep11_data->target);
++                          signature, sig_len, target_info->target);
+     RETRY_END(rc, tokdata, session)
+ 
+     if (rc != CKR_OK) {
+@@ -7377,7 +7451,6 @@ done:
+ CK_RV ep11tok_verify_update(STDLL_TokData_t * tokdata, SESSION * session,
+                             CK_BYTE * in_data, CK_ULONG in_data_len)
+ {
+-    ep11_private_data_t *ep11_data = tokdata->private_data;
+     CK_RV rc;
+     SIGN_VERIFY_CONTEXT *ctx = &session->verify_ctx;
+     size_t keyblobsize = 0;
+@@ -7399,9 +7472,9 @@ CK_RV ep11tok_verify_update(STDLL_TokData_t * tokdata, SESSION * session,
+         return rc;
+     }
+ 
+-    RETRY_START
++    RETRY_START(rc, tokdata)
+         rc = dll_m_VerifyUpdate(ctx->context, ctx->context_len, in_data,
+-                                in_data_len, ep11_data->target);
++                                in_data_len, target_info->target);
+     RETRY_END(rc, tokdata, session)
+ 
+     if (rc != CKR_OK) {
+@@ -7423,7 +7496,6 @@ done:
+ CK_RV ep11tok_verify_final(STDLL_TokData_t * tokdata, SESSION * session,
+                            CK_BYTE * signature, CK_ULONG sig_len)
+ {
+-    ep11_private_data_t *ep11_data = tokdata->private_data;
+     CK_RV rc;
+     SIGN_VERIFY_CONTEXT *ctx = &session->verify_ctx;
+     size_t keyblobsize = 0;
+@@ -7442,9 +7514,9 @@ CK_RV ep11tok_verify_final(STDLL_TokData_t * tokdata, SESSION * session,
+         return rc;
+     }
+ 
+-    RETRY_START
++    RETRY_START(rc, tokdata)
+         rc = dll_m_VerifyFinal(ctx->context, ctx->context_len, signature,
+-                               sig_len, ep11_data->target);
++                               sig_len, target_info->target);
+     RETRY_END(rc, tokdata, session)
+ 
+     if (rc != CKR_OK) {
+@@ -7471,7 +7543,6 @@ CK_RV ep11tok_verify_single(STDLL_TokData_t *tokdata, SESSION *session,
+     CK_BYTE *spki;
+     size_t spki_len = 0;
+     OBJECT *key_obj = NULL;
+-    ep11_private_data_t *ep11_data = tokdata->private_data;
+ 
+     rc = h_opaque_2_blob(tokdata, key, &spki, &spki_len, &key_obj, READ_LOCK);
+     if (rc != CKR_OK) {
+@@ -7495,9 +7566,9 @@ CK_RV ep11tok_verify_single(STDLL_TokData_t *tokdata, SESSION *session,
+         goto done;
+     }
+ 
+-    RETRY_START
++    RETRY_START(rc, tokdata)
+     rc = dll_m_VerifySingle(spki, spki_len, mech, in_data, in_data_len,
+-                            signature, sig_len, ep11_data->target);
++                            signature, sig_len, target_info->target);
+     RETRY_END(rc, tokdata, session)
+     if (rc != CKR_OK) {
+         rc = ep11_error_to_pkcs11_error(rc, session);
+@@ -7517,7 +7588,6 @@ CK_RV ep11tok_decrypt_final(STDLL_TokData_t * tokdata, SESSION * session,
+                             CK_BYTE_PTR output_part,
+                             CK_ULONG_PTR p_output_part_len)
+ {
+-    ep11_private_data_t *ep11_data = tokdata->private_data;
+     CK_RV rc = CKR_OK;
+     ENCR_DECR_CONTEXT *ctx = &session->decr_ctx;
+     CK_BBOOL length_only = (output_part == NULL ? CK_TRUE : CK_FALSE);
+@@ -7538,10 +7608,10 @@ CK_RV ep11tok_decrypt_final(STDLL_TokData_t * tokdata, SESSION * session,
+         return rc;
+     }
+ 
+-    RETRY_START
++    RETRY_START(rc, tokdata)
+         rc = dll_m_DecryptFinal(ctx->context, ctx->context_len,
+                                 output_part, p_output_part_len,
+-                                ep11_data->target);
++                                target_info->target);
+     RETRY_END(rc, tokdata, session)
+ 
+     if (rc != CKR_OK) {
+@@ -7564,7 +7634,6 @@ CK_RV ep11tok_decrypt(STDLL_TokData_t * tokdata, SESSION * session,
+                       CK_BYTE_PTR input_data, CK_ULONG input_data_len,
+                       CK_BYTE_PTR output_data, CK_ULONG_PTR p_output_data_len)
+ {
+-    ep11_private_data_t *ep11_data = tokdata->private_data;
+     CK_RV rc = CKR_OK;
+     ENCR_DECR_CONTEXT *ctx = &session->decr_ctx;
+     CK_BBOOL length_only = (output_data == NULL ? CK_TRUE : CK_FALSE);
+@@ -7586,10 +7655,10 @@ CK_RV ep11tok_decrypt(STDLL_TokData_t * tokdata, SESSION * session,
+         return rc;
+     }
+ 
+-    RETRY_START
++    RETRY_START(rc, tokdata)
+         rc = dll_m_Decrypt(ctx->context, ctx->context_len, input_data,
+                            input_data_len, output_data, p_output_data_len,
+-                           ep11_data->target);
++                           target_info->target);
+     RETRY_END(rc, tokdata, session)
+ 
+     if (rc != CKR_OK) {
+@@ -7613,7 +7682,6 @@ CK_RV ep11tok_decrypt_update(STDLL_TokData_t * tokdata, SESSION * session,
+                              CK_BYTE_PTR output_part,
+                              CK_ULONG_PTR p_output_part_len)
+ {
+-    ep11_private_data_t *ep11_data = tokdata->private_data;
+     CK_RV rc = CKR_OK;
+     ENCR_DECR_CONTEXT *ctx = &session->decr_ctx;
+     CK_BBOOL length_only = (output_part == NULL ? CK_TRUE : CK_FALSE);
+@@ -7640,10 +7708,10 @@ CK_RV ep11tok_decrypt_update(STDLL_TokData_t * tokdata, SESSION * session,
+         return rc;
+     }
+ 
+-    RETRY_START
++    RETRY_START(rc, tokdata)
+         rc = dll_m_DecryptUpdate(ctx->context, ctx->context_len,
+                                  input_part, input_part_len, output_part,
+-                                 p_output_part_len, ep11_data->target);
++                                 p_output_part_len, target_info->target);
+     RETRY_END(rc, tokdata, session)
+ 
+     if (rc != CKR_OK) {
+@@ -7671,7 +7739,6 @@ CK_RV ep11tok_decrypt_single(STDLL_TokData_t *tokdata, SESSION *session,
+     size_t keyblobsize = 0;
+     CK_BYTE *keyblob;
+     OBJECT *key_obj = NULL;
+-    ep11_private_data_t *ep11_data = tokdata->private_data;
+ 
+     UNUSED(length_only);
+ 
+@@ -7688,10 +7755,10 @@ CK_RV ep11tok_decrypt_single(STDLL_TokData_t *tokdata, SESSION *session,
+         goto done;
+     }
+ 
+-    RETRY_START
++    RETRY_START(rc, tokdata)
+     rc = dll_m_DecryptSingle(keyblob, keyblobsize, mech, input_data,
+                              input_data_len, output_data, p_output_data_len,
+-                             ep11_data->target);
++                             target_info->target);
+     RETRY_END(rc, tokdata, session)
+     if (rc != CKR_OK) {
+         rc = ep11_error_to_pkcs11_error(rc, session);
+@@ -7711,7 +7778,6 @@ CK_RV ep11tok_encrypt_final(STDLL_TokData_t * tokdata, SESSION * session,
+                             CK_BYTE_PTR output_part,
+                             CK_ULONG_PTR p_output_part_len)
+ {
+-    ep11_private_data_t *ep11_data = tokdata->private_data;
+     CK_RV rc = CKR_OK;
+     ENCR_DECR_CONTEXT *ctx = &session->encr_ctx;
+     CK_BBOOL length_only = (output_part == NULL ? CK_TRUE : CK_FALSE);
+@@ -7732,10 +7798,10 @@ CK_RV ep11tok_encrypt_final(STDLL_TokData_t * tokdata, SESSION * session,
+         return rc;
+     }
+ 
+-    RETRY_START
++    RETRY_START(rc, tokdata)
+         rc = dll_m_EncryptFinal(ctx->context, ctx->context_len,
+                                 output_part, p_output_part_len,
+-                                ep11_data->target);
++                                target_info->target);
+     RETRY_END(rc, tokdata, session)
+ 
+     if (rc != CKR_OK) {
+@@ -7758,7 +7824,6 @@ CK_RV ep11tok_encrypt(STDLL_TokData_t * tokdata, SESSION * session,
+                       CK_BYTE_PTR input_data, CK_ULONG input_data_len,
+                       CK_BYTE_PTR output_data, CK_ULONG_PTR p_output_data_len)
+ {
+-    ep11_private_data_t *ep11_data = tokdata->private_data;
+     CK_RV rc = CKR_OK;
+     ENCR_DECR_CONTEXT *ctx = &session->encr_ctx;
+     CK_BBOOL length_only = (output_data == NULL ? CK_TRUE : CK_FALSE);
+@@ -7780,10 +7845,10 @@ CK_RV ep11tok_encrypt(STDLL_TokData_t * tokdata, SESSION * session,
+         return rc;
+     }
+ 
+-    RETRY_START
++    RETRY_START(rc, tokdata)
+         rc = dll_m_Encrypt(ctx->context, ctx->context_len, input_data,
+                            input_data_len, output_data, p_output_data_len,
+-                           ep11_data->target);
++                           target_info->target);
+     RETRY_END(rc, tokdata, session)
+ 
+     if (rc != CKR_OK) {
+@@ -7807,7 +7872,6 @@ CK_RV ep11tok_encrypt_update(STDLL_TokData_t * tokdata, SESSION * session,
+                              CK_BYTE_PTR output_part,
+                              CK_ULONG_PTR p_output_part_len)
+ {
+-    ep11_private_data_t *ep11_data = tokdata->private_data;
+     CK_RV rc = CKR_OK;
+     ENCR_DECR_CONTEXT *ctx = &session->encr_ctx;
+     CK_BBOOL length_only = (output_part == NULL ? CK_TRUE : CK_FALSE);
+@@ -7834,10 +7898,10 @@ CK_RV ep11tok_encrypt_update(STDLL_TokData_t * tokdata, SESSION * session,
+         return rc;
+     }
+ 
+-    RETRY_START
++    RETRY_START(rc, tokdata)
+         rc = dll_m_EncryptUpdate(ctx->context, ctx->context_len,
+                                  input_part, input_part_len, output_part,
+-                                 p_output_part_len, ep11_data->target);
++                                 p_output_part_len, target_info->target);
+     RETRY_END(rc, tokdata, session)
+ 
+     if (rc != CKR_OK) {
+@@ -7865,7 +7929,6 @@ CK_RV ep11tok_encrypt_single(STDLL_TokData_t *tokdata, SESSION *session,
+     size_t keyblobsize = 0;
+     CK_BYTE *keyblob;
+     OBJECT *key_obj = NULL;
+-    ep11_private_data_t *ep11_data = tokdata->private_data;
+ 
+     UNUSED(length_only);
+ 
+@@ -7893,10 +7956,10 @@ CK_RV ep11tok_encrypt_single(STDLL_TokData_t *tokdata, SESSION *session,
+         goto done;
+     }
+ 
+-    RETRY_START
++    RETRY_START(rc, tokdata)
+     rc = dll_m_EncryptSingle(keyblob, keyblobsize, mech, input_data,
+                              input_data_len, output_data, p_output_data_len,
+-                             ep11_data->target);
++                             target_info->target);
+     RETRY_END(rc, tokdata, session)
+     if (rc != CKR_OK) {
+         rc = ep11_error_to_pkcs11_error(rc, session);
+@@ -7916,7 +7979,6 @@ static CK_RV ep11_ende_crypt_init(STDLL_TokData_t * tokdata, SESSION * session,
+                                   CK_MECHANISM_PTR mech, CK_OBJECT_HANDLE key,
+                                   int op)
+ {
+-    ep11_private_data_t *ep11_data = tokdata->private_data;
+     CK_RV rc = CKR_OK;
+     CK_BYTE *blob;
+     size_t blob_len = 0;
+@@ -7979,9 +8041,9 @@ static CK_RV ep11_ende_crypt_init(STDLL_TokData_t * tokdata, SESSION * session,
+ 
+     if (op == DECRYPT) {
+         ENCR_DECR_CONTEXT *ctx = &session->decr_ctx;
+-        RETRY_START
++        RETRY_START(rc, tokdata)
+             rc = dll_m_DecryptInit(ep11_state, &ep11_state_l, mech, blob,
+-                                   blob_len, ep11_data->target);
++                                   blob_len, target_info->target);
+         RETRY_END(rc, tokdata, session)
+         ctx->key = key;
+         ctx->active = TRUE;
+@@ -8012,9 +8074,9 @@ static CK_RV ep11_ende_crypt_init(STDLL_TokData_t * tokdata, SESSION * session,
+             goto error;
+         }
+ 
+-        RETRY_START
++        RETRY_START(rc, tokdata)
+             rc = dll_m_EncryptInit(ep11_state, &ep11_state_l, mech, blob,
+-                                   blob_len, ep11_data->target);
++                                   blob_len, target_info->target);
+         RETRY_END(rc, tokdata, session)
+         ctx->key = key;
+         ctx->active = TRUE;
+@@ -8092,7 +8154,6 @@ CK_RV ep11tok_wrap_key(STDLL_TokData_t * tokdata, SESSION * session,
+                        CK_OBJECT_HANDLE key, CK_BYTE_PTR wrapped_key,
+                        CK_ULONG_PTR p_wrapped_key_len)
+ {
+-    ep11_private_data_t *ep11_data = tokdata->private_data;
+     CK_RV rc;
+     CK_BYTE *wrapping_blob;
+     size_t wrapping_blob_len;
+@@ -8192,11 +8253,11 @@ CK_RV ep11tok_wrap_key(STDLL_TokData_t * tokdata, SESSION * session,
+      * (wrapping blob). The wrapped key can be processed by any PKCS11
+      * implementation.
+      */
+-    RETRY_START
++    RETRY_START(rc, tokdata)
+         rc =
+         dll_m_WrapKey(wrap_target_blob, wrap_target_blob_len, wrapping_blob,
+                       wrapping_blob_len, sign_blob, sign_blob_len, mech,
+-                      wrapped_key, p_wrapped_key_len, ep11_data->target);
++                      wrapped_key, p_wrapped_key_len, target_info->target);
+     RETRY_END(rc, tokdata, session)
+ 
+     if (rc != CKR_OK) {
+@@ -8228,7 +8289,6 @@ CK_RV ep11tok_unwrap_key(STDLL_TokData_t * tokdata, SESSION * session,
+                          CK_OBJECT_HANDLE wrapping_key,
+                          CK_OBJECT_HANDLE_PTR p_key)
+ {
+-    ep11_private_data_t *ep11_data = tokdata->private_data;
+     CK_RV rc;
+     CK_BYTE *wrapping_blob, *temp;
+     size_t wrapping_blob_len;
+@@ -8388,13 +8448,13 @@ CK_RV ep11tok_unwrap_key(STDLL_TokData_t * tokdata, SESSION * session,
+     /* we need a blob for the new key created by unwrapping,
+      * the wrapped key comes in BER
+      */
+-    RETRY_START
++    RETRY_START(rc, tokdata)
+         rc = dll_m_UnwrapKey(wrapped_key, wrapped_key_len, wrapping_blob,
+                              wrapping_blob_len, verifyblob, verifyblobsize,
+                              ep11_pin_blob,
+                              ep11_pin_blob_len, mech, new_attrs2, new_attrs2_len,
+                              keyblob, &keyblobsize, csum, &cslen,
+-                             ep11_data->target);
++                             target_info->target);
+     RETRY_END(rc, tokdata, session)
+ 
+     if (rc != CKR_OK) {
+@@ -8657,21 +8717,25 @@ CK_RV ep11tok_get_mechanism_list(STDLL_TokData_t * tokdata,
+                                  CK_MECHANISM_TYPE_PTR pMechanismList,
+                                  CK_ULONG_PTR pulCount)
+ {
+-    ep11_private_data_t *ep11_data = tokdata->private_data;
+     CK_RV rc = 0;
+     CK_ULONG counter = 0, size = 0;
+     CK_MECHANISM_TYPE_PTR mlist = NULL;
+     CK_ULONG i;
++    ep11_target_info_t* target_info;
++
++    target_info = get_target_info(tokdata);
++    if (target_info == NULL)
++        return CKR_FUNCTION_FAILED;
+ 
+     /* size querry */
+     if (pMechanismList == NULL) {
+         rc = dll_m_GetMechanismList(0, pMechanismList, pulCount,
+-                                    ep11_data->target);
++                                    target_info->target);
+         if (rc != CKR_OK) {
+             rc = ep11_error_to_pkcs11_error(rc, NULL);
+             TRACE_ERROR("%s bad rc=0x%lx from m_GetMechanismList() #1\n",
+                         __func__, rc);
+-            return rc;
++            goto out;
+         }
+ 
+         /* adjust the size according to the ban list,
+@@ -8693,16 +8757,16 @@ CK_RV ep11tok_get_mechanism_list(STDLL_TokData_t * tokdata,
+                                     sizeof(CK_MECHANISM_TYPE) * counter);
+             if (!mlist) {
+                 TRACE_ERROR("%s Memory allocation failed\n", __func__);
+-                return CKR_HOST_MEMORY;
++                rc = CKR_HOST_MEMORY;
++                goto out;
+             }
+-            rc = dll_m_GetMechanismList(0, mlist, &counter, ep11_data->target);
++            rc = dll_m_GetMechanismList(0, mlist, &counter, target_info->target);
+             if (rc != CKR_OK) {
+                 rc = ep11_error_to_pkcs11_error(rc, NULL);
+                 TRACE_ERROR("%s bad rc=0x%lx from m_GetMechanismList() #2\n",
+                             __func__, rc);
+-                free(mlist);
+                 if (rc != CKR_BUFFER_TOO_SMALL)
+-                    return rc;
++                    goto out;
+             }
+         } while (rc == CKR_BUFFER_TOO_SMALL);
+ 
+@@ -8722,12 +8786,12 @@ CK_RV ep11tok_get_mechanism_list(STDLL_TokData_t * tokdata,
+          * that comes as parameter, this is a 'reduced size',
+          * ep11 would complain about insufficient list size
+          */
+-        rc = dll_m_GetMechanismList(0, mlist, &counter, ep11_data->target);
++        rc = dll_m_GetMechanismList(0, mlist, &counter, target_info->target);
+         if (rc != CKR_OK) {
+             rc = ep11_error_to_pkcs11_error(rc, NULL);
+             TRACE_ERROR("%s bad rc=0x%lx from m_GetMechanismList() #3\n",
+                         __func__, rc);
+-            return rc;
++            goto out;
+         }
+ 
+         /*
+@@ -8744,17 +8808,17 @@ CK_RV ep11tok_get_mechanism_list(STDLL_TokData_t * tokdata,
+                                     sizeof(CK_MECHANISM_TYPE) * counter);
+             if (!mlist) {
+                 TRACE_ERROR("%s Memory allocation failed\n", __func__);
+-                return CKR_HOST_MEMORY;
++                rc = CKR_HOST_MEMORY;
++                goto out;
+             }
+             /* all the card has */
+-            rc = dll_m_GetMechanismList(0, mlist, &counter, ep11_data->target);
++            rc = dll_m_GetMechanismList(0, mlist, &counter, target_info->target);
+             if (rc != CKR_OK) {
+                 rc = ep11_error_to_pkcs11_error(rc, NULL);
+                 TRACE_ERROR("%s bad rc=0x%lx from m_GetMechanismList() #4\n",
+                             __func__, rc);
+-                free(mlist);
+                 if (rc != CKR_BUFFER_TOO_SMALL)
+-                    return rc;
++                    goto out;
+             }
+         } while (rc == CKR_BUFFER_TOO_SMALL);
+ 
+@@ -8775,8 +8839,10 @@ CK_RV ep11tok_get_mechanism_list(STDLL_TokData_t * tokdata,
+             rc = CKR_BUFFER_TOO_SMALL;
+     }
+ 
++out:
+     if (mlist)
+         free(mlist);
++    put_target_info(tokdata, target_info);
+     return rc;
+ }
+ 
+@@ -8790,6 +8856,8 @@ CK_RV ep11tok_is_mechanism_supported(STDLL_TokData_t *tokdata,
+     CK_BBOOL found = FALSE;
+     CK_ULONG i;
+     int status;
++    CK_RV rc = CKR_OK;
++    ep11_target_info_t* target_info;
+ 
+     for (i = 0; i < supported_mech_list_len; i++) {
+         if (type == ep11_supported_mech_list[i]) {
+@@ -8804,13 +8872,18 @@ CK_RV ep11tok_is_mechanism_supported(STDLL_TokData_t *tokdata,
+         return CKR_MECHANISM_INVALID;
+     }
+ 
++    target_info = get_target_info(tokdata);
++    if (target_info == NULL)
++        return CKR_FUNCTION_FAILED;
++
+     if (check_cps_for_mechanism(ep11_data->cp_config,
+-                                type, ep11_data->control_points,
+-                                ep11_data->control_points_len,
+-                                ep11_data->max_control_point_index) != CKR_OK) {
++                                type, target_info->control_points,
++                                target_info->control_points_len,
++                                target_info->max_control_point_index) != CKR_OK) {
+         TRACE_INFO("%s Mech '%s' banned due to control point\n",
+                                    __func__, ep11_get_ckm(type));
+-        return CKR_MECHANISM_INVALID;
++        rc = CKR_MECHANISM_INVALID;
++        goto out;
+     }
+ 
+     switch(type) {
+@@ -8840,14 +8913,17 @@ CK_RV ep11tok_is_mechanism_supported(STDLL_TokData_t *tokdata,
+         if (status == -1) {
+             TRACE_INFO("%s Mech '%s' banned due to mixed firmware versions\n",
+                         __func__, ep11_get_ckm(type));
+-            return CKR_MECHANISM_INVALID;
++            rc = CKR_MECHANISM_INVALID;
++            goto out;
+         }
+         break;
+ 
+     case CKM_RSA_PKCS_OAEP:
+         /* CKM_RSA_PKCS_OAEP is not supported with EP11 host library <= 1.3 */
+-        if (compare_ck_version(&ep11_data->ep11_lib_version, &ver1_3) <= 0)
+-            return CKR_MECHANISM_INVALID;
++        if (compare_ck_version(&ep11_data->ep11_lib_version, &ver1_3) <= 0) {
++            rc = CKR_MECHANISM_INVALID;
++            goto out;
++        }
+         break;
+ 
+     case CKM_IBM_SHA3_224:
+@@ -8863,7 +8939,8 @@ CK_RV ep11tok_is_mechanism_supported(STDLL_TokData_t *tokdata,
+         if (status != 1) {
+             TRACE_INFO("%s Mech '%s' banned due to mixed firmware versions\n",
+                                     __func__, ep11_get_ckm(type));
+-            return CKR_MECHANISM_INVALID;
++            rc = CKR_MECHANISM_INVALID;
++            goto out;
+         }
+         break;
+ 
+@@ -8876,7 +8953,8 @@ CK_RV ep11tok_is_mechanism_supported(STDLL_TokData_t *tokdata,
+         if (status != 1) {
+             TRACE_INFO("%s Mech '%s' banned due to mixed firmware versions\n",
+                                     __func__, ep11_get_ckm(type));
+-            return CKR_MECHANISM_INVALID;
++            rc = CKR_MECHANISM_INVALID;
++            goto out;
+         }
+         break;
+ 
+@@ -8887,7 +8965,8 @@ CK_RV ep11tok_is_mechanism_supported(STDLL_TokData_t *tokdata,
+         if (compare_ck_version(&ep11_data->ep11_lib_version, &ver3) < 0) {
+             TRACE_INFO("%s Mech '%s' banned due to host library version\n",
+                                     __func__, ep11_get_ckm(type));
+-            return CKR_MECHANISM_INVALID;
++            rc = CKR_MECHANISM_INVALID;
++            goto out;
+         }
+ 
+         status = check_required_versions(tokdata, edwards_req_versions,
+@@ -8895,7 +8974,8 @@ CK_RV ep11tok_is_mechanism_supported(STDLL_TokData_t *tokdata,
+         if (status != 1) {
+             TRACE_INFO("%s Mech '%s' banned due to mixed firmware versions\n",
+                                     __func__, ep11_get_ckm(type));
+-            return CKR_MECHANISM_INVALID;
++            rc = CKR_MECHANISM_INVALID;
++            goto out;
+         }
+         break;
+ 
+@@ -8903,14 +8983,16 @@ CK_RV ep11tok_is_mechanism_supported(STDLL_TokData_t *tokdata,
+         if (compare_ck_version(&ep11_data->ep11_lib_version, &ver3) <= 0) {
+             TRACE_INFO("%s Mech '%s' banned due to host library version\n",
+                                      __func__, ep11_get_ckm(type));
+-            return CKR_MECHANISM_INVALID;
++            rc = CKR_MECHANISM_INVALID;
++            goto out;
+         }
+         status = check_required_versions(tokdata, ibm_dilithium_req_versions,
+                                          NUM_DILITHIUM_REQ);
+         if (status != 1) {
+             TRACE_INFO("%s Mech '%s' banned due to mixed firmware versions\n",
+                                     __func__, ep11_get_ckm(type));
+-            return CKR_MECHANISM_INVALID;
++            rc = CKR_MECHANISM_INVALID;
++            goto out;
+         }
+         break;
+ 
+@@ -8919,19 +9001,23 @@ CK_RV ep11tok_is_mechanism_supported(STDLL_TokData_t *tokdata,
+             TRACE_INFO("%s Mech '%s' banned due to host library version\n",
+                                      __func__, ep11_get_ckm(type));
+ 
+-            return CKR_MECHANISM_INVALID;
++            rc = CKR_MECHANISM_INVALID;
++            goto out;
+         }
+         status = check_required_versions(tokdata, ibm_cpacf_wrap_req_versions,
+                                          NUM_CPACF_WRAP_REQ);
+         if (status != 1) {
+             TRACE_INFO("%s Mech '%s' banned due to mixed firmware versions\n",
+                                     __func__, ep11_get_ckm(type));
+-            return CKR_MECHANISM_INVALID;
++            rc = CKR_MECHANISM_INVALID;
++            goto out;
+         }
+         break;
+     }
+ 
+-    return CKR_OK;
++out:
++    put_target_info(tokdata, target_info);
++    return rc;
+ }
+ 
+ CK_RV ep11tok_is_mechanism_supported_ex(STDLL_TokData_t *tokdata,
+@@ -8976,9 +9062,9 @@ CK_RV ep11tok_get_mechanism_info(STDLL_TokData_t * tokdata,
+                                  CK_MECHANISM_TYPE type,
+                                  CK_MECHANISM_INFO_PTR pInfo)
+ {
+-    ep11_private_data_t *ep11_data = tokdata->private_data;
+     CK_RV rc;
+     int status;
++    ep11_target_info_t* target_info;
+ 
+     rc = ep11tok_is_mechanism_supported(tokdata, type);
+     if (rc != CKR_OK) {
+@@ -8987,7 +9073,14 @@ CK_RV ep11tok_get_mechanism_info(STDLL_TokData_t * tokdata,
+         return rc;
+     }
+ 
+-    rc = dll_m_GetMechanismInfo(0, type, pInfo, ep11_data->target);
++    target_info = get_target_info(tokdata);
++    if (target_info == NULL)
++        return CKR_FUNCTION_FAILED;
++
++    rc = dll_m_GetMechanismInfo(0, type, pInfo, target_info->target);
++
++    put_target_info(tokdata, target_info);
++
+     if (rc != CKR_OK) {
+         rc = ep11_error_to_pkcs11_error(rc, NULL);
+         TRACE_ERROR("%s m_GetMechanismInfo(0x%lx) failed with rc=0x%lx\n",
+@@ -10265,6 +10358,11 @@ static CK_RV generate_ep11_session_id(STDLL_TokData_t * tokdata,
+     CK_MECHANISM mech;
+     CK_ULONG len;
+     libica_sha_context_t ctx;
++    ep11_target_info_t* target_info;
++
++    target_info = get_target_info(tokdata);
++    if (target_info == NULL)
++        return CKR_FUNCTION_FAILED;
+ 
+     session_id_data.handle = session->handle;
+     gettimeofday(&session_id_data.timeofday, NULL);
+@@ -10286,7 +10384,9 @@ static CK_RV generate_ep11_session_id(STDLL_TokData_t * tokdata,
+         rc = dll_m_DigestSingle(&mech, (CK_BYTE_PTR)&session_id_data,
+                                 sizeof(session_id_data),
+                                 ep11_session->session_id, &len,
+-                                ep11_data->target);
++                                target_info->target);
++
++    put_target_info(tokdata, target_info);
+ 
+     if (rc != CKR_OK) {
+         rc = ep11_error_to_pkcs11_error(rc, session);
+@@ -10964,7 +11064,7 @@ static CK_RV get_card_type(uint_32 adapter, CK_ULONG *type)
+ 
+ typedef struct query_version
+ {
+-    ep11_private_data_t *ep11_data;
++    ep11_target_info_t *target_info;
+     CK_CHAR serialNumber[16];
+     CK_BBOOL first;
+     CK_BBOOL error;
+@@ -11005,7 +11105,7 @@ static CK_RV version_query_handler(uint_32 adapter, uint_32 domain,
+     }
+ 
+     /* Try to find existing version info for this card type */
+-    card_version = qv->ep11_data->card_versions;
++    card_version = qv->target_info->card_versions;
+     while (card_version != NULL) {
+         if (card_version->card_type == card_type)
+            break;
+@@ -11050,8 +11150,8 @@ static CK_RV version_query_handler(uint_32 adapter, uint_32 domain,
+         card_version->firmware_version = xcp_info.firmwareVersion;
+ #endif
+ 
+-        card_version->next = qv->ep11_data->card_versions;
+-        qv->ep11_data->card_versions = card_version;
++        card_version->next = qv->target_info->card_versions;
++        qv->target_info->card_versions = card_version;
+     } else {
+         /*
+          * Version info for this card type is already available, so check this
+@@ -11134,23 +11234,16 @@ static CK_RV ep11tok_get_ep11_library_version(CK_VERSION *lib_version)
+     return CKR_OK;
+ }
+ 
+-static CK_RV ep11tok_get_ep11_version(STDLL_TokData_t *tokdata)
++static CK_RV ep11tok_get_ep11_version(STDLL_TokData_t *tokdata,
++                                      ep11_target_info_t *target_info)
+ {
+     ep11_private_data_t *ep11_data = tokdata->private_data;
+     ep11_card_version_t *card_version;
+     query_version_t qv;
+     CK_RV rc;
+ 
+-    rc = ep11tok_get_ep11_library_version(&ep11_data->ep11_lib_version);
+-    if (rc != CKR_OK)
+-        return rc;
+-
+-    TRACE_INFO("%s Host library version: %d.%d\n", __func__,
+-               ep11_data->ep11_lib_version.major,
+-               ep11_data->ep11_lib_version.minor);
+-
+     memset(&qv, 0, sizeof(qv));
+-    qv.ep11_data = ep11_data;
++    qv.target_info = target_info;
+     qv.first = TRUE;
+ 
+     rc = handle_all_ep11_cards(&ep11_data->target_list, version_query_handler,
+@@ -11169,18 +11262,18 @@ static CK_RV ep11tok_get_ep11_version(STDLL_TokData_t *tokdata)
+         return CKR_DEVICE_ERROR;
+     }
+ 
+-    memcpy(ep11_data->serialNumber, qv.serialNumber,
+-           sizeof(ep11_data->serialNumber));
++    memcpy(target_info->serialNumber, qv.serialNumber,
++           sizeof(target_info->serialNumber));
+ 
+-    TRACE_INFO("%s Serial number: %.16s\n", __func__, ep11_data->serialNumber);
++    TRACE_INFO("%s Serial number: %.16s\n", __func__, target_info->serialNumber);
+ 
+     /* EP11 host lib version <= 2 only support API version 2 */
+     if (ep11_data->ep11_lib_version.major <= 2)
+-        ep11_data->used_firmware_API_version = 2;
++        target_info->used_firmware_API_version = 2;
+     else
+-        ep11_data->used_firmware_API_version = 0;
++        target_info->used_firmware_API_version = 0;
+ 
+-    card_version = ep11_data->card_versions;
++    card_version = target_info->card_versions;
+     while (card_version != NULL) {
+         TRACE_INFO("%s Card type: CEX%luP\n", __func__,
+                    card_version->card_type);
+@@ -11190,19 +11283,19 @@ static CK_RV ep11tok_get_ep11_version(STDLL_TokData_t *tokdata)
+                 card_version->firmware_version.major,
+                 card_version->firmware_version.minor);
+ 
+-        if (ep11_data->used_firmware_API_version == 0)
+-            ep11_data->used_firmware_API_version =
++        if (target_info->used_firmware_API_version == 0)
++            target_info->used_firmware_API_version =
+                                 card_version->firmware_API_version;
+         else
+-            ep11_data->used_firmware_API_version =
+-                                MIN(ep11_data->used_firmware_API_version,
++            target_info->used_firmware_API_version =
++                                MIN(target_info->used_firmware_API_version,
+                                     card_version->firmware_API_version);
+ 
+         card_version = card_version->next;
+     }
+ 
+     TRACE_INFO("%s Used Firmware API: %lu\n", __func__,
+-               ep11_data->used_firmware_API_version);
++               target_info->used_firmware_API_version);
+ 
+     return CKR_OK;
+ }
+@@ -11220,20 +11313,29 @@ static void free_card_versions(ep11_card_version_t *card_version)
+     }
+ }
+ 
+-void ep11tok_copy_firmware_info(STDLL_TokData_t *tokdata,
++CK_RV ep11tok_copy_firmware_info(STDLL_TokData_t *tokdata,
+                                  CK_TOKEN_INFO_PTR pInfo)
+ {
+     ep11_private_data_t *ep11_data = tokdata->private_data;
++    ep11_target_info_t* target_info;
++
++    target_info = get_target_info(tokdata);
++    if (target_info == NULL)
++        return CKR_FUNCTION_FAILED;
+ 
+     /*
+      * report the EP11 firmware version as hardware version, and
+      * the EP11 host library version as firmware version
+      */
+-    if (ep11_data->card_versions != NULL)
+-        pInfo->hardwareVersion = ep11_data->card_versions->firmware_version;
++    if (target_info->card_versions != NULL)
++        pInfo->hardwareVersion = target_info->card_versions->firmware_version;
+     pInfo->firmwareVersion = ep11_data->ep11_lib_version;
+-    memcpy(pInfo->serialNumber, ep11_data->serialNumber,
++    memcpy(pInfo->serialNumber, target_info->serialNumber,
+            sizeof(pInfo->serialNumber));
++
++    put_target_info(tokdata, target_info);
++
++    return CKR_OK;
+ }
+ 
+ /**
+@@ -11247,13 +11349,17 @@ static int check_required_versions(STDLL_TokData_t *tokdata,
+                                    const version_req_t req[],
+                                    CK_ULONG num_req)
+ {
+-    ep11_private_data_t *ep11_data = tokdata->private_data;
+     CK_ULONG i, max_card_type = 0, min_card_type = 0xFFFFFFFF;
+     CK_BBOOL req_not_fullfilled = CK_FALSE;
+     CK_BBOOL req_fullfilled = CK_FALSE;
+     ep11_card_version_t *card_version;
++    ep11_target_info_t* target_info;
+     int status;
+ 
++    target_info = get_target_info(tokdata);
++    if (target_info == NULL)
++        return CKR_FUNCTION_FAILED;
++
+     for (i = 0; i < num_req; i++) {
+         status = check_card_version(tokdata, req[i].card_type,
+                                            req[i].min_lib_version,
+@@ -11268,7 +11374,7 @@ static int check_required_versions(STDLL_TokData_t *tokdata,
+     }
+ 
+     /* Are card types < min_card_type present? */
+-    card_version = ep11_data->card_versions;
++    card_version = target_info->card_versions;
+     while (card_version != NULL) {
+          if (card_version->card_type < min_card_type)
+              req_not_fullfilled = CK_TRUE;
+@@ -11276,7 +11382,7 @@ static int check_required_versions(STDLL_TokData_t *tokdata,
+      }
+ 
+     /* Are card types > max_card_type present? */
+-    card_version = ep11_data->card_versions;
++    card_version = target_info->card_versions;
+     while (card_version != NULL) {
+         if (card_version->card_type > max_card_type) {
+             /*
+@@ -11285,9 +11391,10 @@ static int check_required_versions(STDLL_TokData_t *tokdata,
+               * So all others must also meet the version requirements or be
+               * not present.
+               */
++            status = 1;
+              if (req_not_fullfilled == CK_TRUE)
+-                 return -1;
+-             return 1;
++                 status = -1;
++             goto out;
+         }
+         card_version = card_version->next;
+     }
+@@ -11298,13 +11405,19 @@ static int check_required_versions(STDLL_TokData_t *tokdata,
+          * At least one don't meet the requirements, so all other must not
+          * fulfill the requirements, too, or are not present.
+          */
++        status = 0;
+         if (req_fullfilled == CK_TRUE)
+-                return -1;
+-        return 0;
++            status = -1;
++        goto out;
+     } else {
+         /* All of the cards that are present fulfill the requirements */
+-        return 1;
++        status = 1;
++        goto out;
+     }
++
++out:
++    put_target_info(tokdata, target_info);
++    return status;
+ }
+ 
+ /**
+@@ -11320,6 +11433,8 @@ static int check_card_version(STDLL_TokData_t *tokdata, CK_ULONG card_type,
+ {
+     ep11_private_data_t *ep11_data = tokdata->private_data;
+     ep11_card_version_t *card_version;
++    ep11_target_info_t* target_info;
++    int status = 1;
+ 
+     TRACE_DEBUG("%s checking versions for CEX%luP cards.\n", __func__, card_type);
+ 
+@@ -11331,21 +11446,28 @@ static int check_card_version(STDLL_TokData_t *tokdata, CK_ULONG card_type,
+         }
+     }
+ 
+-    card_version = ep11_data->card_versions;
++    target_info = get_target_info(tokdata);
++    if (target_info == NULL)
++        return -1;
++
++    card_version = target_info->card_versions;
+     while (card_version != NULL) {
+         if (card_version->card_type == card_type)
+             break;
+         card_version = card_version->next;
+     }
+ 
+-    if (card_version == NULL)
+-        return -1;
++    if (card_version == NULL) {
++        status = -1;
++        goto out;
++    }
+ 
+     if (firmware_version != NULL) {
+         if (compare_ck_version(&card_version->firmware_version,
+                                firmware_version) < 0) {
+             TRACE_DEBUG("%s firmware_version is less than required\n", __func__);
+-            return 0;
++            status = 0;
++            goto out;
+         }
+     }
+ 
+@@ -11353,53 +11475,57 @@ static int check_card_version(STDLL_TokData_t *tokdata, CK_ULONG card_type,
+         if (card_version->firmware_API_version < *firmware_API_version) {
+             TRACE_DEBUG("%s firmware_API_version is less than required\n",
+                        __func__);
+-            return 0;
++            status = 0;
++            goto out;
+         }
+     }
+ 
+-    return 1;
++ out:
++    put_target_info(tokdata, target_info);
++    return status;
+ }
+ 
+-static CK_RV ep11tok_setup_target(STDLL_TokData_t *tokdata)
++static CK_RV ep11tok_setup_target(STDLL_TokData_t *tokdata,
++                                  ep11_target_info_t *target_info)
+ {
+     ep11_private_data_t *ep11_data = tokdata->private_data;
+     struct XCP_Module module;
+-    CK_RV rc;
++    CK_RV rc = CKR_OK;
+     short i;
+ 
+     if (dll_m_add_module == NULL) {
+         TRACE_WARNING("%s Function dll_m_add_module is not available, falling "
+                       "back to old target handling\n", __func__);
+ 
+-        if (ep11_data->used_firmware_API_version > 2) {
++        if (target_info->used_firmware_API_version > 2) {
+             TRACE_ERROR("%s selecting an API version is not possible with old "
+                         "target handling\n", __func__);
+             return CKR_FUNCTION_FAILED;
+         }
+ 
+-        ep11_data->target = (target_t)&ep11_data->target_list;
++        target_info->target = (target_t)&ep11_data->target_list;
+         return CKR_OK;
+     }
+ 
+-    if (ep11_data->used_firmware_API_version > 2 &&
++    if (target_info->used_firmware_API_version > 2 &&
+         ep11_data->ep11_lib_version.major < 3) {
+         TRACE_ERROR("%s selecting an API version is not possible with an EP11"
+                     " host library version < 3.0\n", __func__);
+         return CKR_FUNCTION_FAILED;
+     }
+ 
+-    ep11_data->target = XCP_TGT_INIT;
++    target_info->target = XCP_TGT_INIT;
+     memset(&module, 0, sizeof(module));
+     module.version = ep11_data->ep11_lib_version.major >= 3 ? XCP_MOD_VERSION_2
+                                                             : XCP_MOD_VERSION_1;
+     module.flags = XCP_MFL_VIRTUAL | XCP_MFL_MODULE;
+-    module.api = ep11_data->used_firmware_API_version;
++    module.api = target_info->used_firmware_API_version;
+ 
+     TRACE_DEVEL("%s XCP_MOD_VERSION: %u\n", __func__, module.version);
+ 
+     if (ep11_data->target_list.length == 0) {
+         /* APQN_ANY: Create an empty module group */
+-        rc = dll_m_add_module(&module, &ep11_data->target);
++        rc = dll_m_add_module(&module, &target_info->target);
+         if (rc != CKR_OK) {
+             TRACE_ERROR("%s dll_m_add_module (ANY) failed: rc=%ld\n",
+                         __func__, rc);
+@@ -11414,11 +11540,12 @@ static CK_RV ep11tok_setup_target(STDLL_TokData_t *tokdata)
+         XCPTGTMASK_SET_DOM(module.domainmask,
+                            ep11_data->target_list.apqns[2 * i + 1]);
+ 
+-        rc = dll_m_add_module(&module, &ep11_data->target);
++        rc = dll_m_add_module(&module, &target_info->target);
+         if (rc != CKR_OK) {
+             TRACE_ERROR("%s dll_m_add_module (%02x.%04x) failed: rc=%ld\n",
+                     __func__, ep11_data->target_list.apqns[2 * i],
+                     ep11_data->target_list.apqns[2 * i + 1], rc);
++            dll_m_rm_module(NULL, target_info->target);
+             return CKR_FUNCTION_FAILED;
+         }
+     }
+@@ -11494,6 +11621,7 @@ CK_RV token_specific_set_attribute_values(STDLL_TokData_t *tokdata, OBJECT *obj,
+     CK_ULONG num_attributes = 0;
+     CK_ATTRIBUTE *attr;
+     CK_RV rc;
++    ep11_target_info_t* target_info;
+ 
+     rc = template_attribute_get_ulong(obj->template, CKA_CLASS, &class);
+     if (rc != CKR_OK) {
+@@ -11575,9 +11703,18 @@ CK_RV token_specific_set_attribute_values(STDLL_TokData_t *tokdata, OBJECT *obj,
+             goto out;
+         }
+ 
++        target_info = get_target_info(tokdata);
++        if (target_info == NULL) {
++            rc = CKR_FUNCTION_FAILED;
++            goto out;
++        }
++
+         rc = dll_m_SetAttributeValue(ibm_opaque_attr->pValue,
+                                      ibm_opaque_attr->ulValueLen, attributes,
+-                                     num_attributes, ep11_data->target);
++                                     num_attributes, target_info->target);
++
++        put_target_info(tokdata, target_info);
++
+         if (rc != CKR_OK) {
+             rc = ep11_error_to_pkcs11_error(rc, NULL);
+             TRACE_ERROR("%s m_SetAttributeValue failed rc=0x%lx\n",
+@@ -11601,3 +11738,233 @@ out:
+     return rc;
+ }
+ 
++/*
++ * ATTENTION: This function is called in a separate thread. All actions
++ * performed by this function must be thread save and use locks to lock
++ * against concurrent access by other threads.
++ */
++static CK_RV ep11tok_handle_apqn_event(STDLL_TokData_t *tokdata,
++                                       unsigned int event_type,
++                                       event_udev_apqn_data_t *apqn_data)
++{
++    ep11_private_data_t *ep11_data = tokdata->private_data;
++    CK_BBOOL found = FALSE;
++    CK_RV rc = CKR_OK;
++    char name[20];
++    int i;
++
++    /* Is it one of the configured APQNs ?*/
++    if (ep11_data->target_list.length > 0) {
++        /* APQN_WHITELIST is specified */
++        for (i = 0; i < ep11_data->target_list.length; i++) {
++            if (ep11_data->target_list.apqns[2 * i] == apqn_data->card &&
++                ep11_data->target_list.apqns[2 * i + 1] == apqn_data->domain) {
++                found = TRUE;
++                break;
++            }
++        }
++    } else {
++        /* APQN_ANY is specified */
++        found = TRUE;
++        if (event_type == EVENT_TYPE_APQN_ADD) {
++            snprintf(name, sizeof(name), "card%02x", apqn_data->card);
++            if (is_card_ep11_and_online(name) != CKR_OK)
++                found = FALSE; /* Not an EP11 APQN */
++        }
++    }
++    if (!found)
++        return CKR_OK;
++
++    TRACE_DEVEL("%s Refreshing target infos due to event for APQN %02x.%04x\n",
++                __func__, apqn_data->card, apqn_data->domain);
++
++    rc = refresh_target_info(tokdata);
++    if (rc != CKR_OK) {
++        TRACE_ERROR("%s Failed to get the target infos (refresh_target_info "
++                    "rc=0x%lx)\n", __func__, rc);
++        OCK_SYSLOG(LOG_ERR, "%s: Failed to get the target info rc=0x%lx\n",
++                   __func__, rc);
++        return rc;
++    }
++
++    return CKR_OK;
++}
++
++/*
++ * Called by the event thread, on receipt of an event.
++ *
++ * ATTENTION: This function is called in a separate thread. All actions
++ * performed by this function must be thread save and use locks to lock
++ * against concurrent access by other threads.
++ */
++CK_RV token_specific_handle_event(STDLL_TokData_t *tokdata,
++                                  unsigned int event_type,
++                                  unsigned int event_flags,
++                                  const char *payload,
++                                  unsigned int payload_len)
++{
++    UNUSED(event_flags);
++
++    switch (event_type) {
++    case EVENT_TYPE_APQN_ADD:
++    case EVENT_TYPE_APQN_REMOVE:
++        if (payload_len != sizeof(event_udev_apqn_data_t))
++            return CKR_FUNCTION_FAILED;
++        return ep11tok_handle_apqn_event(tokdata, event_type,
++                                         (event_udev_apqn_data_t *)payload);
++
++    default:
++        return CKR_FUNCTION_NOT_SUPPORTED;
++    }
++
++    return CKR_OK;
++}
++
++/*
++ * Refreshes the target info using the currently configured and available
++ * APQNs. Registers the newly allocated target info as the current one in a
++ * thread save way and gives back the previous one so that it is release when
++ * no longer used (i.e. by a concurrently running thread).
++ */
++static CK_RV refresh_target_info(STDLL_TokData_t *tokdata)
++{
++    ep11_private_data_t *ep11_data = tokdata->private_data;
++    volatile ep11_target_info_t *prev_info;
++    ep11_target_info_t *target_info;
++    CK_RV rc;
++
++    target_info = calloc(1, sizeof(ep11_target_info_t));
++    if (target_info == NULL) {
++        TRACE_ERROR("%s Memory allocation failed\n", __func__);
++        return CKR_HOST_MEMORY;
++    }
++
++    target_info->ref_count = 1;
++
++    /* Get the version info freshly with the current set of APQNs */
++    rc = ep11tok_get_ep11_version(tokdata, target_info);
++    if (rc != 0)
++        goto error;
++
++    /* Get the control points freshly with the current set of APQNs */
++    target_info->control_points_len = sizeof(target_info->control_points);
++    rc = get_control_points(tokdata, target_info->control_points,
++                            &target_info->control_points_len,
++                            &target_info->max_control_point_index);
++    if (rc != 0)
++        goto error;
++
++    /* Setup the group target freshly with the current set of APQNs */
++    rc = ep11tok_setup_target(tokdata, target_info);
++    if (rc != CKR_OK)
++        goto error;
++
++    /* Set the new one as the current one (locked against concurrent get's) */
++    if (pthread_rwlock_wrlock(&ep11_data->target_rwlock) != 0) {
++        TRACE_DEVEL("Target Write-Lock failed.\n");
++        rc = CKR_CANT_LOCK;
++        goto error;
++    }
++
++    prev_info = ep11_data->target_info;
++    ep11_data->target_info = target_info;
++
++    if (pthread_rwlock_unlock(&ep11_data->target_rwlock) != 0) {
++        TRACE_DEVEL("Target Unlock failed.\n");
++        return CKR_CANT_LOCK;
++    }
++
++    /* Release the previous one */
++    if (prev_info != NULL)
++        put_target_info(tokdata, (ep11_target_info_t *)prev_info);
++
++    return CKR_OK;
++
++error:
++    free_card_versions(target_info->card_versions);
++    free((void *)target_info);
++    return rc;
++}
++
++/*
++ * Get the current EP11 target info.
++ * Do NOT use the ep11_data->target_info directly, always get a copy using
++ * this function. This will increment the reference count of the target info,
++ * and return the current target info in a thread save way.
++ * When no longer needed, put it back using put_target_info().
++ */
++static ep11_target_info_t *get_target_info(STDLL_TokData_t *tokdata)
++{
++    ep11_private_data_t *ep11_data = tokdata->private_data;
++    volatile ep11_target_info_t *target_info;
++#ifdef DEBUG
++    unsigned long ref_count;
++#endif
++
++    /*
++     * Lock until we have obtained the current target info and have
++     * increased the reference counter
++     */
++    if (pthread_rwlock_rdlock(&ep11_data->target_rwlock) != 0) {
++        TRACE_DEVEL("Target Read-Lock failed.\n");
++        return NULL;
++    }
++
++    target_info = *((void * volatile *)&ep11_data->target_info);
++    if (target_info == NULL) {
++        TRACE_ERROR("%s: target_info is NULL\n", __func__);
++        return NULL;
++    }
++
++#ifdef DEBUG
++    ref_count = __sync_add_and_fetch(&target_info->ref_count, 1);
++
++    TRACE_DEBUG("%s: target_info: %p ref_count: %lu\n", __func__,
++                (void *)target_info, ref_count);
++#else
++    __sync_add_and_fetch(&target_info->ref_count, 1);
++#endif
++
++    if (pthread_rwlock_unlock(&ep11_data->target_rwlock) != 0) {
++        TRACE_DEVEL("Target Unlock failed.\n");
++        return NULL;
++    }
++
++    return (ep11_target_info_t *)target_info;
++}
++
++/*
++ * Give back an EP11 target info. This will decrement the reference count,
++ * and will free it if the reference count reaches zero.
++ */
++static void put_target_info(STDLL_TokData_t *tokdata,
++                            ep11_target_info_t *target_info)
++{
++    ep11_private_data_t *ep11_data = tokdata->private_data;
++    unsigned long ref_count;
++
++    if (target_info == NULL)
++        return;
++
++    if (target_info->ref_count > 0) {
++        ref_count = __sync_sub_and_fetch(&target_info->ref_count, 1);
++
++        TRACE_DEBUG("%s: target_info: %p ref_count: %lu\n", __func__,
++                    (void *)target_info, ref_count);
++    } else {
++        TRACE_WARNING("%s: target_info: %p ref_count already 0.\n", __func__,
++                      (void *)target_info);
++        ref_count = 0;
++    }
++
++    if (ref_count == 0 && target_info != ep11_data->target_info) {
++        TRACE_DEBUG("%s: target_info: %p is freed\n", __func__,
++                    (void *)target_info);
++
++        if (dll_m_rm_module != NULL)
++            dll_m_rm_module(NULL, target_info->target);
++        free_card_versions(target_info->card_versions);
++        free(target_info);
++    }
++}
++
+diff --git a/usr/lib/ep11_stdll/ep11_specific.h b/usr/lib/ep11_stdll/ep11_specific.h
+index 55fc023c..343f4b3d 100644
+--- a/usr/lib/ep11_stdll/ep11_specific.h
++++ b/usr/lib/ep11_stdll/ep11_specific.h
+@@ -161,7 +161,7 @@ CK_BBOOL ep11tok_libica_mech_available(STDLL_TokData_t *tokdata,
+                                        CK_MECHANISM_TYPE mech,
+                                        CK_OBJECT_HANDLE hKey);
+ 
+-void ep11tok_copy_firmware_info(STDLL_TokData_t *tokdata,
++CK_RV ep11tok_copy_firmware_info(STDLL_TokData_t *tokdata,
+                                  CK_TOKEN_INFO_PTR pInfo);
+ 
+ CK_BBOOL ep11tok_pkey_usage_ok(STDLL_TokData_t *tokdata, SESSION *session,
+diff --git a/usr/lib/ep11_stdll/new_host.c b/usr/lib/ep11_stdll/new_host.c
+index 4e592363..cd12604e 100644
+--- a/usr/lib/ep11_stdll/new_host.c
++++ b/usr/lib/ep11_stdll/new_host.c
+@@ -298,7 +298,7 @@ CK_RV SC_GetTokenInfo(STDLL_TokData_t *tokdata, CK_SLOT_ID sid,
+         goto done;
+     }
+     copy_token_contents_sensibly(pInfo, tokdata->nv_token_data);
+-    ep11tok_copy_firmware_info(tokdata, pInfo);
++    rc = ep11tok_copy_firmware_info(tokdata, pInfo);
+ 
+     /* Set the time */
+     now = time((time_t *) NULL);
+diff --git a/usr/lib/ep11_stdll/tok_struct.h b/usr/lib/ep11_stdll/tok_struct.h
+index 2c0af9cf..01268c67 100644
+--- a/usr/lib/ep11_stdll/tok_struct.h
++++ b/usr/lib/ep11_stdll/tok_struct.h
+@@ -137,7 +137,7 @@ token_spec_t token_specific = {
+     &token_specific_reencrypt_single,
+     &token_specific_set_attribute_values,
+     &token_specific_set_attrs_for_new_object,
+-    NULL,                       // handle_event
++    &token_specific_handle_event,
+ };
+ 
+ #endif
diff --git a/SOURCES/opencryptoki-3.16.0-342dfbeb8275f5ea6ed52dd3f30126614ec1d037.patch b/SOURCES/opencryptoki-3.16.0-342dfbeb8275f5ea6ed52dd3f30126614ec1d037.patch
new file mode 100644
index 0000000..76ce00d
--- /dev/null
+++ b/SOURCES/opencryptoki-3.16.0-342dfbeb8275f5ea6ed52dd3f30126614ec1d037.patch
@@ -0,0 +1,2159 @@
+commit 342dfbeb8275f5ea6ed52dd3f30126614ec1d037
+Author: Ingo Franzki <ifranzki@linux.ibm.com>
+Date:   Mon Feb 15 14:33:07 2021 +0100
+
+    Event support: pkcsslotd changes
+    
+    Signed-off-by: Ingo Franzki <ifranzki@linux.ibm.com>
+
+diff --git a/configure.ac b/configure.ac
+index e0ae4a82..a0b098e1 100644
+--- a/configure.ac
++++ b/configure.ac
+@@ -234,6 +234,12 @@ AC_ARG_WITH([systemd],
+ 	AS_HELP_STRING([--with-systemd@<:@=DIR@:>@],[systemd system unit files location]),
+ 	[],
+ 	[with_systemd=no])
++	
++dnl --- libudev development files
++AC_ARG_WITH([libudev],
++	AS_HELP_STRING([--with-libudev@<:@=DIR@:>@],[libudev development files location]),
++	[],
++	[with_libudev=check])
+ 
+ dnl ---
+ dnl ---
+@@ -438,6 +444,46 @@ fi
+ AC_SUBST([XCRYPTOLINZ_CFLAGS])
+ AC_SUBST([XCRYPTOLINZ_LIBS])
+ 
++dnl --- with_libudev
++LIBUDEV_CFLAGS=
++LIBUDEV_LIBS=
++if test "x$with_libudev" != "xno"; then
++	if test "x$with_libudev" != "xyes" -a "x$with_libudev" != "xcheck"; then
++		LIBUDEV_CFLAGS="-I$with_libudev"
++		LIBUDEV_LIBS="-L$with_libudev"
++	fi
++	old_cflags="$CFLAGS"
++	old_libs="$LIBS"
++	CFLAGS="$CFLAGS $LIBUDEV_CFLAGS"
++	LIBS="$LIBS $LIBUDEV_LIBS"
++	# Use libudev only on s390 platforms, only s390 emits AP related uevents
++	case $target in
++	     *s390x* | *s390*)
++		CFLAGS="$CFLAGS -DWITH_LIBUDEV"
++		;;
++	     *)
++		if test "x$with_libudev" != "xyes"; then
++			with_libudev=no
++			echo "Default to 'with_libudev=no' on non-s390 platforms"
++		fi
++		;;
++	esac
++	if test "x$with_libudev" != "xno"; then
++		AC_CHECK_HEADER([libudev.h], [with_libudev=yes], [
++			AC_MSG_ERROR([Build with libudev requested but libudev headers couldn't be found])
++		])
++		AC_CHECK_LIB([udev], [udev_monitor_new_from_netlink], [with_libudev=yes], [
++			AC_MSG_ERROR([Build with libudev requested but libudev libraries couldn't be found])
++		])
++	fi
++	if test "x$with_libudev" = "xno"; then
++		CFLAGS="$old_cflags"
++		LIBS="$old_libs"
++	fi
++fi
++AC_SUBST([LIBUDEV_CFLAGS])
++AC_SUBST([LIBUDEV_LIBS])
++AM_CONDITIONAL([HAVE_LIBUDEV], [test "x$with_libudev" = "xyes"])
+ 
+ dnl ---
+ dnl --- Now check enabled features, while making sure every required
+@@ -649,6 +695,7 @@ echo "	Daemon build:		$enable_daemon"
+ echo "	Library build:		$enable_library"
+ echo "	Systemd service:	$enable_systemd"
+ echo "	Build with locks:	$enable_locks"
++echo "	Build with libudev:	$with_libudev"
+ echo "	Build p11sak tool:	$enable_p11sak"
+ echo "	token migrate tool:	$enable_pkcstok_migrate"
+ echo
+diff --git a/usr/include/slotmgr.h b/usr/include/slotmgr.h
+index 4d038435..e37368a5 100644
+--- a/usr/include/slotmgr.h
++++ b/usr/include/slotmgr.h
+@@ -31,6 +31,7 @@
+ #define OCK_API_LOCK_FILE LOCKDIR_PATH "/LCK..APIlock"
+ 
+ #define PROC_SOCKET_FILE_PATH "/var/run/pkcsslotd.socket"
++#define ADMIN_SOCKET_FILE_PATH "/var/run/pkcsslotd.admin.socket"
+ 
+ #define PID_FILE_PATH "/var/run/pkcsslotd.pid"
+ #define OCK_CONFIG OCK_CONFDIR "/opencryptoki.conf"
+@@ -45,6 +46,7 @@
+ 
+ #define NUMBER_SLOTS_MANAGED 1024
+ #define NUMBER_PROCESSES_ALLOWED  1000
++#define NUMBER_ADMINS_ALLOWED     1000
+ 
+ //
+ // Per Process Data structure
+diff --git a/usr/sbin/pkcsslotd/pkcsslotd.h b/usr/sbin/pkcsslotd/pkcsslotd.h
+index 69eb59f3..d7edcb3c 100644
+--- a/usr/sbin/pkcsslotd/pkcsslotd.h
++++ b/usr/sbin/pkcsslotd/pkcsslotd.h
+@@ -92,5 +92,8 @@ int init_socket_server();
+ int term_socket_server();
+ int init_socket_data(Slot_Mgr_Socket_t *sp);
+ int socket_connection_handler(int timeout_secs);
++#ifdef DEV
++void dump_socket_handler();
++#endif
+ 
+ #endif                          /* _SLOTMGR_H */
+diff --git a/usr/sbin/pkcsslotd/pkcsslotd.mk b/usr/sbin/pkcsslotd/pkcsslotd.mk
+index 2d36b4a9..c574edf8 100644
+--- a/usr/sbin/pkcsslotd/pkcsslotd.mk
++++ b/usr/sbin/pkcsslotd/pkcsslotd.mk
+@@ -8,6 +8,9 @@ CLEANFILES += usr/lib/common/parser.c usr/lib/common/parser.h	\
+ 	usr/lib/common/parser.output usr/lib/common/lexer.c
+ 
+ usr_sbin_pkcsslotd_pkcsslotd_LDFLAGS = -lpthread -lcrypto
++if HAVE_LIBUDEV
++usr_sbin_pkcsslotd_pkcsslotd_LDFLAGS += -ludev
++endif
+ 
+ usr_sbin_pkcsslotd_pkcsslotd_CFLAGS = -DPROGRAM_NAME=\"$(@)\"	\
+ 	-I${srcdir}/usr/include -I${srcdir}/usr/lib/common	\
+diff --git a/usr/sbin/pkcsslotd/signal.c b/usr/sbin/pkcsslotd/signal.c
+index 49482a2f..17167632 100644
+--- a/usr/sbin/pkcsslotd/signal.c
++++ b/usr/sbin/pkcsslotd/signal.c
+@@ -21,7 +21,7 @@
+ extern BOOL IsValidProcessEntry(pid_t_64 pid, time_t_64 RegTime);
+ 
+ static int SigsToIntercept[] = {
+-    SIGHUP, SIGINT, SIGQUIT, SIGPIPE, SIGALRM,
++    SIGHUP, SIGINT, SIGQUIT, SIGALRM,
+     SIGTERM, SIGTSTP, SIGTTIN,
+     SIGTTOU, SIGUSR1, SIGUSR2, SIGPROF
+ };
+@@ -32,8 +32,11 @@ static int SigsToIntercept[] = {
+ /* SIGCHLD - Don't want to exit.  Should never receive, but we do, apparently
+  * when something tries to cancel the GC Thread */
+ 
++/* SIGPIPE - Don't want to exit.  May happen when a connection to an admin
++ * event sender or a process is closed before all events are delivered. */
++
+ static int SigsToIgnore[] = {
+-    SIGCHLD,
++    SIGCHLD, SIGPIPE,
+ };
+ 
+ 
+@@ -71,6 +74,10 @@ void slotdGenericSignalHandler(int Signal)
+     CheckForGarbage(shmp);
+ #endif
+ 
++#ifdef DEV
++    dump_socket_handler();
++#endif
++
+     for (procindex = 0; (procindex < NUMBER_PROCESSES_ALLOWED); procindex++) {
+ 
+         Slot_Mgr_Proc_t_64 *pProc = &(shmp->proc_table[procindex]);
+diff --git a/usr/sbin/pkcsslotd/socket_server.c b/usr/sbin/pkcsslotd/socket_server.c
+index 1fae0b95..41408670 100644
+--- a/usr/sbin/pkcsslotd/socket_server.c
++++ b/usr/sbin/pkcsslotd/socket_server.c
+@@ -1,4 +1,6 @@
+ /*
++ * COPYRIGHT (c) International Business Machines Corp. 2013, 2021
++ *
+  * This program is provided under the terms of the Common Public License,
+  * version 1.0 (CPL-1.0). Any use, reproduction or distribution for this
+  * software constitutes recipient's acceptance of CPL-1.0 terms which can be
+@@ -12,6 +14,8 @@
+ #include <stdio.h>
+ #include <unistd.h>
+ #include <string.h>
++#include <stdlib.h>
++#include <fcntl.h>
+ 
+ #include <sys/time.h>
+ #include <sys/socket.h>
+@@ -19,32 +23,1225 @@
+ #include <sys/select.h>
+ #include <sys/stat.h>
+ #include <grp.h>
++#include <sys/epoll.h>
++
++#if defined(__GNUC__) && __GNUC__ >= 7 || defined(__clang__) && __clang_major__ >= 12
++    #define FALL_THROUGH __attribute__ ((fallthrough))
++#else
++    #define FALL_THROUGH ((void)0)
++#endif
++
++#ifdef WITH_LIBUDEV
++#include <libudev.h>
++#endif
+ 
+ #include "log.h"
+ #include "slotmgr.h"
+ #include "pkcsslotd.h"
+ #include "apictl.h"
++#include "dlist.h"
++#include "events.h"
++
++#define MAX_EPOLL_EVENTS            128
++
++#ifdef WITH_LIBUDEV
++#define UDEV_RECV_BUFFFER_SIZE      512 * 1024
++#define UDEV_SUBSYSTEM_AP           "ap"
++#define UDEV_ACTION_BIND            "bind"
++#define UDEV_ACTION_UNBIND          "unbind"
++#define UDEV_ACTION_DEVTYPE_APQN    "ap_queue"
++#define UDEV_PROERTY_DEVTYPE        "DEV_TYPE"
++#endif
++
++struct epoll_info {
++    int (* notify)(int events, void *private);
++    void (* free)(void *private);
++    void *private;
++    unsigned long ref_count;
++};
++
++struct listener_info {
++    int socket;
++    const char *file_path;
++    int (* new_conn)(int socket, struct listener_info *listener);
++    struct epoll_info ep_info;
++    unsigned long num_clients;
++    unsigned long max_num_clients;
++};
++
++enum xfer_state {
++    XFER_IDLE = 0,
++    XFER_RECEIVE = 1,
++    XFER_SEND = 2,
++};
++
++struct client_info {
++    int socket;
++    int (* xfer_complete)(void *client);
++    void (* hangup)(void *client);
++    void (* free)(void *client);
++    void *client;
++    struct epoll_info ep_info;
++    enum xfer_state xfer_state;
++    char *xfer_buffer;
++    size_t xfer_size;
++    size_t xfer_offset;
++};
++
++enum proc_state {
++    PROC_INITIAL_SEND = 0,
++    PROC_WAIT_FOR_EVENT = 1,
++    PROC_SEND_EVENT = 2,
++    PROC_SEND_PAYLOAD = 3,
++    PROC_RECEIVE_REPLY = 4,
++    PROC_HANGUP = 5,
++};
++
++struct proc_conn_info {
++    struct client_info client_info;
++    enum proc_state state;
++    DL_NODE *events;
++    struct event_info *event;
++    event_reply_t reply;
++};
++
++enum admin_state {
++    ADMIN_RECEIVE_EVENT = 0,
++    ADMIN_RECEIVE_PAYLOAD = 1,
++    ADMIN_EVENT_DELIVERED = 2,
++    ADMIN_SEND_REPLY = 3,
++    ADMIN_WAIT_FOR_EVENT_LIMIT = 4,
++    ADMIN_HANGUP = 5,
++};
++
++struct admin_conn_info {
++    struct client_info client_info;
++    enum admin_state state;
++    struct event_info *event;
++};
++
++#ifdef WITH_LIBUDEV
++struct udev_mon {
++    struct udev *udev;
++    struct udev_monitor *mon;
++    int socket;
++    struct epoll_info ep_info;
++    struct event_info *delayed_event;
++};
++#endif
++
++struct event_info {
++    event_msg_t event;
++    char *payload;
++    event_reply_t reply;
++    unsigned long proc_ref_count;      /* # of processes using this event */
++    struct admin_conn_info *admin_ref; /* Admin connection to send reply back */
++};
++
++static int epoll_fd = -1;
++static struct listener_info proc_listener;
++static DL_NODE *proc_connections = NULL;
++static struct listener_info admin_listener;
++static DL_NODE *admin_connections = NULL;
++#ifdef WITH_LIBUDEV
++static struct udev_mon udev_mon;
++#endif
++static DL_NODE *pending_events = NULL;
++static unsigned long pending_events_count = 0;
++
++#define MAX_PENDING_EVENTS      1024
++
++/*
++ * Iterate over all connections in a safe way. Before actually iterating,
++ * increment the ref count of ALL connections, because any processing may
++ * cause any of the connections to be hang-up, and thus freed and removed
++ * from the list. We need to make sure that while we are iterating over the
++ * connections, none of them gets removed from the list.
++ */
++#define FOR_EACH_CONN_SAFE_BEGIN(list, conn) {                              \
++        DL_NODE *_node, *_next;                                             \
++        _node = dlist_get_first(list);                                      \
++        while (_node != NULL) {                                             \
++            conn = _node->data;                                             \
++            _next = dlist_next(_node);                                      \
++            client_socket_get(&(conn)->client_info);                        \
++            _node = _next;                                                  \
++        }                                                                   \
++        _node = dlist_get_first(list);                                      \
++        while (_node != NULL) {                                             \
++            conn = _node->data;                                             \
++            _next = dlist_next(_node);
++
++#define FOR_EACH_CONN_SAFE_END(list, conn)                                  \
++            _node = _next;                                                  \
++        }                                                                   \
++        _node = dlist_get_first(list);                                      \
++        while (_node != NULL) {                                             \
++            conn = _node->data;                                             \
++            _next = dlist_next(_node);                                      \
++            client_socket_put(&(conn)->client_info);                        \
++            _node = _next;                                                  \
++        }                                                                   \
++    }
++
++
++
++static void listener_socket_close(int socketfd, const char *file_path);
++static int listener_client_hangup(struct listener_info *listener);
++static void event_delivered(struct event_info *event);
++static int client_socket_notify(int events, void *private);
++static void client_socket_free(void *private);
++static int proc_xfer_complete(void *client);
++static int proc_start_deliver_event(struct proc_conn_info *conn);
++static int proc_deliver_event(struct proc_conn_info *conn,
++                              struct event_info *event);
++static int proc_event_delivered(struct proc_conn_info *conn,
++                                struct event_info *event);
++static inline void proc_get(struct proc_conn_info *conn);
++static inline void proc_put(struct proc_conn_info *conn);
++static void proc_hangup(void *client);
++static void proc_free(void *client);
++static int admin_xfer_complete(void *client);
++static void admin_event_limit_underrun(struct admin_conn_info *conn);
++static int admin_event_delivered(struct admin_conn_info *conn,
++                                 struct event_info *event);
++static inline void admin_get(struct admin_conn_info *conn);
++static inline void admin_put(struct admin_conn_info *conn);
++static void admin_hangup(void *client);
++static void admin_free(void *client);
++#ifdef WITH_LIBUDEV
++static void udev_mon_term(struct udev_mon *udev_mon);
++static int udev_mon_notify(int events, void *private);
++#endif
++
++static void epoll_info_init(struct epoll_info *epoll_info,
++                    int (* notify)(int events, void *private),
++                    void (* free_cb)(void *private),
++                    void *private)
++{
++    epoll_info->ref_count = 1;
++    epoll_info->notify = notify;
++    epoll_info->free = free_cb;
++    epoll_info->private = private;
++}
++
++static void epoll_info_get(struct epoll_info *epoll_info)
++{
++    epoll_info->ref_count++;
++
++    DbgLog(DL3, "%s: private: %p, ref_count: %lu", __func__,
++           epoll_info->private, epoll_info->ref_count);
++}
++
++static void epoll_info_put(struct epoll_info *epoll_info)
++{
++    if (epoll_info->ref_count > 0)
++        epoll_info->ref_count--;
++
++    DbgLog(DL3, "%s: private: %p, ref_count: %lu", __func__,
++           epoll_info->private, epoll_info->ref_count);
++
++    if (epoll_info->ref_count == 0 && epoll_info->free != NULL)
++        epoll_info->free(epoll_info->private);
++}
++
++static int client_socket_init(int socket, int (* xfer_complete)(void *client),
++                              void (* hangup)(void *client),
++                              void (* free_cb)(void *client), void *client,
++                              struct client_info *client_info)
++{
++    struct epoll_event evt;
++    int rc, err;
++
++    if (xfer_complete == NULL || hangup == NULL)
++        return -EINVAL;
++
++    epoll_info_init(&client_info->ep_info, client_socket_notify,
++                    client_socket_free, client_info);
++    client_info->socket = socket;
++    client_info->xfer_complete = xfer_complete;
++    client_info->hangup = hangup;
++    client_info->free = free_cb;
++    client_info->client = client;
++    client_info->xfer_state = XFER_IDLE;
++
++    rc = fcntl(socket, F_SETFL, O_NONBLOCK);
++    if (rc < 0) {
++        err = errno;
++        InfoLog("%s: Failed to set client socket %d to non-blocking, errno "
++                "%d (%s).", __func__, socket, err, strerror(err));
++        return -err;
++    }
++
++    evt.events = EPOLLIN | EPOLLOUT | EPOLLRDHUP | EPOLLERR | EPOLLET;
++    evt.data.ptr = &client_info->ep_info;
++    rc = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, socket, &evt);
++    if (rc != 0) {
++        err = errno;
++        InfoLog("%s: Failed to add client socket %d to epoll, errno %d (%s).",
++                 __func__, socket, err, strerror(err));
++        close(socket);
++        return -err;
++    }
++
++    return 0;
++}
++
++static inline void client_socket_get(struct client_info *client_info)
++{
++    epoll_info_get(&client_info->ep_info);
++}
++
++static inline void client_socket_put(struct client_info *client_info)
++{
++    epoll_info_put(&client_info->ep_info);
++}
++
++static void client_socket_term(struct client_info *client_info)
++{
++    epoll_ctl(epoll_fd, EPOLL_CTL_DEL, client_info->socket, NULL);
++    close(client_info->socket);
++    client_info->socket = -1;
++}
++
++static int client_socket_notify(int events, void *private)
++{
++    struct client_info *client_info = private;
++    ssize_t num;
++    int rc, err, socket = client_info->socket;
++
++    DbgLog(DL3, "%s: Epoll event on client %p socket %d: events: 0x%x xfer: %d",
++           __func__, client_info, socket, events, client_info->xfer_state);
++
++    if (socket < 0)
++        return -ENOTCONN;
++
++    if (events & (EPOLLHUP | EPOLLERR)) {
++        DbgLog(DL3, "EPOLLHUP | EPOLLERR");
++
++        client_info->hangup(client_info->client);
++        client_info = NULL; /* client_info may have been freed by now */
++        return 0;
++    }
++
++    if (client_info->xfer_state == XFER_RECEIVE && (events & EPOLLIN)) {
++        DbgLog(DL3, "%s: EPOLLIN: buffer: %p size: %lu ofs: %lu", __func__,
++               client_info->xfer_buffer, client_info->xfer_size,
++               client_info->xfer_offset);
++
++        num = read(client_info->socket,
++                   client_info->xfer_buffer + client_info->xfer_offset,
++                   client_info->xfer_size - client_info->xfer_offset);
++        if (num <= 0) {
++            err = errno;
++
++            DbgLog(DL3, "%s: read failed with: num: %d errno: %d (%s)",
++                   __func__, num, num < 0 ? err : 0,
++                   num < 0 ? strerror(err) : "none");
++
++            if (num < 0 && err == EWOULDBLOCK)
++                return 0; /* Will be continued when socket becomes readable */
++
++            /* assume connection closed by peer */
++            client_info->hangup(client_info->client);
++            client_info = NULL; /* client_info may have been freed by now */
++            return 0;
++        } else {
++            DbgLog(DL3, "%s: %lu bytes received", __func__, num);
++
++            client_info->xfer_offset += num;
++
++            DbgLog(DL3, "%s: %lu bytes left", __func__,
++                   client_info->xfer_size - client_info->xfer_offset);
++
++            if (client_info->xfer_offset >= client_info->xfer_size) {
++                client_info->xfer_state = XFER_IDLE;
++                client_info->xfer_buffer = NULL;
++                client_info->xfer_size = 0;
++                client_info->xfer_offset = 0;
++
++                client_socket_get(client_info);
++                rc = client_info->xfer_complete(client_info->client);
++                if (rc != 0) {
++                    InfoLog("%s: xfer_complete callback failed for client "
++                            "socket %d, rc: %d", __func__, socket,
++                            rc);
++                    client_info->hangup(client_info->client);
++                }
++                client_socket_put(client_info);
++                client_info = NULL; /* client_info may have been freed by now */
++                return rc;
++            }
++            return 0;
++        }
++    }
++
++    if (client_info->xfer_state == XFER_SEND && (events & EPOLLOUT)) {
++        DbgLog(DL3, "%s: EPOLLOUT: buffer: %p size: %lu ofs: %lu", __func__,
++               client_info->xfer_buffer, client_info->xfer_size,
++               client_info->xfer_offset);
++
++        num = write(client_info->socket,
++                    client_info->xfer_buffer + client_info->xfer_offset,
++                    client_info->xfer_size - client_info->xfer_offset);
++        if (num < 0) {
++            err = errno;
++
++            DbgLog(DL3, "%s: write failed with: errno: %d (%s)", __func__, err,
++                   strerror(err));
++
++            if (err == EWOULDBLOCK)
++                return 0; /* Will be continued when socket becomes writable */
++
++            /* assume connection closed by peer */
++            client_info->hangup(client_info->client);
++            client_info = NULL; /* client_info may have been freed by now */
++            return 0;
++        } else {
++            DbgLog(DL3, "%s: %lu bytes sent", __func__, num);
++
++            client_info->xfer_offset += num;
++
++            DbgLog(DL3, "%s: %lu bytes left", __func__,
++                   client_info->xfer_size - client_info->xfer_offset);
++
++            if (client_info->xfer_offset >= client_info->xfer_size) {
++                client_info->xfer_state = XFER_IDLE;
++                client_info->xfer_buffer = NULL;
++                client_info->xfer_size = 0;
++                client_info->xfer_offset = 0;
++
++                client_socket_get(client_info);
++                rc = client_info->xfer_complete(client_info->client);
++                if (rc != 0) {
++                    InfoLog("%s: xfer_complete callback failed for client "
++                            "socket %d, rc: %d", __func__, socket,
++                            rc);
++                    client_info->hangup(client_info->client);
++                }
++                client_socket_put(client_info);
++                client_info = NULL; /* client_info may have been freed by now */
++                return rc;
++            }
++            return 0;
++        }
++    }
++
++    return 0;
++}
++
++static void client_socket_free(void *private)
++{
++    struct client_info *client_info = private;
++
++    DbgLog(DL3, "%s: %p", __func__, client_info);
++
++    if (client_info->free != NULL)
++        client_info->free(client_info->client);
++}
++
++static int client_socket_receive(struct client_info *client_info,
++                                 void *buffer, size_t size)
++{
++    if (client_info->socket < 0)
++        return -ENOTCONN;
++
++    client_info->xfer_state = XFER_RECEIVE;
++    client_info->xfer_buffer = (char *)buffer;
++    client_info->xfer_size = size;
++    client_info->xfer_offset = 0;
++
++    DbgLog(DL3, "%s: Start receive on client socket %d: buffer: %p size: %lu",
++            __func__, client_info->socket, buffer, size);
++
++    return client_socket_notify(EPOLLIN, client_info);
++}
++
++
++static int client_socket_send(struct client_info *client_info,
++                              void *buffer, size_t size)
++{
++    if (client_info->socket < 0)
++        return -ENOTCONN;
++
++    client_info->xfer_state = XFER_SEND;
++    client_info->xfer_buffer = (char *)buffer;
++    client_info->xfer_size = size;
++    client_info->xfer_offset = 0;
++
++    DbgLog(DL3, "%s: Start send on client socket %d: buffer: %p size: %lu",
++            __func__, client_info->socket, buffer, size);
++
++    return client_socket_notify(EPOLLOUT, client_info);
++}
++
++static struct event_info *event_new(unsigned int payload_len,
++                                    struct admin_conn_info *admin_conn)
++{
++    struct event_info *event;
++
++    event = calloc(1, sizeof(struct event_info));
++    if (event == NULL) {
++        ErrLog("%s: Failed to allocate the event", __func__);
++        return NULL;
++    }
++
++    event->event.version = EVENT_VERSION_1;
++    event->event.payload_len = payload_len;
++    if (payload_len > 0) {
++        event->payload = malloc(payload_len);
++        if (event->payload == NULL) {
++            ErrLog("%s: Failed to allocate the event payload", __func__);
++            free(event);
++            return NULL;
++        }
++    }
++
++    event->reply.version = EVENT_VERSION_1;
++
++    if (admin_conn != NULL)
++        admin_get(admin_conn);
++    event->admin_ref = admin_conn;
++
++    DbgLog(DL3, "%s: allocated event: %p", __func__, event);
++    return event;
++}
++
++static void event_limit_underrun()
++{
++    struct admin_conn_info *conn;
++
++    DbgLog(DL3, "%s: pending_events_count: %lu", __func__, pending_events_count);
++
++#ifdef WITH_LIBUDEV
++    /* Notify the udev monitor */
++    udev_mon_notify(EPOLLIN, &udev_mon);
++#endif
++
++    /* Notify all admin connections */
++    FOR_EACH_CONN_SAFE_BEGIN(admin_connections, conn) {
++        admin_event_limit_underrun(conn);
++    }
++    FOR_EACH_CONN_SAFE_END(admin_connections, conn)
++}
++
++static void event_free(struct event_info *event)
++{
++    DbgLog(DL3, "%s: free event: %p", __func__, event);
++
++    if (event->payload != NULL)
++        free(event->payload);
++    free(event);
++}
++
++static int event_add_to_pending_list(struct event_info *event)
++{
++    DL_NODE *list;
++
++    list = dlist_add_as_last(pending_events, event);
++    if (list == NULL) {
++        ErrLog("%s: failed add event to list of pending events", __func__);
++        return -ENOMEM;
++    }
++    pending_events = list;
++
++    pending_events_count++;
++
++    return 0;
++}
++
++static void event_remove_from_pending_list(struct event_info *event)
++{
++    DL_NODE *node;
++    int trigger = 0;
++
++    node = dlist_find(pending_events, event);
++    if (node != NULL) {
++        pending_events = dlist_remove_node(pending_events, node);
++
++        if (pending_events_count >= MAX_PENDING_EVENTS)
++            trigger = 1;
++
++        if (pending_events_count > 0)
++            pending_events_count--;
++
++        if (trigger)
++            event_limit_underrun();
++    }
++}
++
++static int event_start_deliver(struct event_info *event)
++{
++    struct proc_conn_info *conn;
++    int rc;
++
++    DbgLog(DL3, "%s: event: %p", __func__, event);
++
++    if (pending_events_count >= MAX_PENDING_EVENTS) {
++        InfoLog("%s: Max pending events reached", __func__);
++        return -ENOSPC;
++    }
++
++    /* Add event of the list of pending events */
++    rc = event_add_to_pending_list(event);
++    if (rc != 0)
++        return rc;
++
++    /*
++     * Need to increment the event's ref count here, proc_deliver_event() may
++     * already complete the event delivery for one process, which then would
++     * free the event but it needs to be passed to other processes here, too.
++     */
++    event->proc_ref_count++;
++    FOR_EACH_CONN_SAFE_BEGIN(proc_connections, conn) {
++        rc = proc_deliver_event(conn, event);
++        if (rc != 0)
++            proc_hangup(conn);
++    }
++    FOR_EACH_CONN_SAFE_END(proc_connections, conn)
++    event->proc_ref_count--;
++
++    DbgLog(DL3, "%s: proc_ref_count: %u", __func__, event->proc_ref_count);
++
++    if (event->proc_ref_count == 0)
++        event_delivered(event);
++
++    return 0;
++}
++
++static void event_delivered(struct event_info *event)
++{
++    struct admin_conn_info *conn;
++    int rc;
++
++    DbgLog(DL3, "%s: event: %p", __func__, event);
++
++    event_remove_from_pending_list(event);
++
++    /* Notify owning admin connection (if available), free otherwise */
++    if (event->admin_ref != NULL) {
++        conn = event->admin_ref;
++        admin_get(conn);
++        rc = admin_event_delivered(conn, event);
++        if (rc != 0) {
++            admin_hangup(conn);
++            event_free(event);
++        }
++        admin_put(conn);
++    } else {
++        event_free(event);
++    }
++}
++
++static int proc_new_conn(int socket, struct listener_info *listener)
++{
++    struct proc_conn_info *conn;
++    struct event_info *event;
++    DL_NODE *list, *node;
++    int rc = 0;
++
++    UNUSED(listener);
++
++    DbgLog(DL0, "%s: Accepted connection from process: socket: %d", __func__,
++           socket);
++
++    conn = calloc(1, sizeof(struct proc_conn_info));
++    if (conn == NULL) {
++        ErrLog("%s: Failed to to allocate memory for the process connection",
++               __func__);
++        return -ENOMEM;
++        /* Caller will close socket */
++    }
++
++    DbgLog(DL3, "%s: process conn: %p", __func__, conn);
++
++    /* Add currently pending events to this connection */
++    node = dlist_get_first(pending_events);
++    while (node != NULL) {
++        event = (struct event_info *)node->data;
++        DbgLog(DL3, "%s: event: %p", __func__, event);
++
++        list = dlist_add_as_last(conn->events, event);
++        if (list == NULL) {
++            ErrLog("%s: failed add event to list of process's pending events",
++                   __func__);
++            rc = -ENOMEM;
++            goto out;
++        }
++        conn->events = list;
++
++        event->proc_ref_count++;
++
++        node = dlist_next(node);
++    }
++
++    conn->state = PROC_INITIAL_SEND;
++
++    rc = client_socket_init(socket, proc_xfer_complete, proc_hangup, proc_free,
++                            conn, &conn->client_info);
++    if (rc != 0)
++        goto out;
++
++    /* Add it to the process connections list */
++    list = dlist_add_as_first(proc_connections, conn);
++    if (list == NULL) {
++        rc = -ENOMEM;
++        goto out;
++    }
++    proc_connections = list;
++
++    proc_get(conn);
++    rc = client_socket_send(&conn->client_info, &socketData,
++                            sizeof(socketData));
++    proc_put(conn);
++    conn = NULL; /* conn may have been freed by now */
++
++out:
++    if (rc != 0 && conn != NULL) {
++        proc_hangup(conn);
++        rc = 0; /* Don't return an error, we have already handled it */
++    }
++
++    return rc;
++}
++
++static int proc_xfer_complete(void *client)
++{
++    struct proc_conn_info *conn = client;
++    int rc;
++
++    DbgLog(DL0, "%s: Xfer completed: process: %p socket: %d state: %d",
++           __func__, conn, conn->client_info.socket, conn->state);
++
++    /*
++     * A non-zero return code returned by this function causes the caller to
++     * call proc_hangup(). Thus, no need to call proc_hangup() ourselves.
++     */
++
++    switch (conn->state) {
++    case PROC_INITIAL_SEND:
++        conn->state = PROC_WAIT_FOR_EVENT;
++        rc = proc_start_deliver_event(conn);
++        conn = NULL; /* conn may have been freed by now */
++        return rc;
++
++    case PROC_WAIT_FOR_EVENT:
++        /* handled in proc_start_deliver_event */
++        break;
++
++    case PROC_SEND_EVENT:
++        if (conn->event == NULL) {
++            TraceLog("%s: No current event to handle", __func__);
++            return -EINVAL;
++        }
++
++        if (conn->event->event.payload_len > 0) {
++            conn->state = PROC_SEND_PAYLOAD;
++            rc = client_socket_send(&conn->client_info, conn->event->payload,
++                                    conn->event->event.payload_len);
++            conn = NULL; /* conn may have been freed by now */
++            return rc;
++        }
++        FALL_THROUGH;
++        /* fall through */
++
++    case PROC_SEND_PAYLOAD:
++        if (conn->event == NULL) {
++            TraceLog("%s: No current event to handle", __func__);
++            return -EINVAL;
++        }
++
++        if (conn->event->event.flags & EVENT_FLAGS_REPLY_REQ) {
++            conn->state = PROC_RECEIVE_REPLY;
++            rc = client_socket_receive(&conn->client_info, &conn->reply,
++                                       sizeof(conn->reply));
++            conn = NULL; /* conn may have been freed by now */
++            return rc;
++        }
++        FALL_THROUGH;
++        /* fall through */
++
++    case PROC_RECEIVE_REPLY:
++        if (conn->event == NULL) {
++            TraceLog("%s: No current event to handle", __func__);
++            return -EINVAL;
++        }
++
++        if (conn->event->event.flags & EVENT_FLAGS_REPLY_REQ) {
++            if (conn->reply.version != EVENT_VERSION_1) {
++                InfoLog("%s: Reply has a wrong version: %u", __func__,
++                        conn->reply.version);
++                return -EINVAL;
++            }
++
++            /* Update reply counters in event */
++            conn->event->reply.positive_replies += conn->reply.positive_replies;
++            conn->event->reply.negative_replies += conn->reply.negative_replies;
++            conn->event->reply.nothandled_replies +=
++                                                conn->reply.nothandled_replies;
++        }
++
++        conn->state = PROC_WAIT_FOR_EVENT;
++
++        rc = proc_event_delivered(conn, conn->event);
++        conn = NULL; /* conn may have been freed by now */
++        return rc;
++
++    case PROC_HANGUP:
++        break;
++    }
++
++    return 0;
++}
++
++static int proc_start_deliver_event(struct proc_conn_info *conn)
++{
++    DL_NODE *node;
++    int rc;
++
++    if (conn->state != PROC_WAIT_FOR_EVENT)
++        return 0;
++
++    node = dlist_get_first(conn->events);
++    if (node == NULL)
++        return 0;
++
++    conn->event = node->data;
++    memset(&conn->reply, 0, sizeof(conn->reply));
++
++    DbgLog(DL3, "%s: process: %p event: %p", __func__, conn, conn->event);
++
++    conn->state = PROC_SEND_EVENT;
++    rc = client_socket_send(&conn->client_info, &conn->event->event,
++                            sizeof(conn->event->event));
++    conn = NULL; /* conn may have been freed by now */
++    return rc;
++}
++
++static int proc_deliver_event(struct proc_conn_info *conn,
++                              struct event_info *event)
++{
++    DL_NODE *list;
++    int rc;
++
++    DbgLog(DL3, "%s: process: %p event: %p", __func__, conn, event);
++
++    if (conn->state == PROC_HANGUP)
++        return 0;
++
++    /* Add to process's event list and incr. reference count */
++    list = dlist_add_as_last(conn->events, event);
++    if (list == NULL) {
++        ErrLog("%s: failed add event to list of process's pending events",
++               __func__);
++        return -ENOMEM;
++    }
++    conn->events = list;
++
++    event->proc_ref_count++;
++
++    rc = proc_start_deliver_event(conn);
++    return rc;
++}
++
++static int proc_event_delivered(struct proc_conn_info *conn,
++                                struct event_info *event)
++{
++    DL_NODE *node;
++    int rc;
++
++    DbgLog(DL3, "%s: process: %p event: %p", __func__, conn, event);
++
++    conn->event = NULL;
++
++    /* Remove from process's event list and decr. reference count */
++    node = dlist_find(conn->events, event);
++    if (node != NULL) {
++        conn->events = dlist_remove_node(conn->events, node);
++        event->proc_ref_count--;
++    }
++
++    DbgLog(DL3, "%s: proc_ref_count: %u", __func__, event->proc_ref_count);
++
++    if (event->proc_ref_count == 0)
++        event_delivered(event);
++
++    /* Deliver further pending events, if any */
++    rc = proc_start_deliver_event(conn);
++    conn = NULL; /* conn may have been freed by now */
++    return rc;
++}
++
++static inline void proc_get(struct proc_conn_info *conn)
++{
++    client_socket_get(&conn->client_info);
++}
++
++static inline void proc_put(struct proc_conn_info *conn)
++{
++    client_socket_put(&conn->client_info);
++}
++
++static void proc_hangup(void *client)
++{
++    struct proc_conn_info *conn = client;
++    struct event_info *event;
++    DL_NODE *node;
++
++    DbgLog(DL0, "%s: process: %p socket: %d state: %d", __func__, conn,
++           conn->client_info.socket, conn->state);
++
++    if (conn->state == PROC_HANGUP)
++        return;
++    conn->state = PROC_HANGUP;
++
++    /* Unlink all pending events */
++    while ((node = dlist_get_first(conn->events)) != NULL) {
++        event = node->data;
++        /* We did not handle this event */
++        event->reply.nothandled_replies++;
++        proc_event_delivered(conn, event);
++    }
++
++    client_socket_term(&conn->client_info);
++    proc_put(conn);
++}
++
++static void proc_free(void *client)
++{
++    struct proc_conn_info *conn = client;
++    DL_NODE *node;
++
++    /* Remove it from the process connections list */
++    node = dlist_find(proc_connections, conn);
++    if (node != NULL) {
++        proc_connections = dlist_remove_node(proc_connections, node);
++        listener_client_hangup(&proc_listener);
++    }
++
++    DbgLog(DL0, "%s: process: %p", __func__, conn);
++    free(conn);
++}
++
++static int admin_new_conn(int socket, struct listener_info *listener)
++{
++    struct admin_conn_info *conn;
++    DL_NODE *list;
++    int rc = 0;
++
++    UNUSED(listener);
++
++    DbgLog(DL0, "%s: Accepted connection from admin: socket: %d", __func__,
++           socket);
++
++    conn = calloc(1, sizeof(struct admin_conn_info));
++    if (conn == NULL) {
++        ErrLog("%s: Failed to to allocate memory for the admin connection",
++               __func__);
++        return -ENOMEM;
++        /* Caller will close socket */
++    }
++
++    DbgLog(DL3, "%s: admin conn: %p", __func__, conn);
++
++    conn->state = ADMIN_RECEIVE_EVENT;
++
++    rc = client_socket_init(socket, admin_xfer_complete, admin_hangup,
++                            admin_free, conn, &conn->client_info);
++    if (rc != 0)
++        goto out;
++
++    conn->event = event_new(0, conn);
++    if (conn->event == NULL) {
++        ErrLog("%s: Failed to allocate a new event", __func__);
++        rc = -ENOMEM;
++        goto out;
++    }
++
++    /* Add it to the admin connections list */
++    list = dlist_add_as_first(admin_connections, conn);
++    if (list == NULL) {
++        ErrLog("%s: Failed to add connection to list of admin connections",
++               __func__);
++        rc = -ENOMEM;
++        goto out;
++    }
++    admin_connections = list;
++
++    admin_get(conn);
++    rc = client_socket_receive(&conn->client_info, &conn->event->event,
++                               sizeof(conn->event->event));
++    admin_put(conn);
++    conn = NULL; /* conn may have been freed by now */
++
++out:
++    if (rc != 0 && conn != NULL) {
++        admin_hangup(conn);
++        rc = 0; /* Don't return an error, we have already handled it */
++    }
++
++    return rc;
++}
++
++static int admin_xfer_complete(void *client)
++{
++    struct admin_conn_info *conn = client;
++    int rc;
++
++    DbgLog(DL0, "%s: Xfer completed: admin: %p socket: %d state: %d",
++           __func__, conn, conn->client_info.socket, conn->state);
++
++    /*
++     * A non-zero return code returned by this function causes the caller to
++     * call admin_hangup(). Thus, no need to call admin_hangup() ourselves.
++     */
++
++    if (conn->event == NULL) {
++        TraceLog("%s: No current event", __func__);
++        return -EINVAL;
++    }
++
++    switch (conn->state) {
++    case ADMIN_RECEIVE_EVENT:
++        /* We have received the event from the admin */
++        DbgLog(DL3, "%s: Event version:      %u", __func__,
++               conn->event->event.version);
++        DbgLog(DL3, "%s: Event type:         0x%08x", __func__,
++               conn->event->event.type);
++        DbgLog(DL3, "%s: Event flags:        0x%08x", __func__,
++               conn->event->event.flags);
++        DbgLog(DL3, "%s: Event token_type:   0x%08x", __func__,
++               conn->event->event.token_type);
++        DbgLog(DL3, "%s: Event token_name:   '%.32s'", __func__,
++               conn->event->event.token_label);
++        DbgLog(DL3, "%s: Event process_id:   %u", __func__,
++               conn->event->event.process_id);
++        DbgLog(DL3, "%s: Event payload_len:  %u", __func__,
++               conn->event->event.payload_len);
++
++        if (conn->event->event.version != EVENT_VERSION_1) {
++            InfoLog("%s: Admin event has invalid version: %d", __func__,
++                    conn->event->event.version);
++            return -EINVAL;
++        }
++        if (conn->event->event.payload_len > EVENT_MAX_PAYLOAD_LENGTH) {
++            InfoLog("%s: Admin event payload is too large: %u", __func__,
++                    conn->event->event.payload_len);
++            return -EMSGSIZE;
++        }
++
++        if (conn->event->event.payload_len > 0) {
++            conn->event->payload = malloc(conn->event->event.payload_len);
++            if (conn->event->payload == NULL) {
++                ErrLog("%s: Failed to allocate the payload buffer", __func__);
++                return -ENOMEM;
++            }
++
++            conn->state = ADMIN_RECEIVE_PAYLOAD;
++            rc = client_socket_receive(&conn->client_info, conn->event->payload,
++                                       conn->event->event.payload_len);
++            conn = NULL; /* conn may have been freed by now */
++            return rc;
++        }
++        FALL_THROUGH;
++        /* fall through */
++
++    case ADMIN_RECEIVE_PAYLOAD:
++        /* We have received the payload (if any) from the admin */
++        conn->state = ADMIN_EVENT_DELIVERED;
++        rc = event_start_deliver(conn->event);
++        if (rc != 0) {
++            if (rc == -ENOSPC) {
++                /* Event limit reached, delay */
++                conn->state = ADMIN_WAIT_FOR_EVENT_LIMIT;
++                return 0;
++            }
++            return rc;
++        }
++        break;
++
++    case ADMIN_WAIT_FOR_EVENT_LIMIT:
++        /* This state is handled in admin_event_limit_underrun() */
++        break;
++
++    case ADMIN_EVENT_DELIVERED:
++        /* This state is handled in admin_event_delivered() */
++        break;
++
++    case ADMIN_SEND_REPLY:
++        /* The reply has been sent to the admin */
++        if (conn->event->admin_ref != NULL)
++            admin_put(conn->event->admin_ref);
++        conn->event->admin_ref = NULL;
++        event_free(conn->event);
++
++        conn->event = event_new(0, conn);
++        if (conn->event == NULL) {
++            ErrLog("%s: Failed to allocate a new event", __func__);
++            return -ENOMEM;
++        }
++
++        conn->state = ADMIN_RECEIVE_EVENT;
++        rc = client_socket_receive(&conn->client_info, &conn->event->event,
++                                   sizeof(conn->event->event));
++        conn = NULL; /* conn may have been freed by now */
++        return rc;
++
++    case ADMIN_HANGUP:
++        break;
++    }
++
++    return 0;
++}
++
++static void admin_event_limit_underrun(struct admin_conn_info *conn)
++{
++    int rc;
++
++    DbgLog(DL3, "%s: admin: %p state: %d", __func__, conn, conn->state);
++
++    if (conn->state != ADMIN_WAIT_FOR_EVENT_LIMIT)
++        return;
++
++    conn->state = ADMIN_EVENT_DELIVERED;
++
++    rc = event_start_deliver(conn->event);
++    if (rc != 0) {
++        if (rc == -ENOSPC) {
++            /* Event limit reached, delay */
++            conn->state = ADMIN_WAIT_FOR_EVENT_LIMIT;
++            return;
++        }
++        admin_hangup(conn);
++    }
++}
++
++static int admin_event_delivered(struct admin_conn_info *conn,
++                                 struct event_info *event)
++{
++    int rc;
++
++    DbgLog(DL3, "%s: admin: %p event: %p", __func__, conn, event);
++
++    /*
++     * A non-zero return code returned by this function causes the caller to
++     * call admin_hangup(). Thus, no need to call admin_hangup() ourselves.
++     */
++
++    if (conn->state != ADMIN_EVENT_DELIVERED) {
++        TraceLog("%s: wrong state: %d", __func__, conn->state);
++        return -EINVAL;
++    }
++
++    if (event->event.flags & EVENT_FLAGS_REPLY_REQ) {
++        if (conn->event != event) {
++            TraceLog("%s: event not the current event", __func__);
++            return -EINVAL;
++        }
++
++        DbgLog(DL3, "%s: Reply version:      %u", __func__,
++               event->reply.version);
++        DbgLog(DL3, "%s: Reply positive:     %lu", __func__,
++               event->reply.positive_replies);
++        DbgLog(DL3, "%s: Reply negative:     %lu", __func__,
++               event->reply.negative_replies);
++        DbgLog(DL3, "%s: Reply not-handled:  %lu", __func__,
++               event->reply.nothandled_replies);
++
++        conn->state = ADMIN_SEND_REPLY;
++        rc = client_socket_send(&conn->client_info, &event->reply,
++                                sizeof(event->reply));
++        return rc;
++    }
++
++    /* No reply required, free the event, and receive the next one */
++    if (event->admin_ref != NULL)
++        admin_put(event->admin_ref);
++    event->admin_ref = NULL;
++    event_free(event);
++
++    conn->event = event_new(0, conn);
++    if (conn->event == NULL) {
++        ErrLog("%s: Failed to allocate a new event", __func__);
++        return -ENOMEM;
++    }
++
++    conn->state = ADMIN_RECEIVE_EVENT;
++    rc = client_socket_receive(&conn->client_info, &conn->event->event,
++                               sizeof(conn->event->event));
++    return rc;
++}
++
++static inline void admin_get(struct admin_conn_info *conn)
++{
++    client_socket_get(&conn->client_info);
++}
++
++static inline void admin_put(struct admin_conn_info *conn)
++{
++    client_socket_put(&conn->client_info);
++}
++
++static void admin_hangup(void *client)
++{
++    struct admin_conn_info *conn = client;
++
++    DbgLog(DL0, "%s: admin: %p socket: %d state: %d", __func__, conn,
++           conn->client_info.socket, conn->state);
++
++    if (conn->state == ADMIN_HANGUP)
++        return;
++    conn->state = ADMIN_HANGUP;
++
++    /* Unlink pending event (if any) */
++    if (conn->event != NULL) {
++        if (conn->event->admin_ref != NULL)
++            admin_put(conn->event->admin_ref);
++        conn->event->admin_ref = NULL;
++        if (conn->event->proc_ref_count == 0) {
++            event_remove_from_pending_list(conn->event);
++            event_free(conn->event);
++        }
++        conn->event = NULL;
++    }
++
++    client_socket_term(&conn->client_info);
++    admin_put(conn);
++}
++
++static void admin_free(void *client)
++{
++    struct admin_conn_info *conn = client;
++    DL_NODE *node;
+ 
+-int proc_listener_socket = -1;
++    /* Remove it from the admin connections list */
++    node = dlist_find(admin_connections, conn);
++    if (node != NULL) {
++        admin_connections = dlist_remove_node(admin_connections, node);
++        listener_client_hangup(&admin_listener);
++    }
+ 
+-static void close_listener_socket(int socketfd, const char *file_path);
++    DbgLog(DL0, "%s: admin: %p", __func__, conn);
++    free(conn);
++}
+ 
+-// Creates the daemon's listener socket, to which clients will connect and
+-// retrieve slot information through.  Returns the file descriptor of the
+-// created socket.
+-static int create_listener_socket(const char *file_path)
++static int listener_socket_create(const char *file_path)
+ {
+     struct sockaddr_un address;
+     struct group *grp;
+-    int socketfd;
++    int listener_socket, err;
+ 
+-    socketfd = socket(PF_UNIX, SOCK_STREAM | SOCK_NONBLOCK, 0);
+-    if (socketfd < 0) {
+-        ErrLog("Failed to create listener socket, errno 0x%X.", errno);
++    listener_socket = socket(PF_UNIX, SOCK_STREAM | SOCK_NONBLOCK, 0);
++    if (listener_socket < 0) {
++        err = errno;
++        ErrLog("%s: Failed to create listener socket, errno %d (%s).",
++               __func__, err, strerror(err));
+         return -1;
+     }
+     if (unlink(file_path) && errno != ENOENT) {
+-        ErrLog("Failed to unlink socket file, errno 0x%X.", errno);
++        err = errno;
++        ErrLog("%s: Failed to unlink socket file, errno %d (%s).", __func__,
++               err, strerror(err));
+         goto error;
+     }
+ 
+@@ -52,50 +1249,389 @@ static int create_listener_socket(const char *file_path)
+     address.sun_family = AF_UNIX;
+     strcpy(address.sun_path, file_path);
+ 
+-    if (bind(socketfd,
++    if (bind(listener_socket,
+              (struct sockaddr *) &address, sizeof(struct sockaddr_un)) != 0) {
+-        ErrLog("Failed to bind to socket, errno 0x%X.", errno);
++        err = errno;
++        ErrLog("%s: Failed to bind to socket, errno %d (%s).", __func__, err,
++               strerror(err));
+         goto error;
+     }
+     // make socket file part of the pkcs11 group, and write accessable
+     // for that group
+     grp = getgrnam("pkcs11");
+     if (!grp) {
+-        ErrLog("Group PKCS#11 does not exist");
++        ErrLog("%s: Group PKCS#11 does not exist", __func__);
+         goto error;
+     }
+     if (chown(file_path, 0, grp->gr_gid)) {
+-        ErrLog("Could not change file group on socket, errno 0x%X.", errno);
++        err = errno;
++        ErrLog("%s: Could not change file group on socket, errno %d (%s).",
++               __func__, err, strerror(err));
+         goto error;
+     }
+     if (chmod(file_path,
+-              S_IRUSR | S_IRGRP | S_IWUSR | S_IWGRP | S_IXUSR | S_IXGRP)) {
+-        ErrLog("Could not change file permissions on socket, errno 0x%X.",
+-               errno);
++              S_IRUSR | S_IRGRP | S_IWUSR | S_IWGRP)) {
++        err = errno;
++        ErrLog("%s: Could not change file permissions on socket, errno %d (%s).",
++                __func__, err, strerror(err));
+         goto error;
+     }
+ 
+-    if (listen(socketfd, 20) != 0) {
+-        ErrLog("Failed to listen to socket, errno 0x%X.", errno);
++    if (listen(listener_socket, 20) != 0) {
++        err = errno;
++        ErrLog("%s: Failed to listen to socket, errno %d (%s).", __func__, err,
++               strerror(err));
+         goto error;
+     }
+ 
+-    return socketfd;
++    return listener_socket;
+ 
+ error:
+-    if (socketfd >= 0)
+-        close_listener_socket(socketfd, file_path);
++    if (listener_socket >= 0)
++        listener_socket_close(listener_socket, file_path);
+ 
+     return -1;
+ }
+ 
+ 
+-static void close_listener_socket(int socketfd, const char *file_path)
++static void listener_socket_close(int listener_socket, const char *file_path)
+ {
+-    close(socketfd);
++    close(listener_socket);
+     unlink(file_path);
+ }
+ 
++
++
++static int listener_notify(int events, void *private)
++{
++    struct listener_info *listener = private;
++    struct sockaddr_un address;
++    socklen_t address_length = sizeof(address);
++    int client_socket, rc, err;
++
++    if ((events & EPOLLIN) == 0)
++        return 0;
++
++    /* epoll is edge triggered. We must call accept until we get EWOULDBLOCK */
++    while (listener->num_clients < listener->max_num_clients) {
++        client_socket = accept(listener->socket, (struct sockaddr *) &address,
++                               &address_length);
++        if (client_socket < 0) {
++            err = errno;
++            if (err == EWOULDBLOCK)
++                break;
++            InfoLog("%s: Failed to accept connection on socket %d, errno %d (%s).",
++                    __func__, listener->socket, err, strerror(err));
++            return -err;
++        }
++
++        rc = listener->new_conn(client_socket, listener);
++        if (rc != 0) {
++            TraceLog("%s: new_conn callback failed for client socket %d, rc: %d",
++                      __func__, client_socket, rc);
++            close(client_socket);
++            continue;
++        }
++
++        listener->num_clients++;
++    }
++
++    return 0;
++}
++
++static int listener_client_hangup(struct listener_info *listener)
++{
++    int rc, trigger = 0;
++
++    if (listener->num_clients >= listener->max_num_clients)
++        trigger = 1; /* We were at max clients, trigger accept now */
++
++    if (listener->num_clients > 0)
++        listener->num_clients--;
++
++    if (trigger && listener->num_clients < listener->max_num_clients) {
++        rc = listener_notify(EPOLLIN, listener);
++        if (rc != 0)
++            return rc;
++    }
++
++    return 0;
++}
++
++static int listener_create(const char *file_path,
++                           struct listener_info *listener,
++                           int (* new_conn)(int socket,
++                                            struct listener_info *listener),
++                           unsigned long max_num_clients)
++{
++    struct epoll_event evt;
++    int rc, err;
++
++    if (listener == NULL || new_conn == NULL)
++        return FALSE;
++
++    memset(listener, 0, sizeof(*listener));
++    epoll_info_init(&listener->ep_info, listener_notify, NULL, listener);
++    listener->file_path = file_path;
++    listener->new_conn = new_conn;
++    listener->max_num_clients = max_num_clients;
++
++    listener->socket = listener_socket_create(file_path);
++    if (listener->socket < 0)
++        return FALSE;
++
++    evt.events = EPOLLIN | EPOLLET;
++    evt.data.ptr = &listener->ep_info;
++    rc = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, listener->socket, &evt);
++    if (rc != 0) {
++        err = errno;
++        TraceLog("%s: Failed add listener socket %d to epoll, errno %d (%s).",
++                  __func__, listener->socket, err, strerror(err));
++        return FALSE;
++    }
++
++    return TRUE;
++}
++
++static void listener_term(struct listener_info *listener)
++{
++    if (listener == NULL || listener->socket < 0)
++        return;
++
++    epoll_ctl(epoll_fd, EPOLL_CTL_DEL, listener->socket, NULL);
++    listener_socket_close(listener->socket, listener->file_path);
++}
++
++#ifdef WITH_LIBUDEV
++
++static int udev_mon_init(const char *subsystem, struct udev_mon *udev_mon)
++{
++    struct epoll_event evt;
++    int rc, err;
++
++    if (subsystem == NULL || udev_mon == NULL)
++        return FALSE;
++
++    udev_mon->delayed_event = 0;
++
++    udev_mon->udev = udev_new();
++    if (udev_mon->udev == NULL) {
++        ErrLog("%s: udev_new failed", __func__);
++        goto error;
++    }
++
++    udev_mon->mon = udev_monitor_new_from_netlink(udev_mon->udev, "udev");
++    if (udev_mon->mon == NULL) {
++        ErrLog("%s: udev_monitor_new_from_netlink failed", __func__);
++        goto error;
++    }
++
++    /*
++     * Try to increase the receive buffer size. This may fail if the required
++     * privileges are not given. Ignore if it fails.
++     */
++    udev_monitor_set_receive_buffer_size(udev_mon->mon, UDEV_RECV_BUFFFER_SIZE);
++
++    rc = udev_monitor_filter_add_match_subsystem_devtype(udev_mon->mon,
++                                                         subsystem, NULL);
++    if (rc != 0) {
++        ErrLog("%s: udev_monitor_filter_add_match_subsystem_devtype failed: "
++               "rc=%d", __func__, rc);
++        goto error;
++    }
++
++    rc = udev_monitor_enable_receiving(udev_mon->mon);
++    if (rc != 0) {
++        ErrLog("%s: udev_monitor_enable_receiving failed: rc=%d", __func__, rc);
++        goto error;
++    }
++
++    udev_mon->socket = udev_monitor_get_fd(udev_mon->mon);
++    if (udev_mon->socket < 0) {
++        ErrLog("%s: udev_monitor_get_fd failed", __func__);
++        goto error;
++    }
++
++    epoll_info_init(&udev_mon->ep_info, udev_mon_notify, NULL, udev_mon);
++
++    evt.events = EPOLLIN | EPOLLET;
++    evt.data.ptr = &udev_mon->ep_info;
++    rc = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, udev_mon->socket, &evt);
++    if (rc != 0) {
++        err = errno;
++        ErrLog("%s: Failed add udev_mon socket %d to epoll, errno %d (%s).",
++                __func__, udev_mon->socket, err, strerror(err));
++        goto error;
++    }
++
++    /* Epoll is edge triggered, thus try to receive once */
++    rc = udev_mon_notify(EPOLLIN, udev_mon);
++    if (rc != 0)
++        goto error;
++
++    return TRUE;
++
++error:
++    udev_mon_term(udev_mon);
++    return FALSE;
++}
++
++
++static int udev_mon_handle_device(struct udev_mon *udev_mon,
++                                  struct udev_device *dev)
++{
++    const char *action, *devname, *devpath, *devtype, *dev_type_prop;
++    unsigned int card, domain, dev_type;
++    struct event_info *event;
++    event_udev_apqn_data_t *apqn_data;
++    int rc;
++
++    UNUSED(udev_mon);
++
++    action = udev_device_get_action(dev);
++    devname = udev_device_get_sysname(dev);
++    devpath = udev_device_get_devpath(dev);
++    devtype = udev_device_get_devtype(dev);
++    dev_type_prop = udev_device_get_property_value(dev, UDEV_PROERTY_DEVTYPE);
++
++    if (action == NULL || devname == NULL || devpath == NULL ||
++        devtype == NULL || dev_type_prop == NULL)
++        return 0;
++
++    DbgLog(DL3, "%s: Uevent: ACTION=%s DEVNAME=%s DEVPATH=%s DEVTYPE=%s "
++           "DEV_TYPE=%s", __func__, action, devname, devpath, devtype,
++           dev_type_prop);
++
++    /* We are only interested in bind and unbind events ... */
++    if (strcmp(action, UDEV_ACTION_BIND) != 0 &&
++        strcmp(action, UDEV_ACTION_UNBIND) != 0)
++        return 0;
++
++    /* ... for an APQN device */
++    if (strcmp(devtype, UDEV_ACTION_DEVTYPE_APQN) != 0)
++        return 0;
++
++    if (sscanf(devname, "%x.%x", &card, &domain) != 2) {
++        TraceLog("%s: failed to parse APQN from DEVNAME: %s", __func__, devname);
++        return -EIO;
++    }
++    if (sscanf(dev_type_prop, "%x", &dev_type) != 1) {
++        TraceLog("%s: failed to parse DEV_TYPE: %s", __func__, dev_type_prop);
++        return -EIO;
++    }
++
++    event = event_new(sizeof(event_udev_apqn_data_t), NULL);
++    if (event == NULL) {
++        ErrLog("%s: failed to allocate an event", __func__);
++        return -ENOMEM;
++    }
++
++    if (strcmp(udev_device_get_action(dev), UDEV_ACTION_BIND) == 0)
++        event->event.type = EVENT_TYPE_APQN_ADD;
++    else
++        event->event.type = EVENT_TYPE_APQN_REMOVE;
++    event->event.flags = EVENT_FLAGS_NONE;
++    event->event.token_type = EVENT_TOK_TYPE_ALL;
++    memset(event->event.token_label, ' ',
++           sizeof(event->event.token_label));
++
++    apqn_data = (event_udev_apqn_data_t *)event->payload;
++    apqn_data->card = card;
++    apqn_data->domain = domain;
++    apqn_data->device_type = dev_type;
++
++    DbgLog(DL3, "%s: Event version:      %u", __func__, event->event.version);
++    DbgLog(DL3, "%s: Event type:         0x%08x", __func__, event->event.type);
++    DbgLog(DL3, "%s: Event flags:        0x%08x", __func__, event->event.flags);
++    DbgLog(DL3, "%s: Event token_type:   0x%08x", __func__,
++           event->event.token_type);
++    DbgLog(DL3, "%s: Event token_name:   '%.32s'", __func__,
++           event->event.token_label);
++    DbgLog(DL3, "%s: Event process_id:   %u", __func__, event->event.process_id);
++    DbgLog(DL3, "%s: Event payload_len:  %u", __func__,
++           event->event.payload_len);
++
++    DbgLog(DL3, "%s: Payload: card:      %u", __func__, apqn_data->card);
++    DbgLog(DL3, "%s: Payload: domain:    %u", __func__, apqn_data->domain);
++    DbgLog(DL3, "%s: Payload: dev.type:  %u", __func__, apqn_data->device_type);
++
++    rc = event_start_deliver(event);
++    if (rc != 0) {
++        if (rc == -ENOSPC) {
++            /* Event limit reached, delay event delivery */
++            udev_mon->delayed_event = event;
++            return -ENOSPC;
++        }
++        event_free(event);
++        return rc;
++    }
++
++    return 0;
++}
++
++static int udev_mon_notify(int events, void *private)
++{
++    struct udev_mon *udev_mon = private;
++    struct udev_device *dev;
++    struct event_info *event;
++    int rc;
++
++    DbgLog(DL3, "%s: Epoll event on udev_mon socket %d: events: 0x%x",
++           __func__, udev_mon->socket, events);
++
++    if (udev_mon->delayed_event != NULL) {
++        /* Deliver delayed event first */
++        event = udev_mon->delayed_event;
++        udev_mon->delayed_event = NULL;
++
++        rc = event_start_deliver(event);
++        if (rc != 0) {
++            if (rc == -ENOSPC) {
++                /* Event limit reached, delay event delivery */
++                udev_mon->delayed_event = event;
++                return 0;
++            }
++            event_free(event);
++            return rc;
++        }
++    }
++
++    while (1) {
++        dev = udev_monitor_receive_device(udev_mon->mon);
++        if (dev == NULL)
++            break; /* this is just like EWOULDBLOCK */
++
++        rc = udev_mon_handle_device(udev_mon, dev);
++        if (rc != 0)
++            TraceLog("%s: udev_mon_handle_device failed, rc: %d", __func__, rc);
++
++        udev_device_unref(dev);
++
++        /* If event limit reached, stop receiving more events */
++        if (rc == -ENOSPC)
++            break;
++    };
++
++    return 0;
++}
++
++static void udev_mon_term(struct udev_mon *udev_mon)
++{
++    if (udev_mon == NULL)
++        return;
++
++    epoll_ctl(epoll_fd, EPOLL_CTL_DEL, udev_mon->socket, NULL);
++    if (udev_mon->udev != NULL)
++        udev_unref(udev_mon->udev);
++    if (udev_mon->mon != NULL)
++        udev_monitor_unref(udev_mon->mon);
++
++    if (udev_mon->delayed_event != NULL)
++        event_free(udev_mon->delayed_event);
++}
++
++#endif
++
+ int init_socket_data(Slot_Mgr_Socket_t *socketData)
+ {
+     unsigned int processed = 0;
+@@ -106,7 +1642,7 @@ int init_socket_data(Slot_Mgr_Socket_t *socketData)
+ 
+     /* check that we read in correct amount of slots */
+     if (processed != NumberSlotsInDB) {
+-        ErrLog("Failed to populate slot info.\n");
++        ErrLog("%s: Failed to populate slot info.", __func__);
+         return FALSE;
+     }
+ 
+@@ -115,72 +1651,277 @@ int init_socket_data(Slot_Mgr_Socket_t *socketData)
+ 
+ int socket_connection_handler(int timeout_secs)
+ {
+-    int returnVal;
+-    fd_set set;
+-    struct timeval timeout;
+-
+-    FD_ZERO(&set);
+-    FD_SET(proc_listener_socket, &set);
++    struct epoll_event events[MAX_EPOLL_EVENTS];
++    int num_events, i, rc, err;
++    struct epoll_info *info;
+ 
+-    timeout.tv_sec = timeout_secs;
+-    timeout.tv_usec = 0;
+-
+-    returnVal = select(proc_listener_socket + 1, &set, NULL, NULL, &timeout);
+-    if (returnVal == -1) {
+-        ErrLog("select failed on socket connection, errno 0x%X.", errno);
+-        return FALSE;
+-    } else if (returnVal == 0) {
+-        // select call timed out, return
+-        return FALSE;
+-    } else {
+-        struct sockaddr_un address;
+-        socklen_t address_length = sizeof(address);
+-
+-        int connectionfd = accept(proc_listener_socket,
+-                                  (struct sockaddr *) &address,
+-                                  &address_length);
+-        if (connectionfd < 0) {
+-            if (errno != EAGAIN && errno != EWOULDBLOCK) {
+-                /* These errors are allowed since
+-                 * socket is non-blocking
+-                 */
+-                ErrLog("Failed to accept socket connection, errno 0x%X.",
+-                       errno);
+-            }
++    do {
++        num_events = epoll_wait(epoll_fd, events, MAX_EPOLL_EVENTS,
++                                timeout_secs * 1000);
++        if (num_events < 0) {
++            err = errno;
++            if (err == EINTR)
++                continue;
++            ErrLog("%s: epoll_wait failed, errno %d (%s).", __func__, err,
++                   strerror(err));
+             return FALSE;
+         }
+ 
+-        DbgLog(DL0, "Accepted connection from process: socket: %d", 
+-               connectionfd);
++        /*
++         * Inc ref count of all epoll_infos returned by epoll before handling
++         * any of them via notify. The notify callback may hangup any of
++         * the connections associated with the returned epoll_infos, and we
++         * need to avoid them getting freed before we all handled them.
++         */
++        for (i = 0; i < num_events; i++)
++            epoll_info_get(events[i].data.ptr);
+ 
+-        if (write(connectionfd, &socketData, sizeof(socketData)) !=
+-            sizeof(socketData)) {
+-            ErrLog("Failed to write socket data, errno 0x%X.", errno);
+-            close(connectionfd);
+-            return FALSE;
++        for (i = 0; i < num_events; i++) {
++            info = events[i].data.ptr;
++            if (info == NULL || info->notify == NULL)
++                continue;
++
++            rc = info->notify(events[i].events, info->private);
++            if (rc != 0)
++                TraceLog("%s: notify callback failed, rc: %d", __func__, rc);
++
++            epoll_info_put(info);
+         }
+-        close(connectionfd);
+-        return TRUE;
+-    }
++    } while (num_events > 0 && rc == 0); /* num_events = 0: timeout */
++
++    return TRUE;
+ }
+ 
+ int init_socket_server()
+ {
+-    proc_listener_socket = create_listener_socket(PROC_SOCKET_FILE_PATH);
+-    if (proc_listener_socket < 0)
++    int err;
++
++    epoll_fd = epoll_create1(0);
++    if (epoll_fd < 0) {
++        err = errno;
++        ErrLog("%s: Failed to open epoll socket, errno %d (%s).", __func__, err,
++               strerror(err));
++        return FALSE;
++    }
++
++    if (!listener_create(PROC_SOCKET_FILE_PATH, &proc_listener,
++                         proc_new_conn, NUMBER_PROCESSES_ALLOWED)) {
++        term_socket_server();
++        return FALSE;
++    }
++
++    if (!listener_create(ADMIN_SOCKET_FILE_PATH, &admin_listener,
++                         admin_new_conn, NUMBER_ADMINS_ALLOWED)) {
++        term_socket_server();
+         return FALSE;
++    }
++
++#ifdef WITH_LIBUDEV
++    if (!udev_mon_init(UDEV_SUBSYSTEM_AP, &udev_mon)) {
++        term_socket_server();
++        return FALSE;
++    }
++#endif
+ 
+-    DbgLog(DL0, "Socket server started");
++    DbgLog(DL0, "%s: Socket server started", __func__);
+ 
+     return TRUE;
+ }
+ 
+ int term_socket_server()
+ {
+-    if (proc_listener_socket >= 0)
+-        close_listener_socket(proc_listener_socket, PROC_SOCKET_FILE_PATH);
++    DL_NODE *node, *next;
++
++#ifdef WITH_LIBUDEV
++    udev_mon_term(&udev_mon);
++#endif
++
++    listener_term(&proc_listener);
++    listener_term(&admin_listener);
++
++    node = dlist_get_first(proc_connections);
++    while (node != NULL) {
++        next = dlist_next(node);
++        proc_hangup(node->data);
++        node = next;
++    }
++    dlist_purge(proc_connections);
+ 
+-    DbgLog(DL0, "Socket server stopped");
++    node = dlist_get_first(admin_connections);
++    while (node != NULL) {
++        next = dlist_next(node);
++        admin_hangup(node->data);
++        node = next;
++    }
++    dlist_purge(admin_connections);
++
++    node = dlist_get_first(pending_events);
++    while (node != NULL) {
++        next = dlist_next(node);
++        event_free((struct event_info *)node->data);
++        node = next;
++    }
++    dlist_purge(pending_events);
++
++    if (epoll_fd >= 0)
++        close(epoll_fd);
++    epoll_fd = -1;
++
++    DbgLog(DL0, "%s: Socket server stopped", __func__);
+ 
+     return TRUE;
+ }
++
++#ifdef DEV
++
++static void dump_listener(struct listener_info *listener)
++{
++    DbgLog(DL0, "    socket: %d", listener->socket);
++    DbgLog(DL0, "    file_path: %s", listener->file_path);
++    DbgLog(DL0, "    ep_info.ref_count: %lu", listener->ep_info.ref_count);
++    DbgLog(DL0, "    num_clients: %lu", listener->num_clients);
++    DbgLog(DL0, "    max_num_clients: %lu", listener->max_num_clients);
++}
++
++static void dump_event_msg(event_msg_t *event, int indent)
++{
++    DbgLog(DL0, "%*sevent version: %u", indent, "", event->version);
++    DbgLog(DL0, "%*sevent type: %08x", indent, "", event->type);
++    DbgLog(DL0, "%*sevent flags: %08x", indent, "", event->flags);
++    DbgLog(DL0, "%*sevent token_type: %08x", indent, "", event->token_type);
++    DbgLog(DL0, "%*sevent token_label: '%.32s'", indent, "", event->token_label);
++    DbgLog(DL0, "%*sevent process_id: %lu", indent, "", event->process_id);
++    DbgLog(DL0, "%*sevent payload_len: %u", indent, "", event->payload_len);
++}
++
++static void dump_event_reply(event_reply_t *reply, int indent)
++{
++    DbgLog(DL0, "%*sreply version: %u", indent, "", reply->version);
++    DbgLog(DL0, "%*sreply positive_replies: %u", indent, "", reply->positive_replies);
++    DbgLog(DL0, "%*sreply negative_replies: %u", indent, "", reply->negative_replies);
++    DbgLog(DL0, "%*sreply nothandled_replies: %u", indent, "", reply->nothandled_replies);
++}
++
++static void dump_event_info(struct event_info *event, int indent)
++{
++    dump_event_msg(&event->event, indent);
++    dump_event_reply(&event->reply, indent);
++    DbgLog(DL0, "%*sproc_ref_count: %lu", indent, "", event->proc_ref_count);
++    if (event->admin_ref != NULL)
++        DbgLog(DL0, "%*sadmin_ref: %p", indent, "", event->admin_ref);
++    else
++        DbgLog(DL0, "%*sadmin_ref: None", indent, "");
++}
++
++static void dump_proc_conn(struct proc_conn_info *proc_conn)
++{
++    DL_NODE *node;
++    unsigned long i;
++
++    DbgLog(DL0, "      socket: %d", proc_conn->client_info.socket);
++    DbgLog(DL0, "      state: %d", proc_conn->state);
++    DbgLog(DL0, "      ref-count: %lu", proc_conn->client_info.ep_info.ref_count);
++    DbgLog(DL0, "      xfer state: %d", proc_conn->client_info.xfer_state);
++    DbgLog(DL0, "      xfer size: %d", proc_conn->client_info.xfer_size);
++    DbgLog(DL0, "      xfer offset: %d", proc_conn->client_info.xfer_offset);
++    DbgLog(DL0, "      pending events:");
++    node = dlist_get_first(proc_conn->events);
++    i = 1;
++    while (node != NULL) {
++        DbgLog(DL0, "        event %lu (%p):", i, node->data);
++        dump_event_info(node->data, 10);
++        node = dlist_next(node);
++        i++;
++    }
++    if (proc_conn->event != NULL) {
++        DbgLog(DL0, "      current event:");
++        dump_event_info(proc_conn->event, 8);
++        DbgLog(DL0, "      current reply:");
++        dump_event_reply(&proc_conn->reply, 8);
++    } else {
++        DbgLog(DL0, "      current event: none");
++    }
++}
++
++static void dump_admin_conn(struct admin_conn_info *admin_conn)
++{
++    DbgLog(DL0, "      socket: %d", admin_conn->client_info.socket);
++    DbgLog(DL0, "      state: %d", admin_conn->state);
++    DbgLog(DL0, "      ref-count: %lu", admin_conn->client_info.ep_info.ref_count);
++    DbgLog(DL0, "      xfer state: %d", admin_conn->client_info.xfer_state);
++    DbgLog(DL0, "      xfer size: %d", admin_conn->client_info.xfer_size);
++    DbgLog(DL0, "      xfer offset: %d", admin_conn->client_info.xfer_offset);
++    if (admin_conn->event != NULL) {
++        DbgLog(DL0, "      current event (%p):", admin_conn->event);
++        dump_event_info(admin_conn->event, 8);
++    } else {
++        DbgLog(DL0, "      current event: none");
++    }
++}
++
++#ifdef WITH_LIBUDEV
++void dump_udev_mon(struct udev_mon *udev_mon)
++{
++    DbgLog(DL0, "    socket: %d", udev_mon->socket);
++    DbgLog(DL0, "    udev: %p", udev_mon->udev);
++    DbgLog(DL0, "    mon: %p", udev_mon->mon);
++    DbgLog(DL0, "    ep_info.ref_count: %lu", udev_mon->ep_info.ref_count);
++    if (udev_mon->delayed_event != NULL) {
++        DbgLog(DL0, "    delayed_event (%p):", udev_mon->delayed_event);
++        dump_event_info(udev_mon->delayed_event, 6);
++    } else {
++        DbgLog(DL0, "    delayed_event: node");
++    }
++}
++#endif
++
++void dump_socket_handler()
++{
++    DL_NODE *node;
++    unsigned long i;
++
++    DbgLog(DL0, "%s: Dump of socket handler data:", __func__);
++    DbgLog(DL0, "  epoll_fd: %d", epoll_fd);
++
++    DbgLog(DL0, "  proc_listener (%p): ", &proc_listener);
++    dump_listener(&proc_listener);
++
++    DbgLog(DL0, "  proc_connections: ");
++    node = dlist_get_first(proc_connections);
++    i = 1;
++    while (node != NULL) {
++        DbgLog(DL0, "    proc_connection %lu (%p): ", i, node->data);
++        dump_proc_conn(node->data);
++        i++;
++        node = dlist_next(node);
++    }
++
++    DbgLog(DL0, "  admin_listener (%p): ", &admin_listener);
++    dump_listener(&admin_listener);
++
++    DbgLog(DL0, "  admin_connections: ");
++    node = dlist_get_first(admin_connections);
++    i = 1;
++    while (node != NULL) {
++        DbgLog(DL0, "    admin_connection %lu (%p): ", i, node->data);
++        dump_admin_conn(node->data);
++        i++;
++        node = dlist_next(node);
++    }
++
++#ifdef WITH_LIBUDEV
++    DbgLog(DL0, "  udev_mon (%p): ", &udev_mon);
++    dump_udev_mon(&udev_mon);
++#endif
++
++    DbgLog(DL0, "  pending events (%lu): ", pending_events_count);
++    node = dlist_get_first(pending_events);
++    i = 1;
++    while (node != NULL) {
++        DbgLog(DL0, "    event %lu (%p): ", i, node->data);
++        dump_event_info(node->data, 6);
++        i++;
++        node = dlist_next(node);
++    }
++}
++#endif
diff --git a/SOURCES/opencryptoki-3.16.0-4e3b43c3d8844402c04a66b55c6c940f965109f0.patch b/SOURCES/opencryptoki-3.16.0-4e3b43c3d8844402c04a66b55c6c940f965109f0.patch
new file mode 100644
index 0000000..8a3d581
--- /dev/null
+++ b/SOURCES/opencryptoki-3.16.0-4e3b43c3d8844402c04a66b55c6c940f965109f0.patch
@@ -0,0 +1,47 @@
+commit 4e3b43c3d8844402c04a66b55c6c940f965109f0
+Author: Ingo Franzki <ifranzki@linux.ibm.com>
+Date:   Mon May 3 10:05:07 2021 +0200
+
+    SOFT: Check the EC Key on C_CreateObject and C_DeriveKey
+    
+    When constructing an OpenSSL EC public or private key from PKCS#11
+    attributes or ECDH public data, check that the key is valid, i.e. that
+    the point is on the curve.
+    
+    This prevents one from creating an EC key object via C_CreateObject with
+    invalid key data. It also prevents C_DeriveKey to derive a secret using
+    ECDH with an EC public key (public data) that uses a different curve
+    or is invalid by other means.
+    
+    Signed-off-by: Ingo Franzki <ifranzki@linux.ibm.com>
+
+diff --git a/usr/lib/soft_stdll/soft_specific.c b/usr/lib/soft_stdll/soft_specific.c
+index c30be1da..aeff39a9 100644
+--- a/usr/lib/soft_stdll/soft_specific.c
++++ b/usr/lib/soft_stdll/soft_specific.c
+@@ -4365,6 +4365,12 @@ static CK_RV fill_ec_key_from_pubkey(EC_KEY *ec_key, const CK_BYTE *data,
+         goto out;
+     }
+ 
++    if (!EC_KEY_check_key(ec_key)) {
++        TRACE_ERROR("EC_KEY_check_key failed\n");
++        rc = CKR_PUBLIC_KEY_INVALID;
++        goto out;
++    }
++
+ out:
+     if (allocated && ecpoint != NULL)
+         free(ecpoint);
+@@ -4404,6 +4410,12 @@ static CK_RV fill_ec_key_from_privkey(EC_KEY *ec_key, const CK_BYTE *data,
+         goto out;
+     }
+ 
++    if (!EC_KEY_check_key(ec_key)) {
++        TRACE_ERROR("EC_KEY_check_key failed\n");
++        rc = CKR_FUNCTION_FAILED;
++        goto out;
++    }
++
+ out:
+     if (point != NULL)
+         EC_POINT_free(point);
diff --git a/SOURCES/opencryptoki-3.16.0-5824364d995e5d2418f885ee57e377e11d1b3302.patch b/SOURCES/opencryptoki-3.16.0-5824364d995e5d2418f885ee57e377e11d1b3302.patch
new file mode 100644
index 0000000..c38fef0
--- /dev/null
+++ b/SOURCES/opencryptoki-3.16.0-5824364d995e5d2418f885ee57e377e11d1b3302.patch
@@ -0,0 +1,28 @@
+commit 5824364d995e5d2418f885ee57e377e11d1b3302
+Author: Ingo Franzki <ifranzki@linux.ibm.com>
+Date:   Wed Jul 7 13:44:46 2021 +0200
+
+    pkcstok_migrate: Quote strings with spaces in opencryptoki.conf
+    
+    When modifying opencryptoki.conf during token migration, put quotes
+    around strings that contain spaces, e.g. for the slot description and
+    manufacturer.
+    
+    Signed-off-by: Ingo Franzki <ifranzki@linux.ibm.com>
+
+diff --git a/usr/sbin/pkcstok_migrate/pkcstok_migrate.c b/usr/sbin/pkcstok_migrate/pkcstok_migrate.c
+index 94fd1196..3df1596e 100644
+--- a/usr/sbin/pkcstok_migrate/pkcstok_migrate.c
++++ b/usr/sbin/pkcstok_migrate/pkcstok_migrate.c
+@@ -2107,7 +2107,10 @@ static int parseupdate_key_str(void *private, int tok, const char *val)
+ {
+ 	struct parseupdate *u = (struct parseupdate *)private;
+ 
+-    if (tok != KW_TOKVERSION)
++    if (tok != KW_HWVERSION && tok != KW_FWVERSION &&
++        strchr(val, ' ') != NULL)
++        fprintf(u->f, "  %s = \"%s\"", keyword_token_to_str(tok), val);
++    else if (tok != KW_TOKVERSION)
+         fprintf(u->f, "  %s = %s", keyword_token_to_str(tok), val);
+     return 0;
+ }
diff --git a/SOURCES/opencryptoki-3.16.0-69244a5e0d9dfec3ef534b19b89a541576bb17dc.patch b/SOURCES/opencryptoki-3.16.0-69244a5e0d9dfec3ef534b19b89a541576bb17dc.patch
new file mode 100644
index 0000000..0494a35
--- /dev/null
+++ b/SOURCES/opencryptoki-3.16.0-69244a5e0d9dfec3ef534b19b89a541576bb17dc.patch
@@ -0,0 +1,23 @@
+commit 69244a5e0d9dfec3ef534b19b89a541576bb17dc
+Author: Ingo Franzki <ifranzki@linux.ibm.com>
+Date:   Tue Feb 9 10:47:57 2021 +0100
+
+    TRACE: Use gettid() if SYS_gettid is not defined
+    
+    Also print the thread ID in the trace, if SYS_gettid is not defined.
+    
+    Signed-off-by: Ingo Franzki <ifranzki@linux.ibm.com>
+
+diff --git a/usr/lib/common/trace.c b/usr/lib/common/trace.c
+index 678c0b96..bdc5256a 100644
+--- a/usr/lib/common/trace.c
++++ b/usr/lib/common/trace.c
+@@ -33,6 +33,8 @@
+ 
+ #ifdef SYS_gettid
+ #define __gettid() syscall(SYS_gettid)
++#else
++#define __gettid() gettid()
+ #endif
+ 
+ pthread_mutex_t tlmtx = PTHREAD_MUTEX_INITIALIZER;
diff --git a/SOURCES/opencryptoki-3.16.0-7b7d83c571ceb3050969359817d4145600f14ae8.patch b/SOURCES/opencryptoki-3.16.0-7b7d83c571ceb3050969359817d4145600f14ae8.patch
new file mode 100644
index 0000000..86ba3f0
--- /dev/null
+++ b/SOURCES/opencryptoki-3.16.0-7b7d83c571ceb3050969359817d4145600f14ae8.patch
@@ -0,0 +1,367 @@
+commit 7b7d83c571ceb3050969359817d4145600f14ae8
+Author: Ingo Franzki <ifranzki@linux.ibm.com>
+Date:   Fri Apr 9 17:07:31 2021 +0200
+
+    Check CKF_LIBRARY_CANT_CREATE_OS_THREADS at C_Initialize
+    
+    Fail if flag CKF_LIBRARY_CANT_CREATE_OS_THREADS is set at C_Initialize,
+    and event support is enabled (this is the default). We need to use pthreads
+    for the event thread, so we can't work if CKF_LIBRARY_CANT_CREATE_OS_THREADS
+    is set. Fail with CKR_NEED_TO_CREATE_THREADS if so.
+    
+    The event support can be globally disabled using keyword 'disable-event-support'
+    in opencryptoki.conf. This disables pkcsslots to accept admin connections,
+    and it does not monitor for AP UDEV events (on s390 platform). No event
+    thread is started in the opencryptoki processes, thus we can accept if flag
+    CKF_LIBRARY_CANT_CREATE_OS_THREADS is set in that case.
+    
+    Signed-off-by: Ingo Franzki <ifranzki@linux.ibm.com>
+
+diff --git a/man/man5/opencryptoki.conf.5.in b/man/man5/opencryptoki.conf.5.in
+index 71218f79..7dc676ab 100644
+--- a/man/man5/opencryptoki.conf.5.in
++++ b/man/man5/opencryptoki.conf.5.in
+@@ -10,8 +10,16 @@ pkcs#11 slots. At startup, the pkcsslotd daemon parses this file to
+ determine which slots will be made available.
+ 
+ .SH SYNTAX
+-This file is made up of slot descriptions. Each slot description
+-is composed of a slot number, brackets and key-value pairs.
++This file is made up of optional global definitions, and slot descriptions.
++
++The following global definitions are valid:
++
++.TP
++.BR disable-event-support
++If this keyword is specified the openCryptoki event support is disabled.
++
++.P
++Each slot description is composed of a slot number, brackets and key-value pairs.
+ 
+  slot number
+  {
+diff --git a/usr/include/slotmgr.h b/usr/include/slotmgr.h
+index e37368a5..451a8cf1 100644
+--- a/usr/include/slotmgr.h
++++ b/usr/include/slotmgr.h
+@@ -99,6 +99,7 @@ typedef struct {
+     LW_SHM_TYPE *shm_addr;      // token specific shm address
+ } Slot_Info_t;
+ 
++#define FLAG_EVENT_SUPPORT_DISABLED   0x01
+ 
+ #ifdef PKCS64
+ 
+@@ -200,6 +201,7 @@ typedef struct {
+ 
+ typedef struct {
+     uint8 num_slots;
++    uint8 flags;
+     CK_INFO_64 ck_info;
+     Slot_Info_t_64 slot_info[NUMBER_SLOTS_MANAGED];
+ } Slot_Mgr_Socket_t;
+@@ -214,6 +216,7 @@ typedef struct {
+ 
+ typedef struct {
+     uint8 num_slots;
++    uint8 flags;
+     CK_INFO ck_info;
+     Slot_Info_t slot_info[NUMBER_SLOTS_MANAGED];
+ } Slot_Mgr_Socket_t;
+diff --git a/usr/lib/api/api_interface.c b/usr/lib/api/api_interface.c
+index 2873a20a..6517ca6c 100644
+--- a/usr/lib/api/api_interface.c
++++ b/usr/lib/api/api_interface.c
+@@ -308,7 +308,8 @@ void parent_fork_after()
+         return;
+ 
+     /* Restart the event thread in the parent when fork is complete */
+-    if (Anchor->event_thread == 0)
++    if ((Anchor->SocketDataP.flags & FLAG_EVENT_SUPPORT_DISABLED) == 0 &&
++        Anchor->event_thread == 0)
+         start_event_thread();
+ }
+ 
+@@ -2752,13 +2753,7 @@ CK_RV C_Initialize(CK_VOID_PTR pVoid)
+                 goto error;
+             }
+         }
+-        // If we EVER need to create threads from this library we must
+-        // check the Flags for the Can_Create_OS_Threads flag
+-        // Right now the library DOES NOT create threads and therefore this
+-        // check is irrelavant.
+-        if (pArg->flags & CKF_LIBRARY_CANT_CREATE_OS_THREADS) {
+-            TRACE_DEVEL("Can't create OS threads...This is OK\n");
+-        }
++
+         // Since this is an initialization path, we will be verbose in the
+         // code rather than efficient.
+         //
+@@ -2848,7 +2843,21 @@ CK_RV C_Initialize(CK_VOID_PTR pVoid)
+         rc = CKR_FUNCTION_FAILED;
+         goto error_shm;
+     }
+-    // Initialize structure values
++
++    if (pVoid != NULL) {
++        pArg = (CK_C_INITIALIZE_ARGS *) pVoid;
++
++        if ((Anchor->SocketDataP.flags & FLAG_EVENT_SUPPORT_DISABLED) == 0 &&
++            (pArg->flags & CKF_LIBRARY_CANT_CREATE_OS_THREADS) != 0) {
++            TRACE_ERROR("Flag CKF_LIBRARY_CANT_CREATE_OS_THREADS is set and "
++                        "event support is enabled\n");
++            OCK_SYSLOG(LOG_ERR, "C_Initialize: Application specified that "
++                       "library can't create OS threads. PKCS11 Module requires "
++                       "to create threads when event support is enabled.\n");
++            rc = CKR_NEED_TO_CREATE_THREADS;
++            goto error;
++        }
++    }
+ 
+     //Register with pkcsslotd
+     if (!API_Register()) {
+@@ -2867,7 +2876,8 @@ CK_RV C_Initialize(CK_VOID_PTR pVoid)
+     }
+ 
+     /* Start event receiver thread */
+-    if (start_event_thread() != 0) {
++    if ((Anchor->SocketDataP.flags & FLAG_EVENT_SUPPORT_DISABLED) == 0 &&
++        start_event_thread() != 0) {
+         TRACE_ERROR("Failed to start event thread\n");
+ 
+         // unload all the STDLL's from the application
+diff --git a/usr/lib/common/configparser.h b/usr/lib/common/configparser.h
+index 13ca648d..b3c32496 100644
+--- a/usr/lib/common/configparser.h
++++ b/usr/lib/common/configparser.h
+@@ -35,6 +35,7 @@ typedef int  (*end_slot_f)(void *private);
+ typedef int  (*key_str_f)(void *private, int tok, const char *val);
+ typedef int  (*key_vers_f)(void *private, int tok, unsigned int vers);
+ typedef void (*eolcomment_f)(void *private, const char *comment);
++typedef void (*disab_event_supp_f)(void *private);
+ /*
+  * Report an error.  If the error is not reported by the parser itself
+  * but via one of the parse functions, \c parsermsg will be \c NULL.
+@@ -52,6 +53,7 @@ typedef void (*error_f)(void *private, int line, const char *parsermsg);
+  */
+ struct parsefuncs {
+     ockversion_f  version;
++    disab_event_supp_f disab_event_supp;
+     eol_f         eol;
+     begin_slot_f  begin_slot;
+     end_slot_f    end_slot;
+diff --git a/usr/lib/common/lexer.l b/usr/lib/common/lexer.l
+index b35a0b72..38cbcb70 100644
+--- a/usr/lib/common/lexer.l
++++ b/usr/lib/common/lexer.l
+@@ -69,6 +69,7 @@ extern char *configparse_strdup(const char *s);
+ 
+ version                 return OCKVERSION;
+ slot                    return SLOT;
++disable-event-support   return DISABLE_EVENT_SUPPORT;
+ 
+ [^\"= \t\n]+		{
+ 			  yylval.str = configparse_strdup(yytext);
+diff --git a/usr/lib/common/parser.y b/usr/lib/common/parser.y
+index 86806fcb..40c3994d 100644
+--- a/usr/lib/common/parser.y
++++ b/usr/lib/common/parser.y
+@@ -65,7 +65,7 @@ int lookup_keyword(const char *key);
+     int err;
+ }
+ 
+-%token EQUAL DOT SLOT EOL OCKVERSION BEGIN_DEF END_DEF
++%token EQUAL DOT SLOT EOL OCKVERSION BEGIN_DEF END_DEF DISABLE_EVENT_SUPPORT
+ %token <str> STRING
+ %token <str> KEYWORD
+ %token <num> INTEGER
+@@ -81,6 +81,7 @@ config_file:
+ 
+ sections:
+ 	version_def eolcomment
++	| disable_event_support_def eolcomment
+ 	| SLOT INTEGER BEGIN_DEF
+ 	{
+         if (parsefuncs->begin_slot && parsefuncs->begin_slot(parsedata, $2, 0)) {
+@@ -125,6 +126,13 @@ version_def:
+         }
+         configparse_freestringsfrom($2);
+     }
++    
++disable_event_support_def:
++    DISABLE_EVENT_SUPPORT
++    {
++        if (parsefuncs->disab_event_supp)
++            parsefuncs->disab_event_supp(parsedata);
++    }
+ 
+ line_def:
+     STRING EQUAL TOKVERSION
+diff --git a/usr/sbin/pkcsslotd/pkcsslotd.h b/usr/sbin/pkcsslotd/pkcsslotd.h
+index d7edcb3c..1dd0bac9 100644
+--- a/usr/sbin/pkcsslotd/pkcsslotd.h
++++ b/usr/sbin/pkcsslotd/pkcsslotd.h
+@@ -88,7 +88,7 @@ int XProcLock(void);
+ int XProcUnLock(void);
+ int CreateXProcLock(void);
+ 
+-int init_socket_server();
++int init_socket_server(int event_support_disabled);
+ int term_socket_server();
+ int init_socket_data(Slot_Mgr_Socket_t *sp);
+ int socket_connection_handler(int timeout_secs);
+diff --git a/usr/sbin/pkcsslotd/slotmgr.c b/usr/sbin/pkcsslotd/slotmgr.c
+index efbfe8fd..3b328a6c 100644
+--- a/usr/sbin/pkcsslotd/slotmgr.c
++++ b/usr/sbin/pkcsslotd/slotmgr.c
+@@ -34,6 +34,7 @@ int shmid;
+ key_t tok;
+ Slot_Info_t_64 sinfo[NUMBER_SLOTS_MANAGED];
+ unsigned int NumberSlotsInDB = 0;
++int event_support_disabled = 0;
+ 
+ Slot_Info_t_64 *psinfo;
+ 
+@@ -467,6 +468,13 @@ static int slotmgr_key_vers(void *private, int tok, unsigned int vers)
+     return 1;
+ }
+ 
++static void slotmgr_disab_event_supp(void *private)
++{
++    UNUSED(private);
++
++    event_support_disabled = 1;
++}
++
+ static void slotmgr_parseerror(void *private, int line, const char *parsermsg)
+ {
+     struct parse_data *d = (struct parse_data *)private;
+@@ -480,6 +488,7 @@ static struct parsefuncs slotmgr_parsefuncs = {
+     .end_slot   = slotmgr_end_slot,
+     .key_str    = slotmgr_key_str,
+     .key_vers   = slotmgr_key_vers,
++    .disab_event_supp = slotmgr_disab_event_supp,
+     .parseerror = slotmgr_parseerror
+ };
+ 
+@@ -568,7 +577,7 @@ int main(int argc, char *argv[], char *envp[])
+     if (!XProcUnLock())
+         return 4;
+ 
+-    if (!init_socket_server()) {
++    if (!init_socket_server(event_support_disabled)) {
+         DestroyMutexes();
+         DetachFromSharedMemory();
+         DestroySharedMemory();
+@@ -582,6 +591,8 @@ int main(int argc, char *argv[], char *envp[])
+         DestroySharedMemory();
+         return 6;
+     }
++    if (event_support_disabled)
++        socketData.flags |= FLAG_EVENT_SUPPORT_DISABLED;
+ 
+     /* Create customized token directories */
+     psinfo = &socketData.slot_info[0];
+diff --git a/usr/sbin/pkcsslotd/socket_server.c b/usr/sbin/pkcsslotd/socket_server.c
+index 41408670..3aa40267 100644
+--- a/usr/sbin/pkcsslotd/socket_server.c
++++ b/usr/sbin/pkcsslotd/socket_server.c
+@@ -139,12 +139,12 @@ struct event_info {
+ };
+ 
+ static int epoll_fd = -1;
+-static struct listener_info proc_listener;
++static struct listener_info proc_listener = { .socket = -1 };
+ static DL_NODE *proc_connections = NULL;
+-static struct listener_info admin_listener;
++static struct listener_info admin_listener = { .socket = -1 };
+ static DL_NODE *admin_connections = NULL;
+ #ifdef WITH_LIBUDEV
+-static struct udev_mon udev_mon;
++static struct udev_mon udev_mon = { .socket = -1 };
+ #endif
+ static DL_NODE *pending_events = NULL;
+ static unsigned long pending_events_count = 0;
+@@ -1620,6 +1620,9 @@ static void udev_mon_term(struct udev_mon *udev_mon)
+     if (udev_mon == NULL)
+         return;
+ 
++    if (udev_mon->socket < 0)
++        return;
++
+     epoll_ctl(epoll_fd, EPOLL_CTL_DEL, udev_mon->socket, NULL);
+     if (udev_mon->udev != NULL)
+         udev_unref(udev_mon->udev);
+@@ -1636,6 +1639,7 @@ int init_socket_data(Slot_Mgr_Socket_t *socketData)
+ {
+     unsigned int processed = 0;
+ 
++    socketData->flags = 0;
+     PopulateCKInfo(&(socketData->ck_info));
+     socketData->num_slots = NumberSlotsInDB;
+     PopulateSlotInfo(socketData->slot_info, &processed);
+@@ -1692,7 +1696,7 @@ int socket_connection_handler(int timeout_secs)
+     return TRUE;
+ }
+ 
+-int init_socket_server()
++int init_socket_server(int event_support_disabled)
+ {
+     int err;
+ 
+@@ -1710,18 +1714,20 @@ int init_socket_server()
+         return FALSE;
+     }
+ 
+-    if (!listener_create(ADMIN_SOCKET_FILE_PATH, &admin_listener,
+-                         admin_new_conn, NUMBER_ADMINS_ALLOWED)) {
+-        term_socket_server();
+-        return FALSE;
+-    }
++    if (!event_support_disabled) {
++        if (!listener_create(ADMIN_SOCKET_FILE_PATH, &admin_listener,
++                             admin_new_conn, NUMBER_ADMINS_ALLOWED)) {
++            term_socket_server();
++            return FALSE;
++        }
+ 
+ #ifdef WITH_LIBUDEV
+-    if (!udev_mon_init(UDEV_SUBSYSTEM_AP, &udev_mon)) {
+-        term_socket_server();
+-        return FALSE;
+-    }
++        if (!udev_mon_init(UDEV_SUBSYSTEM_AP, &udev_mon)) {
++            term_socket_server();
++            return FALSE;
++        }
+ #endif
++    }
+ 
+     DbgLog(DL0, "%s: Socket server started", __func__);
+ 
+diff --git a/usr/sbin/pkcstok_migrate/pkcstok_migrate.c b/usr/sbin/pkcstok_migrate/pkcstok_migrate.c
+index 7c225730..94fd1196 100644
+--- a/usr/sbin/pkcstok_migrate/pkcstok_migrate.c
++++ b/usr/sbin/pkcstok_migrate/pkcstok_migrate.c
+@@ -2066,6 +2066,13 @@ static int parseupdate_ockversion(void *private, const char *version)
+     return 0;
+ }
+ 
++static void parseupdate_disab_event_supp(void *private)
++{
++    struct parseupdate *u = (struct parseupdate *)private;
++
++    fprintf(u->f, "disable-event-support");
++}
++
+ static void parseupdate_eol(void *private)
+ {
+ 	struct parseupdate *u = (struct parseupdate *)private;
+@@ -2124,6 +2131,7 @@ static void parseupdate_eolcomment(void *private, const char *comment)
+ 
+ static struct parsefuncs parseupdatefuncs = {
+     .version    = parseupdate_ockversion,
++    .disab_event_supp = parseupdate_disab_event_supp,
+     .eol        = parseupdate_eol,
+     .begin_slot = parseupdate_begin_slot,
+     .end_slot   = parseupdate_end_slot,
diff --git a/SOURCES/opencryptoki-3.16.0-b048be548508dd1958bb7271568f388d0f6cbcf8.patch b/SOURCES/opencryptoki-3.16.0-b048be548508dd1958bb7271568f388d0f6cbcf8.patch
new file mode 100644
index 0000000..fd0c13c
--- /dev/null
+++ b/SOURCES/opencryptoki-3.16.0-b048be548508dd1958bb7271568f388d0f6cbcf8.patch
@@ -0,0 +1,1023 @@
+commit b048be548508dd1958bb7271568f388d0f6cbcf8
+Author: Ingo Franzki <ifranzki@linux.ibm.com>
+Date:   Mon Feb 8 16:50:00 2021 +0100
+
+    Event support: API and token level changes
+    
+    Signed-off-by: Ingo Franzki <ifranzki@linux.ibm.com>
+
+diff --git a/usr/include/apictl.h b/usr/include/apictl.h
+index 8898cae3..81c65dad 100644
+--- a/usr/include/apictl.h
++++ b/usr/include/apictl.h
+@@ -57,6 +57,8 @@ typedef struct {
+     API_Slot_t SltList[NUMBER_SLOTS_MANAGED];
+     DLL_Load_t DLLs[NUMBER_SLOTS_MANAGED];  // worst case we have a separate DLL
+                                             // per slot
++    int socketfd;
++    pthread_t event_thread;
+ } API_Proc_Struct_t;
+ 
+ #endif
+diff --git a/usr/include/events.h b/usr/include/events.h
+new file mode 100644
+index 00000000..dac6ad52
+--- /dev/null
++++ b/usr/include/events.h
+@@ -0,0 +1,83 @@
++/*
++ * COPYRIGHT (c) International Business Machines Corp. 2021
++ *
++ * This program is provided under the terms of the Common Public License,
++ * version 1.0 (CPL-1.0). Any use, reproduction or distribution for this
++ * software constitutes recipient's acceptance of CPL-1.0 terms which can be
++ * found in the file LICENSE file or at
++ * https://opensource.org/licenses/cpl1.0.php
++ */
++
++#include <stdint.h>
++#include <pkcs11types.h>
++#include <limits.h>
++#include <stdio.h>
++
++#include "local_types.h"
++#include "pkcs32.h"
++
++#ifndef _EVENTS_H
++#define _EVENTS_H
++
++typedef struct {
++    unsigned int version;       /* EVENT_VERSION_xxx */
++    unsigned int type;          /* EVENT_TYPE_xxx */
++    unsigned int flags;         /* EVENT_FLAGS_xxx */
++    unsigned int token_type;    /* Destination token type: EVENT_TOK_TYPE_xxx */
++    char token_label[member_size(CK_TOKEN_INFO_32, label)];
++                                /* Label of destination token (or blanks) */
++    pid_t process_id;           /* Process ID of destination process (or 0) */
++    unsigned int payload_len;   /* Length of payload in bytes */
++    /* Followed by payload_len bytes of payload (event specific) */
++} __attribute__ ((__packed__)) event_msg_t;
++
++typedef struct {
++    unsigned int version;               /* EVENT_VERSION_xxx */
++    unsigned int positive_replies;      /* Number of tokens that replied a */
++    unsigned int negative_replies;      /* positive, or negative feedback, */
++    unsigned int nothandled_replies;    /* or that did not handle the event. */
++                                        /* Note: Only tokens matching the event
++                                         * destination fields (pid, label,
++                                         * token-type) are counted. */
++} __attribute__ ((__packed__)) event_reply_t;
++
++/* Event and reply versions */
++#define EVENT_VERSION_1         1
++
++/* Event classes (encoded into event type) */
++#define EVENT_CLASS_MASK        0xffff0000
++#define EVENT_CLASS_UDEV        0x00010000
++#define EVENT_CLASS_ADMIN       0x00020000
++
++/* Event types */
++#define EVENT_TYPE_APQN_ADD     EVENT_CLASS_UDEV + 0x00000001
++#define EVENT_TYPE_APQN_REMOVE  EVENT_CLASS_UDEV + 0x00000002
++
++/* Event flags */
++#define EVENT_FLAGS_NONE        0x00000000
++#define EVENT_FLAGS_REPLY_REQ   0x00000001
++
++/* Event token destination types */
++#define EVENT_TOK_TYPE_ALL      0x00000000
++#define EVENT_TOK_TYPE_CCA      0x00000001
++#define EVENT_TOK_TYPE_EP11     0x00000002
++
++/* Maximum event payload length 128k */
++#define EVENT_MAX_PAYLOAD_LENGTH    (128 * 1024)
++
++/* Event payload for EVENT_TYPE_APQN_ADD and EVENT_TYPE_APQN_REMOVE */
++typedef struct {
++    unsigned short card;
++    unsigned short domain;
++    unsigned int device_type;            /* from uevent DEV_TYPE property */
++} __attribute__ ((__packed__)) event_udev_apqn_data_t;
++
++/* AP device types */
++#define AP_DEVICE_TYPE_CEX3A        8
++#define AP_DEVICE_TYPE_CEX3C        9
++#define AP_DEVICE_TYPE_CEX4         10
++#define AP_DEVICE_TYPE_CEX5         11
++#define AP_DEVICE_TYPE_CEX6         12
++#define AP_DEVICE_TYPE_CEX7         13
++
++#endif
+diff --git a/usr/include/include.mk b/usr/include/include.mk
+index a36afb25..79e593d7 100644
+--- a/usr/include/include.mk
++++ b/usr/include/include.mk
+@@ -7,4 +7,5 @@ opencryptokiinclude_HEADERS =						\
+ 
+ noinst_HEADERS +=							\
+ 	usr/include/apictl.h usr/include/local_types.h			\
+-	usr/include/pkcs32.h usr/include/slotmgr.h usr/include/stdll.h
++	usr/include/pkcs32.h usr/include/slotmgr.h usr/include/stdll.h	\
++	usr/include/events.h
+diff --git a/usr/include/local_types.h b/usr/include/local_types.h
+index f03c6629..c7c7f5ec 100644
+--- a/usr/include/local_types.h
++++ b/usr/include/local_types.h
+@@ -11,6 +11,8 @@
+ #ifndef __LOCAL_TYPES
+ #define __LOCAL_TYPES
+ 
++#define member_size(type, member) sizeof(((type *)0)->member)
++
+ typedef unsigned char uint8;
+ 
+ typedef unsigned short uint16;
+diff --git a/usr/include/stdll.h b/usr/include/stdll.h
+index 57f6c6e8..9a3b760c 100644
+--- a/usr/include/stdll.h
++++ b/usr/include/stdll.h
+@@ -350,6 +350,11 @@ typedef CK_RV (CK_PTR ST_C_IBM_ReencryptSingle)(STDLL_TokData_t *tokdata,
+                                                 CK_BYTE_PTR pReencryptedData,
+                                             CK_ULONG_PTR pulReencryptedDataLen);
+ 
++typedef CK_RV (CK_PTR ST_C_HandleEvent)(STDLL_TokData_t *tokdata,
++                                        unsigned int event_type,
++                                        unsigned int event_flags,
++                                        const char *payload,
++                                        unsigned int payload_len);
+ 
+ struct ST_FCN_LIST {
+ 
+@@ -424,6 +429,9 @@ struct ST_FCN_LIST {
+     ST_C_CancelFunction ST_CancelFunction;
+ 
+     ST_C_IBM_ReencryptSingle ST_IBM_ReencryptSingle;
++
++    /* The functions defined below are not part of the external API */
++    ST_C_HandleEvent ST_HandleEvent;
+ };
+ 
+ typedef struct ST_FCN_LIST STDLL_FcnList_t;
+diff --git a/usr/lib/api/api_interface.c b/usr/lib/api/api_interface.c
+index f1ee9132..b74b763f 100644
+--- a/usr/lib/api/api_interface.c
++++ b/usr/lib/api/api_interface.c
+@@ -286,7 +286,31 @@ void child_fork_initializer()
+     if (Anchor != NULL)
+         C_Finalize(NULL);
+     in_child_fork_initializer = FALSE;
+- }
++}
++
++void parent_fork_prepare()
++{
++    if (Anchor == NULL)
++        return;
++
++    /*
++     * Stop the event thread in the fork parent, since having the event thread
++     * active when a fork is performed causes various problems (e.g. deadlocks
++     * in glibc).
++     */
++    if (Anchor->event_thread > 0)
++        stop_event_thread();
++}
++
++void parent_fork_after()
++{
++    if (Anchor == NULL)
++        return;
++
++    /* Restart the event thread in the parent when fork is complete */
++    if (Anchor->event_thread == 0)
++        start_event_thread();
++}
+ 
+ //------------------------------------------------------------------------
+ // API function C_CancelFunction
+@@ -1501,6 +1525,20 @@ CK_RV C_Finalize(CK_VOID_PTR pReserved)
+ 
+     shData = &(Anchor->SocketDataP);
+ 
++    /*
++     * Stop the event thread and close the socket.
++     * If C_Finalize is called as part of the fork initializer, don't stop
++     * the thread, since a forked process does not have any threads, and don't
++     * close the socket, as this would close the connection of the parent
++     * process to the pkcsslotd as well.
++     * */
++    if (!in_child_fork_initializer) {
++        if (Anchor->event_thread > 0)
++            stop_event_thread();
++        if (Anchor->socketfd >= 0)
++            close(Anchor->socketfd);
++    }
++
+     // unload all the STDLL's from the application
+     // This is in case the APP decides to do the re-initialize and
+     // continue on
+@@ -2642,6 +2680,8 @@ CK_RV C_Initialize(CK_VOID_PTR pVoid)
+     CK_C_INITIALIZE_ARGS *pArg;
+     char fcnmap = 0;
+     CK_RV rc = CKR_OK;
++    CK_SLOT_ID slotID;
++    API_Slot_t *sltp;
+ 
+     /*
+      * Lock so that only one thread can run C_Initialize or C_Finalize at
+@@ -2674,6 +2714,7 @@ CK_RV C_Initialize(CK_VOID_PTR pVoid)
+     // This must be done prior to all goto error calls, else bt_destroy()
+     // will fail because it accesses uninitialized memory when t->size > 0.
+     memset(Anchor, 0, sizeof(API_Proc_Struct_t));
++    Anchor->socketfd = -1;
+ 
+     TRACE_DEBUG("Anchor allocated at %s\n", (char *) Anchor);
+ 
+@@ -2789,11 +2830,21 @@ CK_RV C_Initialize(CK_VOID_PTR pVoid)
+     }
+     TRACE_DEBUG("Shared memory %p \n", Anchor->SharedMemP);
+ 
+-    if (!init_socket_data()) {
++    /* Connect to slot daemon and retrieve slot infos */
++    Anchor->socketfd = connect_socket(SOCKET_FILE_PATH);
++    if (Anchor->socketfd < 0) {
+         OCK_SYSLOG(LOG_ERR, "C_Initialize: Module failed to create a "
+                    "socket. Verify that the slot management daemon is "
+                    "running.\n");
+-        TRACE_ERROR("Cannot attach to socket.\n");
++        TRACE_ERROR("Failed to connect to slot daemon\n");
++        rc = CKR_FUNCTION_FAILED;
++        goto error_shm;
++    }
++
++    if (!init_socket_data(Anchor->socketfd)) {
++        OCK_SYSLOG(LOG_ERR, "C_Initialize: Module failed to retrieve slot "
++                   "infos from slot deamon.\n");
++        TRACE_ERROR("Failed to receive slot infos from socket.\n");
+         rc = CKR_FUNCTION_FAILED;
+         goto error_shm;
+     }
+@@ -2810,15 +2861,35 @@ CK_RV C_Initialize(CK_VOID_PTR pVoid)
+     }
+     //
+     // load all the slot DLL's here
+-    {
+-        CK_SLOT_ID slotID;
+-        API_Slot_t *sltp;
++    for (slotID = 0; slotID < NUMBER_SLOTS_MANAGED; slotID++) {
++        sltp = &(Anchor->SltList[slotID]);
++        slot_loaded[slotID] = DL_Load_and_Init(sltp, slotID);
++    }
+ 
++    /* Start event receiver thread */
++    if (start_event_thread() != 0) {
++        TRACE_ERROR("Failed to start event thread\n");
++
++        // unload all the STDLL's from the application
++        // This is in case the APP decides to do the re-initialize and
++        // continue on
+         for (slotID = 0; slotID < NUMBER_SLOTS_MANAGED; slotID++) {
+             sltp = &(Anchor->SltList[slotID]);
+-            slot_loaded[slotID] = DL_Load_and_Init(sltp, slotID);
++            if (slot_loaded[slotID]) {
++                if (sltp->pSTfini) {
++                    // call the terminate function..
++                    sltp->pSTfini(sltp->TokData, slotID,
++                                  &Anchor->SocketDataP.slot_info[slotID],
++                                  &trace, 0);
++                }
++            }
++            DL_UnLoad(sltp, slotID);
+         }
+ 
++        API_UnRegister();
++
++        rc = CKR_FUNCTION_FAILED;
++        goto error_shm;
+     }
+ 
+     pthread_mutex_unlock(&GlobMutex);
+@@ -2829,6 +2900,8 @@ error_shm:
+ 
+ error:
+     bt_destroy(&Anchor->sess_btree);
++    if (Anchor->socketfd >= 0)
++        close(Anchor->socketfd);
+ 
+     free((void *) Anchor);
+     Anchor = NULL;
+@@ -5052,7 +5125,8 @@ void api_init(void)
+ {
+     // Should only have to do the atfork stuff at load time...
+     if (!Initialized) {
+-        pthread_atfork(NULL, NULL, (void (*)()) child_fork_initializer);
++        pthread_atfork(parent_fork_prepare, parent_fork_after,
++                       child_fork_initializer);
+         Initialized = 1;
+     }
+ }
+diff --git a/usr/lib/api/apiproto.h b/usr/lib/api/apiproto.h
+index 871f3778..8523fb8e 100644
+--- a/usr/lib/api/apiproto.h
++++ b/usr/lib/api/apiproto.h
+@@ -50,6 +50,9 @@ void CK_Info_From_Internal(CK_INFO_PTR dest, CK_INFO_PTR_64 src);
+ int sessions_exist(CK_SLOT_ID);
+ 
+ void CloseAllSessions(CK_SLOT_ID slot_id, CK_BBOOL in_fork_initializer);
+-int init_socket_data();
++int connect_socket(const char *file_path);
++int init_socket_data(int socketfd);
++int start_event_thread();
++int stop_event_thread();
+ 
+ #endif
+diff --git a/usr/lib/api/socket_client.c b/usr/lib/api/socket_client.c
+index 6bacf151..e344ddbf 100644
+--- a/usr/lib/api/socket_client.c
++++ b/usr/lib/api/socket_client.c
+@@ -23,114 +23,421 @@
+ #include <grp.h>
+ #include <errno.h>
+ #include <stdlib.h>
++#include <stdbool.h>
++#include <poll.h>
+ 
+ #include "apiproto.h"
+ #include "slotmgr.h"
+ #include "apictl.h"
++#include "trace.h"
+ #include "ock_syslog.h"
++#include "events.h"
+ 
+ extern API_Proc_Struct_t *Anchor;
+-//
+-// Will fill out the Slot_Mgr_Socket_t structure in the Anchor global data
+-// structure with the values passed by the pkcsslotd via a socket RPC.
+-int init_socket_data()
++
++int connect_socket(const char *file_path)
+ {
+     int socketfd;
+     struct sockaddr_un daemon_address;
+     struct stat file_info;
+     struct group *grp;
+-    int n;
+-    unsigned int bytes_received = 0;
+-    Slot_Mgr_Socket_t *daemon_socket_data = NULL;
+-    int ret = FALSE;
+ 
+-    if (stat(SOCKET_FILE_PATH, &file_info)) {
++    if (stat(file_path, &file_info)) {
+         OCK_SYSLOG(LOG_ERR,
+-                   "init_socket_data: failed to find socket file, errno=%d",
++                   "connect_socket: failed to find socket file, errno=%d",
+                    errno);
+-        return FALSE;
++        return -1;
+     }
+ 
+     grp = getgrnam("pkcs11");
+     if (!grp) {
+         OCK_SYSLOG(LOG_ERR,
+-                   "init_socket_data: pkcs11 group does not exist, errno=%d",
++                   "connect_socket: pkcs11 group does not exist, errno=%d",
+                    errno);
+-        return FALSE;
++        return -1;
+     }
+ 
+     if (file_info.st_uid != 0 || file_info.st_gid != grp->gr_gid) {
+         OCK_SYSLOG(LOG_ERR,
+-                   "init_socket_data: incorrect permissions on socket file");
+-        return FALSE;
++                   "connect_socket: incorrect permissions on socket file");
++        return -1;
+     }
+ 
+     if ((socketfd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) {
+         OCK_SYSLOG(LOG_ERR,
+-                   "init_socket_data: failed to create socket, errno=%d",
++                   "connect_socket: failed to create socket, errno=%d",
+                    errno);
+-        return FALSE;
++        return -1;
+     }
+ 
+     memset(&daemon_address, 0, sizeof(struct sockaddr_un));
+     daemon_address.sun_family = AF_UNIX;
+-    strcpy(daemon_address.sun_path, SOCKET_FILE_PATH);
++    strcpy(daemon_address.sun_path, file_path);
+ 
+     if (connect(socketfd, (struct sockaddr *) &daemon_address,
+                 sizeof(struct sockaddr_un)) != 0) {
+         OCK_SYSLOG(LOG_ERR,
+-                   "init_socket_data: failed to connect to slotmanager daemon, "
++                   "connect_socket: failed to connect to slotmanager daemon, "
+                    "errno=%d",
+                    errno);
+-        goto exit;
+-    }
+-    // allocate data buffer
+-    daemon_socket_data =
+-        (Slot_Mgr_Socket_t *) malloc(sizeof(*daemon_socket_data));
+-    if (!daemon_socket_data) {
+-        OCK_SYSLOG(LOG_ERR, "init_socket_data: failed to \
+-                   allocate %lu bytes \
+-                   for daemon data, errno=%d",
+-                   sizeof(*daemon_socket_data), errno);
+-        goto exit;
++        goto error;
+     }
+ 
+-    while (bytes_received < sizeof(*daemon_socket_data)) {
+-        n = read(socketfd, ((char *) daemon_socket_data) + bytes_received,
+-                 sizeof(*daemon_socket_data) - bytes_received);
++    return socketfd;
++
++error:
++    close(socketfd);
++    return -1;
++}
++
++static ssize_t read_all(int socketfd, char *buffer, size_t size)
++{
++    size_t bytes_received = 0;
++    ssize_t n;
++
++    while (bytes_received < size) {
++        n = read(socketfd, buffer + bytes_received, size - bytes_received);
+         if (n < 0) {
+             // read error
+             if (errno == EINTR)
+                 continue;
+-            OCK_SYSLOG(LOG_ERR, "init_socket_data: read error \
+-                       on daemon socket, errno=%d", errno);
+-            goto exit;
+-        } else if (n == 0) {
+-            // eof but we still expect some bytes
+-            OCK_SYSLOG(LOG_ERR, "init_socket_data: read returned \
+-                       with eof but we still \
+-                       expect %lu bytes from daemon",
+-                       sizeof(*daemon_socket_data) - bytes_received);
+-            goto exit;
+-        } else {
+-            // n > 0, we got some bytes
+-            bytes_received += n;
++            return -errno;
+         }
++        if (n == 0)
++            break;
++
++        bytes_received += n;
+     }
+ 
+-    ret = TRUE;
++    return bytes_received;
++}
++
++static ssize_t send_all(int socketfd, char *buffer, size_t size)
++{
++    size_t bytes_sent = 0;
++    ssize_t n;
+ 
+-    // copy the Slot_Mgr_Socket_t struct into global
+-    // Anchor SocketDataPdata buffer
+-    memcpy(&(Anchor->SocketDataP), daemon_socket_data,
+-           sizeof(*daemon_socket_data));
++    while (bytes_sent < size) {
++        n = send(socketfd, buffer + bytes_sent, size - bytes_sent, 0);
++        if (n < 0) {
++            // send error
++            if (errno == EINTR)
++                continue;
++            return -errno;
++        }
++        if (n == 0)
++            break;
+ 
+-exit:
+-    //free the data buffer after copy
+-    if (daemon_socket_data)
+-        free(daemon_socket_data);
++        bytes_sent += n;
++    }
+ 
+-    close(socketfd);
++    return bytes_sent;
++}
++
++//
++// Will fill out the Slot_Mgr_Socket_t structure in the Anchor global data
++// structure with the values passed by the pkcsslotd via a socket RPC.
++int init_socket_data(int socketfd)
++{
++    ssize_t n;
++    int ret = TRUE;
++
++    n = read_all(socketfd, (char *)&Anchor->SocketDataP,
++                 sizeof(Anchor->SocketDataP));
++    if (n < 0) {
++        // read error
++        OCK_SYSLOG(LOG_ERR, "init_socket_data: read error \
++                   on daemon socket, errno=%d", -n);
++        ret = FALSE;
++    }
++    if (n != sizeof(Anchor->SocketDataP)) {
++        // eof but we still expect some bytes
++        OCK_SYSLOG(LOG_ERR, "init_socket_data: read returned \
++                   with eof but we still \
++                   expect %lu bytes from daemon",
++                   sizeof(Anchor->SocketDataP) - n);
++        ret = FALSE;
++    }
+ 
+     return ret;
+ }
++
++static bool match_token_label_filter(event_msg_t *event, API_Slot_t *sltp)
++{
++    if (event->token_label[0] == ' ' || event->token_label[0] == '\0')
++        return true;
++
++    return memcmp(event->token_label,
++                  sltp->TokData->nv_token_data->token_info.label,
++                  sizeof(event->token_label)) == 0;
++}
++
++struct type_model {
++    unsigned int type;
++    char model[member_size(CK_TOKEN_INFO_32, model)];
++};
++
++static const struct type_model type_model_flt[] = {
++        { .type = EVENT_TOK_TYPE_CCA,  .model = "CCA             " },
++        { .type = EVENT_TOK_TYPE_EP11, .model = "EP11            " },
++};
++
++static bool match_token_type_filter(event_msg_t *event, API_Slot_t *sltp)
++{
++    size_t i;
++
++    if (event->token_type == EVENT_TOK_TYPE_ALL)
++        return true;
++
++    for (i = 0; i < sizeof(type_model_flt) / sizeof(struct type_model); i++) {
++        if (memcmp(sltp->TokData->nv_token_data->token_info.model,
++                   type_model_flt[i].model,
++                   sizeof(type_model_flt[i].model)) == 0 &&
++            (event->token_type & type_model_flt[i].type) != 0)
++            return true;
++    }
++
++    return false;
++}
++
++static int handle_event(API_Proc_Struct_t *anchor, event_msg_t *event,
++                        char *payload, event_reply_t *reply)
++{
++    CK_SLOT_ID slotID;
++    API_Slot_t *sltp;
++    CK_RV rc;
++
++    /* If its not for our process, ignore it, don't increment reply counters */
++    if (event->process_id != 0 && event->process_id != anchor->Pid)
++        return 0;
++
++    for (slotID = 0; slotID < NUMBER_SLOTS_MANAGED; slotID++) {
++        sltp = &anchor->SltList[slotID];
++        if (sltp->DLLoaded == FALSE || sltp->FcnList == NULL)
++            continue;
++
++        if (!match_token_label_filter(event, sltp))
++            continue;
++        if (!match_token_type_filter(event, sltp))
++            continue;
++
++        if (sltp->FcnList->ST_HandleEvent != NULL)
++            rc = sltp->FcnList->ST_HandleEvent(sltp->TokData, event->type,
++                                               event->flags, payload,
++                                               event->payload_len);
++        else
++            rc = CKR_FUNCTION_NOT_SUPPORTED;
++
++        TRACE_DEVEL("Slot %lu ST_HandleEvent rc: 0x%lx\n", slotID, rc);
++        switch (rc) {
++        case CKR_OK:
++            reply->positive_replies++;
++            break;
++        case CKR_FUNCTION_NOT_SUPPORTED:
++            reply->nothandled_replies++;
++            break;
++        default:
++            reply->negative_replies++;
++            break;
++        }
++    }
++
++    return 0;
++}
++
++static void event_thread_cleanup(void *arg)
++{
++    API_Proc_Struct_t *anchor = arg;
++
++    UNUSED(anchor);
++
++    TRACE_DEVEL("Event thread %lu terminating\n", pthread_self());
++}
++
++static void *event_thread(void *arg)
++{
++    API_Proc_Struct_t *anchor = arg;
++    int oldstate, oldtype;
++    struct pollfd pollfd;
++    event_msg_t event;
++    char *payload;
++    event_reply_t reply;
++    ssize_t num;
++    int rc;
++
++    UNUSED(arg);
++
++    TRACE_DEVEL("Event thread %lu running\n", pthread_self());
++
++    if (anchor->socketfd < 0) {
++        TRACE_ERROR("socket is already closed.\n");
++        TRACE_DEVEL("Event thread %lu terminating\n", pthread_self());
++        return NULL;
++    }
++
++    /* Enable cancellation */
++    pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, &oldstate);
++    pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, &oldtype);
++    pthread_cleanup_push(event_thread_cleanup, anchor);
++
++    pollfd.fd = anchor->socketfd;
++    pollfd.events = POLLIN | POLLHUP | POLLERR;
++
++    while (1) {
++        pollfd.revents = 0;
++        rc = poll(&pollfd, 1, -1);
++        if (rc < 0) {
++            if (errno == EINTR)
++                continue;
++            TRACE_ERROR("poll failed: %d\n", errno);
++            break;
++        }
++
++        if (rc == 0)
++            continue;
++
++        if (pollfd.revents & (POLLHUP | POLLERR)) {
++            TRACE_ERROR("Error on socket, possibly closed by slot daemon\n");
++            break;
++        }
++        if ((pollfd.revents & POLLIN) == 0)
++            continue;
++
++        /* Disable for cancellation while we are working on an event */
++        pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &oldstate);
++
++        TRACE_DEVEL("Receive new event ....\n");
++
++        num = read_all(anchor->socketfd, (char *)&event, sizeof(event));
++        if (num != sizeof(event)) {
++            TRACE_ERROR("Error receiving the event, rc: %ld\n", num);
++            break;
++        }
++
++        TRACE_DEBUG("Event version:      %u\n", event.version);
++        TRACE_DEBUG("Event type:         0x%08x\n", event.type);
++        TRACE_DEBUG("Event flags:        0x%08x\n", event.flags);
++        TRACE_DEBUG("Event token_type:   0x%08x\n", event.token_type);
++        TRACE_DEBUG("Event token_name:   '%.32s'\n", event.token_label);
++        TRACE_DEBUG("Event process_id:   %u\n", event.process_id);
++        TRACE_DEBUG("Event payload_len:  %u\n", event.payload_len);
++
++        if (event.version != EVENT_VERSION_1) {
++            TRACE_ERROR("Event version invalid: %u\n", event.version);
++            break;
++        }
++
++        payload = NULL;
++        if (event.payload_len > 0) {
++            payload = malloc(event.payload_len);
++            if (payload == NULL) {
++                TRACE_ERROR("Failed to allocate buffer for event payload\n");
++                break;
++            }
++
++            num = read_all(anchor->socketfd, payload, event.payload_len);
++            if (num != event.payload_len) {
++                TRACE_ERROR("Error receiving the event payload, rc: %ld\n", num);
++                if (payload != NULL)
++                    free(payload);
++                break;
++            }
++
++            TRACE_DEBUG("Event payload:\n");
++            TRACE_DEBUG_DUMP("  ", payload, event.payload_len);
++        }
++
++        memset(&reply, 0, sizeof(reply));
++        reply.version = EVENT_VERSION_1;
++        rc = handle_event(anchor, &event, payload, &reply);
++        if (rc != 0) {
++            TRACE_ERROR("Error handling the event, rc: %d\n", rc);
++            if (payload != NULL)
++                free(payload);
++            break;
++        }
++
++        TRACE_DEBUG("Reply version:      %u\n", reply.version);
++        TRACE_DEBUG("Reply positive:     %u\n", reply.positive_replies);
++        TRACE_DEBUG("Reply negative:     %u\n", reply.negative_replies);
++        TRACE_DEBUG("Reply not-handled:  %u\n", reply.nothandled_replies);
++
++        if (event.flags & EVENT_FLAGS_REPLY_REQ) {
++            num = send_all(anchor->socketfd, (char *)&reply, sizeof(reply));
++            if (num != sizeof(reply)) {
++                TRACE_ERROR("Error sending the event reply, rc: %ld\n", num);
++                if (payload != NULL)
++                    free(payload);
++                break;
++            }
++        }
++
++        if (payload != NULL)
++            free(payload);
++
++        /* Re-enable for  and test if we got canceled in the meantime */
++        pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, &oldstate);
++        pthread_testcancel();
++    }
++
++    /*
++     * Close the socket if we encounter an unrecoverable error (e.g. received
++     * invalid event) and stop the thread because of that.
++     * If the thread is stopped via stop_event_thread(), then it gets canceled
++     * via pthread_cancel(), and will not reach this place, thus the socket is
++     * not closed. This is intended, and the socket will then be closed by
++     * C_Finalize(). The atfork 'prepare' handler in the parent process also
++     * stops the thread (via stop_event_thread()), and the socket must not be
++     * closed in this case, because the thread is restarted in the atfork
++     * 'parent' handler, and should continue to receive events from the
++     * socket.
++     */
++    close(anchor->socketfd);
++    anchor->socketfd = -1;
++
++    pthread_cleanup_pop(1);
++    return NULL;
++}
++
++int start_event_thread()
++{
++    int rc;
++
++    rc =  pthread_create(&Anchor->event_thread, NULL, event_thread, Anchor);
++    if (rc != 0) {
++        OCK_SYSLOG(LOG_ERR, "start_event_thread: pthread_create failed, "
++                   "errno=%d", rc);
++        TRACE_ERROR("Failed to start event thread, errno=%d\n", rc);
++        return rc;
++    }
++
++    TRACE_DEVEL("Event thread %lu has been started\n", Anchor->event_thread);
++    return 0;
++}
++
++int stop_event_thread()
++{
++    int rc;
++    void *status;
++
++    TRACE_DEVEL("Canceling event thread %lu\n", Anchor->event_thread);
++    rc = pthread_cancel(Anchor->event_thread);
++    if (rc != 0 && rc != ESRCH)
++        return rc;
++
++    TRACE_DEVEL("Waiting for event thread %lu to terminate\n",
++                Anchor->event_thread);
++    rc = pthread_join(Anchor->event_thread, &status);
++    if (rc != 0)
++        return rc;
++
++    if (status != PTHREAD_CANCELED) {
++        TRACE_ERROR("Event thread was stopped, but did not return the "
++                   "expected status\n");
++    }
++
++    TRACE_DEVEL("Event thread %lu has terminated\n", Anchor->event_thread);
++
++    Anchor->event_thread = 0;
++    return 0;
++}
+diff --git a/usr/lib/cca_stdll/tok_struct.h b/usr/lib/cca_stdll/tok_struct.h
+index 2b43fa8e..182e2ac2 100644
+--- a/usr/lib/cca_stdll/tok_struct.h
++++ b/usr/lib/cca_stdll/tok_struct.h
+@@ -134,6 +134,7 @@ token_spec_t token_specific = {
+     &token_specific_reencrypt_single,
+     NULL,                       // set_attribute_values
+     NULL,                       // set_attrs_for_new_object
++    NULL,                       // handle_event
+ };
+ 
+ #endif
+diff --git a/usr/lib/common/new_host.c b/usr/lib/common/new_host.c
+index aae00984..a3749d26 100644
+--- a/usr/lib/common/new_host.c
++++ b/usr/lib/common/new_host.c
+@@ -4039,6 +4039,24 @@ done:
+     return rc;
+ }
+ 
++CK_RV SC_HandleEvent(STDLL_TokData_t *tokdata, unsigned int event_type,
++                     unsigned int event_flags, const char *payload,
++                     unsigned int payload_len)
++{
++    CK_RV rc;
++
++    if (token_specific.t_handle_event == NULL)
++        return CKR_FUNCTION_NOT_SUPPORTED;
++
++    rc = token_specific.t_handle_event(tokdata, event_type, event_flags,
++                                       payload, payload_len);
++
++    TRACE_INFO("SC_HandleEvent: rc = 0x%08lx, event_type = 0x%08x, "
++               "event_flags = 0x%08x\n", rc, event_type, event_flags);
++
++    return rc;
++}
++
+ void SC_SetFunctionList(void)
+ {
+     function_list.ST_Initialize = ST_Initialize;
+@@ -4104,4 +4122,6 @@ void SC_SetFunctionList(void)
+     function_list.ST_CancelFunction = NULL;     // SC_CancelFunction;
+ 
+     function_list.ST_IBM_ReencryptSingle = SC_IBM_ReencryptSingle;
++
++    function_list.ST_HandleEvent = SC_HandleEvent;
+ }
+diff --git a/usr/lib/common/tok_spec_struct.h b/usr/lib/common/tok_spec_struct.h
+index 30ffcf02..0e90d411 100644
+--- a/usr/lib/common/tok_spec_struct.h
++++ b/usr/lib/common/tok_spec_struct.h
+@@ -278,6 +278,10 @@ struct token_specific_struct {
+ 
+     CK_RV(*t_set_attrs_for_new_object) (STDLL_TokData_t *, CK_OBJECT_CLASS,
+                                         CK_ULONG, TEMPLATE *);
++
++    CK_RV(*t_handle_event) (STDLL_TokData_t *tokdata, unsigned int event_type,
++                            unsigned int event_flags, const char *payload,
++                            unsigned int payload_len);
+ };
+ 
+ typedef struct token_specific_struct token_spec_t;
+diff --git a/usr/lib/common/tok_specific.h b/usr/lib/common/tok_specific.h
+index ffb72909..997fa7e1 100644
+--- a/usr/lib/common/tok_specific.h
++++ b/usr/lib/common/tok_specific.h
+@@ -326,4 +326,10 @@ CK_RV token_specific_set_attrs_for_new_object(STDLL_TokData_t *,
+                                               CK_OBJECT_CLASS, CK_ULONG,
+                                               TEMPLATE *);
+ 
++CK_RV token_specific_handle_event(STDLL_TokData_t *tokdata,
++                                  unsigned int event_type,
++                                  unsigned int event_flags,
++                                  const char *payload,
++                                  unsigned int payload_len);
++
+ #endif
+diff --git a/usr/lib/ep11_stdll/new_host.c b/usr/lib/ep11_stdll/new_host.c
+index 6fcef68a..4e592363 100644
+--- a/usr/lib/ep11_stdll/new_host.c
++++ b/usr/lib/ep11_stdll/new_host.c
+@@ -4262,6 +4262,24 @@ done:
+     return rc;
+ }
+ 
++CK_RV SC_HandleEvent(STDLL_TokData_t *tokdata, unsigned int event_type,
++                     unsigned int event_flags, const char *payload,
++                     unsigned int payload_len)
++{
++    CK_RV rc;
++
++    if (token_specific.t_handle_event == NULL)
++        return CKR_FUNCTION_NOT_SUPPORTED;
++
++    rc = token_specific.t_handle_event(tokdata, event_type, event_flags,
++                                       payload, payload_len);
++
++    TRACE_INFO("SC_HandleEvent: rc = 0x%08lx, event_type = 0x%08x, "
++               "event_flags = 0x%08x\n", rc, event_type, event_flags);
++
++    return rc;
++}
++
+ void SC_SetFunctionList(void)
+ {
+     function_list.ST_Initialize = ST_Initialize;
+@@ -4327,4 +4345,6 @@ void SC_SetFunctionList(void)
+     function_list.ST_CancelFunction = NULL;     // SC_CancelFunction;
+ 
+     function_list.ST_IBM_ReencryptSingle = SC_IBM_ReencryptSingle;
++
++    function_list.ST_HandleEvent = SC_HandleEvent;
+ }
+diff --git a/usr/lib/ep11_stdll/tok_struct.h b/usr/lib/ep11_stdll/tok_struct.h
+index 51aae6fb..2c0af9cf 100644
+--- a/usr/lib/ep11_stdll/tok_struct.h
++++ b/usr/lib/ep11_stdll/tok_struct.h
+@@ -137,6 +137,7 @@ token_spec_t token_specific = {
+     &token_specific_reencrypt_single,
+     &token_specific_set_attribute_values,
+     &token_specific_set_attrs_for_new_object,
++    NULL,                       // handle_event
+ };
+ 
+ #endif
+diff --git a/usr/lib/ica_s390_stdll/tok_struct.h b/usr/lib/ica_s390_stdll/tok_struct.h
+index 13ee72c9..a260a276 100644
+--- a/usr/lib/ica_s390_stdll/tok_struct.h
++++ b/usr/lib/ica_s390_stdll/tok_struct.h
+@@ -147,6 +147,7 @@ token_spec_t token_specific = {
+     NULL,                       // reencrypt_single
+     NULL,                       // set_attribute_values
+     NULL,                       // set_attrs_for_new_object
++    NULL,                       // handle_event
+ };
+ 
+ #endif
+diff --git a/usr/lib/icsf_stdll/new_host.c b/usr/lib/icsf_stdll/new_host.c
+index 0f93ce5c..cfef7425 100644
+--- a/usr/lib/icsf_stdll/new_host.c
++++ b/usr/lib/icsf_stdll/new_host.c
+@@ -3332,6 +3332,24 @@ done:
+     return rc;
+ }
+ 
++CK_RV SC_HandleEvent(STDLL_TokData_t *tokdata, unsigned int event_type,
++                     unsigned int event_flags, const char *payload,
++                     unsigned int payload_len)
++{
++    CK_RV rc;
++
++    if (token_specific.t_handle_event == NULL)
++        return CKR_FUNCTION_NOT_SUPPORTED;
++
++    rc = token_specific.t_handle_event(tokdata, event_type, event_flags,
++                                       payload, payload_len);
++
++    TRACE_INFO("SC_HandleEvent: rc = 0x%08lx, event_type = 0x%08x, "
++               "event_flags = 0x%08x\n", rc, event_type, event_flags);
++
++    return rc;
++}
++
+ void SC_SetFunctionList(void)
+ {
+     function_list.ST_Initialize = ST_Initialize;
+@@ -3397,4 +3415,6 @@ void SC_SetFunctionList(void)
+     function_list.ST_CancelFunction = NULL;     // SC_CancelFunction;
+ 
+     function_list.ST_IBM_ReencryptSingle = SC_IBM_ReencryptSingle;
++
++    function_list.ST_HandleEvent = SC_HandleEvent;
+ }
+diff --git a/usr/lib/icsf_stdll/tok_struct.h b/usr/lib/icsf_stdll/tok_struct.h
+index fb1619ee..0f930a29 100644
+--- a/usr/lib/icsf_stdll/tok_struct.h
++++ b/usr/lib/icsf_stdll/tok_struct.h
+@@ -129,6 +129,7 @@ token_spec_t token_specific = {
+     NULL,                       // reencrypt_single
+     NULL,                       // set_attribute_values
+     NULL,                       // set_attrs_for_new_object
++    NULL,                       // handle_event
+ };
+ 
+ #endif
+diff --git a/usr/lib/soft_stdll/tok_struct.h b/usr/lib/soft_stdll/tok_struct.h
+index acf7c5d7..e43df038 100644
+--- a/usr/lib/soft_stdll/tok_struct.h
++++ b/usr/lib/soft_stdll/tok_struct.h
+@@ -172,6 +172,7 @@ token_spec_t token_specific = {
+     NULL,                       // reencrypt_single
+     NULL,                       // set_attribute_values
+     NULL,                       // set_attrs_for_new_object
++    NULL,                       // handle_event
+ };
+ 
+ #endif
+diff --git a/usr/lib/tpm_stdll/tok_struct.h b/usr/lib/tpm_stdll/tok_struct.h
+index d48b93e5..8903f123 100644
+--- a/usr/lib/tpm_stdll/tok_struct.h
++++ b/usr/lib/tpm_stdll/tok_struct.h
+@@ -120,4 +120,5 @@ struct token_specific_struct token_specific = {
+     NULL,                       // reencrypt_single
+     NULL,                       // set_attribute_values
+     NULL,                       // set_attrs_for_new_object
++    NULL,                       // handle_event
+ };
diff --git a/SOURCES/opencryptoki-3.16.0-b07505993dd8b2f367cf3b630f6da186e4e8550d.patch b/SOURCES/opencryptoki-3.16.0-b07505993dd8b2f367cf3b630f6da186e4e8550d.patch
new file mode 100644
index 0000000..6936783
--- /dev/null
+++ b/SOURCES/opencryptoki-3.16.0-b07505993dd8b2f367cf3b630f6da186e4e8550d.patch
@@ -0,0 +1,37 @@
+commit b07505993dd8b2f367cf3b630f6da186e4e8550d
+Author: Ingo Franzki <ifranzki@linux.ibm.com>
+Date:   Wed Feb 10 15:12:25 2021 +0100
+
+    Avoid deadlock in dlclose() after a fork
+    
+    Calling dlclose() in a atfork handler may cause a deadlock.
+    dlclose() may itself modify the atfork handler table to remove
+    any fork handlers that the to be unloaded library has registered.
+    Since the atfork handler table is currently locked when we are in
+    an atfork handler, this would produce a deadlock.
+    
+    Skip the dlclose() if we are in an atfork handler to avoid the deadlock.
+    
+    Signed-off-by: Ingo Franzki <ifranzki@linux.ibm.com>
+
+diff --git a/usr/lib/api/api_interface.c b/usr/lib/api/api_interface.c
+index 3ccb6d41..f1ee9132 100644
+--- a/usr/lib/api/api_interface.c
++++ b/usr/lib/api/api_interface.c
+@@ -1516,7 +1516,15 @@ CK_RV C_Finalize(CK_VOID_PTR pReserved)
+             }
+         }
+ 
+-        DL_UnLoad(sltp, slotID);
++        /*
++         * Calling dlclose() in a atfork handler may cause a deadlock.
++         * dlclose() may itself modify the atfork handler table to remove
++         * any fork handlers that the to be unloaded library has registered.
++         * Since the atfork handler table is currently locked when we are in
++         * an atfork handler, this would produce a deadlock.
++         */
++        if (!in_child_fork_initializer)
++            DL_UnLoad(sltp, slotID);
+     }
+ 
+     // Un register from Slot D
diff --git a/SOURCES/opencryptoki-3.16.0-bf812c652c49d7e248b115d121a4f7f6568941a2.patch b/SOURCES/opencryptoki-3.16.0-bf812c652c49d7e248b115d121a4f7f6568941a2.patch
new file mode 100644
index 0000000..f5a7617
--- /dev/null
+++ b/SOURCES/opencryptoki-3.16.0-bf812c652c49d7e248b115d121a4f7f6568941a2.patch
@@ -0,0 +1,21 @@
+commit bf812c652c49d7e248b115d121a4f7f6568941a2
+Author: Ingo Franzki <ifranzki@linux.ibm.com>
+Date:   Tue Apr 6 13:41:55 2021 +0200
+
+    Update travis yaml file to install libudev development files
+    
+    Signed-off-by: Ingo Franzki <ifranzki@linux.ibm.com>
+
+diff --git a/.travis.yml b/.travis.yml
+index d2907246..fd4092e3 100644
+--- a/.travis.yml
++++ b/.travis.yml
+@@ -5,7 +5,7 @@ language: c
+ 
+ before_install:
+     - sudo apt-get -qq update
+-    - sudo apt-get install -y expect trousers libldap2-dev libtspi-dev wget
++    - sudo apt-get install -y expect trousers libldap2-dev libtspi-dev wget libudev-dev
+     - sudo wget https://launchpad.net/ubuntu/+archive/primary/+files/libica3_3.4.0-0ubuntu1_s390x.deb
+     - sudo wget https://launchpad.net/ubuntu/+archive/primary/+files/libica-dev_3.4.0-0ubuntu1_s390x.deb
+     - sudo dpkg -i libica3_3.4.0-0ubuntu1_s390x.deb || true    # icatok needs libica >= 3.3
diff --git a/SOURCES/opencryptoki-3.16.0-c79e899d77a5724635a9d4451a34a240e2c7e891.patch b/SOURCES/opencryptoki-3.16.0-c79e899d77a5724635a9d4451a34a240e2c7e891.patch
new file mode 100644
index 0000000..d515e15
--- /dev/null
+++ b/SOURCES/opencryptoki-3.16.0-c79e899d77a5724635a9d4451a34a240e2c7e891.patch
@@ -0,0 +1,462 @@
+commit c79e899d77a5724635a9d4451a34a240e2c7e891
+Author: Ingo Franzki <ifranzki@linux.ibm.com>
+Date:   Fri Apr 16 13:41:41 2021 +0200
+
+    Fix potential deadlock situation with double read-locks
+    
+    Do not get and read-lock an object twice within the same thread via
+    function object_mgr_find_in_map1(), as this would read-lock the object
+    twice.
+    
+    This could cause a deadlock situation, when in-between the first
+    and the second call to object_mgr_find_in_map1() the token object is
+    modified by another process. The second object_mgr_find_in_map1() would
+    detect that the object has been modified (object_mgr_check_shm()), and
+    would try to re-load the object from the disk. For re-loading, the
+    object is unlocked once, and a write-lock is acquired instead.
+    However, if the current thread has read-locked the object twice, but
+    releases only one read-lock, then it will never get the write lock,
+    because it still owns the read lock itself.
+    
+    To avoid this situation, release the read-lock before calling another
+    function that also acquires the read lock of the object. That way, only
+    one read-lock is held by the current thread, and re-loading the object
+    will not cause a deadlock.
+    
+    Signed-off-by: Ingo Franzki <ifranzki@linux.ibm.com>
+
+diff --git a/usr/lib/common/decr_mgr.c b/usr/lib/common/decr_mgr.c
+index 317ef995..9842302b 100644
+--- a/usr/lib/common/decr_mgr.c
++++ b/usr/lib/common/decr_mgr.c
+@@ -540,6 +540,10 @@ CK_RV decr_mgr_init(STDLL_TokData_t *tokdata,
+         }
+         memset(ctx->context, 0x0, sizeof(AES_GCM_CONTEXT));
+ 
++        /* Release obj lock, token specific aes-gcm may re-acquire the lock */
++        object_put(tokdata, key_obj, TRUE);
++        key_obj = NULL;
++
+         rc = aes_gcm_init(tokdata, sess, ctx, mech, key_handle, 0);
+         if (rc) {
+             TRACE_ERROR("Could not initialize AES_GCM parms.\n");
+diff --git a/usr/lib/common/encr_mgr.c b/usr/lib/common/encr_mgr.c
+index d3ecdeee..3e85ceab 100644
+--- a/usr/lib/common/encr_mgr.c
++++ b/usr/lib/common/encr_mgr.c
+@@ -537,6 +537,10 @@ CK_RV encr_mgr_init(STDLL_TokData_t *tokdata,
+         }
+         memset(ctx->context, 0x0, sizeof(AES_GCM_CONTEXT));
+ 
++        /* Release obj lock, token specific aes-gcm may re-acquire the lock */
++        object_put(tokdata, key_obj, TRUE);
++        key_obj = NULL;
++
+         rc = aes_gcm_init(tokdata, sess, ctx, mech, key_handle, 1);
+         if (rc != CKR_OK) {
+             TRACE_ERROR("Could not initialize AES_GCM parms.\n");
+diff --git a/usr/lib/common/mech_rsa.c b/usr/lib/common/mech_rsa.c
+index 1652f90a..e35b383c 100644
+--- a/usr/lib/common/mech_rsa.c
++++ b/usr/lib/common/mech_rsa.c
+@@ -602,6 +602,10 @@ CK_RV rsa_oaep_crypt(STDLL_TokData_t *tokdata, SESSION *sess,
+             goto done;
+         }
+ 
++        /* Release obj lock, token specific rsa-oaep may re-acquire the lock */
++        object_put(tokdata, key_obj, TRUE);
++        key_obj = NULL;
++
+         rc = token_specific.t_rsa_oaep_encrypt(tokdata, ctx, in_data,
+                                                in_data_len, out_data,
+                                                out_data_len, hash, hlen);
+@@ -625,6 +629,10 @@ CK_RV rsa_oaep_crypt(STDLL_TokData_t *tokdata, SESSION *sess,
+             goto done;
+         }
+ 
++        /* Release obj lock, token specific rsa-oaep may re-acquire the lock */
++        object_put(tokdata, key_obj, TRUE);
++        key_obj = NULL;
++
+         rc = token_specific.t_rsa_oaep_decrypt(tokdata, ctx, in_data,
+                                                in_data_len, out_data,
+                                                out_data_len, hash, hlen);
+@@ -1331,6 +1339,10 @@ CK_RV rsa_pss_sign(STDLL_TokData_t *tokdata, SESSION *sess,
+         goto done;
+     }
+ 
++    /* Release obj lock, token specific rsa_pss may re-acquire the lock */
++    object_put(tokdata, key_obj, TRUE);
++    key_obj = NULL;
++
+     rc = token_specific.t_rsa_pss_sign(tokdata, sess, ctx, in_data, in_data_len,
+                                        out_data, out_data_len);
+     if (rc != CKR_OK)
+@@ -1389,6 +1401,10 @@ CK_RV rsa_pss_verify(STDLL_TokData_t *tokdata, SESSION *sess,
+         goto done;
+     }
+ 
++    /* Release obj lock, token specific rsa_pss may re-acquire the lock */
++    object_put(tokdata, key_obj, TRUE);
++    key_obj = NULL;
++
+     rc = token_specific.t_rsa_pss_verify(tokdata, sess, ctx, in_data,
+                                          in_data_len, signature, sig_len);
+     if (rc != CKR_OK)
+diff --git a/usr/lib/common/sign_mgr.c b/usr/lib/common/sign_mgr.c
+index 937a371a..c7268e01 100644
+--- a/usr/lib/common/sign_mgr.c
++++ b/usr/lib/common/sign_mgr.c
+@@ -424,6 +424,10 @@ CK_RV sign_mgr_init(STDLL_TokData_t *tokdata,
+         ctx->context_len = 0;
+         ctx->context = NULL;
+ 
++        /* Release obj lock, token specific hmac-sign may re-acquire the lock */
++        object_put(tokdata, key_obj, TRUE);
++        key_obj = NULL;
++
+         rc = hmac_sign_init(tokdata, sess, mech, key);
+         if (rc != CKR_OK) {
+             TRACE_ERROR("Failed to initialize hmac.\n");
+diff --git a/usr/lib/ep11_stdll/ep11_specific.c b/usr/lib/ep11_stdll/ep11_specific.c
+index 3ac3768a..52f95d7a 100644
+--- a/usr/lib/ep11_stdll/ep11_specific.c
++++ b/usr/lib/ep11_stdll/ep11_specific.c
+@@ -6948,6 +6948,13 @@ CK_RV ep11tok_sign_init(STDLL_TokData_t * tokdata, SESSION * session,
+     rc = ep11tok_pkey_check(tokdata, session, key_obj, mech);
+     switch (rc) {
+     case CKR_OK:
++        /*
++         * Release obj lock, sign_mgr_init or ep11tok_sign_verify_init_ibm_ed
++         * may re-acquire the lock
++         */
++        object_put(tokdata, key_obj, TRUE);
++        key_obj = NULL;
++
+         /* Note that Edwards curves in general are not yet supported in
+          * opencryptoki. These two special IBM specific ED mechs are only
+          * supported by the ep11token, so let's keep them local here. */
+@@ -7029,11 +7036,16 @@ CK_RV ep11tok_sign(STDLL_TokData_t * tokdata, SESSION * session,
+          * opencryptoki. These two special IBM specific ED mechs are only
+          * supported by the ep11token, so let's keep them local here. */
+         if (ctx->mech.mechanism == CKM_IBM_ED25519_SHA512 ||
+-            ctx->mech.mechanism == CKM_IBM_ED448_SHA3)
++            ctx->mech.mechanism == CKM_IBM_ED448_SHA3) {
+             rc = pkey_ibm_ed_sign(key_obj, in_data, in_data_len, signature, sig_len);
+-        else
++        } else {
++            /* Release obj lock, sign_mgr_sign may re-acquire the lock */
++            object_put(tokdata, key_obj, TRUE);
++            key_obj = NULL;
++
+             rc = sign_mgr_sign(tokdata, session, length_only, ctx, in_data,
+                                in_data_len, signature, sig_len);
++        }
+         goto done; /* no ep11 fallback possible */
+     }
+ 
+@@ -7071,6 +7083,11 @@ CK_RV ep11tok_sign_update(STDLL_TokData_t * tokdata, SESSION * session,
+     if (!in_data || !in_data_len)
+         return CKR_OK;
+ 
++    if (ctx->pkey_active) {
++        rc = sign_mgr_sign_update(tokdata, session, ctx, in_data, in_data_len);
++        goto done; /* no ep11 fallback possible */
++    }
++
+     rc = h_opaque_2_blob(tokdata, ctx->key, &keyblob, &keyblobsize, &key_obj,
+                           READ_LOCK);
+     if (rc != CKR_OK) {
+@@ -7078,11 +7095,6 @@ CK_RV ep11tok_sign_update(STDLL_TokData_t * tokdata, SESSION * session,
+         return rc;
+     }
+ 
+-    if (ctx->pkey_active) {
+-        rc = sign_mgr_sign_update(tokdata, session, ctx, in_data, in_data_len);
+-        goto done; /* no ep11 fallback possible */
+-    }
+-
+     RETRY_START
+         rc = dll_m_SignUpdate(ctx->context, ctx->context_len, in_data,
+                               in_data_len, ep11_data->target);
+@@ -7115,6 +7127,11 @@ CK_RV ep11tok_sign_final(STDLL_TokData_t * tokdata, SESSION * session,
+     CK_BYTE *keyblob;
+     OBJECT *key_obj = NULL;
+ 
++    if (ctx->pkey_active) {
++        rc = sign_mgr_sign_final(tokdata, session, length_only, ctx, signature, sig_len);
++        goto done; /* no ep11 fallback possible */
++    }
++
+     rc = h_opaque_2_blob(tokdata, ctx->key, &keyblob, &keyblobsize, &key_obj,
+                           READ_LOCK);
+     if (rc != CKR_OK) {
+@@ -7122,11 +7139,6 @@ CK_RV ep11tok_sign_final(STDLL_TokData_t * tokdata, SESSION * session,
+         return rc;
+     }
+ 
+-    if (ctx->pkey_active) {
+-        rc = sign_mgr_sign_final(tokdata, session, length_only, ctx, signature, sig_len);
+-        goto done; /* no ep11 fallback possible */
+-    }
+-
+     RETRY_START
+         rc = dll_m_SignFinal(ctx->context, ctx->context_len, signature, sig_len,
+                              ep11_data->target);
+@@ -7241,6 +7253,13 @@ CK_RV ep11tok_verify_init(STDLL_TokData_t * tokdata, SESSION * session,
+     rc = ep11tok_pkey_check(tokdata, session, key_obj, mech);
+     switch (rc) {
+     case CKR_OK:
++        /*
++         * Release obj lock, verify_mgr_init or ep11tok_sign_verify_init_ibm_ed
++         * may re-acquire the lock
++         */
++        object_put(tokdata, key_obj, TRUE);
++        key_obj = NULL;
++
+         /* Note that Edwards curves in general are not yet supported in
+          * opencryptoki. These two special IBM specific ED mechs are only
+          * supported by the ep11token, so let's keep them local here. */
+@@ -7320,12 +7339,17 @@ CK_RV ep11tok_verify(STDLL_TokData_t * tokdata, SESSION * session,
+          * opencryptoki. These two special IBM specific ED mechs are only
+          * supported by the ep11token, so let's keep them local here. */
+         if (ctx->mech.mechanism == CKM_IBM_ED25519_SHA512 ||
+-            ctx->mech.mechanism == CKM_IBM_ED448_SHA3)
++            ctx->mech.mechanism == CKM_IBM_ED448_SHA3) {
+             rc = pkey_ibm_ed_verify(key_obj, in_data, in_data_len,
+                                     signature, sig_len);
+-        else
++        } else {
++            /* Release obj lock, verify_mgr_verify may re-acquire the lock */
++            object_put(tokdata, key_obj, TRUE);
++            key_obj = NULL;
++
+             rc = verify_mgr_verify(tokdata, session, ctx, in_data,
+                                    in_data_len, signature, sig_len);
++        }
+         goto done; /* no ep11 fallback possible */
+     }
+ 
+@@ -7363,6 +7387,11 @@ CK_RV ep11tok_verify_update(STDLL_TokData_t * tokdata, SESSION * session,
+     if (!in_data || !in_data_len)
+         return CKR_OK;
+ 
++    if (ctx->pkey_active) {
++        rc = verify_mgr_verify_update(tokdata, session, ctx, in_data, in_data_len);
++        goto done; /* no ep11 fallback possible */
++    }
++
+     rc = h_opaque_2_blob(tokdata, ctx->key, &keyblob, &keyblobsize, &key_obj,
+                          READ_LOCK);
+     if (rc != CKR_OK) {
+@@ -7370,11 +7399,6 @@ CK_RV ep11tok_verify_update(STDLL_TokData_t * tokdata, SESSION * session,
+         return rc;
+     }
+ 
+-    if (ctx->pkey_active) {
+-        rc = verify_mgr_verify_update(tokdata, session, ctx, in_data, in_data_len);
+-        goto done; /* no ep11 fallback possible */
+-    }
+-
+     RETRY_START
+         rc = dll_m_VerifyUpdate(ctx->context, ctx->context_len, in_data,
+                                 in_data_len, ep11_data->target);
+@@ -7406,6 +7430,11 @@ CK_RV ep11tok_verify_final(STDLL_TokData_t * tokdata, SESSION * session,
+     CK_BYTE *keyblob;
+     OBJECT *key_obj = NULL;
+ 
++    if (ctx->pkey_active) {
++        rc = verify_mgr_verify_final(tokdata, session, ctx, signature, sig_len);
++        goto done; /* no ep11 fallback possible */
++    }
++
+     rc = h_opaque_2_blob(tokdata, ctx->key, &keyblob, &keyblobsize, &key_obj,
+                          READ_LOCK);
+     if (rc != CKR_OK) {
+@@ -7413,11 +7442,6 @@ CK_RV ep11tok_verify_final(STDLL_TokData_t * tokdata, SESSION * session,
+         return rc;
+     }
+ 
+-    if (ctx->pkey_active) {
+-        rc = verify_mgr_verify_final(tokdata, session, ctx, signature, sig_len);
+-        goto done; /* no ep11 fallback possible */
+-    }
+-
+     RETRY_START
+         rc = dll_m_VerifyFinal(ctx->context, ctx->context_len, signature,
+                                sig_len, ep11_data->target);
+@@ -7501,6 +7525,12 @@ CK_RV ep11tok_decrypt_final(STDLL_TokData_t * tokdata, SESSION * session,
+     CK_BYTE *keyblob;
+     OBJECT *key_obj = NULL;
+ 
++    if (ctx->pkey_active) {
++        rc = decr_mgr_decrypt_final(tokdata, session, length_only,
++                                    ctx, output_part, p_output_part_len);
++        goto done; /* no ep11 fallback possible */
++    }
++
+     rc = h_opaque_2_blob(tokdata, ctx->key, &keyblob, &keyblobsize, &key_obj,
+                          READ_LOCK);
+     if (rc != CKR_OK) {
+@@ -7508,12 +7538,6 @@ CK_RV ep11tok_decrypt_final(STDLL_TokData_t * tokdata, SESSION * session,
+         return rc;
+     }
+ 
+-    if (ctx->pkey_active) {
+-        rc = decr_mgr_decrypt_final(tokdata, session, length_only,
+-                                    ctx, output_part, p_output_part_len);
+-        goto done; /* no ep11 fallback possible */
+-    }
+-
+     RETRY_START
+         rc = dll_m_DecryptFinal(ctx->context, ctx->context_len,
+                                 output_part, p_output_part_len,
+@@ -7548,13 +7572,6 @@ CK_RV ep11tok_decrypt(STDLL_TokData_t * tokdata, SESSION * session,
+     CK_BYTE *keyblob;
+     OBJECT *key_obj = NULL;
+ 
+-    rc = h_opaque_2_blob(tokdata, ctx->key, &keyblob, &keyblobsize, &key_obj,
+-                         READ_LOCK);
+-    if (rc != CKR_OK) {
+-        TRACE_ERROR("%s h_opaque_2_blob, rc=0x%lx\n", __func__, rc);
+-        return rc;
+-    }
+-
+     if (ctx->pkey_active) {
+         rc = decr_mgr_decrypt(tokdata, session, length_only, ctx,
+                               input_data, input_data_len, output_data,
+@@ -7562,6 +7579,13 @@ CK_RV ep11tok_decrypt(STDLL_TokData_t * tokdata, SESSION * session,
+         goto done; /* no ep11 fallback possible */
+     }
+ 
++    rc = h_opaque_2_blob(tokdata, ctx->key, &keyblob, &keyblobsize, &key_obj,
++                         READ_LOCK);
++    if (rc != CKR_OK) {
++        TRACE_ERROR("%s h_opaque_2_blob, rc=0x%lx\n", __func__, rc);
++        return rc;
++    }
++
+     RETRY_START
+         rc = dll_m_Decrypt(ctx->context, ctx->context_len, input_data,
+                            input_data_len, output_data, p_output_data_len,
+@@ -7602,13 +7626,6 @@ CK_RV ep11tok_decrypt_update(STDLL_TokData_t * tokdata, SESSION * session,
+         return CKR_OK;          /* nothing to update, keep context */
+     }
+ 
+-    rc = h_opaque_2_blob(tokdata, ctx->key, &keyblob, &keyblobsize, &key_obj,
+-                         READ_LOCK);
+-    if (rc != CKR_OK) {
+-        TRACE_ERROR("%s h_opaque_2_blob, rc=0x%lx\n", __func__, rc);
+-        return rc;
+-    }
+-
+     if (ctx->pkey_active) {
+         rc = decr_mgr_decrypt_update(tokdata, session, length_only,
+                                      ctx, input_part, input_part_len,
+@@ -7616,6 +7633,13 @@ CK_RV ep11tok_decrypt_update(STDLL_TokData_t * tokdata, SESSION * session,
+         goto done; /* no ep11 fallback possible */
+     }
+ 
++    rc = h_opaque_2_blob(tokdata, ctx->key, &keyblob, &keyblobsize, &key_obj,
++                         READ_LOCK);
++    if (rc != CKR_OK) {
++        TRACE_ERROR("%s h_opaque_2_blob, rc=0x%lx\n", __func__, rc);
++        return rc;
++    }
++
+     RETRY_START
+         rc = dll_m_DecryptUpdate(ctx->context, ctx->context_len,
+                                  input_part, input_part_len, output_part,
+@@ -7695,6 +7719,12 @@ CK_RV ep11tok_encrypt_final(STDLL_TokData_t * tokdata, SESSION * session,
+     CK_BYTE *keyblob;
+     OBJECT *key_obj = NULL;
+ 
++    if (ctx->pkey_active) {
++        rc = encr_mgr_encrypt_final(tokdata, session, length_only,
++                                    ctx, output_part, p_output_part_len);
++        goto done; /* no ep11 fallback possible */
++    }
++
+     rc = h_opaque_2_blob(tokdata, ctx->key, &keyblob, &keyblobsize, &key_obj,
+                          READ_LOCK);
+     if (rc != CKR_OK) {
+@@ -7702,12 +7732,6 @@ CK_RV ep11tok_encrypt_final(STDLL_TokData_t * tokdata, SESSION * session,
+         return rc;
+     }
+ 
+-    if (ctx->pkey_active) {
+-        rc = encr_mgr_encrypt_final(tokdata, session, length_only,
+-                                    ctx, output_part, p_output_part_len);
+-        goto done; /* no ep11 fallback possible */
+-    }
+-
+     RETRY_START
+         rc = dll_m_EncryptFinal(ctx->context, ctx->context_len,
+                                 output_part, p_output_part_len,
+@@ -7742,13 +7766,6 @@ CK_RV ep11tok_encrypt(STDLL_TokData_t * tokdata, SESSION * session,
+     CK_BYTE *keyblob;
+     OBJECT *key_obj = NULL;
+ 
+-    rc = h_opaque_2_blob(tokdata, ctx->key, &keyblob, &keyblobsize, &key_obj,
+-                         READ_LOCK);
+-    if (rc != CKR_OK) {
+-        TRACE_ERROR("%s h_opaque_2_blob, rc=0x%lx\n", __func__, rc);
+-        return rc;
+-    }
+-
+     if (ctx->pkey_active) {
+         rc = encr_mgr_encrypt(tokdata, session, length_only, ctx,
+                               input_data, input_data_len, output_data,
+@@ -7756,6 +7773,13 @@ CK_RV ep11tok_encrypt(STDLL_TokData_t * tokdata, SESSION * session,
+         goto done; /* no ep11 fallback possible */
+     }
+ 
++    rc = h_opaque_2_blob(tokdata, ctx->key, &keyblob, &keyblobsize, &key_obj,
++                         READ_LOCK);
++    if (rc != CKR_OK) {
++        TRACE_ERROR("%s h_opaque_2_blob, rc=0x%lx\n", __func__, rc);
++        return rc;
++    }
++
+     RETRY_START
+         rc = dll_m_Encrypt(ctx->context, ctx->context_len, input_data,
+                            input_data_len, output_data, p_output_data_len,
+@@ -7796,13 +7820,6 @@ CK_RV ep11tok_encrypt_update(STDLL_TokData_t * tokdata, SESSION * session,
+         return CKR_OK;          /* nothing to update, keep context */
+     }
+ 
+-    rc = h_opaque_2_blob(tokdata, ctx->key, &keyblob, &keyblobsize, &key_obj,
+-                         READ_LOCK);
+-    if (rc != CKR_OK) {
+-        TRACE_ERROR("%s h_opaque_2_blob, rc=0x%lx\n", __func__, rc);
+-        return rc;
+-    }
+-
+     if (ctx->pkey_active) {
+         rc = encr_mgr_encrypt_update(tokdata, session, length_only, ctx,
+                                      input_part, input_part_len, output_part,
+@@ -7810,6 +7827,13 @@ CK_RV ep11tok_encrypt_update(STDLL_TokData_t * tokdata, SESSION * session,
+         goto done; /* no ep11 fallback possible */
+     }
+ 
++    rc = h_opaque_2_blob(tokdata, ctx->key, &keyblob, &keyblobsize, &key_obj,
++                         READ_LOCK);
++    if (rc != CKR_OK) {
++        TRACE_ERROR("%s h_opaque_2_blob, rc=0x%lx\n", __func__, rc);
++        return rc;
++    }
++
+     RETRY_START
+         rc = dll_m_EncryptUpdate(ctx->context, ctx->context_len,
+                                  input_part, input_part_len, output_part,
+@@ -7921,6 +7945,10 @@ static CK_RV ep11_ende_crypt_init(STDLL_TokData_t * tokdata, SESSION * session,
+     rc = ep11tok_pkey_check(tokdata, session, key_obj, mech);
+     switch (rc) {
+     case CKR_OK:
++        /* Release obj lock, encr/decr_mgr_init may re-acquire the lock */
++        object_put(tokdata, key_obj, TRUE);
++        key_obj = NULL;
++
+         if (op == DECRYPT) {
+             rc = decr_mgr_init(tokdata, session, &session->decr_ctx,
+                                OP_DECRYPT_INIT, mech, key);
diff --git a/SOURCES/opencryptoki-3.16.0-d2f137cce5e6efb123842509352c7c49f889c67f.patch b/SOURCES/opencryptoki-3.16.0-d2f137cce5e6efb123842509352c7c49f889c67f.patch
new file mode 100644
index 0000000..8f1477c
--- /dev/null
+++ b/SOURCES/opencryptoki-3.16.0-d2f137cce5e6efb123842509352c7c49f889c67f.patch
@@ -0,0 +1,104 @@
+commit d2f137cce5e6efb123842509352c7c49f889c67f
+Author: Ingo Franzki <ifranzki@linux.ibm.com>
+Date:   Thu Jul 22 15:55:02 2021 +0200
+
+    pkcstok_migrate: Rework string quoting for opencryptoki.conf migration
+    
+    Due to the way the parser works, a slot description like
+    'description = "slot"' works, but not without quotes ('description = slot').
+    The word 'slot' is treated as a keyword if not quoted (besides other keywords,
+    too), so if the word 'slot' would appear in an unquoted string, the
+    configuration file would fail to parse.
+    
+    Always quote the value of 'description' and 'manufacturer'. Quote the
+    value of 'stdll', 'confname', and 'tokname' if it contains spaces, and
+    never quote the value of 'hwversion', 'firmwareversion', and 'tokversion'.
+    
+    Signed-off-by: Ingo Franzki <ifranzki@linux.ibm.com>
+
+diff --git a/usr/sbin/pkcstok_migrate/pkcstok_migrate.c b/usr/sbin/pkcstok_migrate/pkcstok_migrate.c
+index a29dc8f7..853986e8 100644
+--- a/usr/sbin/pkcstok_migrate/pkcstok_migrate.c
++++ b/usr/sbin/pkcstok_migrate/pkcstok_migrate.c
+@@ -2060,7 +2060,7 @@ done:
+  */
+ static int parseupdate_ockversion(void *private, const char *version)
+ {
+-	struct parseupdate *u = (struct parseupdate *)private;
++    struct parseupdate *u = (struct parseupdate *)private;
+ 
+     fprintf(u->f, "version %s", version);
+     return 0;
+@@ -2075,14 +2075,14 @@ static void parseupdate_disab_event_supp(void *private)
+ 
+ static void parseupdate_eol(void *private)
+ {
+-	struct parseupdate *u = (struct parseupdate *)private;
++    struct parseupdate *u = (struct parseupdate *)private;
+ 
+     fputc('\n', u->f);
+ }
+ 
+ static int parseupdate_begin_slot(void *private, int slot, int nl_before_begin)
+ {
+-	struct parseupdate *u = (struct parseupdate *)private;
++    struct parseupdate *u = (struct parseupdate *)private;
+ 
+     u->activeslot = (slot == u->slotnum);
+     if (nl_before_begin)
+@@ -2094,7 +2094,7 @@ static int parseupdate_begin_slot(void *private, int slot, int nl_before_begin)
+ 
+ static int parseupdate_end_slot(void *private)
+ {
+-	struct parseupdate *u = (struct parseupdate *)private;
++    struct parseupdate *u = (struct parseupdate *)private;
+ 
+     if (u->activeslot)
+         fprintf(u->f, "  tokversion = 3.12\n");
+@@ -2105,19 +2105,32 @@ static int parseupdate_end_slot(void *private)
+ 
+ static int parseupdate_key_str(void *private, int tok, const char *val)
+ {
+-	struct parseupdate *u = (struct parseupdate *)private;
++    struct parseupdate *u = (struct parseupdate *)private;
+ 
+-    if (tok != KW_HWVERSION && tok != KW_FWVERSION &&
+-        strchr(val, ' ') != NULL)
++    switch (tok) {
++    case KW_SLOTDESC:
++    case KW_MANUFID:
+         fprintf(u->f, "  %s = \"%s\"", keyword_token_to_str(tok), val);
+-    else if (tok != KW_TOKVERSION)
++        break;
++    case KW_STDLL:
++    case KW_CONFNAME:
++    case KW_TOKNAME:
++        if (strchr(val, ' ') != NULL)
++            fprintf(u->f, "  %s = \"%s\"", keyword_token_to_str(tok), val);
++        else
++            fprintf(u->f, "  %s = %s", keyword_token_to_str(tok), val);
++        break;
++    case KW_HWVERSION:
++    case KW_FWVERSION:
+         fprintf(u->f, "  %s = %s", keyword_token_to_str(tok), val);
++        break;
++	}
+     return 0;
+ }
+ 
+ static int parseupdate_key_vers(void *private, int tok, unsigned int vers)
+ {
+-	struct parseupdate *u = (struct parseupdate *)private;
++    struct parseupdate *u = (struct parseupdate *)private;
+ 
+     if (tok == KW_TOKVERSION && !u->activeslot)
+         fprintf(u->f, "  %s = %d.%d", keyword_token_to_str(tok),
+@@ -2127,7 +2140,7 @@ static int parseupdate_key_vers(void *private, int tok, unsigned int vers)
+ 
+ static void parseupdate_eolcomment(void *private, const char *comment)
+ {
+-	struct parseupdate *u = (struct parseupdate *)private;
++    struct parseupdate *u = (struct parseupdate *)private;
+ 
+     fprintf(u->f, "#%s", comment);
+ }
diff --git a/SOURCES/opencryptoki-3.16.0-d7de5092247a0efc2c397f12977a7c9925420143.patch b/SOURCES/opencryptoki-3.16.0-d7de5092247a0efc2c397f12977a7c9925420143.patch
new file mode 100644
index 0000000..40a4962
--- /dev/null
+++ b/SOURCES/opencryptoki-3.16.0-d7de5092247a0efc2c397f12977a7c9925420143.patch
@@ -0,0 +1,239 @@
+commit d7de5092247a0efc2c397f12977a7c9925420143
+Author: Ingo Franzki <ifranzki@linux.ibm.com>
+Date:   Tue Feb 16 17:15:20 2021 +0100
+
+    TESTCASES: Add event support tests
+    
+    Signed-off-by: Ingo Franzki <ifranzki@linux.ibm.com>
+
+diff --git a/testcases/misc_tests/events.c b/testcases/misc_tests/events.c
+new file mode 100644
+index 00000000..fecc7bfe
+--- /dev/null
++++ b/testcases/misc_tests/events.c
+@@ -0,0 +1,190 @@
++/*
++ * COPYRIGHT (c) International Business Machines Corp. 2021
++ *
++ * This program is provided under the terms of the Common Public License,
++ * version 1.0 (CPL-1.0). Any use, reproduction or distribution for this
++ * software constitutes recipient's acceptance of CPL-1.0 terms which can be
++ * found in the file LICENSE file or at
++ * https://opensource.org/licenses/cpl1.0.php
++ */
++
++
++#include <stdio.h>
++#include <stdlib.h>
++#include <string.h>
++
++#include "event_client.h"
++#include "regress.h"
++#include "defs.h"
++
++const char payload[20] = "12345678901234567890";
++
++static inline void init_event_destination(struct event_destination *dest,
++                                          unsigned int token_type,
++                                          const char *label,
++                                          pid_t process_id)
++{
++    size_t len;
++
++    dest->token_type = token_type;
++    dest->process_id = process_id;
++
++    memset(dest->token_label, ' ', sizeof(dest->token_label));
++    if (label != NULL) {
++        len = strlen(label);
++        memcpy(dest->token_label, label, len > sizeof(dest->token_label) ?
++                                    sizeof(dest->token_label) : len);
++    }
++}
++
++int main(int argc, char **argv)
++{
++    CK_C_INITIALIZE_ARGS cinit_args;
++    int rc, fd = -1, ret = 1;
++    struct event_destination dest;
++    struct event_reply reply;
++
++    UNUSED(argc);
++    UNUSED(argv);
++
++    rc = do_GetFunctionList();
++    if (!rc) {
++        testcase_error("do_getFunctionList(), rc=%s", p11_get_ckr(rc));
++        return rc;
++    }
++
++    /*
++     * Initialize Opencryptoki in this process, so that at least one
++     * process is receiving the events.
++     */
++    memset(&cinit_args, 0x0, sizeof(cinit_args));
++    cinit_args.flags = CKF_OS_LOCKING_OK;
++    funcs->C_Initialize(&cinit_args);
++
++    testcase_setup(0);
++    testcase_begin("Starting event tests");
++
++    // Test fork before C_Initialize
++    testcase_new_assertion();
++
++    rc = send_event(-1, 0x12345, EVENT_FLAGS_NONE, 0, NULL, NULL, NULL);
++    if (rc != 0) {
++        testcase_fail("send_event (simple, one-shot) rc = %d (%s)", rc,
++                      strerror(-rc));
++        goto out;
++    }
++    testcase_pass("send_event (simple, one-shot)");
++
++    rc = send_event(-1, 0x12345, EVENT_FLAGS_NONE, sizeof(payload), payload,
++                    NULL, NULL);
++    if (rc != 0) {
++        testcase_fail("send_event (payload, one-shot) rc = %d (%s)", rc,
++                      strerror(-rc));
++        goto out;
++    }
++    testcase_pass("send_event (payload, one-shot)");
++
++    init_event_destination(&dest, EVENT_TOK_TYPE_CCA, NULL, 0);
++
++    rc = send_event(-1, 0x12345, EVENT_FLAGS_NONE, 0, NULL, &dest, NULL);
++    if (rc != 0) {
++        testcase_fail("send_event (token-type, one-shot) rc = %d (%s)", rc,
++                      strerror(-rc));
++        goto out;
++    }
++    testcase_pass("send_event (token-type, one-shot)");
++
++    init_event_destination(&dest, EVENT_TOK_TYPE_ALL, "cca", 0);
++
++    rc = send_event(-1, 0x12345, EVENT_FLAGS_NONE, 0, NULL, &dest, NULL);
++    if (rc != 0) {
++        testcase_fail("send_event (token-label, one-shot) rc = %d (%s)", rc,
++                      strerror(-rc));
++        goto out;
++    }
++    testcase_pass("send_event (token-label, one-shot)");
++
++    init_event_destination(&dest, EVENT_TOK_TYPE_ALL, NULL, 12345);
++
++    rc = send_event(-1, 0x12345, EVENT_FLAGS_NONE, 0, NULL, &dest, NULL);
++    if (rc != 0) {
++        testcase_fail("send_event (pid, one-shot) rc = %d (%s)", rc,
++                      strerror(-rc));
++        goto out;
++    }
++    testcase_pass("send_event (pid, one-shot)");
++
++    memset(&reply, 0, sizeof(reply));
++
++    rc = send_event(-1, 0x12345, EVENT_FLAGS_REPLY_REQ, 0, NULL, NULL, &reply);
++    if (rc != 0) {
++        testcase_fail("send_event (reply, one-shot) rc = %d (%s)", rc,
++                      strerror(-rc));
++        goto out;
++    }
++    printf("Reply: positive_replies:    %lu\n", reply.positive_replies);
++    printf("       negative_replies:    %lu\n", reply.negative_replies);
++    printf("       nothandled_replies:  %lu\n", reply.nothandled_replies);
++    if (reply.positive_replies + reply.negative_replies +
++            reply.nothandled_replies == 0) {
++        testcase_fail("send_event (reply, one-shot) replies all zero");
++        goto out;
++    }
++    testcase_pass("send_event (reply, one-shot)");
++
++
++    fd = init_event_client();
++    if (fd < 0) {
++        testcase_fail("init_event_client rc = %d (%s)", fd, strerror(-fd));
++        goto out;
++    }
++    testcase_pass("init_event_client()");
++
++    rc = send_event(fd, 0x12345, EVENT_FLAGS_NONE, 0, NULL, NULL, NULL);
++    if (rc != 0) {
++        testcase_fail("send_event (simple) rc = %d (%s)", rc, strerror(-rc));
++        goto out;
++    }
++    testcase_pass("send_event (simple)");
++
++    rc = send_event(fd, 0x12345, EVENT_FLAGS_NONE, sizeof(payload), payload,
++                    NULL, NULL);
++    if (rc != 0) {
++        testcase_fail("send_event (payload) rc = %d (%s)", rc,
++                      strerror(-rc));
++        goto out;
++    }
++    testcase_pass("send_event (payload)");
++
++    memset(&reply, 0, sizeof(reply));
++
++    rc = send_event(-1, 0x12345, EVENT_FLAGS_REPLY_REQ, 0, NULL, NULL, &reply);
++    if (rc != 0) {
++        testcase_fail("send_event (reply) rc = %d (%s)", rc,
++                      strerror(-rc));
++        goto out;
++    }
++    printf("Reply: positive_replies:    %lu\n", reply.positive_replies);
++    printf("       negative_replies:    %lu\n", reply.negative_replies);
++    printf("       nothandled_replies:  %lu\n", reply.nothandled_replies);
++    if (reply.positive_replies + reply.negative_replies +
++            reply.nothandled_replies == 0) {
++        testcase_fail("send_event (reply) replies all zero");
++        goto out;
++    }
++    testcase_pass("send_event (reply)");
++
++    term_event_client(fd);
++    fd = -1;
++
++    ret = 0;
++
++out:
++    if (fd >= 0)
++        term_event_client(fd);
++
++    funcs->C_Finalize(NULL);
++
++    testcase_print_result();
++    return ret;
++}
+diff --git a/testcases/misc_tests/misc_tests.mk b/testcases/misc_tests/misc_tests.mk
+index 3de11ebe..fb7cc0a1 100644
+--- a/testcases/misc_tests/misc_tests.mk
++++ b/testcases/misc_tests/misc_tests.mk
+@@ -7,7 +7,8 @@ noinst_PROGRAMS +=							\
+ 	testcases/misc_tests/fork testcases/misc_tests/multi_instance   \
+ 	testcases/misc_tests/obj_lock testcases/misc_tests/tok2tok_transport \
+ 	testcases/misc_tests/obj_lock testcases/misc_tests/reencrypt    \
+-	testcases/misc_tests/cca_export_import_test
++	testcases/misc_tests/cca_export_import_test			\
++	testcases/misc_tests/events
+ 
+ testcases_misc_tests_obj_mgmt_tests_CFLAGS = ${testcases_inc}
+ testcases_misc_tests_obj_mgmt_tests_LDADD =				\
+@@ -73,3 +74,8 @@ testcases_misc_tests_cca_export_import_test_LDADD =			\
+ 	testcases/common/libcommon.la
+ testcases_misc_tests_cca_export_import_test_SOURCES =			\
+ 	testcases/misc_tests/cca_export_import_test.c
++	
++testcases_misc_tests_events_CFLAGS = ${testcases_inc}
++testcases_misc_tests_events_LDADD = testcases/common/libcommon.la
++testcases_misc_tests_events_SOURCES = testcases/misc_tests/events.c	\
++	usr/lib/common/event_client.c
+diff --git a/testcases/ock_tests.sh.in b/testcases/ock_tests.sh.in
+index 64c77a7d..6558b031 100755
+--- a/testcases/ock_tests.sh.in
++++ b/testcases/ock_tests.sh.in
+@@ -53,6 +53,7 @@ OCK_TESTS+=" pkcs11/findobjects pkcs11/generate_keypair"
+ OCK_TESTS+=" pkcs11/get_interface pkcs11/getobjectsize pkcs11/sess_opstate"
+ OCK_TESTS+=" misc_tests/fork misc_tests/obj_mgmt_tests" 
+ OCK_TESTS+=" misc_tests/obj_mgmt_lock_tests misc_tests/reencrypt"
++OCK_TESTS+=" misc_tests/events"
+ OCK_TEST=""
+ OCK_BENCHS="pkcs11/*bench"
+ 
diff --git a/SOURCES/opencryptoki-3.16.0-d929fe8470e99f4dcbbd889e7aa87e147d0d5b48.patch b/SOURCES/opencryptoki-3.16.0-d929fe8470e99f4dcbbd889e7aa87e147d0d5b48.patch
new file mode 100644
index 0000000..5e9a346
--- /dev/null
+++ b/SOURCES/opencryptoki-3.16.0-d929fe8470e99f4dcbbd889e7aa87e147d0d5b48.patch
@@ -0,0 +1,619 @@
+commit d929fe8470e99f4dcbbd889e7aa87e147d0d5b48
+Author: Ingo Franzki <ifranzki@linux.ibm.com>
+Date:   Fri Feb 12 11:25:21 2021 +0100
+
+    Externalize linked list functions
+    
+    Externalize the linked list functions (dlist_xxx), so that they
+    can also be used on pkcsslotd.
+    
+    Signed-off-by: Ingo Franzki <ifranzki@linux.ibm.com>
+
+diff --git a/usr/lib/cca_stdll/cca_stdll.mk b/usr/lib/cca_stdll/cca_stdll.mk
+index bd230b9f..c5e86fa7 100644
+--- a/usr/lib/cca_stdll/cca_stdll.mk
++++ b/usr/lib/cca_stdll/cca_stdll.mk
+@@ -35,7 +35,8 @@ opencryptoki_stdll_libpkcs11_cca_la_SOURCES =				\
+ 	usr/lib/common/mech_ssl3.c usr/lib/common/verify_mgr.c		\
+ 	usr/lib/common/p11util.c usr/lib/common/sw_crypt.c		\
+ 	usr/lib/common/shared_memory.c usr/lib/common/profile_obj.c	\
+-	usr/lib/cca_stdll/cca_specific.c usr/lib/common/attributes.c
++	usr/lib/cca_stdll/cca_specific.c usr/lib/common/attributes.c	\
++	usr/lib/common/dlist.c
+ 
+ if ENABLE_LOCKS
+ opencryptoki_stdll_libpkcs11_cca_la_SOURCES +=				\
+diff --git a/usr/lib/common/dlist.c b/usr/lib/common/dlist.c
+new file mode 100644
+index 00000000..1fee1ea9
+--- /dev/null
++++ b/usr/lib/common/dlist.c
+@@ -0,0 +1,218 @@
++/*
++ * COPYRIGHT (c) International Business Machines Corp. 2021
++ *
++ * This program is provided under the terms of the Common Public License,
++ * version 1.0 (CPL-1.0). Any use, reproduction or distribution for this
++ * software constitutes recipient's acceptance of CPL-1.0 terms which can be
++ * found in the file LICENSE file or at
++ * https://opensource.org/licenses/cpl1.0.php
++ */
++
++#include <stdio.h>
++#include <stdlib.h>
++#include <string.h>
++#include <unistd.h>
++#include <sys/types.h>
++#include <errno.h>
++
++#include "dlist.h"
++#include "host_defs.h"
++#include "h_extern.h"
++
++
++// Function:  dlist_add_as_first()
++//
++// Adds the specified node to the start of the list
++//
++// Returns:  pointer to the start of the list
++//
++DL_NODE *dlist_add_as_first(DL_NODE *list, void *data)
++{
++    DL_NODE *node = NULL;
++
++    if (!data)
++        return list;
++
++    node = (DL_NODE *) malloc(sizeof(DL_NODE));
++    if (!node)
++        return NULL;
++
++    node->data = data;
++    node->prev = NULL;
++    node->next = list;
++    if (list)
++        list->prev = node;
++
++    return node;
++}
++
++// Function:  dlist_add_as_last()
++//
++// Adds the specified node to the end of the list
++//
++// Returns:  pointer to the start of the list
++//
++DL_NODE *dlist_add_as_last(DL_NODE *list, void *data)
++{
++    DL_NODE *node = NULL;
++
++    if (!data)
++        return list;
++
++    node = (DL_NODE *) malloc(sizeof(DL_NODE));
++    if (!node)
++        return NULL;
++
++    node->data = data;
++    node->next = NULL;
++
++    if (!list) {
++        node->prev = NULL;
++        return node;
++    } else {
++        DL_NODE *temp = dlist_get_last(list);
++        temp->next = node;
++        node->prev = temp;
++
++        return list;
++    }
++}
++
++// Function:  dlist_find()
++//
++DL_NODE *dlist_find(DL_NODE *list, void *data)
++{
++    DL_NODE *node = list;
++
++    while (node && node->data != data)
++        node = node->next;
++
++    return node;
++}
++
++// Function:  dlist_get_first()
++//
++// Returns the last node in the list or NULL if list is empty
++//
++DL_NODE *dlist_get_first(DL_NODE *list)
++{
++    DL_NODE *temp = list;
++
++    if (!list)
++        return NULL;
++
++    while (temp->prev != NULL)
++        temp = temp->prev;
++
++    return temp;
++}
++
++// Function:  dlist_get_last()
++//
++// Returns the last node in the list or NULL if list is empty
++//
++DL_NODE *dlist_get_last(DL_NODE *list)
++{
++    DL_NODE *temp = list;
++
++    if (!list)
++        return NULL;
++
++    while (temp->next != NULL)
++        temp = temp->next;
++
++    return temp;
++}
++
++//
++//
++CK_ULONG dlist_length(DL_NODE *list)
++{
++    DL_NODE *temp = list;
++    CK_ULONG len = 0;
++
++    while (temp) {
++        len++;
++        temp = temp->next;
++    }
++
++    return len;
++}
++
++//
++//
++DL_NODE *dlist_next(DL_NODE *node)
++{
++    if (!node)
++        return NULL;
++
++    return node->next;
++}
++
++//
++//
++DL_NODE *dlist_prev(DL_NODE *node)
++{
++    if (!node)
++        return NULL;
++
++    return node->prev;
++}
++
++//
++//
++void dlist_purge(DL_NODE *list)
++{
++    DL_NODE *node;
++
++    if (!list)
++        return;
++
++    do {
++        node = list->next;
++        free(list);
++        list = node;
++    } while (list);
++}
++
++// Function:  dlist_remove_node()
++//
++// Attempts to remove the specified node from the list.  The caller is
++// responsible for freeing the data associated with the node prior to
++// calling this routine
++//
++DL_NODE *dlist_remove_node(DL_NODE *list, DL_NODE *node)
++{
++    DL_NODE *temp = list;
++
++    if (!list || !node)
++        return NULL;
++
++    // special case:  removing head of the list
++    //
++    if (list == node) {
++        temp = list->next;
++        if (temp)
++            temp->prev = NULL;
++
++        free(list);
++        return temp;
++    }
++    // we have no guarantee that the node is in the list
++    // so search through the list to find it
++    //
++    while ((temp != NULL) && (temp->next != node))
++        temp = temp->next;
++
++    if (temp != NULL) {
++        DL_NODE *next = node->next;
++
++        temp->next = next;
++        if (next)
++            next->prev = temp;
++
++        free(node);
++    }
++
++    return list;
++}
+diff --git a/usr/lib/common/dlist.h b/usr/lib/common/dlist.h
+new file mode 100644
+index 00000000..eda4af9c
+--- /dev/null
++++ b/usr/lib/common/dlist.h
+@@ -0,0 +1,32 @@
++/*
++ * COPYRIGHT (c) International Business Machines Corp. 2021
++ *
++ * This program is provided under the terms of the Common Public License,
++ * version 1.0 (CPL-1.0). Any use, reproduction or distribution for this
++ * software constitutes recipient's acceptance of CPL-1.0 terms which can be
++ * found in the file LICENSE file or at
++ * https://opensource.org/licenses/cpl1.0.php
++ */
++
++
++
++#ifndef _DLIST_H_
++#define _DLIST_H_
++
++#include "pkcs11types.h"
++#include "defs.h"
++
++// linked-list routines
++//
++DL_NODE *dlist_add_as_first(DL_NODE *list, void *data);
++DL_NODE *dlist_add_as_last(DL_NODE *list, void *data);
++DL_NODE *dlist_find(DL_NODE *list, void *data);
++DL_NODE *dlist_get_first(DL_NODE *list);
++DL_NODE *dlist_get_last(DL_NODE *list);
++CK_ULONG dlist_length(DL_NODE *list);
++DL_NODE *dlist_next(DL_NODE *list);
++DL_NODE *dlist_prev(DL_NODE *list);
++void dlist_purge(DL_NODE *list);
++DL_NODE *dlist_remove_node(DL_NODE *list, DL_NODE *node);
++
++#endif
+diff --git a/usr/lib/common/h_extern.h b/usr/lib/common/h_extern.h
+index 63aff79f..5e251d95 100644
+--- a/usr/lib/common/h_extern.h
++++ b/usr/lib/common/h_extern.h
+@@ -24,6 +24,7 @@
+ #define _H_EXTERN_H
+ 
+ #include <stdio.h>
++#include "dlist.h"
+ 
+ // global variables
+ //
+@@ -1759,19 +1760,6 @@ int ec_point_from_public_data(const CK_BYTE *data, CK_ULONG data_len,
+                               CK_BBOOL *allocated, CK_BYTE **ec_point,
+                               CK_ULONG *ec_point_len);
+ 
+-// linked-list routines
+-//
+-DL_NODE *dlist_add_as_first(DL_NODE *list, void *data);
+-DL_NODE *dlist_add_as_last(DL_NODE *list, void *data);
+-DL_NODE *dlist_find(DL_NODE *list, void *data);
+-DL_NODE *dlist_get_first(DL_NODE *list);
+-DL_NODE *dlist_get_last(DL_NODE *list);
+-CK_ULONG dlist_length(DL_NODE *list);
+-DL_NODE *dlist_next(DL_NODE *list);
+-DL_NODE *dlist_prev(DL_NODE *list);
+-void dlist_purge(DL_NODE *list);
+-DL_NODE *dlist_remove_node(DL_NODE *list, DL_NODE *node);
+-
+ CK_RV attach_shm(STDLL_TokData_t *tokdata, CK_SLOT_ID slot_id);
+ CK_RV detach_shm(STDLL_TokData_t *tokdata, CK_BBOOL ignore_ref_count);
+ 
+diff --git a/usr/lib/common/utility.c b/usr/lib/common/utility.c
+index 38d8d959..b2c6ee50 100644
+--- a/usr/lib/common/utility.c
++++ b/usr/lib/common/utility.c
+@@ -40,203 +40,6 @@
+ #include <sys/file.h>
+ #include <syslog.h>
+ 
+-// Function:  dlist_add_as_first()
+-//
+-// Adds the specified node to the start of the list
+-//
+-// Returns:  pointer to the start of the list
+-//
+-DL_NODE *dlist_add_as_first(DL_NODE *list, void *data)
+-{
+-    DL_NODE *node = NULL;
+-
+-    if (!data)
+-        return list;
+-
+-    node = (DL_NODE *) malloc(sizeof(DL_NODE));
+-    if (!node)
+-        return NULL;
+-
+-    node->data = data;
+-    node->prev = NULL;
+-    node->next = list;
+-    if (list)
+-        list->prev = node;
+-
+-    return node;
+-}
+-
+-// Function:  dlist_add_as_last()
+-//
+-// Adds the specified node to the end of the list
+-//
+-// Returns:  pointer to the start of the list
+-//
+-DL_NODE *dlist_add_as_last(DL_NODE *list, void *data)
+-{
+-    DL_NODE *node = NULL;
+-
+-    if (!data)
+-        return list;
+-
+-    node = (DL_NODE *) malloc(sizeof(DL_NODE));
+-    if (!node)
+-        return NULL;
+-
+-    node->data = data;
+-    node->next = NULL;
+-
+-    if (!list) {
+-        node->prev = NULL;
+-        return node;
+-    } else {
+-        DL_NODE *temp = dlist_get_last(list);
+-        temp->next = node;
+-        node->prev = temp;
+-
+-        return list;
+-    }
+-}
+-
+-// Function:  dlist_find()
+-//
+-DL_NODE *dlist_find(DL_NODE *list, void *data)
+-{
+-    DL_NODE *node = list;
+-
+-    while (node && node->data != data)
+-        node = node->next;
+-
+-    return node;
+-}
+-
+-// Function:  dlist_get_first()
+-//
+-// Returns the last node in the list or NULL if list is empty
+-//
+-DL_NODE *dlist_get_first(DL_NODE *list)
+-{
+-    DL_NODE *temp = list;
+-
+-    if (!list)
+-        return NULL;
+-
+-    while (temp->prev != NULL)
+-        temp = temp->prev;
+-
+-    return temp;
+-}
+-
+-// Function:  dlist_get_last()
+-//
+-// Returns the last node in the list or NULL if list is empty
+-//
+-DL_NODE *dlist_get_last(DL_NODE *list)
+-{
+-    DL_NODE *temp = list;
+-
+-    if (!list)
+-        return NULL;
+-
+-    while (temp->next != NULL)
+-        temp = temp->next;
+-
+-    return temp;
+-}
+-
+-//
+-//
+-CK_ULONG dlist_length(DL_NODE *list)
+-{
+-    DL_NODE *temp = list;
+-    CK_ULONG len = 0;
+-
+-    while (temp) {
+-        len++;
+-        temp = temp->next;
+-    }
+-
+-    return len;
+-}
+-
+-//
+-//
+-DL_NODE *dlist_next(DL_NODE *node)
+-{
+-    if (!node)
+-        return NULL;
+-
+-    return node->next;
+-}
+-
+-//
+-//
+-DL_NODE *dlist_prev(DL_NODE *node)
+-{
+-    if (!node)
+-        return NULL;
+-
+-    return node->prev;
+-}
+-
+-//
+-//
+-void dlist_purge(DL_NODE *list)
+-{
+-    DL_NODE *node;
+-
+-    if (!list)
+-        return;
+-
+-    do {
+-        node = list->next;
+-        free(list);
+-        list = node;
+-    } while (list);
+-}
+-
+-// Function:  dlist_remove_node()
+-//
+-// Attempts to remove the specified node from the list.  The caller is
+-// responsible for freeing the data associated with the node prior to
+-// calling this routine
+-//
+-DL_NODE *dlist_remove_node(DL_NODE *list, DL_NODE *node)
+-{
+-    DL_NODE *temp = list;
+-
+-    if (!list || !node)
+-        return NULL;
+-
+-    // special case:  removing head of the list
+-    //
+-    if (list == node) {
+-        temp = list->next;
+-        if (temp)
+-            temp->prev = NULL;
+-
+-        free(list);
+-        return temp;
+-    }
+-    // we have no guarantee that the node is in the list
+-    // so search through the list to find it
+-    //
+-    while ((temp != NULL) && (temp->next != node))
+-        temp = temp->next;
+-
+-    if (temp != NULL) {
+-        DL_NODE *next = node->next;
+-
+-        temp->next = next;
+-        if (next)
+-            next->prev = temp;
+-
+-        free(node);
+-    }
+-
+-    return list;
+-}
+-
+ CK_RV CreateXProcLock(char *tokname, STDLL_TokData_t *tokdata)
+ {
+     char lockfile[PATH_MAX];
+diff --git a/usr/lib/ep11_stdll/ep11_stdll.mk b/usr/lib/ep11_stdll/ep11_stdll.mk
+index bc617124..b5574d9e 100644
+--- a/usr/lib/ep11_stdll/ep11_stdll.mk
++++ b/usr/lib/ep11_stdll/ep11_stdll.mk
+@@ -36,7 +36,7 @@ opencryptoki_stdll_libpkcs11_ep11_la_SOURCES =				\
+ 	usr/lib/common/utility.c usr/lib/common/trace.c			\
+ 	usr/lib/common/mech_list.c usr/lib/common/shared_memory.c	\
+ 	usr/lib/common/attributes.c usr/lib/common/sw_crypt.c		\
+-	usr/lib/common/profile_obj.c					\
++	usr/lib/common/profile_obj.c usr/lib/common/dlist.c		\
+ 	usr/lib/common/pkey_utils.c					\
+ 	usr/lib/ep11_stdll/new_host.c usr/lib/ep11_stdll/ep11_specific.c
+ 
+diff --git a/usr/lib/ica_s390_stdll/ica_s390_stdll.mk b/usr/lib/ica_s390_stdll/ica_s390_stdll.mk
+index d8448486..8f467e11 100644
+--- a/usr/lib/ica_s390_stdll/ica_s390_stdll.mk
++++ b/usr/lib/ica_s390_stdll/ica_s390_stdll.mk
+@@ -34,7 +34,7 @@ opencryptoki_stdll_libpkcs11_ica_la_SOURCES =				\
+ 	usr/lib/common/verify_mgr.c usr/lib/common/trace.c		\
+ 	usr/lib/common/mech_list.c usr/lib/common/shared_memory.c	\
+ 	usr/lib/common/profile_obj.c usr/lib/common/attributes.c	\
+-	usr/lib/ica_s390_stdll/ica_specific.c
++	usr/lib/ica_s390_stdll/ica_specific.c usr/lib/common/dlist.c
+ 
+ if ENABLE_LOCKS
+ opencryptoki_stdll_libpkcs11_ica_la_SOURCES +=				\
+diff --git a/usr/lib/icsf_stdll/icsf_stdll.mk b/usr/lib/icsf_stdll/icsf_stdll.mk
+index 788478c2..21c64f9a 100644
+--- a/usr/lib/icsf_stdll/icsf_stdll.mk
++++ b/usr/lib/icsf_stdll/icsf_stdll.mk
+@@ -43,7 +43,7 @@ opencryptoki_stdll_libpkcs11_icsf_la_SOURCES =				\
+ 	usr/lib/common/mech_ssl3.c usr/lib/common/verify_mgr.c		\
+ 	usr/lib/common/mech_list.c usr/lib/common/shared_memory.c	\
+ 	usr/lib/common/attributes.c usr/lib/icsf_stdll/new_host.c	\
+-	usr/lib/common/profile_obj.c					\
++	usr/lib/common/profile_obj.c usr/lib/common/dlist.c		\
+ 	usr/lib/icsf_stdll/pbkdf.c usr/lib/icsf_stdll/icsf_specific.c	\
+ 	usr/lib/icsf_stdll/icsf_config_parse.y				\
+ 	usr/lib/icsf_stdll/icsf_config_lexer.l				\
+diff --git a/usr/lib/soft_stdll/soft_stdll.mk b/usr/lib/soft_stdll/soft_stdll.mk
+index cea802b5..ac401539 100644
+--- a/usr/lib/soft_stdll/soft_stdll.mk
++++ b/usr/lib/soft_stdll/soft_stdll.mk
+@@ -32,7 +32,8 @@ opencryptoki_stdll_libpkcs11_sw_la_SOURCES =				\
+ 	usr/lib/common/utility.c usr/lib/common/verify_mgr.c		\
+ 	usr/lib/common/trace.c usr/lib/common/mech_list.c		\
+ 	usr/lib/common/shared_memory.c usr/lib/common/profile_obj.c	\
+-	usr/lib/soft_stdll/soft_specific.c usr/lib/common/attributes.c
++	usr/lib/soft_stdll/soft_specific.c usr/lib/common/attributes.c	\
++	usr/lib/common/dlist.c
+ 
+ if ENABLE_LOCKS
+ opencryptoki_stdll_libpkcs11_sw_la_SOURCES +=				\
+diff --git a/usr/lib/tpm_stdll/tpm_stdll.mk b/usr/lib/tpm_stdll/tpm_stdll.mk
+index f199a103..0e0eb024 100644
+--- a/usr/lib/tpm_stdll/tpm_stdll.mk
++++ b/usr/lib/tpm_stdll/tpm_stdll.mk
+@@ -34,7 +34,8 @@ opencryptoki_stdll_libpkcs11_tpm_la_SOURCES =				\
+ 	usr/lib/common/verify_mgr.c usr/lib/common/mech_list.c		\
+ 	usr/lib/common/shared_memory.c usr/lib/common/profile_obj.c	\
+ 	usr/lib/tpm_stdll/tpm_specific.c usr/lib/common/attributes.c	\
+-	usr/lib/tpm_stdll/tpm_openssl.c usr/lib/tpm_stdll/tpm_util.c
++	usr/lib/tpm_stdll/tpm_openssl.c usr/lib/tpm_stdll/tpm_util.c	\
++	usr/lib/common/dlist.c
+ 
+ if ENABLE_LOCKS
+ opencryptoki_stdll_libpkcs11_tpm_la_SOURCES +=				\
+diff --git a/usr/sbin/pkcscca/pkcscca.mk b/usr/sbin/pkcscca/pkcscca.mk
+index a223265f..cc40f819 100644
+--- a/usr/sbin/pkcscca/pkcscca.mk
++++ b/usr/sbin/pkcscca/pkcscca.mk
+@@ -36,7 +36,7 @@ usr_sbin_pkcscca_pkcscca_SOURCES =					\
+ 	usr/lib/common/p11util.c usr/lib/common/sw_crypt.c		\
+ 	usr/lib/common/shared_memory.c usr/lib/common/profile_obj.c	\
+ 	usr/lib/common/attributes.c usr/lib/common/mech_rng.c		\
+-	usr/lib/common/pkcs_utils.c 					\
++	usr/lib/common/pkcs_utils.c  usr/lib/common/dlist.c		\
+ 	usr/sbin/pkcscca/pkcscca.c
+ 	
+ 	
+diff --git a/usr/sbin/pkcsslotd/pkcsslotd.mk b/usr/sbin/pkcsslotd/pkcsslotd.mk
+index 4f0e3c56..2d36b4a9 100644
+--- a/usr/sbin/pkcsslotd/pkcsslotd.mk
++++ b/usr/sbin/pkcsslotd/pkcsslotd.mk
+@@ -21,5 +21,6 @@ usr_sbin_pkcsslotd_pkcsslotd_SOURCES =							\
+ 	usr/sbin/pkcsslotd/socket_server.c
+ 
+ nodist_usr_sbin_pkcsslotd_pkcsslotd_SOURCES = \
+-	usr/lib/common/parser.h usr/lib/common/parser.c	usr/lib/common/lexer.c
++	usr/lib/common/parser.h usr/lib/common/parser.c	usr/lib/common/lexer.c		\
++	usr/lib/common/dlist.c
+ usr/sbin/pkcsslotd/slotmgr.$(OBJEXT): usr/lib/common/parser.h
diff --git a/SOURCES/opencryptoki-3.16.0-e88a9de3128df1c4b89bd4c7312c15bb3eb34593.patch b/SOURCES/opencryptoki-3.16.0-e88a9de3128df1c4b89bd4c7312c15bb3eb34593.patch
new file mode 100644
index 0000000..a3bf6ea
--- /dev/null
+++ b/SOURCES/opencryptoki-3.16.0-e88a9de3128df1c4b89bd4c7312c15bb3eb34593.patch
@@ -0,0 +1,25 @@
+commit e88a9de3128df1c4b89bd4c7312c15bb3eb34593
+Author: Ingo Franzki <ifranzki@linux.ibm.com>
+Date:   Thu Jul 8 15:18:30 2021 +0200
+
+    pkcstok_migrate: Don't remove 'tokversion = x.y' during migration
+    
+    When migrating a slot the opencryptoki.conf file is modified. If it
+    contains slots that already contain the 'tokversion = x.y' keyword,
+    this is accidentally removed when migrating another slot.
+    
+    Signed-off-by: Ingo Franzki <ifranzki@linux.ibm.com>
+
+diff --git a/usr/sbin/pkcstok_migrate/pkcstok_migrate.c b/usr/sbin/pkcstok_migrate/pkcstok_migrate.c
+index 3df1596e..05081aff 100644
+--- a/usr/sbin/pkcstok_migrate/pkcstok_migrate.c
++++ b/usr/sbin/pkcstok_migrate/pkcstok_migrate.c
+@@ -2119,7 +2119,7 @@ static int parseupdate_key_vers(void *private, int tok, unsigned int vers)
+ {
+ 	struct parseupdate *u = (struct parseupdate *)private;
+ 
+-    if (tok != KW_TOKVERSION)
++    if (tok == KW_TOKVERSION && !u->activeslot)
+         fprintf(u->f, "  %s = %d.%d", keyword_token_to_str(tok),
+                 vers >> 16, vers & 0xffu);
+     return 0;
diff --git a/SOURCES/opencryptoki-3.16.0-e9548127edae313da7840bcb87fd0afd04549c2e.patch b/SOURCES/opencryptoki-3.16.0-e9548127edae313da7840bcb87fd0afd04549c2e.patch
new file mode 100644
index 0000000..8e81324
--- /dev/null
+++ b/SOURCES/opencryptoki-3.16.0-e9548127edae313da7840bcb87fd0afd04549c2e.patch
@@ -0,0 +1,310 @@
+commit e9548127edae313da7840bcb87fd0afd04549c2e
+Author: Ingo Franzki <ifranzki@linux.ibm.com>
+Date:   Mon Feb 8 15:26:23 2021 +0100
+
+    pkcsslotd: Refactoring in preparation for event support
+    
+    No functional change so far, just making things a bit bore clearer.
+    
+    Signed-off-by: Ingo Franzki <ifranzki@linux.ibm.com>
+
+diff --git a/usr/include/slotmgr.h b/usr/include/slotmgr.h
+index 3950a9a3..4d038435 100644
+--- a/usr/include/slotmgr.h
++++ b/usr/include/slotmgr.h
+@@ -30,7 +30,7 @@
+ #define TOK_PATH  SBIN_PATH "/pkcsslotd"
+ #define OCK_API_LOCK_FILE LOCKDIR_PATH "/LCK..APIlock"
+ 
+-#define SOCKET_FILE_PATH "/var/run/pkcsslotd.socket"
++#define PROC_SOCKET_FILE_PATH "/var/run/pkcsslotd.socket"
+ 
+ #define PID_FILE_PATH "/var/run/pkcsslotd.pid"
+ #define OCK_CONFIG OCK_CONFDIR "/opencryptoki.conf"
+diff --git a/usr/lib/api/api_interface.c b/usr/lib/api/api_interface.c
+index b74b763f..2873a20a 100644
+--- a/usr/lib/api/api_interface.c
++++ b/usr/lib/api/api_interface.c
+@@ -2831,7 +2831,7 @@ CK_RV C_Initialize(CK_VOID_PTR pVoid)
+     TRACE_DEBUG("Shared memory %p \n", Anchor->SharedMemP);
+ 
+     /* Connect to slot daemon and retrieve slot infos */
+-    Anchor->socketfd = connect_socket(SOCKET_FILE_PATH);
++    Anchor->socketfd = connect_socket(PROC_SOCKET_FILE_PATH);
+     if (Anchor->socketfd < 0) {
+         OCK_SYSLOG(LOG_ERR, "C_Initialize: Module failed to create a "
+                    "socket. Verify that the slot management daemon is "
+diff --git a/usr/sbin/pkcsslotd/pkcsslotd.h b/usr/sbin/pkcsslotd/pkcsslotd.h
+index 813db9f4..69eb59f3 100644
+--- a/usr/sbin/pkcsslotd/pkcsslotd.h
++++ b/usr/sbin/pkcsslotd/pkcsslotd.h
+@@ -61,7 +61,6 @@ extern key_t tok;
+ extern Slot_Info_t_64 sinfo[NUMBER_SLOTS_MANAGED];
+ extern unsigned int NumberSlotsInDB;
+ 
+-extern int socketfd;
+ extern Slot_Mgr_Socket_t socketData;
+ 
+ 
+@@ -89,9 +88,9 @@ int XProcLock(void);
+ int XProcUnLock(void);
+ int CreateXProcLock(void);
+ 
+-int CreateListenerSocket(void);
+-int InitSocketData(Slot_Mgr_Socket_t *sp);
+-int SocketConnectionHandler(int socketfd, int timeout_secs);
+-void DetachSocketListener(int socketfd);
++int init_socket_server();
++int term_socket_server();
++int init_socket_data(Slot_Mgr_Socket_t *sp);
++int socket_connection_handler(int timeout_secs);
+ 
+ #endif                          /* _SLOTMGR_H */
+diff --git a/usr/sbin/pkcsslotd/signal.c b/usr/sbin/pkcsslotd/signal.c
+index cf7b9087..49482a2f 100644
+--- a/usr/sbin/pkcsslotd/signal.c
++++ b/usr/sbin/pkcsslotd/signal.c
+@@ -101,7 +101,7 @@ void slotdGenericSignalHandler(int Signal)
+ 
+     InfoLog("Exiting on %s (%d; %#x)", SignalConst(Signal), Signal, Signal);
+ 
+-    DetachSocketListener(socketfd);
++    term_socket_server();
+     DestroyMutexes();
+     DetachFromSharedMemory();
+     DestroySharedMemory();
+diff --git a/usr/sbin/pkcsslotd/slotmgr.c b/usr/sbin/pkcsslotd/slotmgr.c
+index ea5c86f5..94288f13 100644
+--- a/usr/sbin/pkcsslotd/slotmgr.c
++++ b/usr/sbin/pkcsslotd/slotmgr.c
+@@ -37,7 +37,6 @@ unsigned int NumberSlotsInDB = 0;
+ 
+ Slot_Info_t_64 *psinfo;
+ 
+-int socketfd;
+ Slot_Mgr_Socket_t socketData;
+ 
+ struct dircheckinfo_s {
+@@ -569,15 +568,15 @@ int main(int argc, char *argv[], char *envp[])
+     if (!XProcUnLock())
+         return 4;
+ 
+-    if ((socketfd = CreateListenerSocket()) < 0) {
++    if (!init_socket_server()) {
+         DestroyMutexes();
+         DetachFromSharedMemory();
+         DestroySharedMemory();
+         return 5;
+     }
+ 
+-    if (!InitSocketData(&socketData)) {
+-        DetachSocketListener(socketfd);
++    if (!init_socket_data(&socketData)) {
++        term_socket_server();
+         DestroyMutexes();
+         DetachFromSharedMemory();
+         DestroySharedMemory();
+@@ -598,7 +597,7 @@ int main(int argc, char *argv[], char *envp[])
+     if (Daemon) {
+         pid_t pid;
+         if ((pid = fork()) < 0) {
+-            DetachSocketListener(socketfd);
++            term_socket_server();
+             DestroyMutexes();
+             DetachFromSharedMemory();
+             DestroySharedMemory();
+@@ -643,7 +642,7 @@ int main(int argc, char *argv[], char *envp[])
+      * the daemonization process redefines our handler for (at least) SIGTERM
+      */
+     if (!SetupSignalHandlers()) {
+-        DetachSocketListener(socketfd);
++        term_socket_server();
+         DestroyMutexes();
+         DetachFromSharedMemory();
+         DestroySharedMemory();
+@@ -664,7 +663,7 @@ int main(int argc, char *argv[], char *envp[])
+     printf("Start garbage \n");
+     /* start garbage collection thread */
+     if (!StartGCThread(shmp)) {
+-        DetachSocketListener(socketfd);
++        term_socket_server();
+         DestroyMutexes();
+         DetachFromSharedMemory();
+         DestroySharedMemory();
+@@ -684,7 +683,7 @@ int main(int argc, char *argv[], char *envp[])
+ #if !(THREADED) && !(NOGARBAGE)
+         CheckForGarbage(shmp);
+ #endif
+-        SocketConnectionHandler(socketfd, 10);
++        socket_connection_handler(10);
+     }
+ 
+     /*************************************************************
+diff --git a/usr/sbin/pkcsslotd/socket_server.c b/usr/sbin/pkcsslotd/socket_server.c
+index ae0eff92..1fae0b95 100644
+--- a/usr/sbin/pkcsslotd/socket_server.c
++++ b/usr/sbin/pkcsslotd/socket_server.c
+@@ -25,10 +25,14 @@
+ #include "pkcsslotd.h"
+ #include "apictl.h"
+ 
++int proc_listener_socket = -1;
++
++static void close_listener_socket(int socketfd, const char *file_path);
++
+ // Creates the daemon's listener socket, to which clients will connect and
+ // retrieve slot information through.  Returns the file descriptor of the
+ // created socket.
+-int CreateListenerSocket(void)
++static int create_listener_socket(const char *file_path)
+ {
+     struct sockaddr_un address;
+     struct group *grp;
+@@ -39,53 +43,60 @@ int CreateListenerSocket(void)
+         ErrLog("Failed to create listener socket, errno 0x%X.", errno);
+         return -1;
+     }
+-    if (unlink(SOCKET_FILE_PATH) && errno != ENOENT) {
++    if (unlink(file_path) && errno != ENOENT) {
+         ErrLog("Failed to unlink socket file, errno 0x%X.", errno);
+-        close(socketfd);
+-        return -1;
++        goto error;
+     }
+ 
+     memset(&address, 0, sizeof(struct sockaddr_un));
+     address.sun_family = AF_UNIX;
+-    strcpy(address.sun_path, SOCKET_FILE_PATH);
++    strcpy(address.sun_path, file_path);
+ 
+     if (bind(socketfd,
+              (struct sockaddr *) &address, sizeof(struct sockaddr_un)) != 0) {
+         ErrLog("Failed to bind to socket, errno 0x%X.", errno);
+-        close(socketfd);
+-        return -1;
++        goto error;
+     }
+     // make socket file part of the pkcs11 group, and write accessable
+     // for that group
+     grp = getgrnam("pkcs11");
+     if (!grp) {
+         ErrLog("Group PKCS#11 does not exist");
+-        DetachSocketListener(socketfd);
+-        return -1;
++        goto error;
+     }
+-    if (chown(SOCKET_FILE_PATH, 0, grp->gr_gid)) {
++    if (chown(file_path, 0, grp->gr_gid)) {
+         ErrLog("Could not change file group on socket, errno 0x%X.", errno);
+-        DetachSocketListener(socketfd);
+-        return -1;
++        goto error;
+     }
+-    if (chmod(SOCKET_FILE_PATH,
++    if (chmod(file_path,
+               S_IRUSR | S_IRGRP | S_IWUSR | S_IWGRP | S_IXUSR | S_IXGRP)) {
+         ErrLog("Could not change file permissions on socket, errno 0x%X.",
+                errno);
+-        DetachSocketListener(socketfd);
+-        return -1;
++        goto error;
+     }
+ 
+     if (listen(socketfd, 20) != 0) {
+         ErrLog("Failed to listen to socket, errno 0x%X.", errno);
+-        DetachSocketListener(socketfd);
+-        return -1;
++        goto error;
+     }
+ 
+     return socketfd;
++
++error:
++    if (socketfd >= 0)
++        close_listener_socket(socketfd, file_path);
++
++    return -1;
++}
++
++
++static void close_listener_socket(int socketfd, const char *file_path)
++{
++    close(socketfd);
++    unlink(file_path);
+ }
+ 
+-int InitSocketData(Slot_Mgr_Socket_t *socketData)
++int init_socket_data(Slot_Mgr_Socket_t *socketData)
+ {
+     unsigned int processed = 0;
+ 
+@@ -102,19 +113,19 @@ int InitSocketData(Slot_Mgr_Socket_t *socketData)
+     return TRUE;
+ }
+ 
+-int SocketConnectionHandler(int socketfd, int timeout_secs)
++int socket_connection_handler(int timeout_secs)
+ {
+     int returnVal;
+     fd_set set;
+     struct timeval timeout;
+ 
+     FD_ZERO(&set);
+-    FD_SET(socketfd, &set);
++    FD_SET(proc_listener_socket, &set);
+ 
+     timeout.tv_sec = timeout_secs;
+     timeout.tv_usec = 0;
+ 
+-    returnVal = select(socketfd + 1, &set, NULL, NULL, &timeout);
++    returnVal = select(proc_listener_socket + 1, &set, NULL, NULL, &timeout);
+     if (returnVal == -1) {
+         ErrLog("select failed on socket connection, errno 0x%X.", errno);
+         return FALSE;
+@@ -125,7 +136,7 @@ int SocketConnectionHandler(int socketfd, int timeout_secs)
+         struct sockaddr_un address;
+         socklen_t address_length = sizeof(address);
+ 
+-        int connectionfd = accept(socketfd,
++        int connectionfd = accept(proc_listener_socket,
+                                   (struct sockaddr *) &address,
+                                   &address_length);
+         if (connectionfd < 0) {
+@@ -138,6 +149,10 @@ int SocketConnectionHandler(int socketfd, int timeout_secs)
+             }
+             return FALSE;
+         }
++
++        DbgLog(DL0, "Accepted connection from process: socket: %d", 
++               connectionfd);
++
+         if (write(connectionfd, &socketData, sizeof(socketData)) !=
+             sizeof(socketData)) {
+             ErrLog("Failed to write socket data, errno 0x%X.", errno);
+@@ -149,8 +164,23 @@ int SocketConnectionHandler(int socketfd, int timeout_secs)
+     }
+ }
+ 
+-void DetachSocketListener(int socketfd)
++int init_socket_server()
+ {
+-    close(socketfd);
+-    unlink(SOCKET_FILE_PATH);
++    proc_listener_socket = create_listener_socket(PROC_SOCKET_FILE_PATH);
++    if (proc_listener_socket < 0)
++        return FALSE;
++
++    DbgLog(DL0, "Socket server started");
++
++    return TRUE;
++}
++
++int term_socket_server()
++{
++    if (proc_listener_socket >= 0)
++        close_listener_socket(proc_listener_socket, PROC_SOCKET_FILE_PATH);
++
++    DbgLog(DL0, "Socket server stopped");
++
++    return TRUE;
+ }
diff --git a/SOURCES/opencryptoki-3.16.0-fa94a16116d8382a987ddf9e8cdd88027dd1f647.patch b/SOURCES/opencryptoki-3.16.0-fa94a16116d8382a987ddf9e8cdd88027dd1f647.patch
new file mode 100644
index 0000000..30a506a
--- /dev/null
+++ b/SOURCES/opencryptoki-3.16.0-fa94a16116d8382a987ddf9e8cdd88027dd1f647.patch
@@ -0,0 +1,287 @@
+commit fa94a16116d8382a987ddf9e8cdd88027dd1f647
+Author: Ingo Franzki <ifranzki@linux.ibm.com>
+Date:   Tue Feb 16 17:13:34 2021 +0100
+
+    Event support: Add event client
+    
+    Signed-off-by: Ingo Franzki <ifranzki@linux.ibm.com>
+
+diff --git a/usr/lib/common/common.mk b/usr/lib/common/common.mk
+index 2178ad45..882c84f4 100644
+--- a/usr/lib/common/common.mk
++++ b/usr/lib/common/common.mk
+@@ -4,7 +4,7 @@ noinst_HEADERS +=							\
+ 	usr/lib/common/shared_memory.h usr/lib/common/tok_spec_struct.h	\
+ 	usr/lib/common/trace.h usr/lib/common/h_extern.h		\
+ 	usr/lib/common/sw_crypt.h usr/lib/common/defs.h			\
+-	usr/lib/common/p11util.h					\
++	usr/lib/common/p11util.h usr/lib/common/event_client.h		\
+ 	usr/lib/common/list.h usr/lib/common/tok_specific.h
+ 
+ usr/lib/common/lexer.c: usr/lib/common/parser.h
+diff --git a/usr/lib/common/event_client.c b/usr/lib/common/event_client.c
+new file mode 100644
+index 00000000..86117b84
+--- /dev/null
++++ b/usr/lib/common/event_client.c
+@@ -0,0 +1,215 @@
++/*
++ * COPYRIGHT (c) International Business Machines Corp. 2021
++ *
++ * This program is provided under the terms of the Common Public License,
++ * version 1.0 (CPL-1.0). Any use, reproduction or distribution for this
++ * software constitutes recipient's acceptance of CPL-1.0 terms which can be
++ * found in the file LICENSE file or at
++ * https://opensource.org/licenses/cpl1.0.php
++ */
++
++#include <stdlib.h>
++#include <string.h>
++#include <errno.h>
++#include <sys/un.h>
++#include <sys/socket.h>
++#include <sys/stat.h>
++#include <stdio.h>
++#include <unistd.h>
++#include <grp.h>
++
++#include "slotmgr.h"
++#include "event_client.h"
++
++static int connect_socket(const char *file_path)
++{
++    int socketfd;
++    struct sockaddr_un daemon_address;
++    struct stat file_info;
++    struct group *grp;
++    int rc;
++
++    if (stat(file_path, &file_info))
++        return -errno;
++
++    grp = getgrnam("pkcs11");
++    if (!grp)
++        return -errno;
++
++    if (file_info.st_uid != 0 || file_info.st_gid != grp->gr_gid)
++        return -EPERM;
++
++    if ((socketfd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0)
++        return -errno;
++
++    memset(&daemon_address, 0, sizeof(struct sockaddr_un));
++    daemon_address.sun_family = AF_UNIX;
++    strcpy(daemon_address.sun_path, file_path);
++
++    if (connect(socketfd, (struct sockaddr *) &daemon_address,
++                sizeof(struct sockaddr_un)) != 0) {
++        rc = -errno;
++        goto error;
++    }
++
++    return socketfd;
++
++error:
++    close(socketfd);
++    return rc;
++}
++
++static ssize_t read_all(int socketfd, char *buffer, size_t size)
++{
++    size_t bytes_received = 0;
++    ssize_t n;
++
++    while (bytes_received < size) {
++        n = read(socketfd, buffer + bytes_received, size - bytes_received);
++        if (n < 0) {
++            // read error
++            if (errno == EINTR)
++                continue;
++            return -errno;
++        }
++        if (n == 0)
++            break;
++
++        bytes_received += n;
++    }
++
++    return bytes_received;
++}
++
++static ssize_t send_all(int socketfd, char *buffer, size_t size)
++{
++    size_t bytes_sent = 0;
++    ssize_t n;
++
++    while (bytes_sent < size) {
++        n = send(socketfd, buffer + bytes_sent, size - bytes_sent, 0);
++        if (n < 0) {
++            // send error
++            if (errno == EINTR)
++                continue;
++            return -errno;
++        }
++        if (n == 0)
++            break;
++
++        bytes_sent += n;
++    }
++
++    return bytes_sent;
++}
++
++/*
++ * Initialize an admin connection to the pkcsslotd.
++ * Returns a file descriptor representing the connection, or a negative errno
++ * in case of an error.
++ */
++int init_event_client()
++{
++    int fd;
++
++    fd = connect_socket(ADMIN_SOCKET_FILE_PATH);
++
++    return fd;
++}
++
++/*
++ * Send an event though the admin connection to the pkcsslotd, and thus to
++ * all active token instances.
++ * If parameter fd is < 0, then a connection to pkcsslotd is established
++ * inside the function and closed before return. This is for a one shot event.
++ * Otherwise, pass a file descriptor received from init_event_client(). This
++ * is to send multiple events.
++ * Event type is mandatory, flags can be zero.
++ * The event payload is optional, if payload_len is non-zero, then payload must
++ * point to a buffer containing the payload to send with the event.
++ * The event destination can be used to selectively send the event to certain
++ * token instances only. If destination is NULL, it is sent to all token
++ * instances.
++ * If flag EVENT_FLAGS_REPLY_REQ is on in the flags parameter, then it is waited
++ * until all active token instances have replied. The combined result of the
++ * replies from the token instances is returned in the reply structure.
++ * Parameter reply must be non-NULL if flag EVENT_FLAGS_REPLY_REQ is set.
++ * Returns zero for success, or a negative errno in case of an error. In most
++ * error cases the connection to the pkcsslotd is out of sequence and can no
++ * longer be used to send further events.
++ */
++int send_event(int fd, unsigned int type, unsigned int flags,
++               unsigned int payload_len, const char *payload,
++               const struct event_destination *destination,
++               struct event_reply *reply)
++{
++    event_msg_t event_msg;
++    event_reply_t event_reply;
++    int rc, term = 0;
++
++    if (payload_len > 0 && payload == NULL)
++        return -EINVAL;
++    if ((flags & EVENT_FLAGS_REPLY_REQ) && reply == NULL)
++        return -EINVAL;
++    if (payload_len > EVENT_MAX_PAYLOAD_LENGTH)
++        return -EMSGSIZE;
++
++    if (fd < 0) {
++        fd = init_event_client();
++        if (fd < 0)
++            return fd;
++        term = 1;
++    }
++
++    memset(&event_msg, 0, sizeof(event_msg));
++    event_msg.version = EVENT_VERSION_1;
++    event_msg.type = type;
++    event_msg.flags = flags;
++    if (destination != NULL) {
++        event_msg.token_type = destination->token_type;
++        memcpy(event_msg.token_label, destination->token_label,
++               sizeof(event_msg.token_label));
++        event_msg.process_id = destination->process_id;
++    } else {
++        memset(event_msg.token_label, ' ', sizeof(event_msg.token_label));
++    }
++    event_msg.payload_len = payload_len;
++
++    rc = send_all(fd, (char *)&event_msg, sizeof(event_msg));
++    if (rc < 0)
++        goto out;
++
++    if (payload_len > 0) {
++        rc = send_all(fd, (char *)payload, payload_len);
++        if (rc < 0)
++            goto out;
++    }
++
++    if (flags & EVENT_FLAGS_REPLY_REQ) {
++        rc = read_all(fd, (char *)&event_reply, sizeof(event_reply));
++        if (rc < 0)
++            goto out;
++
++        reply->positive_replies = event_reply.positive_replies;
++        reply->negative_replies = event_reply.negative_replies;
++        reply->nothandled_replies = event_reply.nothandled_replies;
++    }
++
++    rc = 0;
++
++out:
++    if (term)
++        term_event_client(fd);
++
++    return rc;
++}
++
++/*
++ * Terminate the admin connection to the pkcsslotd.
++ */
++void term_event_client(int fd)
++{
++    if (fd >= 0)
++        close(fd);
++}
++
+diff --git a/usr/lib/common/event_client.h b/usr/lib/common/event_client.h
+new file mode 100644
+index 00000000..2e4917b0
+--- /dev/null
++++ b/usr/lib/common/event_client.h
+@@ -0,0 +1,39 @@
++/*
++ * COPYRIGHT (c) International Business Machines Corp. 2021
++ *
++ * This program is provided under the terms of the Common Public License,
++ * version 1.0 (CPL-1.0). Any use, reproduction or distribution for this
++ * software constitutes recipient's acceptance of CPL-1.0 terms which can be
++ * found in the file LICENSE file or at
++ * https://opensource.org/licenses/cpl1.0.php
++ */
++
++
++#ifndef _EVENT_CLIENT_H_
++#define _EVENT_CLIENT_H_
++
++#include "events.h"
++
++struct event_destination {
++    unsigned int token_type;    /* Destination token type: EVENT_TOK_TYPE_xxx */
++    char token_label[member_size(event_msg_t, token_label)];
++                                /* Label of destination token (or blanks) */
++    pid_t process_id;           /* Process ID of destination process (or 0) */
++};
++
++struct event_reply {
++    unsigned long positive_replies;
++    unsigned long negative_replies;
++    unsigned long nothandled_replies;
++};
++
++int init_event_client();
++
++int send_event(int fd, unsigned int type, unsigned int flags,
++               unsigned int payload_len, const char *payload,
++               const struct event_destination *destination,
++               struct event_reply *reply);
++
++void term_event_client(int fd);
++
++#endif
diff --git a/SOURCES/opencryptoki-3.16.0-pkcstok_migrate-detection_if_pkcsslotd_is_still_running.patch b/SOURCES/opencryptoki-3.16.0-pkcstok_migrate-detection_if_pkcsslotd_is_still_running.patch
new file mode 100644
index 0000000..128ea06
--- /dev/null
+++ b/SOURCES/opencryptoki-3.16.0-pkcstok_migrate-detection_if_pkcsslotd_is_still_running.patch
@@ -0,0 +1,106 @@
+commit 5951869263b556280da53498270cf4826f779c5b
+Author: Ingo Franzki <ifranzki@linux.ibm.com>
+Date:   Tue Jul 13 09:05:22 2021 +0200
+
+    pkcstok_migrate: Fix detection if pkcsslotd is still running
+    
+    Change the code to use the pid file that pkcsslotd creates, and check
+    if the process with the pid contained in the pid file still exists and
+    runs pkcsslotd.
+    
+    Signed-off-by: Ingo Franzki <ifranzki@linux.ibm.com>
+
+diff --git a/usr/sbin/pkcstok_migrate/pkcstok_migrate.c b/usr/sbin/pkcstok_migrate/pkcstok_migrate.c
+index 05081aff..a29dc8f7 100644
+--- a/usr/sbin/pkcstok_migrate/pkcstok_migrate.c
++++ b/usr/sbin/pkcstok_migrate/pkcstok_migrate.c
+@@ -2474,54 +2474,53 @@ static CK_RV backup_repository(const char *data_store)
+  */
+ static CK_BBOOL pkcsslotd_running(void)
+ {
+-    DIR *dir;
+     FILE *fp;
+-    struct dirent* ent;
+     char* endptr;
+-    char buf[PATH_MAX];
++    long lpid;
+     char fname[PATH_MAX];
++    char buf[PATH_MAX];
++    char* first;
+ 
+     TRACE_INFO("Checking if pkcsslotd is running ...\n");
+-    if (!(dir = opendir("/proc"))) {
+-        TRACE_WARN("Cannot open /proc, i.e. cannot check if pkcsslotd is running.\n");
+-        return CK_TRUE;
++
++    fp = fopen(PID_FILE_PATH, "r");
++    if (fp == NULL) {
++        TRACE_INFO("Pid file '%s' not existent, pkcsslotd is not running\n",
++                   PID_FILE_PATH);
++        return CK_FALSE;
+     }
+ 
+-    while ((ent = readdir(dir)) != NULL) {
+-        /* if endptr is not a null character, the directory is not
+-         * entirely numeric, so ignore it */
+-        long lpid = strtol(ent->d_name, &endptr, 10);
+-        if (*endptr != '\0') {
+-            continue;
+-        }
++    if (fgets(buf, sizeof(buf), fp) == NULL) {
++        TRACE_WARN("Cannot read pid file '%s': %s\n", PID_FILE_PATH,
++                   strerror(errno));
++        fclose(fp);
++        return CK_FALSE;
++    }
++    fclose(fp);
+ 
+-        /* try to open the cmdline file */
+-        snprintf(fname, sizeof(fname), "/proc/%ld/cmdline", lpid);
+-        fp = fopen(fname, "r");
+-        if (!fp) {
+-            warnx("fopen(%s) failed, errno=%s", fname, strerror(errno));
+-            return CK_TRUE;
+-        }
++    lpid = strtol(buf, &endptr, 10);
++    if (*endptr != '\0' && *endptr != '\n') {
++        TRACE_WARN("Failed to parse pid file '%s': %s\n", PID_FILE_PATH,
++                           buf);
++        return CK_FALSE;
++    }
+ 
+-        /* check the first token in the file: the program pathname */
+-        if (fgets(buf, sizeof(buf), fp) != NULL) {
+-            char* first = strtok(buf, " ");
+-            if (!first) {
+-                TRACE_WARN("Cannot read program name from %s, i.e. cannot check if pkcsslotd is running.\n",
+-                           fname);
+-                return CK_TRUE;
+-            }
+-            if (strstr(first, "pkcsslotd") != NULL) {
+-                fclose(fp);
+-                closedir(dir);
+-                return CK_TRUE;
+-            }
+-        }
++    snprintf(fname, sizeof(fname), "/proc/%ld/cmdline", lpid);
++    fp = fopen(fname, "r");
++    if (fp == NULL) {
++        TRACE_INFO("Stale pid file, pkcsslotd is not running\n");
++        return CK_FALSE;
++    }
++
++    if (fgets(buf, sizeof(buf), fp) == NULL) {
++        TRACE_INFO("Failed to read '%s'\n", fname);
+         fclose(fp);
++        return CK_FALSE;
+     }
++    fclose(fp);
+ 
+-    closedir(dir);
+-    return CK_FALSE;
++    first = strtok(buf, " ");
++    return (first != NULL && strstr(first, "pkcsslotd") != NULL);
+ }
+ 
+ /**
diff --git a/SOURCES/opencryptoki-openssl3-11196c4d7e221d29f0d385bd48ae4d6023a6e874.patch b/SOURCES/opencryptoki-openssl3-11196c4d7e221d29f0d385bd48ae4d6023a6e874.patch
new file mode 100644
index 0000000..3448bff
--- /dev/null
+++ b/SOURCES/opencryptoki-openssl3-11196c4d7e221d29f0d385bd48ae4d6023a6e874.patch
@@ -0,0 +1,24 @@
+commit 11196c4d7e221d29f0d385bd48ae4d6023a6e874
+Author: Ingo Franzki <ifranzki@linux.ibm.com>
+Date:   Wed Jun 30 10:56:17 2021 +0200
+
+    CONFIGURE: fix configure.ac for --with-openssl
+    
+    The openSSL include files are in <openssl-path>/include while
+    the libraries are in <openssl-path> directly.
+    
+    Signed-off-by: Ingo Franzki <ifranzki@linux.ibm.com>
+
+diff --git a/configure.ac b/configure.ac
+index e2cc537a..d3374476 100644
+--- a/configure.ac
++++ b/configure.ac
+@@ -272,7 +272,7 @@ OPENSSL_CFLAGS=
+ OPENSSL_LIBS=
+ if test "x$with_openssl" != "xno"; then
+ 	if test "x$with_openssl" != "xyes" -a "x$with_openssl" != "xcheck"; then
+-		OPENSSL_CFLAGS="-I$with_openssl"
++		OPENSSL_CFLAGS="-I$with_openssl/include"
+ 		OPENSSL_LIBS="-L$with_openssl"
+ 	fi
+ 	old_cflags="$CFLAGS"
diff --git a/SOURCES/opencryptoki-openssl3-11a53055b22d590bd3c197908b0ff63f6fd3c520.patch b/SOURCES/opencryptoki-openssl3-11a53055b22d590bd3c197908b0ff63f6fd3c520.patch
new file mode 100644
index 0000000..996eb6a
--- /dev/null
+++ b/SOURCES/opencryptoki-openssl3-11a53055b22d590bd3c197908b0ff63f6fd3c520.patch
@@ -0,0 +1,123 @@
+commit 11a53055b22d590bd3c197908b0ff63f6fd3c520
+Author: Ingo Franzki <ifranzki@linux.ibm.com>
+Date:   Tue Jun 29 17:35:18 2021 +0200
+
+    COMMON: mech_ec: Remove deprecated OpenSSL functions
+    
+    All low level EC_KEY functions are deprecated in OpenSSL 3.0.
+    Update the code to not use any of those.
+    
+    Signed-off-by: Ingo Franzki <ifranzki@linux.ibm.com>
+
+diff --git a/usr/lib/common/ec_defs.h b/usr/lib/common/ec_defs.h
+index 1f48794b..897cf891 100644
+--- a/usr/lib/common/ec_defs.h
++++ b/usr/lib/common/ec_defs.h
+@@ -14,13 +14,6 @@
+ #include <openssl/opensslv.h>
+ #include "ec_curves.h"
+ 
+-/* OpenSSL compat */
+-#if OPENSSL_VERSION_NUMBER < 0x10101000L
+-# define EC_POINT_get_affine_coordinates EC_POINT_get_affine_coordinates_GFp
+-# define EC_POINT_set_compressed_coordinates \
+-                                     EC_POINT_set_compressed_coordinates_GFp
+-#endif
+-
+ // Elliptic Curve type
+ //
+ #define PRIME_CURVE         0x00
+diff --git a/usr/lib/common/mech_ec.c b/usr/lib/common/mech_ec.c
+index b54e2db9..a0a06302 100644
+--- a/usr/lib/common/mech_ec.c
++++ b/usr/lib/common/mech_ec.c
+@@ -32,34 +32,6 @@
+ #include "openssl/obj_mac.h"
+ #include <openssl/ec.h>
+ 
+-#if OPENSSL_VERSION_NUMBER < 0x10100000L
+-/*
+- * Older OpenSLL versions do not have BN_bn2binpad, so implement it here
+- */
+-static int BN_bn2binpad(const BIGNUM *a, unsigned char *to, int tolen)
+-{
+-    int len, pad;
+-    unsigned char *buf;
+-
+-    len = BN_num_bytes(a);
+-    buf = (unsigned char *)malloc(len);
+-    if (buf == NULL)
+-        return -1;
+-    BN_bn2bin(a, buf);
+-
+-    if (len >= tolen) {
+-        memcpy(to, buf, tolen);
+-    } else {
+-        pad = tolen - len;
+-        memset(to, 0, pad);
+-        memcpy(to + pad, buf, len);
+-    }
+-
+-    free(buf);
+-    return tolen;
+-}
+-#endif
+-
+ #ifndef NID_brainpoolP160r1
+ /*
+  * Older OpenSLL versions may not have the brainpool NIDs defined, define them
+@@ -1522,9 +1494,8 @@ CK_RV ec_point_from_priv_key(CK_BYTE *parms, CK_ULONG parms_len,
+                              CK_BYTE *d, CK_ULONG d_len,
+                              CK_BYTE **point, CK_ULONG *point_len)
+ {
+-    EC_KEY *eckey = NULL;
+     EC_POINT *pub_key = NULL;
+-    const EC_GROUP *group = NULL;
++    EC_GROUP *group = NULL;
+     int nid, p_len;
+     BIGNUM *bn_d = NULL, *bn_x = NULL, *bn_y = NULL;
+     CK_RV rc = CKR_OK;
+@@ -1541,17 +1512,7 @@ CK_RV ec_point_from_priv_key(CK_BYTE *parms, CK_ULONG parms_len,
+         goto done;
+     }
+ 
+-    eckey = EC_KEY_new_by_curve_name(nid);
+-    if (eckey == NULL) {
+-        rc = CKR_FUNCTION_FAILED;
+-        goto done;
+-    }
+-    if (EC_KEY_set_private_key(eckey, bn_d) != 1) {
+-        rc = CKR_FUNCTION_FAILED;
+-        goto done;
+-    }
+-
+-    group = EC_KEY_get0_group(eckey);
++    group = EC_GROUP_new_by_curve_name(nid);
+     if (group == NULL) {
+         rc = CKR_FUNCTION_FAILED;
+         goto done;
+@@ -1576,7 +1537,7 @@ CK_RV ec_point_from_priv_key(CK_BYTE *parms, CK_ULONG parms_len,
+         rc = CKR_HOST_MEMORY;
+         goto done;
+     }
+-    if (!EC_POINT_get_affine_coordinates_GFp(group, pub_key, bn_x, bn_y, NULL)) {
++    if (!EC_POINT_get_affine_coordinates(group, pub_key, bn_x, bn_y, NULL)) {
+         rc = CKR_FUNCTION_FAILED;
+         goto done;
+     }
+@@ -1599,13 +1560,13 @@ CK_RV ec_point_from_priv_key(CK_BYTE *parms, CK_ULONG parms_len,
+ done:
+     if (pub_key)
+         EC_POINT_free(pub_key);
+-    if (eckey)
+-        EC_KEY_free(eckey);
+     BN_clear_free(bn_x);
+     BN_clear_free(bn_y);
+     BN_clear_free(bn_d);
+     if (ec_point != NULL)
+         free(ec_point);
++    if (group != NULL)
++        EC_GROUP_free(group);
+ 
+     return rc;
+ }
diff --git a/SOURCES/opencryptoki-openssl3-145a696d478a1694ef314659a3d374f03f75c1b1.patch b/SOURCES/opencryptoki-openssl3-145a696d478a1694ef314659a3d374f03f75c1b1.patch
new file mode 100644
index 0000000..9f87102
--- /dev/null
+++ b/SOURCES/opencryptoki-openssl3-145a696d478a1694ef314659a3d374f03f75c1b1.patch
@@ -0,0 +1,30 @@
+commit 145a696d478a1694ef314659a3d374f03f75c1b1
+Author: Ingo Franzki <ifranzki@linux.ibm.com>
+Date:   Mon Jul 5 13:49:09 2021 +0200
+
+    CONFIGURE: Remove AC_FUNC_MALLOC and AC_FUNC_REALLOC
+    
+    The AC_FUNC_MALLOC configure check might add the rpl_malloc() entry if it
+    does not like the default malloc implementation. The user would need to
+    provide the rpl_malloc implementation. This happens depending on compiler and
+    OS/distro being used. Same applies for AC_FUNC_REALLOC and rpl_realloc.
+    It happened for me when I configured it with address sanitizer (libubsan,
+    libasan) activated.
+    
+    Signed-off-by: Ingo Franzki <ifranzki@linux.ibm.com>
+
+diff --git a/configure.ac b/configure.ac
+index d3374476..286b7408 100644
+--- a/configure.ac
++++ b/configure.ac
+@@ -39,10 +39,8 @@ dnl Checks for library functions.
+ AC_FUNC_ALLOCA
+ AC_FUNC_CHOWN
+ AC_FUNC_FORK
+-AC_FUNC_MALLOC
+ AC_FUNC_MKTIME
+ AC_FUNC_MMAP
+-AC_FUNC_REALLOC
+ AC_FUNC_STRERROR_R
+ AC_CHECK_FUNCS([atexit ftruncate gettimeofday localtime_r memchr memmove \
+ 		memset mkdir munmap regcomp select socket strchr strcspn \
diff --git a/SOURCES/opencryptoki-openssl3-2c116d49359a5eb91ad7f1483c64650c7874a513.patch b/SOURCES/opencryptoki-openssl3-2c116d49359a5eb91ad7f1483c64650c7874a513.patch
new file mode 100644
index 0000000..171c080
--- /dev/null
+++ b/SOURCES/opencryptoki-openssl3-2c116d49359a5eb91ad7f1483c64650c7874a513.patch
@@ -0,0 +1,38 @@
+commit 2c116d49359a5eb91ad7f1483c64650c7874a513
+Author: Ingo Franzki <ifranzki@linux.ibm.com>
+Date:   Wed Jun 30 14:08:03 2021 +0200
+
+    TESTCASES: Skip test if operation state is not savable
+    
+    The sess_opstate testcase now handles the return code of CKR_STATE_UNSAVEABLE
+    from C_GetOperationState() and skips the test if that return code is
+    encountered.
+    
+    Signed-off-by: Ingo Franzki <ifranzki@linux.ibm.com>
+
+diff --git a/testcases/pkcs11/sess_opstate.c b/testcases/pkcs11/sess_opstate.c
+index 3235b450..3d1ab9d7 100644
+--- a/testcases/pkcs11/sess_opstate.c
++++ b/testcases/pkcs11/sess_opstate.c
+@@ -123,6 +123,10 @@ int sess_opstate_funcs(int loops)
+         opstatelen = 0;
+         rc = funcs->C_GetOperationState(s2, NULL, &opstatelen);
+         if (rc != CKR_OK) {
++            if (rc == CKR_STATE_UNSAVEABLE) {
++                testcase_skip("Get/SetOperationState digest test: state unsavable");
++                goto out;
++            }
+             testcase_error("C_GetOperationState rc=%s", p11_get_ckr(rc));
+             goto out;
+         }
+@@ -135,6 +139,10 @@ int sess_opstate_funcs(int loops)
+ 
+         rc = funcs->C_GetOperationState(s2, opstate, &opstatelen);
+         if (rc != CKR_OK) {
++            if (rc == CKR_STATE_UNSAVEABLE) {
++                testcase_skip("Get/SetOperationState digest test: state unsavable");
++                goto out;
++            }
+             testcase_error("C_GetOperationState rc=%s", p11_get_ckr(rc));
+             goto out;
+         }
diff --git a/SOURCES/opencryptoki-openssl3-376e664f082b66de970b62a81588b034fd560d27.patch b/SOURCES/opencryptoki-openssl3-376e664f082b66de970b62a81588b034fd560d27.patch
new file mode 100644
index 0000000..8c81fe6
--- /dev/null
+++ b/SOURCES/opencryptoki-openssl3-376e664f082b66de970b62a81588b034fd560d27.patch
@@ -0,0 +1,41 @@
+commit 376e664f082b66de970b62a81588b034fd560d27
+Author: Ingo Franzki <ifranzki@linux.ibm.com>
+Date:   Fri Aug 13 10:54:44 2021 +0200
+
+    TESTCASES: Remove RSA public exponent restriction for Soft token
+    
+    Since commit "Allow small RSA exponents in the default provider"
+    https://github.com/openssl/openssl/commit/254957f768a61c91c14d89566224173d0831c2ce
+    in OpenSSL 3.0, we do no longer need to restrict the tests for the Soft
+    token to RSA public exponents of 3 and 65537 only.
+    
+    Signed-off-by: Ingo Franzki <ifranzki@linux.ibm.com>
+
+diff --git a/testcases/common/common.c b/testcases/common/common.c
+index 0a64ecf2..abbe354f 100644
+--- a/testcases/common/common.c
++++ b/testcases/common/common.c
+@@ -16,6 +16,8 @@
+ #include "pkcs11types.h"
+ #include "regress.h"
+ 
++#define UNUSED(var)            ((void)(var))
++
+ CK_FUNCTION_LIST *funcs;
+ CK_FUNCTION_LIST_3_0 *funcs3;
+ CK_INTERFACE *ifs;
+@@ -879,11 +881,10 @@ int is_valid_cca_pubexp(CK_BYTE pubexp[], CK_ULONG pubexp_len)
+ /** Returns true if pubexp is valid for Soft Tokens **/
+ int is_valid_soft_pubexp(CK_BYTE pubexp[], CK_ULONG pubexp_len)
+ {
+-    CK_BYTE exp3[] = { 0x03 };  // 3
+-    CK_BYTE exp65537[] = { 0x01, 0x00, 0x01 };  // 65537
++    UNUSED(pubexp);
++    UNUSED(pubexp_len);
+ 
+-    return (pubexp_len == 1 && (!memcmp(pubexp, exp3, 1)))
+-        || (pubexp_len == 3 && (!memcmp(pubexp, exp65537, 3)));
++    return TRUE;
+ }
+ 
+ /** Returns true if slot_id is an ICSF token
diff --git a/SOURCES/opencryptoki-openssl3-4dd8a952fc00dd54cce090e4c053de408ba3884b.patch b/SOURCES/opencryptoki-openssl3-4dd8a952fc00dd54cce090e4c053de408ba3884b.patch
new file mode 100644
index 0000000..66e0bba
--- /dev/null
+++ b/SOURCES/opencryptoki-openssl3-4dd8a952fc00dd54cce090e4c053de408ba3884b.patch
@@ -0,0 +1,37 @@
+commit 4dd8a952fc00dd54cce090e4c053de408ba3884b
+Author: Ingo Franzki <ifranzki@linux.ibm.com>
+Date:   Tue Aug 24 10:14:39 2021 +0200
+
+    SOFT: Detect unsupported EC curves with OpenSSL 3.0
+    
+    OpenSSL 3.0 behaves different in reporting an error when an unsupported
+    EC curve is used to generate an EC key. OpenSSL 1.1.1 returns an error
+    at EVP_PKEY_CTX_set_ec_paramgen_curve_nid() already, but OpenSSL 3.0 returns
+    an error only at EVP_PKEY_keygen().
+    
+    Signed-off-by: Ingo Franzki <ifranzki@linux.ibm.com>
+
+diff --git a/usr/lib/soft_stdll/soft_specific.c b/usr/lib/soft_stdll/soft_specific.c
+index 43fd17c3..03767ec8 100644
+--- a/usr/lib/soft_stdll/soft_specific.c
++++ b/usr/lib/soft_stdll/soft_specific.c
+@@ -51,6 +51,7 @@
+ #include <openssl/cmac.h>
+ #include <openssl/ec.h>
+ #include <openssl/bn.h>
++#include <openssl/err.h>
+ #if OPENSSL_VERSION_PREREQ(3, 0)
+ #include <openssl/core_names.h>
+ #include <openssl/param_build.h>
+@@ -4548,7 +4549,10 @@ CK_RV token_specific_ec_generate_keypair(STDLL_TokData_t *tokdata,
+ 
+     if (EVP_PKEY_keygen(ctx, &ec_pkey) <= 0) {
+         TRACE_ERROR("EVP_PKEY_keygen failed\n");
+-        rc = CKR_FUNCTION_FAILED;
++        if (ERR_GET_REASON(ERR_peek_last_error()) == EC_R_INVALID_CURVE)
++            rc = CKR_CURVE_NOT_SUPPORTED;
++        else
++            rc = CKR_FUNCTION_FAILED;
+         goto out;
+     }
+ 
diff --git a/SOURCES/opencryptoki-openssl3-50408fc3ae0f25b256dda2033d538f88c9b4f903.patch b/SOURCES/opencryptoki-openssl3-50408fc3ae0f25b256dda2033d538f88c9b4f903.patch
new file mode 100644
index 0000000..3070552
--- /dev/null
+++ b/SOURCES/opencryptoki-openssl3-50408fc3ae0f25b256dda2033d538f88c9b4f903.patch
@@ -0,0 +1,322 @@
+commit 50408fc3ae0f25b256dda2033d538f88c9b4f903
+Author: Ingo Franzki <ifranzki@linux.ibm.com>
+Date:   Mon Jul 5 16:02:28 2021 +0200
+
+    COMMON: Fix memory leaks
+    
+    Signed-off-by: Ingo Franzki <ifranzki@linux.ibm.com>
+
+diff --git a/usr/lib/common/mech_aes.c b/usr/lib/common/mech_aes.c
+index 59f82482..a1241693 100644
+--- a/usr/lib/common/mech_aes.c
++++ b/usr/lib/common/mech_aes.c
+@@ -2359,6 +2359,8 @@ CK_RV aes_mac_sign(STDLL_TokData_t *tokdata,
+         memcpy(out_data, ((AES_DATA_CONTEXT *) ctx->context)->iv, mac_len);
+         *out_data_len = mac_len;
+ 
++        sign_mgr_cleanup(tokdata, sess, ctx);
++
+         return rc;
+     }
+ }
+@@ -2497,6 +2499,8 @@ CK_RV aes_mac_sign_final(STDLL_TokData_t *tokdata,
+     memcpy(out_data, context->iv, mac_len);
+     *out_data_len = mac_len;
+ 
++    sign_mgr_cleanup(tokdata, sess, ctx);
++
+     return rc;
+ }
+ 
+@@ -2554,8 +2558,12 @@ CK_RV aes_mac_verify(STDLL_TokData_t *tokdata,
+         }
+ 
+         if (CRYPTO_memcmp(out_data, ((AES_DATA_CONTEXT *) ctx->context)->iv,
+-                          out_data_len) == 0)
++                          out_data_len) == 0) {
++            verify_mgr_cleanup(tokdata, sess, ctx);
+             return CKR_OK;
++        }
++
++        verify_mgr_cleanup(tokdata, sess, ctx);
+ 
+         return CKR_SIGNATURE_INVALID;
+     }
+@@ -2685,8 +2693,12 @@ CK_RV aes_mac_verify_final(STDLL_TokData_t *tokdata,
+         }
+     }
+ 
+-    if (CRYPTO_memcmp(signature, context->iv, signature_len) == 0) 
++    if (CRYPTO_memcmp(signature, context->iv, signature_len) == 0) {
++        verify_mgr_cleanup(tokdata, sess, ctx);
+         return CKR_OK;
++    }
++
++    verify_mgr_cleanup(tokdata, sess, ctx);
+ 
+     return CKR_SIGNATURE_INVALID;
+ }
+@@ -2766,6 +2778,8 @@ CK_RV aes_cmac_sign(STDLL_TokData_t *tokdata,
+     memcpy(out_data, ((AES_CMAC_CONTEXT *) ctx->context)->iv, mac_len);
+     *out_data_len = mac_len;
+ 
++    sign_mgr_cleanup(tokdata, sess, ctx);
++
+ done:
+     object_put(tokdata, key_obj, TRUE);
+     key_obj = NULL;
+@@ -2913,6 +2927,8 @@ done:
+     object_put(tokdata, key_obj, TRUE);
+     key_obj = NULL;
+ 
++    sign_mgr_cleanup(tokdata, sess, ctx);
++
+     return rc;
+ }
+ 
+@@ -2969,9 +2985,12 @@ CK_RV aes_cmac_verify(STDLL_TokData_t *tokdata,
+ 
+     if (CRYPTO_memcmp(out_data, ((AES_CMAC_CONTEXT *) ctx->context)->iv,
+                       out_data_len) == 0) {
++        verify_mgr_cleanup(tokdata, sess, ctx);
+         return CKR_OK;
+     }
+ 
++    verify_mgr_cleanup(tokdata, sess, ctx);
++
+     return CKR_SIGNATURE_INVALID;
+ }
+ 
+@@ -3105,8 +3124,12 @@ CK_RV aes_cmac_verify_final(STDLL_TokData_t *tokdata,
+         return rc;
+     }
+ 
+-    if (CRYPTO_memcmp(signature, context->iv, signature_len) == 0)
++    if (CRYPTO_memcmp(signature, context->iv, signature_len) == 0) {
++        verify_mgr_cleanup(tokdata, sess, ctx);
+         return CKR_OK;
++    }
++
++    verify_mgr_cleanup(tokdata, sess, ctx);
+ 
+     return CKR_SIGNATURE_INVALID;
+ }
+diff --git a/usr/lib/common/mech_des3.c b/usr/lib/common/mech_des3.c
+index 591ad3fa..3582102a 100644
+--- a/usr/lib/common/mech_des3.c
++++ b/usr/lib/common/mech_des3.c
+@@ -2006,6 +2006,8 @@ CK_RV des3_mac_sign(STDLL_TokData_t *tokdata,
+ 
+         *out_data_len = mac_len;
+ 
++        sign_mgr_cleanup(tokdata, sess, ctx);
++
+         return rc;
+     }
+ }
+@@ -2144,6 +2146,8 @@ CK_RV des3_mac_sign_final(STDLL_TokData_t *tokdata,
+ 
+     *out_data_len = mac_len;
+ 
++    sign_mgr_cleanup(tokdata, sess, ctx);
++
+     return rc;
+ }
+ 
+@@ -2197,8 +2201,12 @@ CK_RV des3_mac_verify(STDLL_TokData_t *tokdata,
+         key_obj = NULL;
+ 
+         if (CRYPTO_memcmp(out_data, ((DES_DATA_CONTEXT *) ctx->context)->iv,
+-                          out_data_len) == 0)
++                          out_data_len) == 0) {
++            verify_mgr_cleanup(tokdata, sess, ctx);
+             return CKR_OK;
++        }
++
++        verify_mgr_cleanup(tokdata, sess, ctx);
+ 
+         return CKR_SIGNATURE_INVALID;
+     }
+@@ -2328,8 +2336,12 @@ CK_RV des3_mac_verify_final(STDLL_TokData_t *tokdata,
+         }
+     }
+ 
+-    if (CRYPTO_memcmp(signature, context->iv, signature_len) == 0) 
++    if (CRYPTO_memcmp(signature, context->iv, signature_len) == 0) {
++        verify_mgr_cleanup(tokdata, sess, ctx);
+         return CKR_OK;
++    }
++
++    verify_mgr_cleanup(tokdata, sess, ctx);
+ 
+     return CKR_SIGNATURE_INVALID;
+ }
+@@ -2410,6 +2422,8 @@ CK_RV des3_cmac_sign(STDLL_TokData_t *tokdata,
+     object_put(tokdata, key_obj, TRUE);
+     key_obj = NULL;
+ 
++    sign_mgr_cleanup(tokdata, sess, ctx);
++
+     return rc;
+ }
+ 
+@@ -2553,6 +2567,8 @@ done:
+     object_put(tokdata, key_obj, TRUE);
+     key_obj = NULL;
+ 
++   sign_mgr_cleanup(tokdata, sess, ctx);
++
+     return rc;
+ }
+ 
+@@ -2605,8 +2621,12 @@ CK_RV des3_cmac_verify(STDLL_TokData_t *tokdata,
+ 
+     if (CRYPTO_memcmp(out_data, ((DES_CMAC_CONTEXT *) ctx->context)->iv,
+                       out_data_len) == 0) {
++        verify_mgr_cleanup(tokdata, sess, ctx);
+         return CKR_OK;
+     }
++
++    verify_mgr_cleanup(tokdata, sess, ctx);
++
+     return CKR_SIGNATURE_INVALID;
+ }
+ 
+@@ -2739,8 +2759,12 @@ CK_RV des3_cmac_verify_final(STDLL_TokData_t *tokdata,
+ 
+     ctx->context_free_func = des3_cmac_cleanup;
+ 
+-    if (CRYPTO_memcmp(signature, context->iv, signature_len) == 0)
++    if (CRYPTO_memcmp(signature, context->iv, signature_len) == 0) {
++        verify_mgr_cleanup(tokdata, sess, ctx);
+         return CKR_OK;
++    }
++
++    verify_mgr_cleanup(tokdata, sess, ctx);
+ 
+     return CKR_SIGNATURE_INVALID;
+ }
+diff --git a/usr/lib/common/new_host.c b/usr/lib/common/new_host.c
+index d01091f9..8bff6ada 100644
+--- a/usr/lib/common/new_host.c
++++ b/usr/lib/common/new_host.c
+@@ -174,6 +174,7 @@ CK_RV ST_Initialize(API_Slot_t *sltp, CK_SLOT_ID SlotNumber,
+         if (rc != 0) {
+             sltp->FcnList = NULL;
+             detach_shm(sltp->TokData, 0);
++            final_data_store(sltp->TokData);
+             if (sltp->TokData)
+                 free(sltp->TokData);
+             sltp->TokData = NULL;
+@@ -186,6 +187,7 @@ CK_RV ST_Initialize(API_Slot_t *sltp, CK_SLOT_ID SlotNumber,
+     rc = load_token_data(sltp->TokData, SlotNumber);
+     if (rc != CKR_OK) {
+         sltp->FcnList = NULL;
++        final_data_store(sltp->TokData);
+         if (sltp->TokData)
+             free(sltp->TokData);
+         sltp->TokData = NULL;
+@@ -218,6 +220,7 @@ done:
+             SC_Finalize(sltp->TokData, SlotNumber, sinfp, NULL, 0);
+         } else {
+             CloseXProcLock(sltp->TokData);
++            final_data_store(sltp->TokData);
+             free(sltp->TokData);
+             sltp->TokData = NULL;
+         }
+diff --git a/usr/lib/ep11_stdll/new_host.c b/usr/lib/ep11_stdll/new_host.c
+index a0e7517c..45f13551 100644
+--- a/usr/lib/ep11_stdll/new_host.c
++++ b/usr/lib/ep11_stdll/new_host.c
+@@ -164,6 +164,7 @@ CK_RV ST_Initialize(API_Slot_t *sltp, CK_SLOT_ID SlotNumber,
+         if (rc != 0) {
+             sltp->FcnList = NULL;
+             detach_shm(sltp->TokData, 0);
++            final_data_store(sltp->TokData);
+             if (sltp->TokData)
+                 free(sltp->TokData);
+             sltp->TokData = NULL;
+@@ -176,6 +177,7 @@ CK_RV ST_Initialize(API_Slot_t *sltp, CK_SLOT_ID SlotNumber,
+     rc = load_token_data(sltp->TokData, SlotNumber);
+     if (rc != CKR_OK) {
+         sltp->FcnList = NULL;
++        final_data_store(sltp->TokData);
+         if (sltp->TokData)
+             free(sltp->TokData);
+         sltp->TokData = NULL;
+@@ -208,6 +210,7 @@ done:
+             SC_Finalize(sltp->TokData, SlotNumber, sinfp, NULL, 0);
+         } else {
+             CloseXProcLock(sltp->TokData);
++            final_data_store(sltp->TokData);
+             free(sltp->TokData);
+             sltp->TokData = NULL;
+         }
+diff --git a/usr/lib/icsf_stdll/new_host.c b/usr/lib/icsf_stdll/new_host.c
+index 09e9d27a..eed632c3 100644
+--- a/usr/lib/icsf_stdll/new_host.c
++++ b/usr/lib/icsf_stdll/new_host.c
+@@ -162,6 +162,7 @@ CK_RV ST_Initialize(API_Slot_t *sltp, CK_SLOT_ID SlotNumber,
+         if (rc != 0) {
+             sltp->FcnList = NULL;
+             detach_shm(sltp->TokData, 0);
++            final_data_store(sltp->TokData);
+             if (sltp->TokData)
+                 free(sltp->TokData);
+             sltp->TokData = NULL;
+@@ -174,6 +175,7 @@ CK_RV ST_Initialize(API_Slot_t *sltp, CK_SLOT_ID SlotNumber,
+     rc = load_token_data(sltp->TokData, SlotNumber);
+     if (rc != CKR_OK) {
+         sltp->FcnList = NULL;
++        final_data_store(sltp->TokData);
+         if (sltp->TokData)
+             free(sltp->TokData);
+         sltp->TokData = NULL;
+@@ -206,6 +208,7 @@ done:
+             SC_Finalize(sltp->TokData, SlotNumber, sinfp, NULL, 0);
+         } else {
+             CloseXProcLock(sltp->TokData);
++            final_data_store(sltp->TokData);
+             free(sltp->TokData);
+             sltp->TokData = NULL;
+         }
+diff --git a/usr/lib/tpm_stdll/tpm_specific.c b/usr/lib/tpm_stdll/tpm_specific.c
+index 45bc4b78..c7557108 100644
+--- a/usr/lib/tpm_stdll/tpm_specific.c
++++ b/usr/lib/tpm_stdll/tpm_specific.c
+@@ -213,6 +213,10 @@ CK_RV token_specific_init(STDLL_TokData_t * tokdata, CK_SLOT_ID SlotNumber,
+     }
+ 
+     tpm_data = (tpm_private_data_t *)calloc(1, sizeof(tpm_private_data_t));
++    if (tpm_data == NULL) {
++        TRACE_ERROR("calloc failed\n");
++        return CKR_HOST_MEMORY;
++    }
+     tokdata->private_data = tpm_data;
+ 
+     tpm_data->tspContext = NULL_HCONTEXT;
+@@ -221,12 +225,15 @@ CK_RV token_specific_init(STDLL_TokData_t * tokdata, CK_SLOT_ID SlotNumber,
+     result = Tspi_Context_Create(&tpm_data->tspContext);
+     if (result) {
+         TRACE_ERROR("Tspi_Context_Create failed. rc=0x%x\n", result);
++        free(tpm_data);
+         return CKR_FUNCTION_FAILED;
+     }
+ 
+     result = Tspi_Context_Connect(tpm_data->tspContext, NULL);
+     if (result) {
+         TRACE_ERROR("Tspi_Context_Connect failed. rc=0x%x\n", result);
++        Tspi_Context_Close(tpm_data->tspContext);
++        free(tpm_data);
+         return CKR_FUNCTION_FAILED;
+     }
+ 
+@@ -234,6 +241,8 @@ CK_RV token_specific_init(STDLL_TokData_t * tokdata, CK_SLOT_ID SlotNumber,
+                                            &tpm_data->hDefaultPolicy);
+     if (result) {
+         TRACE_ERROR("Tspi_Context_GetDefaultPolicy failed. rc=0x%x\n", result);
++        Tspi_Context_Close(tpm_data->tspContext);
++        free(tpm_data);
+         return CKR_FUNCTION_FAILED;
+     }
+ 
diff --git a/SOURCES/opencryptoki-openssl3-50e3f06823696c74eea90a77e16b28da1f79cd47.patch b/SOURCES/opencryptoki-openssl3-50e3f06823696c74eea90a77e16b28da1f79cd47.patch
new file mode 100644
index 0000000..08998cc
--- /dev/null
+++ b/SOURCES/opencryptoki-openssl3-50e3f06823696c74eea90a77e16b28da1f79cd47.patch
@@ -0,0 +1,3420 @@
+commit 50e3f06823696c74eea90a77e16b28da1f79cd47
+Author: Ingo Franzki <ifranzki@linux.ibm.com>
+Date:   Wed Jun 30 14:33:33 2021 +0200
+
+    SOFT: Remove deprecated OpenSSL functions
+    
+    All low level RSA, EC_KEY, and DH functions are deprecated in OpenSSL 3.0.
+    Update the code to not use any of those.
+    
+    Change the digest operation context to store the OpenSSL digest context,
+    instead of the deprecated way of retrieving and restoring the digest state.
+    This makes the digest operation context 'non savable'.
+    
+    Also remove support for OpenSSL < v1.1.1. This code used even more
+    low level OpenSSL functions.
+    
+    Signed-off-by: Ingo Franzki <ifranzki@linux.ibm.com>
+
+diff --git a/usr/lib/soft_stdll/soft_specific.c b/usr/lib/soft_stdll/soft_specific.c
+index 5ca22693..43fd17c3 100644
+--- a/usr/lib/soft_stdll/soft_specific.c
++++ b/usr/lib/soft_stdll/soft_specific.c
+@@ -26,10 +26,6 @@
+ 
+ #include <openssl/opensslv.h>
+ 
+-#if OPENSSL_VERSION_NUMBER < 0x10101000L
+-#define NO_EC 1
+-#endif
+-
+ #include "pkcs11types.h"
+ #include "defs.h"
+ #include "host_defs.h"
+@@ -54,14 +50,10 @@
+ #include <openssl/crypto.h>
+ #include <openssl/cmac.h>
+ #include <openssl/ec.h>
+-
+-/*
+- * In order to make opencryptoki compatible with
+- * OpenSSL 1.1 API Changes and backward compatible
+- * we need to check for its version
+- */
+-#if OPENSSL_VERSION_NUMBER < 0x10100000L
+-#define OLDER_OPENSSL
++#include <openssl/bn.h>
++#if OPENSSL_VERSION_PREREQ(3, 0)
++#include <openssl/core_names.h>
++#include <openssl/param_build.h>
+ #endif
+ 
+ #define MAX_GENERIC_KEY_SIZE 256
+@@ -76,7 +68,10 @@ static const MECH_LIST_ELEMENT soft_mech_list[] = {
+ #if !(NODSA)
+     {CKM_DSA_KEY_PAIR_GEN, {512, 1024, CKF_GENERATE_KEY_PAIR}},
+ #endif
++#if !OPENSSL_VERSION_PREREQ(3, 0)
++    /* OpenSSL 3.0 supports single-DES only with the legacy provider */
+     {CKM_DES_KEY_GEN, {8, 8, CKF_GENERATE}},
++#endif
+     {CKM_DES3_KEY_GEN, {24, 24, CKF_GENERATE}},
+ #if !(NOCDMF)
+     {CKM_CDMF_KEY_GEN, {0, 0, CKF_GENERATE}},
+@@ -120,10 +115,13 @@ static const MECH_LIST_ELEMENT soft_mech_list[] = {
+     {CKM_DH_PKCS_KEY_PAIR_GEN, {512, 2048, CKF_GENERATE_KEY_PAIR}},
+ #endif
+ /* End code contributed by Corrent corp. */
++#if !OPENSSL_VERSION_PREREQ(3, 0)
++    /* OpenSSL 3.0 supports single-DES only with the legacy provider */
+     {CKM_DES_ECB, {8, 8, CKF_ENCRYPT | CKF_DECRYPT | CKF_WRAP | CKF_UNWRAP}},
+     {CKM_DES_CBC, {8, 8, CKF_ENCRYPT | CKF_DECRYPT | CKF_WRAP | CKF_UNWRAP}},
+     {CKM_DES_CBC_PAD,
+      {8, 8, CKF_ENCRYPT | CKF_DECRYPT | CKF_WRAP | CKF_UNWRAP}},
++#endif
+ #if !(NOCDMF)
+     {CKM_CDMF_ECB, {0, 0, CKF_ENCRYPT | CKF_DECRYPT | CKF_WRAP | CKF_UNWRAP}},
+     {CKM_CDMF_CBC, {0, 0, CKF_ENCRYPT | CKF_DECRYPT | CKF_WRAP | CKF_UNWRAP}},
+@@ -286,58 +284,6 @@ CK_RV token_specific_des_ecb(STDLL_TokData_t *tokdata,
+                              CK_ULONG *out_data_len,
+                              OBJECT *key, CK_BYTE encrypt)
+ {
+-#if OPENSSL_VERSION_NUMBER < 0x10100000L
+-    CK_RV rc;
+-    DES_key_schedule des_key2;
+-    const_DES_cblock key_val_SSL, in_key_data;
+-    DES_cblock out_key_data;
+-    unsigned int i, j;
+-    CK_ATTRIBUTE *attr = NULL;
+-
+-    UNUSED(tokdata);
+-
+-    // get the key value
+-    rc = template_attribute_get_non_empty(key->template, CKA_VALUE, &attr);
+-    if (rc != CKR_OK) {
+-        TRACE_ERROR("Could not find CKA_VALUE for the key\n");
+-        return rc;
+-    }
+-    // Create the key schedule
+-    memcpy(&key_val_SSL, attr->pValue, 8);
+-    DES_set_key_unchecked(&key_val_SSL, &des_key2);
+-
+-    // the des decrypt will only fail if the data length is not evenly divisible
+-    // by 8
+-    if (in_data_len % DES_BLOCK_SIZE) {
+-        TRACE_ERROR("%s\n", ock_err(ERR_DATA_LEN_RANGE));
+-        return CKR_DATA_LEN_RANGE;
+-    }
+-    // Both the encrypt and the decrypt are done 8 bytes at a time
+-    if (encrypt) {
+-        for (i = 0; i < in_data_len; i = i + 8) {
+-            memcpy(in_key_data, in_data + i, 8);
+-            DES_ecb_encrypt(&in_key_data, &out_key_data, &des_key2,
+-                            DES_ENCRYPT);
+-            memcpy(out_data + i, out_key_data, 8);
+-        }
+-
+-        *out_data_len = in_data_len;
+-        rc = CKR_OK;
+-    } else {
+-
+-        for (j = 0; j < in_data_len; j = j + 8) {
+-            memcpy(in_key_data, in_data + j, 8);
+-            DES_ecb_encrypt(&in_key_data, &out_key_data, &des_key2,
+-                            DES_DECRYPT);
+-            memcpy(out_data + j, out_key_data, 8);
+-        }
+-
+-        *out_data_len = in_data_len;
+-        rc = CKR_OK;
+-    }
+-
+-    return rc;
+-#else
+     const EVP_CIPHER *cipher = EVP_des_ecb();
+     EVP_CIPHER_CTX *ctx = NULL;
+     CK_ATTRIBUTE *attr = NULL;
+@@ -384,7 +330,6 @@ done:
+     OPENSSL_cleanse(dkey, sizeof(dkey));
+     EVP_CIPHER_CTX_free(ctx);
+     return rc;
+-#endif
+ }
+ 
+ CK_RV token_specific_des_cbc(STDLL_TokData_t *tokdata,
+@@ -394,47 +339,6 @@ CK_RV token_specific_des_cbc(STDLL_TokData_t *tokdata,
+                              CK_ULONG *out_data_len,
+                              OBJECT *key, CK_BYTE *init_v, CK_BYTE encrypt)
+ {
+-#if OPENSSL_VERSION_NUMBER < 0x10100000L
+-    CK_RV rc;
+-    CK_ATTRIBUTE *attr = NULL;
+-    DES_cblock ivec;
+-    DES_key_schedule des_key2;
+-    const_DES_cblock key_val_SSL;
+-
+-    UNUSED(tokdata);
+-
+-    // get the key value
+-    rc = template_attribute_get_non_empty(key->template, CKA_VALUE, &attr);
+-    if (rc != CKR_OK) {
+-        TRACE_ERROR("Could not find CKA_VALUE for the key\n");
+-        return rc;
+-    }
+-    // Create the key schedule
+-    memcpy(&key_val_SSL, attr->pValue, 8);
+-    DES_set_key_unchecked(&key_val_SSL, &des_key2);
+-
+-    memcpy(&ivec, init_v, 8);
+-    // the des decrypt will only fail if the data length is not evenly divisible
+-    // by 8
+-    if (in_data_len % DES_BLOCK_SIZE) {
+-        TRACE_ERROR("%s\n", ock_err(ERR_DATA_LEN_RANGE));
+-        return CKR_DATA_LEN_RANGE;
+-    }
+-
+-    if (encrypt) {
+-        DES_ncbc_encrypt(in_data, out_data, in_data_len, &des_key2, &ivec,
+-                         DES_ENCRYPT);
+-        *out_data_len = in_data_len;
+-        rc = CKR_OK;
+-    } else {
+-        DES_ncbc_encrypt(in_data, out_data, in_data_len, &des_key2, &ivec,
+-                         DES_DECRYPT);
+-        *out_data_len = in_data_len;
+-        rc = CKR_OK;
+-    }
+-
+-    return rc;
+-#else
+     const EVP_CIPHER *cipher = EVP_des_cbc();
+     EVP_CIPHER_CTX *ctx = NULL;
+     CK_ATTRIBUTE *attr = NULL;
+@@ -481,7 +385,6 @@ done:
+     OPENSSL_cleanse(dkey, sizeof(dkey));
+     EVP_CIPHER_CTX_free(ctx);
+     return rc;
+-#endif
+ }
+ 
+ CK_RV token_specific_tdes_ecb(STDLL_TokData_t *tokdata,
+@@ -491,80 +394,6 @@ CK_RV token_specific_tdes_ecb(STDLL_TokData_t *tokdata,
+                               CK_ULONG *out_data_len,
+                               OBJECT *key, CK_BYTE encrypt)
+ {
+-#if OPENSSL_VERSION_NUMBER < 0x10100000L
+-    CK_RV rc;
+-    CK_ATTRIBUTE *attr = NULL;
+-    CK_BYTE key_value[3 * DES_KEY_SIZE];
+-    CK_KEY_TYPE keytype;
+-    unsigned int k, j;
+-    DES_key_schedule des_key1;
+-    DES_key_schedule des_key2;
+-    DES_key_schedule des_key3;
+-    const_DES_cblock key_SSL1, key_SSL2, key_SSL3, in_key_data;
+-    DES_cblock out_key_data;
+-
+-    UNUSED(tokdata);
+-
+-    // get the key type
+-    rc = template_attribute_get_ulong(key->template, CKA_KEY_TYPE, &keytype);
+-    if (rc != CKR_OK) {
+-        TRACE_ERROR("Could not find CKA_KEY_TYPE for the key\n");
+-        return rc;
+-    }
+-
+-    // get the key value
+-    rc = template_attribute_get_non_empty(key->template, CKA_VALUE, &attr);
+-    if (rc != CKR_OK) {
+-        TRACE_ERROR("Could not find CKA_VALUE for the key\n");
+-        return rc;
+-    }
+-    if (keytype == CKK_DES2) {
+-        memcpy(key_value, attr->pValue, 2 * DES_KEY_SIZE);
+-        memcpy(key_value + (2 * DES_KEY_SIZE), attr->pValue, DES_KEY_SIZE);
+-    } else {
+-        memcpy(key_value, attr->pValue, 3 * DES_KEY_SIZE);
+-    }
+-
+-    // The key as passed is a 24 byte long string containing three des keys
+-    // pick them apart and create the 3 corresponding key schedules
+-    memcpy(&key_SSL1, key_value, 8);
+-    memcpy(&key_SSL2, key_value + 8, 8);
+-    memcpy(&key_SSL3, key_value + 16, 8);
+-    DES_set_key_unchecked(&key_SSL1, &des_key1);
+-    DES_set_key_unchecked(&key_SSL2, &des_key2);
+-    DES_set_key_unchecked(&key_SSL3, &des_key3);
+-
+-    // the des decrypt will only fail if the data length is not evenly divisible
+-    // by 8
+-    if (in_data_len % DES_BLOCK_SIZE) {
+-        TRACE_ERROR("%s\n", ock_err(ERR_DATA_LEN_RANGE));
+-        return CKR_DATA_LEN_RANGE;
+-    }
+-    // the encrypt and decrypt are done 8 bytes at a time
+-    if (encrypt) {
+-        for (k = 0; k < in_data_len; k = k + 8) {
+-            memcpy(in_key_data, in_data + k, 8);
+-            DES_ecb3_encrypt((const_DES_cblock *) & in_key_data,
+-                             (DES_cblock *) & out_key_data,
+-                             &des_key1, &des_key2, &des_key3, DES_ENCRYPT);
+-            memcpy(out_data + k, out_key_data, 8);
+-        }
+-        *out_data_len = in_data_len;
+-        rc = CKR_OK;
+-    } else {
+-        for (j = 0; j < in_data_len; j = j + 8) {
+-            memcpy(in_key_data, in_data + j, 8);
+-            DES_ecb3_encrypt((const_DES_cblock *) & in_key_data,
+-                             (DES_cblock *) & out_key_data,
+-                             &des_key1, &des_key2, &des_key3, DES_DECRYPT);
+-            memcpy(out_data + j, out_key_data, 8);
+-        }
+-        *out_data_len = in_data_len;
+-        rc = CKR_OK;
+-    }
+-
+-    return rc;
+-#else
+     const EVP_CIPHER *cipher = EVP_des_ede3_ecb();
+     EVP_CIPHER_CTX *ctx = NULL;
+     CK_ATTRIBUTE *attr = NULL;
+@@ -624,7 +453,6 @@ done:
+     OPENSSL_cleanse(dkey, sizeof(dkey));
+     EVP_CIPHER_CTX_free(ctx);
+     return rc;
+-#endif
+ }
+ 
+ CK_RV token_specific_tdes_cbc(STDLL_TokData_t *tokdata,
+@@ -634,78 +462,6 @@ CK_RV token_specific_tdes_cbc(STDLL_TokData_t *tokdata,
+                               CK_ULONG *out_data_len,
+                               OBJECT *key, CK_BYTE *init_v, CK_BYTE encrypt)
+ {
+-#if OPENSSL_VERSION_NUMBER < 0x10100000L
+-    CK_ATTRIBUTE *attr = NULL;
+-    CK_RV rc = CKR_OK;
+-    CK_BYTE key_value[3 * DES_KEY_SIZE];
+-    CK_KEY_TYPE keytype;
+-    DES_key_schedule des_key1;
+-    DES_key_schedule des_key2;
+-    DES_key_schedule des_key3;
+-    const_DES_cblock key_SSL1, key_SSL2, key_SSL3;
+-    DES_cblock ivec;
+-
+-    UNUSED(tokdata);
+-
+-    // get the key type
+-    rc = template_attribute_get_ulong(key->template, CKA_KEY_TYPE, &keytype);
+-    if (rc != CKR_OK) {
+-        TRACE_ERROR("Could not find CKA_KEY_TYPE for the key\n");
+-        return rc;
+-    }
+-
+-    // get the key value
+-    rc = template_attribute_get_non_empty(key->template, CKA_VALUE, &attr);
+-    if (rc != CKR_OK) {
+-        TRACE_ERROR("Could not find CKA_VALUE for the key\n");
+-        return rc;
+-    }
+-    if (keytype == CKK_DES2) {
+-        memcpy(key_value, attr->pValue, 2 * DES_KEY_SIZE);
+-        memcpy(key_value + (2 * DES_KEY_SIZE), attr->pValue, DES_KEY_SIZE);
+-    } else {
+-        memcpy(key_value, attr->pValue, 3 * DES_KEY_SIZE);
+-    }
+-
+-    // The key as passed in is a 24 byte string containing 3 keys
+-    // pick it apart and create the key schedules
+-    memcpy(&key_SSL1, key_value, 8);
+-    memcpy(&key_SSL2, key_value + 8, 8);
+-    memcpy(&key_SSL3, key_value + 16, 8);
+-    DES_set_key_unchecked(&key_SSL1, &des_key1);
+-    DES_set_key_unchecked(&key_SSL2, &des_key2);
+-    DES_set_key_unchecked(&key_SSL3, &des_key3);
+-
+-    memcpy(ivec, init_v, sizeof(ivec));
+-
+-    // the des decrypt will only fail if the data length is not evenly divisible
+-    // by 8
+-    if (in_data_len % DES_BLOCK_SIZE) {
+-        TRACE_ERROR("%s\n", ock_err(ERR_DATA_LEN_RANGE));
+-        return CKR_DATA_LEN_RANGE;
+-    }
+-    // Encrypt or decrypt the data
+-    if (encrypt) {
+-        DES_ede3_cbc_encrypt(in_data,
+-                             out_data,
+-                             in_data_len,
+-                             &des_key1,
+-                             &des_key2, &des_key3, &ivec, DES_ENCRYPT);
+-        *out_data_len = in_data_len;
+-        rc = CKR_OK;
+-    } else {
+-        DES_ede3_cbc_encrypt(in_data,
+-                             out_data,
+-                             in_data_len,
+-                             &des_key1,
+-                             &des_key2, &des_key3, &ivec, DES_DECRYPT);
+-
+-        *out_data_len = in_data_len;
+-        rc = CKR_OK;
+-    }
+-
+-    return rc;
+-#else
+     const EVP_CIPHER *cipher = EVP_des_ede3_cbc();
+     EVP_CIPHER_CTX *ctx = NULL;
+     CK_ATTRIBUTE *attr = NULL;
+@@ -765,7 +521,6 @@ done:
+     OPENSSL_cleanse(dkey, sizeof(dkey));
+     EVP_CIPHER_CTX_free(ctx);
+     return rc;
+-#endif
+ }
+ 
+ CK_RV token_specific_tdes_mac(STDLL_TokData_t *tokdata, CK_BYTE *message,
+@@ -795,14 +550,20 @@ CK_RV token_specific_tdes_mac(STDLL_TokData_t *tokdata, CK_BYTE *message,
+ // convert from the local PKCS11 template representation to
+ // the underlying requirement
+ // returns the pointer to the local key representation
+-static void *rsa_convert_public_key(OBJECT *key_obj)
++static EVP_PKEY *rsa_convert_public_key(OBJECT *key_obj)
+ {
+     CK_BBOOL rc;
+     CK_ATTRIBUTE *modulus = NULL;
+     CK_ATTRIBUTE *pub_exp = NULL;
+-
+-    RSA *rsa;
++    EVP_PKEY *pkey = NULL;
+     BIGNUM *bn_mod, *bn_exp;
++#if OPENSSL_VERSION_PREREQ(3, 0)
++    EVP_PKEY_CTX *pctx = NULL;
++    OSSL_PARAM_BLD *tmpl = NULL;
++    OSSL_PARAM *params = NULL;
++#else
++    RSA *rsa;
++#endif
+ 
+     rc = template_attribute_get_non_empty(key_obj->template, CKA_MODULUS,
+                                           &modulus);
+@@ -813,12 +574,7 @@ static void *rsa_convert_public_key(OBJECT *key_obj)
+     if (rc != CKR_OK)
+         return NULL;
+ 
+-    // Create an RSA key struct to return
+-    rsa = RSA_new();
+-    if (rsa == NULL)
+-        return NULL;
+-
+-    // Create and init BIGNUM structs to stick in the RSA struct
++    // Create and init BIGNUM structs
+     bn_mod = BN_new();
+     bn_exp = BN_new();
+ 
+@@ -827,24 +583,74 @@ static void *rsa_convert_public_key(OBJECT *key_obj)
+             free(bn_mod);
+         if (bn_exp)
+             free(bn_exp);
+-        RSA_free(rsa);
+         return NULL;
+     }
+-    // Convert from strings to BIGNUMs and stick them in the RSA struct
++    // Convert from strings to BIGNUMs
+     BN_bin2bn((unsigned char *) modulus->pValue, modulus->ulValueLen, bn_mod);
+     BN_bin2bn((unsigned char *) pub_exp->pValue, pub_exp->ulValueLen, bn_exp);
+ 
+-#ifdef OLDER_OPENSSL
+-    rsa->n = bn_mod;
+-    rsa->e = bn_exp;
+-#else
++#if !OPENSSL_VERSION_PREREQ(3, 0)
++    // Create an RSA key struct to return
++    rsa = RSA_new();
++    if (rsa == NULL) {
++        if (bn_mod)
++             free(bn_mod);
++         if (bn_exp)
++             free(bn_exp);
++        return NULL;
++    }
++
+     RSA_set0_key(rsa, bn_mod, bn_exp, NULL);
++
++    pkey = EVP_PKEY_new();
++    if (pkey == NULL) {
++       RSA_free(rsa);
++       return NULL;
++    }
++
++    if (EVP_PKEY_assign_RSA(pkey, rsa) != 1) {
++        RSA_free(rsa);
++        EVP_PKEY_free(pkey);
++        return NULL;
++    }
++#else
++    tmpl = OSSL_PARAM_BLD_new();
++    if (tmpl == NULL)
++        goto out;
++
++    if (!OSSL_PARAM_BLD_push_BN(tmpl, OSSL_PKEY_PARAM_RSA_N, bn_mod) ||
++        !OSSL_PARAM_BLD_push_BN(tmpl, OSSL_PKEY_PARAM_RSA_E, bn_exp))
++        goto out;
++
++    params = OSSL_PARAM_BLD_to_param(tmpl);
++    if (params == NULL)
++        goto out;
++
++    pctx = EVP_PKEY_CTX_new_id(EVP_PKEY_RSA, NULL);
++    if (pctx == NULL)
++        goto out;
++
++    if (!EVP_PKEY_fromdata_init(pctx) ||
++        !EVP_PKEY_fromdata(pctx, &pkey, EVP_PKEY_PUBLIC_KEY, params))
++        goto out;
++
++out:
++    if (pctx != NULL)
++        EVP_PKEY_CTX_free(pctx);
++    if (tmpl != NULL)
++        OSSL_PARAM_BLD_free(tmpl);
++    if (params != NULL)
++        OSSL_PARAM_free(params);
++    if (bn_mod != NULL)
++        BN_free(bn_mod);
++    if (bn_exp != NULL)
++        BN_free(bn_exp);
+ #endif
+ 
+-    return (void *) rsa;
++    return pkey;
+ }
+ 
+-static void *rsa_convert_private_key(OBJECT *key_obj)
++static EVP_PKEY *rsa_convert_private_key(OBJECT *key_obj)
+ {
+     CK_ATTRIBUTE *modulus = NULL;
+     CK_ATTRIBUTE *pub_exp = NULL;
+@@ -854,9 +660,15 @@ static void *rsa_convert_private_key(OBJECT *key_obj)
+     CK_ATTRIBUTE *exp1 = NULL;
+     CK_ATTRIBUTE *exp2 = NULL;
+     CK_ATTRIBUTE *coeff = NULL;
+-
++    EVP_PKEY *pkey = NULL;
++#if OPENSSL_VERSION_PREREQ(3, 0)
++    EVP_PKEY_CTX *pctx = NULL;
++    OSSL_PARAM_BLD *tmpl = NULL;
++    OSSL_PARAM *params = NULL;
++#else
+     RSA *rsa;
+     RSA_METHOD *meth;
++#endif
+     BIGNUM *bn_mod, *bn_pub_exp, *bn_priv_exp, *bn_p1, *bn_p2, *bn_e1, *bn_e2,
+         *bn_cf;
+ 
+@@ -873,6 +685,8 @@ static void *rsa_convert_private_key(OBJECT *key_obj)
+     if (!prime2 && !modulus) {
+         return NULL;
+     }
++
++#if !OPENSSL_VERSION_PREREQ(3, 0)
+     // Create and init all the RSA and BIGNUM structs we need.
+     rsa = RSA_new();
+     if (rsa == NULL)
+@@ -884,17 +698,6 @@ static void *rsa_convert_private_key(OBJECT *key_obj)
+      * Token doesn't implement RSA and, instead, calls OpenSSL for it.
+      * So to avoid it we set RSA methods to the default rsa methods.
+      */
+-#ifdef OLDER_OPENSSL
+-    if (rsa->engine) {
+-        meth = (RSA_METHOD *) rsa->meth;
+-        const RSA_METHOD *meth2 = RSA_PKCS1_SSLeay();
+-        meth->rsa_pub_enc = meth2->rsa_pub_enc;
+-        meth->rsa_pub_dec = meth2->rsa_pub_dec;
+-        meth->rsa_priv_enc = meth2->rsa_priv_enc;
+-        meth->rsa_priv_dec = meth2->rsa_priv_dec;
+-        meth->rsa_mod_exp = meth2->rsa_mod_exp;
+-        meth->bn_mod_exp = meth2->bn_mod_exp;
+-#else
+ /*
+  * XXX I dont see a better way than to ignore this warning for now.
+  * Note that the GCC pragma also works for clang.
+@@ -912,8 +715,8 @@ static void *rsa_convert_private_key(OBJECT *key_obj)
+         RSA_meth_set_mod_exp(meth, RSA_meth_get_mod_exp(meth2));
+         RSA_meth_set_bn_mod_exp(meth, RSA_meth_get_bn_mod_exp(meth2));
+ # pragma GCC diagnostic pop
+-#endif
+     }
++#endif
+ 
+     bn_mod = BN_new();
+     bn_pub_exp = BN_new();
+@@ -926,33 +729,14 @@ static void *rsa_convert_private_key(OBJECT *key_obj)
+ 
+     if ((bn_cf == NULL) || (bn_e2 == NULL) || (bn_e1 == NULL) ||
+         (bn_p2 == NULL) || (bn_p1 == NULL) || (bn_priv_exp == NULL) ||
+-        (bn_pub_exp == NULL) || (bn_mod == NULL)) {
+-        if (rsa)
+-            RSA_free(rsa);
+-        if (bn_mod)
+-            BN_free(bn_mod);
+-        if (bn_pub_exp)
+-            BN_free(bn_pub_exp);
+-        if (bn_priv_exp)
+-            BN_free(bn_priv_exp);
+-        if (bn_p1)
+-            BN_free(bn_p1);
+-        if (bn_p2)
+-            BN_free(bn_p2);
+-        if (bn_e1)
+-            BN_free(bn_e1);
+-        if (bn_e2)
+-            BN_free(bn_e2);
+-        if (bn_cf)
+-            BN_free(bn_cf);
+-        return NULL;
+-    }
++        (bn_pub_exp == NULL) || (bn_mod == NULL))
++        goto out;
+ 
+     // CRT key?
+     if (prime1) {
+-        if (!prime2 || !exp1 || !exp2 || !coeff) {
+-            return NULL;
+-        }
++        if (!prime2 || !exp1 || !exp2 || !coeff)
++            goto out;
++
+         // Even though this is CRT key, OpenSSL requires the
+         // modulus and exponents filled in or encrypt and decrypt will
+         // not work
+@@ -969,20 +753,44 @@ static void *rsa_convert_private_key(OBJECT *key_obj)
+         BN_bin2bn((unsigned char *) exp1->pValue, exp1->ulValueLen, bn_e1);
+         BN_bin2bn((unsigned char *) exp2->pValue, exp2->ulValueLen, bn_e2);
+         BN_bin2bn((unsigned char *) coeff->pValue, coeff->ulValueLen, bn_cf);
+-#ifdef OLDER_OPENSSL
+-        rsa->n = bn_mod;
+-        rsa->d = bn_priv_exp;
+-        rsa->p = bn_p1;
+-        rsa->q = bn_p2;
+-        rsa->dmp1 = bn_e1;
+-        rsa->dmq1 = bn_e2;
+-        rsa->iqmp = bn_cf;
+-#else
++
++#if !OPENSSL_VERSION_PREREQ(3, 0)
+         RSA_set0_key(rsa, bn_mod, bn_pub_exp, bn_priv_exp);
++        bn_mod = NULL;
++        bn_pub_exp = NULL;
++        bn_priv_exp = NULL;
+         RSA_set0_factors(rsa, bn_p1, bn_p2);
++        bn_p1 = NULL;
++        bn_p2 = NULL;
+         RSA_set0_crt_params(rsa, bn_e1, bn_e2, bn_cf);
++        bn_e1 = NULL;
++        bn_e2 = NULL;
++        bn_cf = NULL;
++
++        pkey = EVP_PKEY_new();
++        if (pkey == NULL)
++            goto out;
++
++        if (EVP_PKEY_assign_RSA(pkey, rsa) != 1)
++            goto out;
++#else
++        tmpl = OSSL_PARAM_BLD_new();
++        if (tmpl == NULL)
++            goto out;
++
++        if (!OSSL_PARAM_BLD_push_BN(tmpl, OSSL_PKEY_PARAM_RSA_N, bn_mod) ||
++            !OSSL_PARAM_BLD_push_BN(tmpl, OSSL_PKEY_PARAM_RSA_E, bn_pub_exp) ||
++            !OSSL_PARAM_BLD_push_BN(tmpl, OSSL_PKEY_PARAM_RSA_D, bn_priv_exp) ||
++            !OSSL_PARAM_BLD_push_BN(tmpl, OSSL_PKEY_PARAM_RSA_FACTOR1, bn_p1) ||
++            !OSSL_PARAM_BLD_push_BN(tmpl, OSSL_PKEY_PARAM_RSA_FACTOR2, bn_p2) ||
++            !OSSL_PARAM_BLD_push_BN(tmpl, OSSL_PKEY_PARAM_RSA_EXPONENT1,
++                                                                       bn_e1) ||
++            !OSSL_PARAM_BLD_push_BN(tmpl, OSSL_PKEY_PARAM_RSA_EXPONENT2,
++                                                                       bn_e2) ||
++            !OSSL_PARAM_BLD_push_BN(tmpl, OSSL_PKEY_PARAM_RSA_COEFFICIENT1,
++                                                                       bn_cf))
++            goto out;
+ #endif
+-        return rsa;
+     } else {                    // must be a non-CRT key
+         if (!priv_exp) {
+             return NULL;
+@@ -993,15 +801,90 @@ static void *rsa_convert_private_key(OBJECT *key_obj)
+                   bn_pub_exp);
+         BN_bin2bn((unsigned char *) priv_exp->pValue, priv_exp->ulValueLen,
+                   bn_priv_exp);
+-#ifdef OLDER_OPENSSL
+-        rsa->n = bn_mod;
+-        rsa->d = bn_priv_exp;
+-#else
++
++#if !OPENSSL_VERSION_PREREQ(3, 0)
+         RSA_set0_key(rsa, bn_mod, bn_pub_exp, bn_priv_exp);
++        bn_mod = NULL;
++        bn_pub_exp = NULL;
++        bn_priv_exp = NULL;
++
++        pkey = EVP_PKEY_new();
++        if (pkey == NULL)
++            goto out;
++
++        if (EVP_PKEY_assign_RSA(pkey, rsa) != 1)
++            goto out;
++#else
++        tmpl = OSSL_PARAM_BLD_new();
++        if (tmpl == NULL)
++            goto out;
++
++        if (!OSSL_PARAM_BLD_push_BN(tmpl, OSSL_PKEY_PARAM_RSA_N, bn_mod) ||
++            !OSSL_PARAM_BLD_push_BN(tmpl, OSSL_PKEY_PARAM_RSA_E, bn_pub_exp) ||
++            !OSSL_PARAM_BLD_push_BN(tmpl, OSSL_PKEY_PARAM_RSA_D, bn_priv_exp))
++            goto out;
+ #endif
+     }
+ 
+-    return (void *) rsa;
++#if OPENSSL_VERSION_PREREQ(3, 0)
++    params = OSSL_PARAM_BLD_to_param(tmpl);
++    if (params == NULL)
++        goto out;
++
++    pctx = EVP_PKEY_CTX_new_id(EVP_PKEY_RSA, NULL);
++    if (pctx == NULL)
++        goto out;
++
++    if (!EVP_PKEY_fromdata_init(pctx) ||
++        !EVP_PKEY_fromdata(pctx, &pkey, EVP_PKEY_PUBLIC_KEY, params))
++        goto out;
++
++    EVP_PKEY_CTX_free(pctx);
++    OSSL_PARAM_BLD_free(tmpl);
++    OSSL_PARAM_free(params);
++    BN_free(bn_mod);
++    BN_free(bn_pub_exp);
++    BN_free(bn_priv_exp);
++    BN_free(bn_p1);
++    BN_free(bn_p2);
++    BN_free(bn_e1);
++    BN_free(bn_e2);
++    BN_free(bn_cf);
++#endif
++
++    return pkey;
++out:
++#if !OPENSSL_VERSION_PREREQ(3, 0)
++    if (rsa)
++        RSA_free(rsa);
++#else
++    if (pctx != NULL)
++        EVP_PKEY_CTX_free(pctx);
++    if (tmpl != NULL)
++        OSSL_PARAM_BLD_free(tmpl);
++    if (params != NULL)
++        OSSL_PARAM_free(params);
++#endif
++    if (pkey)
++        EVP_PKEY_free(pkey);
++    if (bn_mod)
++        BN_free(bn_mod);
++    if (bn_pub_exp)
++        BN_free(bn_pub_exp);
++    if (bn_priv_exp)
++        BN_free(bn_priv_exp);
++    if (bn_p1)
++        BN_free(bn_p1);
++    if (bn_p2)
++        BN_free(bn_p2);
++    if (bn_e1)
++        BN_free(bn_e1);
++    if (bn_e2)
++        BN_free(bn_e2);
++    if (bn_cf)
++        BN_free(bn_cf);
++
++    return NULL;
+ }
+ 
+ static CK_RV os_specific_rsa_keygen(TEMPLATE *publ_tmpl, TEMPLATE *priv_tmpl)
+@@ -1012,14 +895,16 @@ static CK_RV os_specific_rsa_keygen(TEMPLATE *publ_tmpl, TEMPLATE *priv_tmpl)
+     CK_BBOOL flag;
+     CK_RV rc;
+     CK_ULONG BNLength;
+-    RSA *rsa = NULL;
++#if !OPENSSL_VERSION_PREREQ(3, 0)
++    const RSA *rsa = NULL;
+     const BIGNUM *bignum = NULL;
++#else
++    BIGNUM *bignum = NULL;
++#endif
+     CK_BYTE *ssl_ptr = NULL;
+     BIGNUM *e = NULL;
+-#ifndef OLDER_OPENSSL
+     EVP_PKEY *pkey = NULL;
+     EVP_PKEY_CTX *ctx = NULL;
+-#endif
+ 
+     rc = template_attribute_get_ulong(publ_tmpl, CKA_MODULUS_BITS, &mod_bits);
+     if (rc != CKR_OK) {
+@@ -1052,20 +937,6 @@ static CK_RV os_specific_rsa_keygen(TEMPLATE *publ_tmpl, TEMPLATE *priv_tmpl)
+     }
+     BN_bin2bn(publ_exp->pValue, publ_exp->ulValueLen, e);
+ 
+-#ifdef OLDER_OPENSSL
+-    rsa = RSA_new();
+-    if (rsa == NULL) {
+-        TRACE_ERROR("%s\n", ock_err(ERR_HOST_MEMORY));
+-        return CKR_HOST_MEMORY;
+-    }
+-
+-    if (!RSA_generate_key_ex(rsa, mod_bits, e, NULL)) {
+-        TRACE_ERROR("%s\n", ock_err(ERR_FUNCTION_FAILED));
+-        rc = CKR_FUNCTION_FAILED;
+-        goto done;
+-    }
+-    bignum = rsa->n;
+-#else
+     ctx = EVP_PKEY_CTX_new_id(EVP_PKEY_RSA, NULL);
+     if (ctx == NULL) {
+         TRACE_ERROR("%s\n", ock_err(ERR_HOST_MEMORY));
+@@ -1084,22 +955,36 @@ static CK_RV os_specific_rsa_keygen(TEMPLATE *publ_tmpl, TEMPLATE *priv_tmpl)
+         rc = CKR_FUNCTION_FAILED;
+         goto done;
+     }
++#if !OPENSSL_VERSION_PREREQ(3, 0)
+     if (EVP_PKEY_CTX_set_rsa_keygen_pubexp(ctx, e) != 1) {
++#else
++    if (EVP_PKEY_CTX_set1_rsa_keygen_pubexp(ctx, e) != 1) {
++#endif
+         TRACE_ERROR("%s\n", ock_err(ERR_FUNCTION_FAILED));
+         rc = CKR_FUNCTION_FAILED;
+         goto done;
+     }
++#if !OPENSSL_VERSION_PREREQ(3, 0)
++    e = NULL; // will be freed as part of the context
++#endif
+     if (EVP_PKEY_keygen(ctx, &pkey) != 1) {
+         TRACE_ERROR("%s\n", ock_err(ERR_FUNCTION_FAILED));
+         rc = CKR_FUNCTION_FAILED;
+         goto done;
+     }
++#if !OPENSSL_VERSION_PREREQ(3, 0)
+     if ((rsa = EVP_PKEY_get0_RSA(pkey)) == NULL) {
+         TRACE_ERROR("%s\n", ock_err(ERR_FUNCTION_FAILED));
+         rc = CKR_FUNCTION_FAILED;
+         goto done;
+     }
+     RSA_get0_key(rsa, &bignum, NULL, NULL);
++#else
++    if (!EVP_PKEY_get_bn_param(pkey, OSSL_PKEY_PARAM_RSA_N, &bignum)) {
++        TRACE_ERROR("%s\n", ock_err(ERR_FUNCTION_FAILED));
++        rc = CKR_FUNCTION_FAILED;
++        goto done;
++    }
+ #endif
+     BNLength = BN_num_bytes(bignum);
+     ssl_ptr = malloc(BNLength);
+@@ -1122,12 +1007,20 @@ static CK_RV os_specific_rsa_keygen(TEMPLATE *publ_tmpl, TEMPLATE *priv_tmpl)
+     }
+     free(ssl_ptr);
+     ssl_ptr = NULL;
++#if OPENSSL_VERSION_PREREQ(3, 0)
++    BN_free(bignum);
++    bignum = NULL;
++#endif
+ 
+     // Public Exponent
+-#ifdef OLDER_OPENSSL
+-    bignum = rsa->e;
+-#else
++#if !OPENSSL_VERSION_PREREQ(3, 0)
+     RSA_get0_key(rsa, NULL, &bignum, NULL);
++#else
++    if (!EVP_PKEY_get_bn_param(pkey, OSSL_PKEY_PARAM_RSA_E, &bignum)) {
++        TRACE_ERROR("%s\n", ock_err(ERR_FUNCTION_FAILED));
++        rc = CKR_FUNCTION_FAILED;
++        goto done;
++    }
+ #endif
+     BNLength = BN_num_bytes(bignum);
+     ssl_ptr = malloc(BNLength);
+@@ -1166,6 +1059,10 @@ static CK_RV os_specific_rsa_keygen(TEMPLATE *publ_tmpl, TEMPLATE *priv_tmpl)
+     }
+     free(ssl_ptr);
+     ssl_ptr = NULL;
++#if OPENSSL_VERSION_PREREQ(3, 0)
++    BN_free(bignum);
++    bignum = NULL;
++#endif
+ 
+     // local = TRUE
+     //
+@@ -1189,10 +1086,14 @@ static CK_RV os_specific_rsa_keygen(TEMPLATE *publ_tmpl, TEMPLATE *priv_tmpl)
+     // to force the system to not return this for RSA keys..
+ 
+     // Add the modulus to the private key information
+-#ifdef OLDER_OPENSSL
+-    bignum = rsa->n;
+-#else
++#if !OPENSSL_VERSION_PREREQ(3, 0)
+     RSA_get0_key(rsa, &bignum, NULL, NULL);
++#else
++    if (!EVP_PKEY_get_bn_param(pkey, OSSL_PKEY_PARAM_RSA_N, &bignum)) {
++        TRACE_ERROR("%s\n", ock_err(ERR_FUNCTION_FAILED));
++        rc = CKR_FUNCTION_FAILED;
++        goto done;
++    }
+ #endif
+     BNLength = BN_num_bytes(bignum);
+     ssl_ptr = malloc(BNLength);
+@@ -1215,12 +1116,20 @@ static CK_RV os_specific_rsa_keygen(TEMPLATE *publ_tmpl, TEMPLATE *priv_tmpl)
+     }
+     free(ssl_ptr);
+     ssl_ptr = NULL;
++#if OPENSSL_VERSION_PREREQ(3, 0)
++    BN_free(bignum);
++    bignum = NULL;
++#endif
+ 
+     // Private Exponent
+-#ifdef OLDER_OPENSSL
+-    bignum = rsa->d;
+-#else
++#if !OPENSSL_VERSION_PREREQ(3, 0)
+     RSA_get0_key(rsa, NULL, NULL, &bignum);
++#else
++    if (!EVP_PKEY_get_bn_param(pkey, OSSL_PKEY_PARAM_RSA_D, &bignum)) {
++        TRACE_ERROR("%s\n", ock_err(ERR_FUNCTION_FAILED));
++        rc = CKR_FUNCTION_FAILED;
++        goto done;
++    }
+ #endif
+     BNLength = BN_num_bytes(bignum);
+     ssl_ptr = malloc(BNLength);
+@@ -1245,13 +1154,20 @@ static CK_RV os_specific_rsa_keygen(TEMPLATE *publ_tmpl, TEMPLATE *priv_tmpl)
+     OPENSSL_cleanse(ssl_ptr, BNLength);
+     free(ssl_ptr);
+     ssl_ptr = NULL;
++#if OPENSSL_VERSION_PREREQ(3, 0)
++    BN_free(bignum);
++    bignum = NULL;
++#endif
+ 
+     // prime #1: p
+-    //
+-#ifdef OLDER_OPENSSL
+-    bignum = rsa->p;
+-#else
++#if !OPENSSL_VERSION_PREREQ(3, 0)
+     RSA_get0_factors(rsa, &bignum, NULL);
++#else
++    if (!EVP_PKEY_get_bn_param(pkey, OSSL_PKEY_PARAM_RSA_FACTOR1, &bignum)) {
++        TRACE_ERROR("%s\n", ock_err(ERR_FUNCTION_FAILED));
++        rc = CKR_FUNCTION_FAILED;
++        goto done;
++    }
+ #endif
+     BNLength = BN_num_bytes(bignum);
+     ssl_ptr = malloc(BNLength);
+@@ -1276,13 +1192,20 @@ static CK_RV os_specific_rsa_keygen(TEMPLATE *publ_tmpl, TEMPLATE *priv_tmpl)
+     OPENSSL_cleanse(ssl_ptr, BNLength);
+     free(ssl_ptr);
+     ssl_ptr = NULL;
++#if OPENSSL_VERSION_PREREQ(3, 0)
++    BN_free(bignum);
++    bignum = NULL;
++#endif
+ 
+     // prime #2: q
+-    //
+-#ifdef OLDER_OPENSSL
+-    bignum = rsa->q;
+-#else
++#if !OPENSSL_VERSION_PREREQ(3, 0)
+     RSA_get0_factors(rsa, NULL, &bignum);
++#else
++    if (!EVP_PKEY_get_bn_param(pkey, OSSL_PKEY_PARAM_RSA_FACTOR2, &bignum)) {
++        TRACE_ERROR("%s\n", ock_err(ERR_FUNCTION_FAILED));
++        rc = CKR_FUNCTION_FAILED;
++        goto done;
++    }
+ #endif
+     BNLength = BN_num_bytes(bignum);
+     ssl_ptr = malloc(BNLength);
+@@ -1307,13 +1230,20 @@ static CK_RV os_specific_rsa_keygen(TEMPLATE *publ_tmpl, TEMPLATE *priv_tmpl)
+     OPENSSL_cleanse(ssl_ptr, BNLength);
+     free(ssl_ptr);
+     ssl_ptr = NULL;
++#if OPENSSL_VERSION_PREREQ(3, 0)
++    BN_free(bignum);
++    bignum = NULL;
++#endif
+ 
+     // exponent 1: d mod(p-1)
+-    //
+-#ifdef OLDER_OPENSSL
+-    bignum = rsa->dmp1;
+-#else
++#if !OPENSSL_VERSION_PREREQ(3, 0)
+     RSA_get0_crt_params(rsa, &bignum, NULL, NULL);
++#else
++    if (!EVP_PKEY_get_bn_param(pkey, OSSL_PKEY_PARAM_RSA_EXPONENT1, &bignum)) {
++        TRACE_ERROR("%s\n", ock_err(ERR_FUNCTION_FAILED));
++        rc = CKR_FUNCTION_FAILED;
++        goto done;
++    }
+ #endif
+     BNLength = BN_num_bytes(bignum);
+     ssl_ptr = malloc(BNLength);
+@@ -1338,13 +1268,20 @@ static CK_RV os_specific_rsa_keygen(TEMPLATE *publ_tmpl, TEMPLATE *priv_tmpl)
+     OPENSSL_cleanse(ssl_ptr, BNLength);
+     free(ssl_ptr);
+     ssl_ptr = NULL;
++#if OPENSSL_VERSION_PREREQ(3, 0)
++    BN_free(bignum);
++    bignum = NULL;
++#endif
+ 
+     // exponent 2: d mod(q-1)
+-    //
+-#ifdef OLDER_OPENSSL
+-    bignum = rsa->dmq1;
+-#else
++#if !OPENSSL_VERSION_PREREQ(3, 0)
+     RSA_get0_crt_params(rsa, NULL, &bignum, NULL);
++#else
++    if (!EVP_PKEY_get_bn_param(pkey, OSSL_PKEY_PARAM_RSA_EXPONENT2, &bignum)) {
++        TRACE_ERROR("%s\n", ock_err(ERR_FUNCTION_FAILED));
++        rc = CKR_FUNCTION_FAILED;
++        goto done;
++    }
+ #endif
+     BNLength = BN_num_bytes(bignum);
+     ssl_ptr = malloc(BNLength);
+@@ -1369,13 +1306,21 @@ static CK_RV os_specific_rsa_keygen(TEMPLATE *publ_tmpl, TEMPLATE *priv_tmpl)
+     OPENSSL_cleanse(ssl_ptr, BNLength);
+     free(ssl_ptr);
+     ssl_ptr = NULL;
++#if OPENSSL_VERSION_PREREQ(3, 0)
++    BN_free(bignum);
++    bignum = NULL;
++#endif
+ 
+     // CRT coefficient:  q_inverse mod(p)
+-    //
+-#ifdef OLDER_OPENSSL
+-    bignum = rsa->iqmp;
+-#else
++#if !OPENSSL_VERSION_PREREQ(3, 0)
+     RSA_get0_crt_params(rsa, NULL, NULL, &bignum);
++#else
++    if (!EVP_PKEY_get_bn_param(pkey, OSSL_PKEY_PARAM_RSA_COEFFICIENT1,
++                               &bignum)) {
++        TRACE_ERROR("%s\n", ock_err(ERR_FUNCTION_FAILED));
++        rc = CKR_FUNCTION_FAILED;
++        goto done;
++    }
+ #endif
+     BNLength = BN_num_bytes(bignum);
+     ssl_ptr = malloc(BNLength);
+@@ -1400,6 +1345,10 @@ static CK_RV os_specific_rsa_keygen(TEMPLATE *publ_tmpl, TEMPLATE *priv_tmpl)
+     OPENSSL_cleanse(ssl_ptr, BNLength);
+     free(ssl_ptr);
+     ssl_ptr = NULL;
++#if OPENSSL_VERSION_PREREQ(3, 0)
++    BN_free(bignum);
++    bignum = NULL;
++#endif
+ 
+     flag = TRUE;
+     rc = build_attribute(CKA_LOCAL, &flag, sizeof(CK_BBOOL), &attr);
+@@ -1415,16 +1364,6 @@ static CK_RV os_specific_rsa_keygen(TEMPLATE *publ_tmpl, TEMPLATE *priv_tmpl)
+     }
+ 
+ done:
+-#ifdef OLDER_OPENSSL
+-    if (e != NULL)
+-        BN_free(e);
+-    if (rsa != NULL)
+-        RSA_free(rsa);
+-    if (ssl_ptr != NULL) {
+-        OPENSSL_cleanse(ssl_ptr, BNLength);
+-        free(ssl_ptr);
+-    }
+-#else
+     if (ssl_ptr != NULL) {
+         OPENSSL_cleanse(ssl_ptr, BNLength);
+         free(ssl_ptr);
+@@ -1433,6 +1372,11 @@ done:
+         EVP_PKEY_free(pkey);
+     if (ctx != NULL)
+         EVP_PKEY_CTX_free(ctx);
++    if (e != NULL)
++        BN_free(e);
++#if OPENSSL_VERSION_PREREQ(3, 0)
++    if (bignum != NULL)
++        BN_free(bignum);
+ #endif
+     return rc;
+ }
+@@ -1457,60 +1401,17 @@ static CK_RV os_specific_rsa_encrypt(CK_BYTE *in_data,
+                                      CK_ULONG in_data_len,
+                                      CK_BYTE *out_data, OBJECT *key_obj)
+ {
+-#ifdef OLDER_OPENSSL
+-    CK_RV rc;
+-    RSA *rsa;
+-    int size;
+-
+-    // Convert the local representation to an RSA representation
+-    rsa = (RSA *) rsa_convert_public_key(key_obj);
+-    if (rsa == NULL) {
+-        TRACE_ERROR("%s\n", ock_err(ERR_FUNCTION_FAILED));
+-        rc = CKR_FUNCTION_FAILED;
+-        return rc;
+-    }
+-    // Do an RSA public encryption
+-    size =
+-        RSA_public_encrypt(in_data_len, in_data, out_data, rsa, RSA_NO_PADDING);
+-    if (size == -1) {
+-        TRACE_ERROR("%s\n", ock_err(ERR_FUNCTION_FAILED));
+-        rc = CKR_ARGUMENTS_BAD;
+-        goto done;
+-    }
+-
+-    rc = CKR_OK;
+-
+-done:
+-    RSA_free(rsa);
+-
+-    return rc;
+-#else
+     EVP_PKEY_CTX *ctx = NULL;
+     EVP_PKEY *pkey = NULL;
+-    RSA *rsa = NULL;
+     CK_RV rc;
+     size_t outlen = in_data_len;
+ 
+-    rsa = (RSA *)rsa_convert_public_key(key_obj);
+-    if (rsa == NULL) {
+-        TRACE_ERROR("%s\n", ock_err(ERR_FUNCTION_FAILED));
+-        rc = CKR_FUNCTION_FAILED;
+-        return rc;
+-    }
+-
+-    pkey = EVP_PKEY_new();
++    pkey = rsa_convert_public_key(key_obj);
+     if (pkey == NULL) {
+-        TRACE_ERROR("%s\n", ock_err(ERR_HOST_MEMORY));
+-        rc = CKR_HOST_MEMORY;
+-        goto done;
+-    }
+-
+-    if (EVP_PKEY_assign_RSA(pkey, rsa) != 1) {
+         TRACE_ERROR("%s\n", ock_err(ERR_FUNCTION_FAILED));
+         rc = CKR_FUNCTION_FAILED;
+-        goto done;
++        return rc;
+     }
+-    rsa = NULL; /* freed together with pkey */
+ 
+     ctx = EVP_PKEY_CTX_new(pkey, NULL);
+     if (ctx == NULL) {
+@@ -1538,76 +1439,28 @@ done:
+ 
+     rc = CKR_OK;
+ done:
+-    if (rsa != NULL)
+-        RSA_free(rsa);
+     if (pkey != NULL)
+         EVP_PKEY_free(pkey);
+     if (ctx != NULL)
+         EVP_PKEY_CTX_free(ctx);
+     return rc;
+-#endif
+ }
+ 
+ static CK_RV os_specific_rsa_decrypt(CK_BYTE *in_data,
+                                      CK_ULONG in_data_len,
+                                      CK_BYTE *out_data, OBJECT *key_obj)
+ {
+-#ifdef OLDER_OPENSSL
+-    CK_RV rc;
+-    RSA *rsa;
+-    int size;
+-
+-    // Convert the local key representation to an RSA key representaion
+-    rsa = (RSA *) rsa_convert_private_key(key_obj);
+-    if (rsa == NULL) {
+-        TRACE_ERROR("%s\n", ock_err(ERR_FUNCTION_FAILED));
+-        rc = CKR_FUNCTION_FAILED;
+-        return rc;
+-    }
+-    // Do the private decryption
+-    size =
+-        RSA_private_decrypt(in_data_len, in_data, out_data, rsa,
+-                            RSA_NO_PADDING);
+-
+-    if (size == -1) {
+-        TRACE_ERROR("%s\n", ock_err(ERR_FUNCTION_FAILED));
+-        rc = CKR_FUNCTION_FAILED;
+-        goto done;
+-    }
+-
+-    rc = CKR_OK;
+-
+-done:
+-    RSA_free(rsa);
+-
+-    return rc;
+-#else
+     EVP_PKEY_CTX *ctx = NULL;
+     EVP_PKEY *pkey = NULL;
+-    RSA *rsa = NULL;
+     size_t outlen = in_data_len;
+     CK_RV rc;
+ 
+-    rsa = (RSA *)rsa_convert_private_key(key_obj);
+-    if (rsa == NULL) {
+-        TRACE_ERROR("%s\n", ock_err(ERR_FUNCTION_FAILED));
+-        rc = CKR_FUNCTION_FAILED;
+-        return rc;
+-    }
+-
+-    pkey = EVP_PKEY_new();
++    pkey = rsa_convert_private_key(key_obj);
+     if (pkey == NULL) {
+-        TRACE_ERROR("%s\n", ock_err(ERR_HOST_MEMORY));
+-        rc = CKR_HOST_MEMORY;
+-        goto done;
+-    }
+-
+-    if (EVP_PKEY_assign_RSA(pkey, rsa) != 1) {
+         TRACE_ERROR("%s\n", ock_err(ERR_FUNCTION_FAILED));
+         rc = CKR_FUNCTION_FAILED;
+-        goto done;
++        return rc;
+     }
+-    rsa = NULL; /* freed together with pkey */
+ 
+     ctx = EVP_PKEY_CTX_new(pkey, NULL);
+     if (ctx == NULL) {
+@@ -1635,14 +1488,11 @@ done:
+ 
+     rc = CKR_OK;
+ done:
+-    if (rsa != NULL)
+-        RSA_free(rsa);
+     if (pkey != NULL)
+         EVP_PKEY_free(pkey);
+     if (ctx != NULL)
+         EVP_PKEY_CTX_free(ctx);
+     return rc;
+-#endif
+ }
+ 
+ CK_RV token_specific_rsa_encrypt(STDLL_TokData_t *tokdata, CK_BYTE *in_data,
+@@ -2407,48 +2257,6 @@ CK_RV token_specific_aes_ecb(STDLL_TokData_t *tokdata,
+                              CK_ULONG *out_data_len,
+                              OBJECT *key, CK_BYTE encrypt)
+ {
+-#if OPENSSL_VERSION_NUMBER < 0x10100000L
+-    AES_KEY ssl_aes_key;
+-    unsigned int i;
+-    CK_ATTRIBUTE *attr = NULL;
+-    /* There's a previous check that in_data_len % AES_BLOCK_SIZE == 0,
+-     * so this is fine */
+-    CK_ULONG loops = (CK_ULONG) (in_data_len / AES_BLOCK_SIZE);
+-    CK_RV rc;
+-
+-    UNUSED(tokdata);
+-
+-    memset(&ssl_aes_key, 0, sizeof(AES_KEY));
+-
+-    // get key value
+-    rc = template_attribute_get_non_empty(key->template, CKA_VALUE, &attr);
+-    if (rc != CKR_OK) {
+-        TRACE_ERROR("Could not find CKA_VALUE for the key\n");
+-        return rc;
+-    }
+-    // AES_ecb_encrypt encrypts only a single block, so we have to break up the
+-    // input data here
+-    if (encrypt) {
+-        AES_set_encrypt_key((unsigned char *) attr->pValue,
+-                            (attr->ulValueLen * 8), &ssl_aes_key);
+-        for (i = 0; i < loops; i++) {
+-            AES_ecb_encrypt((unsigned char *) in_data + (i * AES_BLOCK_SIZE),
+-                            (unsigned char *) out_data + (i * AES_BLOCK_SIZE),
+-                            &ssl_aes_key, AES_ENCRYPT);
+-        }
+-    } else {
+-        AES_set_decrypt_key((unsigned char *) attr->pValue,
+-                            (attr->ulValueLen * 8), &ssl_aes_key);
+-        for (i = 0; i < loops; i++) {
+-            AES_ecb_encrypt((unsigned char *) in_data + (i * AES_BLOCK_SIZE),
+-                            (unsigned char *) out_data + (i * AES_BLOCK_SIZE),
+-                            &ssl_aes_key, AES_DECRYPT);
+-        }
+-    }
+-    *out_data_len = in_data_len;
+-
+-    return CKR_OK;
+-#else
+     CK_RV rc;
+     int outlen;
+     unsigned char akey[32];
+@@ -2505,7 +2313,6 @@ done:
+     OPENSSL_cleanse(akey, sizeof(akey));
+     EVP_CIPHER_CTX_free(ctx);
+     return rc;
+-#endif
+ }
+ 
+ CK_RV token_specific_aes_cbc(STDLL_TokData_t *tokdata,
+@@ -2515,38 +2322,6 @@ CK_RV token_specific_aes_cbc(STDLL_TokData_t *tokdata,
+                              CK_ULONG *out_data_len,
+                              OBJECT *key, CK_BYTE *init_v, CK_BYTE encrypt)
+ {
+-#if OPENSSL_VERSION_NUMBER < 0x10100000L
+-    AES_KEY ssl_aes_key;
+-    CK_ATTRIBUTE *attr = NULL;
+-    CK_RV rc;
+-
+-    UNUSED(tokdata);
+-
+-    memset(&ssl_aes_key, 0, sizeof(AES_KEY));
+-
+-    // get key value
+-    rc = template_attribute_get_non_empty(key->template, CKA_VALUE, &attr);
+-    if (rc != CKR_OK) {
+-        TRACE_ERROR("Could not find CKA_VALUE for the key\n");
+-        return rc;
+-    }
+-    // AES_cbc_encrypt chunks the data into AES_BLOCK_SIZE blocks, unlike
+-    // AES_ecb_encrypt, so no looping required.
+-    if (encrypt) {
+-        AES_set_encrypt_key((unsigned char *) attr->pValue,
+-                            (attr->ulValueLen * 8), &ssl_aes_key);
+-        AES_cbc_encrypt((unsigned char *) in_data, (unsigned char *) out_data,
+-                        in_data_len, &ssl_aes_key, init_v, AES_ENCRYPT);
+-    } else {
+-        AES_set_decrypt_key((unsigned char *) attr->pValue,
+-                            (attr->ulValueLen * 8), &ssl_aes_key);
+-        AES_cbc_encrypt((unsigned char *) in_data, (unsigned char *) out_data,
+-                        in_data_len, &ssl_aes_key, init_v, AES_DECRYPT);
+-    }
+-    *out_data_len = in_data_len;
+-
+-    return CKR_OK;
+-#else
+     CK_RV rc;
+     int outlen;
+     unsigned char akey[32];
+@@ -2603,7 +2378,6 @@ done:
+     OPENSSL_cleanse(akey, sizeof(akey));
+     EVP_CIPHER_CTX_free(ctx);
+     return rc;
+-#endif
+ }
+ 
+ CK_RV token_specific_aes_mac(STDLL_TokData_t *tokdata, CK_BYTE *message,
+@@ -2716,275 +2490,145 @@ CK_RV token_specific_dh_pkcs_key_pair_gen(STDLL_TokData_t *tokdata,
+                                           TEMPLATE *publ_tmpl,
+                                           TEMPLATE *priv_tmpl)
+ {
+-#if OPENSSL_VERSION_NUMBER < 0x10100000L
+     CK_RV rv;
+     CK_BBOOL rc;
+     CK_ATTRIBUTE *prime_attr = NULL;
+     CK_ATTRIBUTE *base_attr = NULL;
+     CK_ATTRIBUTE *temp_attr = NULL;
+     CK_ATTRIBUTE *value_bits_attr = NULL;
+-    CK_BYTE *temp_byte;
++    CK_BYTE *temp_byte = NULL, *temp_byte2 = NULL;
+     CK_ULONG temp_bn_len;
+-    DH *dh;
+-    BIGNUM *bn_p;
+-    BIGNUM *bn_g;
+-    const BIGNUM *temp_bn;
++#if !OPENSSL_VERSION_PREREQ(3, 0)
++    DH *dh = NULL;
++#else
++    EVP_PKEY_CTX *pctx = NULL;
++    OSSL_PARAM_BLD *tmpl = NULL;
++    OSSL_PARAM *osparams = NULL;
++#endif
++    BIGNUM *bn_p = NULL;
++    BIGNUM *bn_g = NULL;
++#if !OPENSSL_VERSION_PREREQ(3, 0)
++    const BIGNUM *temp_bn = NULL;
++#else
++    BIGNUM *temp_bn = NULL;
++#endif
++    EVP_PKEY *params = NULL, *pkey = NULL;
++    EVP_PKEY_CTX *ctx = NULL;
+ 
+     UNUSED(tokdata);
+ 
+     rv = template_attribute_get_non_empty(publ_tmpl, CKA_PRIME, &prime_attr);
+     if (rv != CKR_OK) {
+         TRACE_ERROR("Could not find CKA_PRIME for the key.\n");
+-        return rv;
++        goto done;
+     }
+     rv = template_attribute_get_non_empty(publ_tmpl, CKA_BASE, &base_attr);
+     if (rv != CKR_OK) {
+         TRACE_ERROR("Could not find CKA_BASE for the key.\n");
+-        return rv;
++        goto done;
+     }
+ 
+     if ((prime_attr->ulValueLen > 256) || (prime_attr->ulValueLen < 64)) {
+         TRACE_ERROR("CKA_PRIME attribute value is invalid.\n");
+-        return CKR_ATTRIBUTE_VALUE_INVALID;
++        rv = CKR_ATTRIBUTE_VALUE_INVALID;
++        goto done;
+     }
+ 
+-    dh = DH_new();
+-    if (dh == NULL) {
+-        TRACE_ERROR("%s\n", ock_err(ERR_FUNCTION_FAILED));
+-        return CKR_FUNCTION_FAILED;
+-    }
+-    // Create and init BIGNUM structs to stick in the DH struct
++    // Create and init BIGNUM structs
+     bn_p = BN_new();
+     bn_g = BN_new();
+     if (bn_g == NULL || bn_p == NULL) {
+-        if (bn_g)
+-            BN_free(bn_g);
+-        if (bn_p)
+-            BN_free(bn_p);
+         TRACE_ERROR("%s\n", ock_err(ERR_HOST_MEMORY));
+-        return CKR_HOST_MEMORY;
++        rv = CKR_HOST_MEMORY;
++        goto done;
+     }
+-    // Convert from strings to BIGNUMs and stick them in the DH struct
++    // Convert from strings to BIGNUMs
+     BN_bin2bn((unsigned char *) prime_attr->pValue, prime_attr->ulValueLen,
+               bn_p);
+     BN_bin2bn((unsigned char *) base_attr->pValue, base_attr->ulValueLen, bn_g);
+-    dh->p = bn_p;
+-    dh->g = bn_g;
++#if !OPENSSL_VERSION_PREREQ(3, 0)
++    dh = DH_new();
++    if (dh == NULL) {
++        TRACE_ERROR("%s\n", ock_err(ERR_FUNCTION_FAILED));
++        rv = CKR_FUNCTION_FAILED;
++        goto done;
++    }
++
++    DH_set0_pqg(dh, bn_p, NULL, bn_g);
++    /* bn_p and bn_q freed together with dh */
++    bn_p = NULL;
++    bn_g = NULL;
++
++    params = EVP_PKEY_new();
++    if (params == NULL) {
++        TRACE_ERROR("%s\n", ock_err(ERR_HOST_MEMORY));
++        rv = CKR_HOST_MEMORY;
++        goto done;
++    }
+ 
+-    // Generate the DH Key
+-    if (!DH_generate_key(dh)) {
++    if (EVP_PKEY_assign_DH(params, dh) != 1) {
+         TRACE_ERROR("%s\n", ock_err(ERR_FUNCTION_FAILED));
+-        DH_free(dh);
+-        return CKR_FUNCTION_FAILED;
++        rv = CKR_FUNCTION_FAILED;
++        goto done;
++    }
++    dh = NULL; /* freed together with params */
++#else
++    tmpl = OSSL_PARAM_BLD_new();
++    if (tmpl == NULL)
++        goto done;
++
++    if (!OSSL_PARAM_BLD_push_BN(tmpl, OSSL_PKEY_PARAM_FFC_P, bn_p) ||
++        !OSSL_PARAM_BLD_push_BN(tmpl, OSSL_PKEY_PARAM_FFC_G, bn_g))
++        goto done;
++
++    osparams = OSSL_PARAM_BLD_to_param(tmpl);
++    if (osparams == NULL)
++        goto done;
++
++    pctx = EVP_PKEY_CTX_new_id(EVP_PKEY_DH, NULL);
++    if (pctx == NULL)
++        goto done;
++
++    if (!EVP_PKEY_fromdata_init(pctx) ||
++        !EVP_PKEY_fromdata(pctx, &params, EVP_PKEY_PUBLIC_KEY, osparams))
++        goto done;
++#endif
++
++    ctx = EVP_PKEY_CTX_new(params, NULL);
++    if (ctx == NULL) {
++        TRACE_ERROR("%s\n", ock_err(ERR_HOST_MEMORY));
++        rv = CKR_HOST_MEMORY;
++        goto done;
++    }
++
++    if (EVP_PKEY_keygen_init(ctx) != 1
++        || EVP_PKEY_keygen(ctx, &pkey) != 1
++#if !OPENSSL_VERSION_PREREQ(3, 0)
++        /* dh is freed together with pkey */
++        || (dh = (DH *)EVP_PKEY_get0_DH(pkey)) == NULL) {
++#else
++        ) {
++#endif
++        TRACE_ERROR("%s\n", ock_err(ERR_FUNCTION_FAILED));
++        rv = CKR_FUNCTION_FAILED;
++        goto done;
+     }
++
+     // Extract the public and private key components from the DH struct,
+     // and insert them in the publ_tmpl and priv_tmpl
+ 
+     //
+     // pub_key
+     //
+-    //temp_bn = BN_new();
+-    temp_bn = dh->pub_key;
+-    temp_bn_len = BN_num_bytes(temp_bn);
+-    temp_byte = malloc(temp_bn_len);
+-    temp_bn_len = BN_bn2bin(temp_bn, temp_byte);
+-    // in bytes
+-    rc = build_attribute(CKA_VALUE, temp_byte, temp_bn_len, &temp_attr);
+-    if (rc != CKR_OK) {
+-        TRACE_DEVEL("build_attribute failed\n");
+-        DH_free(dh);
+-        free(temp_byte);
+-        return CKR_FUNCTION_FAILED;
+-    }
+-    rc = template_update_attribute(publ_tmpl, temp_attr);
+-    if (rc != CKR_OK) {
+-        TRACE_ERROR("template_update_attribute failed\n");
+-        free(temp_attr);
+-        DH_free(dh);
+-        free(temp_byte);
+-        return rc;
+-    }
+-    free(temp_byte);
+-
+-    //
+-    // priv_key
+-    //
+-    //temp_bn = BN_new();
+-    temp_bn = dh->priv_key;
+-    temp_bn_len = BN_num_bytes(temp_bn);
+-    temp_byte = malloc(temp_bn_len);
+-    temp_bn_len = BN_bn2bin(temp_bn, temp_byte);
+-    // in bytes
+-    rc = build_attribute(CKA_VALUE, temp_byte, temp_bn_len, &temp_attr);
+-    if (rc != CKR_OK) {
+-        TRACE_DEVEL("build_attribute failed\n");
+-        DH_free(dh);
+-        free(temp_byte);
+-        return CKR_FUNCTION_FAILED;
+-    }
+-    rc = template_update_attribute(priv_tmpl, temp_attr);
+-    if (rc != CKR_OK) {
+-        TRACE_ERROR("template_update_attribute failed\n");
+-        free(temp_attr);
+-        DH_free(dh);
+-        free(temp_byte);
+-        return rc;
+-    }
+-    free(temp_byte);
+-
+-    // Update CKA_VALUE_BITS attribute in the private key
+-    value_bits_attr =
+-        (CK_ATTRIBUTE *) malloc(sizeof(CK_ATTRIBUTE) + sizeof(CK_ULONG));
+-    if (value_bits_attr == NULL) {
+-        TRACE_ERROR("malloc failed\n");
+-        DH_free(dh);
+-        return CKR_HOST_MEMORY;
+-    }
+-    value_bits_attr->type = CKA_VALUE_BITS;
+-    value_bits_attr->ulValueLen = sizeof(CK_ULONG);
+-    value_bits_attr->pValue =
+-        (CK_BYTE *) value_bits_attr + sizeof(CK_ATTRIBUTE);
+-    *(CK_ULONG *) value_bits_attr->pValue = 8 * temp_bn_len;
+-    rc = template_update_attribute(priv_tmpl, value_bits_attr);
+-    if (rc != CKR_OK) {
+-        TRACE_ERROR("template_update_attribute failed\n");
+-        free(value_bits_attr);
+-        DH_free(dh);
+-        return rc;
+-    }
+-
+-    // Add prime and base to the private key template
+-    rc = build_attribute(CKA_PRIME,
+-                         (unsigned char *) prime_attr->pValue,
+-                         prime_attr->ulValueLen, &temp_attr);  // in bytes
+-    if (rc != CKR_OK) {
+-        TRACE_DEVEL("build_attribute failed\n");
+-        DH_free(dh);
+-        return CKR_FUNCTION_FAILED;
+-    }
+-    rc = template_update_attribute(priv_tmpl, temp_attr);
+-    if (rc != CKR_OK) {
+-        TRACE_ERROR("template_update_attribute failed\n");
+-        free(temp_attr);
+-        DH_free(dh);
+-        return rc;
+-    }
+-
+-    rc = build_attribute(CKA_BASE,
+-                         (unsigned char *) base_attr->pValue,
+-                         base_attr->ulValueLen, &temp_attr);     // in bytes
+-    if (rc != CKR_OK) {
+-        TRACE_DEVEL("build_attribute failed\n");
+-        DH_free(dh);
+-        return CKR_FUNCTION_FAILED;
+-    }
+-    rc = template_update_attribute(priv_tmpl, temp_attr);
+-    if (rc != CKR_OK) {
+-        TRACE_ERROR("template_update_attribute failed\n");
+-        free(temp_attr);
+-        DH_free(dh);
+-        return rc;
+-    }
+-
+-    // Cleanup DH key
+-    DH_free(dh);
+-
+-    return CKR_OK;
++#if !OPENSSL_VERSION_PREREQ(3, 0)
++    DH_get0_key(dh, &temp_bn, NULL);
+ #else
+-    CK_RV rv;
+-    CK_BBOOL rc;
+-    CK_ATTRIBUTE *prime_attr = NULL;
+-    CK_ATTRIBUTE *base_attr = NULL;
+-    CK_ATTRIBUTE *temp_attr = NULL;
+-    CK_ATTRIBUTE *value_bits_attr = NULL;
+-    CK_BYTE *temp_byte = NULL, *temp_byte2 = NULL;
+-    CK_ULONG temp_bn_len;
+-    DH *dh = NULL;
+-    BIGNUM *bn_p = NULL;
+-    BIGNUM *bn_g = NULL;
+-    const BIGNUM *temp_bn = NULL;
+-    EVP_PKEY *params = NULL, *pkey = NULL;
+-    EVP_PKEY_CTX *ctx = NULL;
+-
+-    UNUSED(tokdata);
+-
+-    rv = template_attribute_get_non_empty(publ_tmpl, CKA_PRIME, &prime_attr);
+-    if (rv != CKR_OK) {
+-        TRACE_ERROR("Could not find CKA_PRIME for the key.\n");
+-        goto done;
+-    }
+-    rv = template_attribute_get_non_empty(publ_tmpl, CKA_BASE, &base_attr);
+-    if (rv != CKR_OK) {
+-        TRACE_ERROR("Could not find CKA_BASE for the key.\n");
+-        goto done;
+-    }
+-
+-    if ((prime_attr->ulValueLen > 256) || (prime_attr->ulValueLen < 64)) {
+-        TRACE_ERROR("CKA_PRIME attribute value is invalid.\n");
+-        rv = CKR_ATTRIBUTE_VALUE_INVALID;
+-        goto done;
+-    }
+-
+-    dh = DH_new();
+-    if (dh == NULL) {
+-        TRACE_ERROR("%s\n", ock_err(ERR_FUNCTION_FAILED));
+-        rv = CKR_FUNCTION_FAILED;
+-        goto done;
+-    }
+-    // Create and init BIGNUM structs to stick in the DH struct
+-    bn_p = BN_new();
+-    bn_g = BN_new();
+-    if (bn_g == NULL || bn_p == NULL) {
+-        TRACE_ERROR("%s\n", ock_err(ERR_HOST_MEMORY));
+-        rv = CKR_HOST_MEMORY;
+-        goto done;
+-    }
+-    // Convert from strings to BIGNUMs and stick them in the DH struct
+-    BN_bin2bn((unsigned char *) prime_attr->pValue, prime_attr->ulValueLen,
+-              bn_p);
+-    BN_bin2bn((unsigned char *) base_attr->pValue, base_attr->ulValueLen, bn_g);
+-    DH_set0_pqg(dh, bn_p, NULL, bn_g);
+-    /* bn_p and bn_q freed together with dh */
+-    bn_p = NULL;
+-    bn_g = NULL;
+-
+-    params = EVP_PKEY_new();
+-    if (params == NULL) {
+-        TRACE_ERROR("%s\n", ock_err(ERR_HOST_MEMORY));
+-        rv = CKR_HOST_MEMORY;
+-        goto done;
+-    }
+-
+-    if (EVP_PKEY_assign_DH(params, dh) != 1) {
+-        TRACE_ERROR("%s\n", ock_err(ERR_FUNCTION_FAILED));
+-        rv = CKR_FUNCTION_FAILED;
+-        goto done;
+-    }
+-    dh = NULL; /* freed together with params */
+-
+-    ctx = EVP_PKEY_CTX_new(params, NULL);
+-    if (ctx == NULL) {
+-        TRACE_ERROR("%s\n", ock_err(ERR_HOST_MEMORY));
+-        rv = CKR_HOST_MEMORY;
+-        goto done;
+-    }
+-
+-    if (EVP_PKEY_keygen_init(ctx) != 1
+-        || EVP_PKEY_keygen(ctx, &pkey) != 1
+-        /* dh is freed together with pkey */
+-        || (dh = EVP_PKEY_get0_DH(pkey)) == NULL) {
++    if (!EVP_PKEY_get_bn_param(pkey, OSSL_PKEY_PARAM_PUB_KEY, &temp_bn)) {
+         TRACE_ERROR("%s\n", ock_err(ERR_FUNCTION_FAILED));
+-        rv = CKR_FUNCTION_FAILED;
++        rc = CKR_FUNCTION_FAILED;
+         goto done;
+     }
+-
+-    // Extract the public and private key components from the DH struct,
+-    // and insert them in the publ_tmpl and priv_tmpl
+-
+-    //
+-    // pub_key
+-    //
+-    DH_get0_key(dh, &temp_bn, NULL);
++#endif
+ 
+     temp_bn_len = BN_num_bytes(temp_bn);
+     temp_byte = malloc(temp_bn_len);
+@@ -3001,11 +2645,23 @@ CK_RV token_specific_dh_pkcs_key_pair_gen(STDLL_TokData_t *tokdata,
+         free(temp_attr);
+         goto done;
+     }
++#if OPENSSL_VERSION_PREREQ(3, 0)
++    BN_free(temp_bn);
++    temp_bn = NULL;
++#endif
+ 
+     //
+     // priv_key
+     //
++#if !OPENSSL_VERSION_PREREQ(3, 0)
+     DH_get0_key(dh, NULL, &temp_bn);
++#else
++    if (!EVP_PKEY_get_bn_param(pkey, OSSL_PKEY_PARAM_PRIV_KEY, &temp_bn)) {
++        TRACE_ERROR("%s\n", ock_err(ERR_FUNCTION_FAILED));
++        rc = CKR_FUNCTION_FAILED;
++        goto done;
++    }
++#endif
+     temp_bn_len = BN_num_bytes(temp_bn);
+     temp_byte2 = malloc(temp_bn_len);
+     temp_bn_len = BN_bn2bin(temp_bn, temp_byte2);
+@@ -3022,6 +2678,10 @@ CK_RV token_specific_dh_pkcs_key_pair_gen(STDLL_TokData_t *tokdata,
+         free(temp_attr);
+         goto done;
+     }
++#if OPENSSL_VERSION_PREREQ(3, 0)
++    BN_free(temp_bn);
++    temp_bn = NULL;
++#endif
+ 
+     // Update CKA_VALUE_BITS attribute in the private key
+     value_bits_attr =
+@@ -3086,8 +2746,17 @@ done:
+         EVP_PKEY_free(params);
+     free(temp_byte);
+     free(temp_byte2);
+-    return rv;
++#if OPENSSL_VERSION_PREREQ(3, 0)
++    if (pctx != NULL)
++        EVP_PKEY_CTX_free(pctx);
++    if (tmpl != NULL)
++        OSSL_PARAM_BLD_free(tmpl);
++    if (osparams != NULL)
++        OSSL_PARAM_free(osparams);
++    if (temp_bn != NULL)
++        BN_free(temp_bn);
+ #endif
++    return rv;
+ }                               /* end token_specific_dh_key_pair_gen() */
+ #endif
+ /* End code contributed by Corrent corp. */
+@@ -3106,11 +2775,6 @@ CK_RV token_specific_get_mechanism_info(STDLL_TokData_t *tokdata,
+     return ock_generic_get_mechanism_info(tokdata, type, pInfo);
+ }
+ 
+-#ifdef OLDER_OPENSSL
+-#define EVP_MD_meth_get_app_datasize(md)        md->ctx_size
+-#define EVP_MD_CTX_md_data(ctx)                 ctx->md_data
+-#endif
+-
+ static const EVP_MD *md_from_mech(CK_MECHANISM *mech)
+ {
+     const EVP_MD *md = NULL;
+@@ -3168,16 +2832,13 @@ static const EVP_MD *md_from_mech(CK_MECHANISM *mech)
+     return md;
+ }
+ 
++#if !OPENSSL_VERSION_PREREQ(3, 0)
+ static EVP_MD_CTX *md_ctx_from_context(DIGEST_CONTEXT *ctx)
+ {
+     const EVP_MD *md;
+     EVP_MD_CTX *md_ctx;
+ 
+-#if OPENSSL_VERSION_NUMBER < 0x10101000L
+-    md_ctx = EVP_MD_CTX_create();
+-#else
+     md_ctx = EVP_MD_CTX_new();
+-#endif
+     if (md_ctx == NULL)
+         return NULL;
+ 
+@@ -3185,11 +2846,7 @@ static EVP_MD_CTX *md_ctx_from_context(DIGEST_CONTEXT *ctx)
+     if (md == NULL ||
+         !EVP_DigestInit_ex(md_ctx, md, NULL)) {
+         TRACE_ERROR("md_from_mech or EVP_DigestInit_ex failed\n");
+-#if OPENSSL_VERSION_NUMBER < 0x10101000L
+-        EVP_MD_CTX_destroy(md_ctx);
+-#else
+         EVP_MD_CTX_free(md_ctx);
+-#endif
+         return NULL;
+     }
+ 
+@@ -3198,11 +2855,7 @@ static EVP_MD_CTX *md_ctx_from_context(DIGEST_CONTEXT *ctx)
+         ctx->context = malloc(ctx->context_len);
+         if (ctx->context == NULL) {
+             TRACE_ERROR("malloc failed\n");
+-    #if OPENSSL_VERSION_NUMBER < 0x10101000L
+-            EVP_MD_CTX_destroy(md_ctx);
+-    #else
+             EVP_MD_CTX_free(md_ctx);
+-    #endif
+             ctx->context_len = 0;
+             return NULL;
+         }
+@@ -3221,27 +2874,60 @@ static EVP_MD_CTX *md_ctx_from_context(DIGEST_CONTEXT *ctx)
+ 
+     return md_ctx;
+ }
++#endif
++
++#if OPENSSL_VERSION_PREREQ(3, 0)
++static void token_specific_sha_free(STDLL_TokData_t *tokdata, SESSION *sess,
++                                    CK_BYTE *context, CK_ULONG context_len)
++{
++    UNUSED(tokdata);
++    UNUSED(sess);
++    UNUSED(context_len);
++
++    EVP_MD_CTX_free((EVP_MD_CTX *)context);
++}
++#endif
+ 
+ CK_RV token_specific_sha_init(STDLL_TokData_t *tokdata, DIGEST_CONTEXT *ctx,
+                               CK_MECHANISM *mech)
+ {
++#if !OPENSSL_VERSION_PREREQ(3, 0)
+     EVP_MD_CTX *md_ctx;
++#else
++    const EVP_MD *md;
++#endif
+ 
+     UNUSED(tokdata);
+ 
+     ctx->mech.ulParameterLen = mech->ulParameterLen;
+     ctx->mech.mechanism = mech->mechanism;
+ 
++#if !OPENSSL_VERSION_PREREQ(3, 0)
+     md_ctx = md_ctx_from_context(ctx);
+     if (md_ctx == NULL) {
+         TRACE_ERROR("%s\n", ock_err(ERR_HOST_MEMORY));
+         return CKR_HOST_MEMORY;
+     }
+ 
+-#if OPENSSL_VERSION_NUMBER < 0x10101000L
+-    EVP_MD_CTX_destroy(md_ctx);
+-#else
+     EVP_MD_CTX_free(md_ctx);
++#else
++    ctx->context_len = 1;
++    ctx->context = (CK_BYTE *)EVP_MD_CTX_new();
++    if (ctx->context == NULL) {
++        TRACE_ERROR("%s\n", ock_err(ERR_HOST_MEMORY));
++        return CKR_HOST_MEMORY;
++    }
++
++    md = md_from_mech(&ctx->mech);
++    if (md == NULL ||
++        !EVP_DigestInit_ex((EVP_MD_CTX *)ctx->context, md, NULL)) {
++        TRACE_ERROR("%s\n", ock_err(ERR_FUNCTION_FAILED));
++        EVP_MD_CTX_free((EVP_MD_CTX *)ctx->context);
++        return CKR_FUNCTION_FAILED;
++    }
++
++    ctx->state_unsaveable = CK_TRUE;
++    ctx->context_free_func = token_specific_sha_free;
+ #endif
+ 
+     return CKR_OK;
+@@ -3253,7 +2939,9 @@ CK_RV token_specific_sha(STDLL_TokData_t *tokdata, DIGEST_CONTEXT *ctx,
+ {
+     unsigned int len;
+     CK_RV rc = CKR_OK;
++#if !OPENSSL_VERSION_PREREQ(3, 0)
+     EVP_MD_CTX *md_ctx;
++#endif
+ 
+     UNUSED(tokdata);
+ 
+@@ -3263,6 +2951,7 @@ CK_RV token_specific_sha(STDLL_TokData_t *tokdata, DIGEST_CONTEXT *ctx,
+     if (!in_data || !out_data)
+         return CKR_ARGUMENTS_BAD;
+ 
++#if !OPENSSL_VERSION_PREREQ(3, 0)
+     /* Recreate the OpenSSL MD context from the saved context */
+     md_ctx = md_ctx_from_context(ctx);
+     if (md_ctx == NULL) {
+@@ -3275,21 +2964,38 @@ CK_RV token_specific_sha(STDLL_TokData_t *tokdata, DIGEST_CONTEXT *ctx,
+ 
+     if (!EVP_DigestUpdate(md_ctx, in_data, in_data_len) ||
+         !EVP_DigestFinal(md_ctx, out_data, &len)) {
++        TRACE_ERROR("%s\n", ock_err(ERR_FUNCTION_FAILED));
+         rc = CKR_FUNCTION_FAILED;
+         goto out;
+     }
+ 
+     *out_data_len = len;
++#else
++    if (*out_data_len < (CK_ULONG)EVP_MD_CTX_size((EVP_MD_CTX *)ctx->context)) {
++        TRACE_ERROR("%s\n", ock_err(ERR_BUFFER_TOO_SMALL));
++        return CKR_BUFFER_TOO_SMALL;
++    }
++
++    len = *out_data_len;
++    if (!EVP_DigestUpdate((EVP_MD_CTX *)ctx->context, in_data, in_data_len) ||
++        !EVP_DigestFinal((EVP_MD_CTX *)ctx->context, out_data, &len)) {
++        TRACE_ERROR("%s\n", ock_err(ERR_FUNCTION_FAILED));
++        return CKR_FUNCTION_FAILED;
++    }
++
++    *out_data_len = len;
++#endif
+ 
++#if !OPENSSL_VERSION_PREREQ(3, 0)
+ out:
+-#if OPENSSL_VERSION_NUMBER < 0x10101000L
+-    EVP_MD_CTX_destroy(md_ctx);
+-#else
+     EVP_MD_CTX_free(md_ctx);
+-#endif
+     free(ctx->context);
++#else
++    EVP_MD_CTX_free((EVP_MD_CTX *)ctx->context);
++#endif
+     ctx->context = NULL;
+     ctx->context_len = 0;
++    ctx->context_free_func = NULL;
+ 
+     return rc;
+ }
+@@ -3297,7 +3003,9 @@ out:
+ CK_RV token_specific_sha_update(STDLL_TokData_t *tokdata, DIGEST_CONTEXT *ctx,
+                                 CK_BYTE *in_data, CK_ULONG in_data_len)
+ {
++#if !OPENSSL_VERSION_PREREQ(3, 0)
+     EVP_MD_CTX *md_ctx;
++#endif
+ 
+     UNUSED(tokdata);
+ 
+@@ -3307,6 +3015,7 @@ CK_RV token_specific_sha_update(STDLL_TokData_t *tokdata, DIGEST_CONTEXT *ctx,
+     if (!in_data)
+         return CKR_ARGUMENTS_BAD;
+ 
++#if !OPENSSL_VERSION_PREREQ(3, 0)
+     /* Recreate the OpenSSL MD context from the saved context */
+     md_ctx = md_ctx_from_context(ctx);
+     if (md_ctx == NULL) {
+@@ -3315,24 +3024,24 @@ CK_RV token_specific_sha_update(STDLL_TokData_t *tokdata, DIGEST_CONTEXT *ctx,
+     }
+ 
+     if (!EVP_DigestUpdate(md_ctx, in_data, in_data_len)) {
+-#if OPENSSL_VERSION_NUMBER < 0x10101000L
+-        EVP_MD_CTX_destroy(md_ctx);
+-#else
+         EVP_MD_CTX_free(md_ctx);
+-#endif
+         free(ctx->context);
+         ctx->context = NULL;
+         ctx->context_len = 0;
++        ctx->context_free_func = NULL;
++        TRACE_ERROR("%s\n", ock_err(ERR_FUNCTION_FAILED));
+         return CKR_FUNCTION_FAILED;
+     }
+ 
+     /* Save context data for later use */
+     memcpy(ctx->context,  EVP_MD_CTX_md_data(md_ctx), ctx->context_len);
+ 
+-#if OPENSSL_VERSION_NUMBER < 0x10101000L
+-    EVP_MD_CTX_destroy(md_ctx);
+-#else
+     EVP_MD_CTX_free(md_ctx);
++#else
++    if (!EVP_DigestUpdate((EVP_MD_CTX *)ctx->context, in_data, in_data_len)) {
++        TRACE_ERROR("%s\n", ock_err(ERR_FUNCTION_FAILED));
++        return CKR_FUNCTION_FAILED;
++    }
+ #endif
+ 
+     return CKR_OK;
+@@ -3343,7 +3052,9 @@ CK_RV token_specific_sha_final(STDLL_TokData_t *tokdata, DIGEST_CONTEXT *ctx,
+ {
+     unsigned int len;
+     CK_RV rc = CKR_OK;
++#if !OPENSSL_VERSION_PREREQ(3, 0)
+     EVP_MD_CTX *md_ctx;
++#endif
+ 
+     UNUSED(tokdata);
+ 
+@@ -3353,6 +3064,7 @@ CK_RV token_specific_sha_final(STDLL_TokData_t *tokdata, DIGEST_CONTEXT *ctx,
+     if (!out_data)
+         return CKR_ARGUMENTS_BAD;
+ 
++#if !OPENSSL_VERSION_PREREQ(3, 0)
+     /* Recreate the OpenSSL MD context from the saved context */
+     md_ctx = md_ctx_from_context(ctx);
+     if (md_ctx == NULL) {
+@@ -3370,14 +3082,30 @@ CK_RV token_specific_sha_final(STDLL_TokData_t *tokdata, DIGEST_CONTEXT *ctx,
+     *out_data_len = len;
+ 
+ out:
+-#if OPENSSL_VERSION_NUMBER < 0x10101000L
+-    EVP_MD_CTX_destroy(md_ctx);
+-#else
+     EVP_MD_CTX_free(md_ctx);
+-#endif
+     free(ctx->context);
+     ctx->context = NULL;
+     ctx->context_len = 0;
++    ctx->context_free_func = NULL;
++#else
++    if (*out_data_len < (CK_ULONG)EVP_MD_CTX_size((EVP_MD_CTX *)ctx->context)) {
++        TRACE_ERROR("%s\n", ock_err(ERR_BUFFER_TOO_SMALL));
++        return CKR_BUFFER_TOO_SMALL;
++    }
++
++    len = *out_data_len;
++    if (!EVP_DigestFinal((EVP_MD_CTX *)ctx->context, out_data, &len)) {
++        TRACE_ERROR("%s\n", ock_err(ERR_FUNCTION_FAILED));
++        return CKR_FUNCTION_FAILED;
++    }
++
++    *out_data_len = len;
++
++    EVP_MD_CTX_free((EVP_MD_CTX *)ctx->context);
++    ctx->context = NULL;
++    ctx->context_len = 0;
++    ctx->context_free_func = NULL;
++#endif
+ 
+     return rc;
+ }
+@@ -3897,99 +3625,26 @@ CK_RV token_specific_tdes_cmac(STDLL_TokData_t *tokdata, CK_BYTE *message,
+                                CK_ULONG message_len, OBJECT *key, CK_BYTE *mac,
+                                CK_BBOOL first, CK_BBOOL last, CK_VOID_PTR *ctx)
+ {
+-#if OPENSSL_VERSION_NUMBER < 0x10101000L
+     int rc;
+-    CK_RV rv = CKR_OK;
+-    CK_ATTRIBUTE *attr = NULL;
+-    CK_KEY_TYPE keytype;
+-    CMAC_CTX *cmac_ctx;
+-    const EVP_CIPHER *cipher;
+-    size_t maclen;
+-
+-    UNUSED(tokdata);
+-
+-    if (first) {
+-        // get the key type
+-        rc = template_attribute_get_ulong(key->template, CKA_KEY_TYPE, &keytype);
+-        if (rc != CKR_OK) {
+-            TRACE_ERROR("Could not find CKA_KEY_TYPE for the key.\n");
+-            return CKR_FUNCTION_FAILED;
+-        }
+-
+-        // get the key value
+-        rc = template_attribute_get_non_empty(key->template, CKA_VALUE, &attr);
+-        if (rc != CKR_OK) {
+-            TRACE_ERROR("Could not find CKA_VALUE for the key.\n");
+-            return rc;
+-        }
+-        switch (keytype) {
+-        case CKK_DES2:
+-            cipher = EVP_des_ede_cbc();
+-            break;
+-        case CKK_DES3:
+-            cipher = EVP_des_ede3_cbc();
+-            break;
+-        default:
+-            TRACE_ERROR("Invalid key type: %lu\n", keytype);
+-            return CKR_KEY_TYPE_INCONSISTENT;
+-        }
+-        if (cipher == NULL) {
+-            TRACE_ERROR("Failed to allocate cipher\n");
+-            return CKR_HOST_MEMORY;
+-        }
+-
+-        cmac_ctx = CMAC_CTX_new();
+-        if (cmac_ctx == NULL) {
+-            TRACE_ERROR("Failed to allocate CMAC context\n");
+-            return CKR_HOST_MEMORY;
+-        }
+-
+-        rc = CMAC_Init(cmac_ctx, attr->pValue, attr->ulValueLen, cipher, NULL);
+-        if (rc != 1) {
+-            TRACE_ERROR("CMAC_Init failed\n");
+-            CMAC_CTX_free(cmac_ctx);
+-            return CKR_FUNCTION_FAILED;
+-        }
+-
+-        *ctx = cmac_ctx;
+-    }
+-
+-    cmac_ctx = (CMAC_CTX *)*ctx;
+-
+-    rc = CMAC_Update(cmac_ctx, message, message_len);
+-    if (rc != 1) {
+-        TRACE_ERROR("CMAC_Update failed\n");
+-        rv =  CKR_FUNCTION_FAILED;
+-    }
+-
+-    if (last) {
+-        maclen = AES_BLOCK_SIZE;
+-        rc = CMAC_Final(cmac_ctx, mac, &maclen);
+-        if (rc != 1) {
+-            TRACE_ERROR("CMAC_Final failed\n");
+-            rv = CKR_FUNCTION_FAILED;
+-        }
+-    }
+-
+-    if (last || (first && rv != CKR_OK)) {
+-        CMAC_CTX_free(cmac_ctx);
+-        *ctx = NULL;
+-    }
+-
+-    return rv;
+-#else
+-    int rc;
+-    size_t maclen;
++    size_t maclen;
+     CK_RV rv = CKR_OK;
+     CK_ATTRIBUTE *attr = NULL;
+     CK_KEY_TYPE keytype;
+     const EVP_CIPHER *cipher;
+     struct cmac_ctx {
++#if !OPENSSL_VERSION_PREREQ(3, 0)
+         EVP_MD_CTX *mctx;
+         EVP_PKEY_CTX *pctx;
+         EVP_PKEY *pkey;
++#else
++        EVP_MAC *mac;
++        EVP_MAC_CTX *mctx;
++#endif
+     };
+     struct cmac_ctx *cmac = NULL;
++#if OPENSSL_VERSION_PREREQ(3, 0)
++    OSSL_PARAM params[2];
++#endif
+ 
+     UNUSED(tokdata);
+ 
+@@ -4031,10 +3686,11 @@ CK_RV token_specific_tdes_cmac(STDLL_TokData_t *tokdata, CK_BYTE *message,
+             goto err;
+         }
+ 
++#if !OPENSSL_VERSION_PREREQ(3, 0)
+         cmac->mctx = EVP_MD_CTX_new();
+         if (cmac->mctx == NULL) {
+             TRACE_ERROR("%s\n", ock_err(ERR_HOST_MEMORY));
+-            rv = ERR_HOST_MEMORY;
++            rv = CKR_HOST_MEMORY;
+             goto err;
+         }
+ 
+@@ -4053,6 +3709,31 @@ CK_RV token_specific_tdes_cmac(STDLL_TokData_t *tokdata, CK_BYTE *message,
+             rv = CKR_FUNCTION_FAILED;
+             goto err;
+         }
++#else
++        cmac->mac = EVP_MAC_fetch(NULL, "CMAC", NULL);
++        if (cmac->mac == NULL) {
++            TRACE_ERROR("%s\n", ock_err(ERR_FUNCTION_FAILED));
++            rv = CKR_FUNCTION_FAILED;
++            goto err;
++        }
++
++        cmac->mctx = EVP_MAC_CTX_new(cmac->mac);
++        if (cmac->mctx == NULL) {
++            TRACE_ERROR("%s\n", ock_err(ERR_HOST_MEMORY));
++            rv = CKR_HOST_MEMORY;
++            goto err;
++        }
++
++        params[0] = OSSL_PARAM_construct_utf8_string(OSSL_MAC_PARAM_CIPHER,
++                                      (char *)EVP_CIPHER_get0_name(cipher), 0);
++        params[1] = OSSL_PARAM_construct_end();
++
++        if (!EVP_MAC_init(cmac->mctx, attr->pValue, attr->ulValueLen, params)) {
++            TRACE_ERROR("%s\n", ock_err(ERR_FUNCTION_FAILED));
++            rv = CKR_FUNCTION_FAILED;
++            goto err;
++        }
++#endif
+ 
+         *ctx = cmac;
+     }
+@@ -4064,9 +3745,17 @@ CK_RV token_specific_tdes_cmac(STDLL_TokData_t *tokdata, CK_BYTE *message,
+         goto err;
+     }
+ 
++#if !OPENSSL_VERSION_PREREQ(3, 0)
+     rc = EVP_DigestSignUpdate(cmac->mctx, message, message_len);
++#else
++    rc = EVP_MAC_update(cmac->mctx, message, message_len);
++#endif
+     if (rc != 1 || message_len > INT_MAX) {
++#if !OPENSSL_VERSION_PREREQ(3, 0)
+         TRACE_ERROR("EVP_DigestSignUpdate failed\n");
++#else
++        TRACE_ERROR("EVP_MAC_update failed\n");
++#endif
+         rv =  CKR_FUNCTION_FAILED;
+         goto err;
+     }
+@@ -4074,15 +3763,28 @@ CK_RV token_specific_tdes_cmac(STDLL_TokData_t *tokdata, CK_BYTE *message,
+     if (last) {
+         maclen = AES_BLOCK_SIZE;
+ 
++#if !OPENSSL_VERSION_PREREQ(3, 0)
+         rc = EVP_DigestSignFinal(cmac->mctx, mac, &maclen);
++#else
++        rc = EVP_MAC_final(cmac->mctx, mac, &maclen, maclen);
++#endif
+         if (rc != 1) {
++#if !OPENSSL_VERSION_PREREQ(3, 0)
+             TRACE_ERROR("EVP_DigestSignFinal failed\n");
++#else
++            TRACE_ERROR("EVP_MAC_final failed\n");
++#endif
+             rv = CKR_FUNCTION_FAILED;
+             goto err;
+         }
+ 
++#if !OPENSSL_VERSION_PREREQ(3, 0)
+         EVP_MD_CTX_free(cmac->mctx); /* frees pctx */
+         EVP_PKEY_free(cmac->pkey);
++#else
++        EVP_MAC_CTX_free(cmac->mctx);
++        EVP_MAC_free(cmac->mac);
++#endif
+         free(cmac);
+         *ctx = NULL;
+     }
+@@ -4090,15 +3792,21 @@ CK_RV token_specific_tdes_cmac(STDLL_TokData_t *tokdata, CK_BYTE *message,
+     return CKR_OK;
+ err:
+     if (cmac != NULL) {
++#if !OPENSSL_VERSION_PREREQ(3, 0)
+         if (cmac->mctx != NULL)
+             EVP_MD_CTX_free(cmac->mctx); /* frees pctx */
+         if (cmac->pkey != NULL)
+             EVP_PKEY_free(cmac->pkey);
++#else
++        if (cmac->mctx != NULL)
++            EVP_MAC_CTX_free(cmac->mctx);
++        if (cmac->mac != NULL)
++            EVP_MAC_free(cmac->mac);
++#endif
+         free(cmac);
+     }
+     *ctx = NULL;
+     return rv;
+-#endif
+ }
+ 
+ 
+@@ -4106,93 +3814,25 @@ CK_RV token_specific_aes_cmac(STDLL_TokData_t *tokdata, CK_BYTE *message,
+                               CK_ULONG message_len, OBJECT *key, CK_BYTE *mac,
+                               CK_BBOOL first, CK_BBOOL last, CK_VOID_PTR *ctx)
+ {
+-#if OPENSSL_VERSION_NUMBER < 0x10101000L
+-    int rc;
+-    CK_RV rv = CKR_OK;
+-    CK_ATTRIBUTE *attr = NULL;
+-    CMAC_CTX *cmac_ctx;
+-    const EVP_CIPHER *cipher;
+-    size_t maclen;
+-
+-    UNUSED(tokdata);
+-
+-    if (first) {
+-        rc = template_attribute_get_non_empty(key->template, CKA_VALUE, &attr);
+-        if (rc != CKR_OK) {
+-            TRACE_ERROR("Could not find CKA_VALUE for the key.\n");
+-            return rc;
+-        }
+-
+-        switch (attr->ulValueLen * 8) {
+-        case 128:
+-            cipher = EVP_aes_128_cbc();
+-            break;
+-        case 192:
+-            cipher = EVP_aes_192_cbc();
+-            break;
+-        case 256:
+-            cipher = EVP_aes_256_cbc();
+-            break;
+-        default:
+-            TRACE_ERROR("Invalid key size: %lu\n", attr->ulValueLen);
+-            return CKR_KEY_TYPE_INCONSISTENT;
+-        }
+-        if (cipher == NULL) {
+-            TRACE_ERROR("Failed to allocate cipher\n");
+-            return CKR_HOST_MEMORY;
+-        }
+-
+-        cmac_ctx = CMAC_CTX_new();
+-        if (cmac_ctx == NULL) {
+-            TRACE_ERROR("Failed to allocate CMAC context\n");
+-            return CKR_HOST_MEMORY;
+-        }
+-
+-        rc = CMAC_Init(cmac_ctx, attr->pValue, attr->ulValueLen, cipher, NULL);
+-        if (rc != 1) {
+-            TRACE_ERROR("CMAC_Init failed\n");
+-            CMAC_CTX_free(cmac_ctx);
+-            return CKR_FUNCTION_FAILED;
+-        }
+-
+-        *ctx = cmac_ctx;
+-    }
+-
+-    cmac_ctx = (CMAC_CTX *)*ctx;
+-
+-    rc = CMAC_Update(cmac_ctx, message, message_len);
+-    if (rc != 1) {
+-        TRACE_ERROR("CMAC_Update failed\n");
+-        rv =  CKR_FUNCTION_FAILED;
+-    }
+-
+-    if (last) {
+-        maclen = AES_BLOCK_SIZE;
+-        rc = CMAC_Final(cmac_ctx, mac, &maclen);
+-        if (rc != 1) {
+-            TRACE_ERROR("CMAC_Final failed\n");
+-            rv = CKR_FUNCTION_FAILED;
+-        }
+-    }
+-
+-    if (last || (first && rv != CKR_OK)) {
+-        CMAC_CTX_free(cmac_ctx);
+-        *ctx = NULL;
+-    }
+-
+-    return rv;
+-#else
+     int rc;
+     size_t maclen;
+     CK_RV rv = CKR_OK;
+     CK_ATTRIBUTE *attr = NULL;
+     const EVP_CIPHER *cipher;
+     struct cmac_ctx {
++#if !OPENSSL_VERSION_PREREQ(3, 0)
+         EVP_MD_CTX *mctx;
+         EVP_PKEY_CTX *pctx;
+         EVP_PKEY *pkey;
++#else
++        EVP_MAC *mac;
++        EVP_MAC_CTX *mctx;
++#endif
+     };
+     struct cmac_ctx *cmac = NULL;
++#if OPENSSL_VERSION_PREREQ(3, 0)
++    OSSL_PARAM params[2];
++#endif
+ 
+     UNUSED(tokdata);
+ 
+@@ -4229,6 +3869,7 @@ CK_RV token_specific_aes_cmac(STDLL_TokData_t *tokdata, CK_BYTE *message,
+             goto err;
+         }
+ 
++#if !OPENSSL_VERSION_PREREQ(3, 0)
+         cmac->mctx = EVP_MD_CTX_new();
+         if (cmac->mctx == NULL) {
+             TRACE_ERROR("%s\n", ock_err(ERR_HOST_MEMORY));
+@@ -4251,6 +3892,31 @@ CK_RV token_specific_aes_cmac(STDLL_TokData_t *tokdata, CK_BYTE *message,
+             rv = CKR_FUNCTION_FAILED;
+             goto err;
+         }
++#else
++        cmac->mac = EVP_MAC_fetch(NULL, "CMAC", NULL);
++        if (cmac->mac == NULL) {
++            TRACE_ERROR("%s\n", ock_err(ERR_FUNCTION_FAILED));
++            rv = CKR_FUNCTION_FAILED;
++            goto err;
++        }
++
++        cmac->mctx = EVP_MAC_CTX_new(cmac->mac);
++        if (cmac->mctx == NULL) {
++            TRACE_ERROR("%s\n", ock_err(ERR_HOST_MEMORY));
++            rv = CKR_HOST_MEMORY;
++            goto err;
++        }
++
++        params[0] = OSSL_PARAM_construct_utf8_string(OSSL_MAC_PARAM_CIPHER,
++                                      (char *)EVP_CIPHER_get0_name(cipher), 0);
++        params[1] = OSSL_PARAM_construct_end();
++
++        if (!EVP_MAC_init(cmac->mctx, attr->pValue, attr->ulValueLen, params)) {
++            TRACE_ERROR("%s\n", ock_err(ERR_FUNCTION_FAILED));
++            rv = CKR_FUNCTION_FAILED;
++            goto err;
++        }
++#endif
+ 
+         *ctx = cmac;
+     }
+@@ -4262,9 +3928,17 @@ CK_RV token_specific_aes_cmac(STDLL_TokData_t *tokdata, CK_BYTE *message,
+         goto err;
+     }
+ 
++#if !OPENSSL_VERSION_PREREQ(3, 0)
+     rc = EVP_DigestSignUpdate(cmac->mctx, message, message_len);
++#else
++    rc = EVP_MAC_update(cmac->mctx, message, message_len);
++#endif
+     if (rc != 1 || message_len > INT_MAX) {
++#if !OPENSSL_VERSION_PREREQ(3, 0)
+         TRACE_ERROR("EVP_DigestSignUpdate failed\n");
++#else
++        TRACE_ERROR("EVP_MAC_update failed\n");
++#endif
+         rv =  CKR_FUNCTION_FAILED;
+         goto err;
+     }
+@@ -4272,15 +3946,28 @@ CK_RV token_specific_aes_cmac(STDLL_TokData_t *tokdata, CK_BYTE *message,
+     if (last) {
+         maclen = AES_BLOCK_SIZE;
+ 
++#if !OPENSSL_VERSION_PREREQ(3, 0)
+         rc = EVP_DigestSignFinal(cmac->mctx, mac, &maclen);
++#else
++        rc = EVP_MAC_final(cmac->mctx, mac, &maclen, maclen);
++#endif
+         if (rc != 1) {
++#if !OPENSSL_VERSION_PREREQ(3, 0)
+             TRACE_ERROR("EVP_DigestSignFinal failed\n");
++#else
++            TRACE_ERROR("EVP_MAC_final failed\n");
++#endif
+             rv = CKR_FUNCTION_FAILED;
+             goto err;
+         }
+ 
++#if !OPENSSL_VERSION_PREREQ(3, 0)
+         EVP_MD_CTX_free(cmac->mctx); /* frees pctx */
+         EVP_PKEY_free(cmac->pkey);
++#else
++        EVP_MAC_CTX_free(cmac->mctx);
++        EVP_MAC_free(cmac->mac);
++#endif
+         free(cmac);
+         *ctx = NULL;
+     }
+@@ -4288,37 +3975,90 @@ CK_RV token_specific_aes_cmac(STDLL_TokData_t *tokdata, CK_BYTE *message,
+     return CKR_OK;
+ err:
+     if (cmac != NULL) {
++#if !OPENSSL_VERSION_PREREQ(3, 0)
+         if (cmac->mctx != NULL)
+             EVP_MD_CTX_free(cmac->mctx); /* frees pctx */
+         if (cmac->pkey != NULL)
+             EVP_PKEY_free(cmac->pkey);
++#else
++        if (cmac->mctx != NULL)
++            EVP_MAC_CTX_free(cmac->mctx);
++        if (cmac->mac != NULL)
++            EVP_MAC_free(cmac->mac);
++#endif
+         free(cmac);
+     }
+     *ctx = NULL;
+     return rv;
+-#endif
+ }
+ 
+ #ifndef NO_EC
+ 
+-static CK_RV make_ec_key_from_params(const CK_BYTE *params, CK_ULONG params_len,
+-                                     EC_KEY **key)
++static int curve_nid_from_params(const CK_BYTE *params, CK_ULONG params_len)
+ {
+     const unsigned char *oid;
+     ASN1_OBJECT *obj = NULL;
+-    EC_KEY *ec_key = NULL;
+     int nid;
+-    CK_RV rc = CKR_OK;
+ 
+     oid = params;
+     obj = d2i_ASN1_OBJECT(NULL, &oid, params_len);
+     if (obj == NULL) {
+         TRACE_ERROR("curve not supported by OpenSSL.\n");
+-        rc = CKR_CURVE_NOT_SUPPORTED;
+-        goto out;
++        return NID_undef;
+     }
+ 
+     nid = OBJ_obj2nid(obj);
++    ASN1_OBJECT_free(obj);
++
++    return nid;
++}
++
++static int ec_prime_len_from_nid(int nid)
++{
++    EC_GROUP *group;
++    int primelen;
++
++    group = EC_GROUP_new_by_curve_name(nid);
++    if (group == NULL)
++        return -1;
++
++    primelen = EC_GROUP_order_bits(group);
++
++    EC_GROUP_free(group);
++
++    if ((primelen % 8) == 0)
++        return primelen / 8;
++    else
++        return (primelen / 8) + 1;
++}
++
++int ec_prime_len_from_pkey(EVP_PKEY *pkey)
++{
++#if !OPENSSL_VERSION_PREREQ(3, 0)
++    return (EC_GROUP_order_bits(EC_KEY_get0_group(
++                             EVP_PKEY_get0_EC_KEY(pkey))) + 7) / 8;
++#else
++    size_t curve_len;
++    char curve[80];
++
++    if (!EVP_PKEY_get_utf8_string_param(pkey, OSSL_PKEY_PARAM_GROUP_NAME,
++                                        curve, sizeof(curve), &curve_len))
++        return -1;
++
++    return ec_prime_len_from_nid(OBJ_sn2nid(curve));
++#endif
++}
++
++
++#if !OPENSSL_VERSION_PREREQ(3, 0)
++static CK_RV make_ec_key_from_params(const CK_BYTE *params, CK_ULONG params_len,
++                                     EC_KEY **key)
++{
++    EC_KEY *ec_key = NULL;
++    int nid;
++    CK_RV rc = CKR_OK;
++
++    nid = curve_nid_from_params(params, params_len);
+     if (nid == NID_undef) {
+         TRACE_ERROR("curve not supported by OpenSSL.\n");
+         rc = CKR_CURVE_NOT_SUPPORTED;
+@@ -4333,9 +4073,6 @@ static CK_RV make_ec_key_from_params(const CK_BYTE *params, CK_ULONG params_len,
+     }
+ 
+ out:
+-    if (obj != NULL)
+-        ASN1_OBJECT_free(obj);
+-
+     if (rc != CKR_OK) {
+         if (ec_key != NULL)
+             EC_KEY_free(ec_key);
+@@ -4347,16 +4084,97 @@ out:
+ 
+     return CKR_OK;
+ }
++#endif
++
++#if OPENSSL_VERSION_PREREQ(3, 0)
++static CK_RV build_pkey_from_params(OSSL_PARAM_BLD *tmpl, int selection,
++                                    EVP_PKEY **pkey)
++{
++
++    OSSL_PARAM *params = NULL;
++    EVP_PKEY_CTX *pctx = NULL;
++    CK_RV rc = CKR_OK;
++
++    params = OSSL_PARAM_BLD_to_param(tmpl);
++    if (params == NULL) {
++        TRACE_ERROR("OSSL_PARAM_BLD_to_param failed\n");
++        rc = CKR_FUNCTION_FAILED;
++        goto out;
++    }
++
++    pctx = EVP_PKEY_CTX_new_id(EVP_PKEY_EC, NULL);
++    if (pctx == NULL) {
++        TRACE_ERROR("EVP_PKEY_CTX_new_id failed\n");
++        rc = CKR_FUNCTION_FAILED;
++        goto out;
++    }
++
++    if (!EVP_PKEY_fromdata_init(pctx) ||
++        !EVP_PKEY_fromdata(pctx, pkey, selection, params)) {
++        TRACE_ERROR("EVP_PKEY_fromdata failed\n");
++        rc = CKR_FUNCTION_FAILED;
++        goto out;
++    }
++
++    EVP_PKEY_CTX_free(pctx);
++    pctx = EVP_PKEY_CTX_new(*pkey, NULL);
++    if (pctx == NULL) {
++        TRACE_ERROR("EVP_PKEY_CTX_new failed\n");
++        rc = CKR_FUNCTION_FAILED;
++        goto out;
++    }
++
++    if (selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) {
++        if (EVP_PKEY_check(pctx) != 1) {
++            TRACE_ERROR("EVP_PKEY_check failed\n");
++            rc = CKR_FUNCTION_FAILED;
++            goto out;
++        }
++    } else {
++        if (EVP_PKEY_public_check(pctx) != 1) {
++            TRACE_ERROR("EVP_PKEY_public_check failed\n");
++            rc = CKR_FUNCTION_FAILED;
++            goto out;
++        }
++    }
++
++out:
++    if (pctx != NULL)
++        EVP_PKEY_CTX_free(pctx);
++    if (params != NULL)
++        OSSL_PARAM_free(params);
++
++    if (rc != 0 && *pkey != NULL) {
++        EVP_PKEY_free(*pkey);
++        *pkey = NULL;
++    }
++
++    return rc;
++}
++#endif
+ 
++#if !OPENSSL_VERSION_PREREQ(3, 0)
+ static CK_RV fill_ec_key_from_pubkey(EC_KEY *ec_key, const CK_BYTE *data,
+-                                     CK_ULONG data_len, CK_BBOOL allow_raw)
++                                     CK_ULONG data_len, CK_BBOOL allow_raw,
++                                     int nid, EVP_PKEY **ec_pkey)
++#else
++static CK_RV fill_ec_key_from_pubkey(OSSL_PARAM_BLD *tmpl, const CK_BYTE *data,
++                                     CK_ULONG data_len, CK_BBOOL allow_raw,
++                                     int nid, EVP_PKEY **ec_pkey)
++#endif
+ {
+     CK_BYTE *ecpoint = NULL;
+     CK_ULONG ecpoint_len, privlen;
+     CK_BBOOL allocated = FALSE;
++
+     CK_RV rc;
+ 
+-    privlen = (EC_GROUP_order_bits(EC_KEY_get0_group(ec_key)) + 7) / 8;
++    privlen = ec_prime_len_from_nid(nid);
++    if (privlen <= 0) {
++        TRACE_ERROR("ec_prime_len_from_nid failed\n");
++        rc = CKR_CURVE_NOT_SUPPORTED;
++        goto out;
++    }
+ 
+     rc = ec_point_from_public_data(data, data_len, privlen, allow_raw,
+                                    &allocated, &ecpoint, &ecpoint_len);
+@@ -4365,6 +4183,7 @@ static CK_RV fill_ec_key_from_pubkey(EC_KEY *ec_key, const CK_BYTE *data,
+         goto out;
+     }
+ 
++#if !OPENSSL_VERSION_PREREQ(3, 0)
+     if (!EC_KEY_oct2key(ec_key, ecpoint, ecpoint_len, NULL)) {
+         TRACE_ERROR("EC_KEY_oct2key failed\n");
+         rc = CKR_FUNCTION_FAILED;
+@@ -4377,6 +4196,34 @@ static CK_RV fill_ec_key_from_pubkey(EC_KEY *ec_key, const CK_BYTE *data,
+         goto out;
+     }
+ 
++    *ec_pkey = EVP_PKEY_new();
++    if (*ec_pkey == NULL) {
++       TRACE_ERROR("EVP_PKEY_CTX_new failed.\n");
++       rc = CKR_HOST_MEMORY;
++       goto out;
++    }
++
++    if (!EVP_PKEY_assign_EC_KEY(*ec_pkey, ec_key)) {
++        TRACE_ERROR("EVP_PKEY_assign_EC_KEY failed.\n");
++        rc = CKR_FUNCTION_FAILED;
++        goto out;
++    }
++#else
++    if (!OSSL_PARAM_BLD_push_octet_string(tmpl,
++                                          OSSL_PKEY_PARAM_PUB_KEY,
++                                          ecpoint, ecpoint_len)) {
++        TRACE_ERROR("OSSL_PARAM_BLD_push_octet_string failed\n");
++        rc = CKR_FUNCTION_FAILED;
++        goto out;
++    }
++
++    rc = build_pkey_from_params(tmpl, EVP_PKEY_PUBLIC_KEY, ec_pkey);
++    if (rc != CKR_OK) {
++        TRACE_ERROR("build_pkey_from_params failed\n");
++        goto out;
++    }
++ #endif
++
+ out:
+     if (allocated && ecpoint != NULL)
+         free(ecpoint);
+@@ -4384,12 +4231,26 @@ out:
+     return rc;
+ }
+ 
++#if !OPENSSL_VERSION_PREREQ(3, 0)
+ static CK_RV fill_ec_key_from_privkey(EC_KEY *ec_key, const CK_BYTE *data,
+-                                      CK_ULONG data_len)
++                                      CK_ULONG data_len, EVP_PKEY **ec_pkey)
++#else
++static CK_RV fill_ec_key_from_privkey(OSSL_PARAM_BLD *tmpl, const CK_BYTE *data,
++                                      CK_ULONG data_len, int nid,
++                                      EVP_PKEY **ec_pkey)
++#endif
+ {
+     EC_POINT *point = NULL;
++#if OPENSSL_VERSION_PREREQ(3, 0)
++    EC_GROUP *group = NULL;
++    BIGNUM *bn_priv = NULL;
++    unsigned char *pub_key = NULL;
++    unsigned int pub_key_len;
++    point_conversion_form_t form;
++#endif
+     CK_RV rc = CKR_OK;
+ 
++#if !OPENSSL_VERSION_PREREQ(3, 0)
+     if (!EC_KEY_oct2priv(ec_key, data, data_len)) {
+         TRACE_ERROR("EC_KEY_oct2priv failed\n");
+         rc = CKR_FUNCTION_FAILED;
+@@ -4422,18 +4283,102 @@ static CK_RV fill_ec_key_from_privkey(EC_KEY *ec_key, const CK_BYTE *data,
+         goto out;
+     }
+ 
++    *ec_pkey = EVP_PKEY_new();
++    if (*ec_pkey == NULL) {
++       TRACE_ERROR("EVP_PKEY_CTX_new failed.\n");
++       rc = CKR_HOST_MEMORY;
++       goto out;
++    }
++
++    if (!EVP_PKEY_assign_EC_KEY(*ec_pkey, ec_key)) {
++        TRACE_ERROR("EVP_PKEY_assign_EC_KEY failed.\n");
++        rc = CKR_FUNCTION_FAILED;
++        goto out;
++    }
++#else
++    group = EC_GROUP_new_by_curve_name(nid);
++    if (group == NULL) {
++        TRACE_ERROR("EC_GROUP_new_by_curve_name failed\n");
++        rc = CKR_CURVE_NOT_SUPPORTED;
++        goto out;
++    }
++
++    point = EC_POINT_new(group);
++    if (point == NULL) {
++        TRACE_ERROR("EC_POINT_new failed\n");
++        rc = CKR_FUNCTION_FAILED;
++        goto out;
++    }
++
++    bn_priv = BN_bin2bn(data, data_len, NULL);
++    if (bn_priv == NULL) {
++        rc = CKR_FUNCTION_FAILED;
++        goto out;
++    }
++
++    if (!EC_POINT_mul(group, point, bn_priv, NULL, NULL, NULL)) {
++        TRACE_ERROR("EC_POINT_mul failed\n");
++        rc = CKR_FUNCTION_FAILED;
++        goto out;
++    }
++
++    form = EC_GROUP_get_point_conversion_form(group);
++    pub_key_len = EC_POINT_point2buf(group, point, form, &pub_key,
++                                     NULL);
++    if (pub_key_len == 0) {
++        TRACE_ERROR("EC_POINT_point2buf failed\n");
++        rc = CKR_FUNCTION_FAILED;
++        goto out;
++    }
++
++    if (!OSSL_PARAM_BLD_push_octet_string(tmpl, OSSL_PKEY_PARAM_PUB_KEY,
++                                          pub_key, pub_key_len)) {
++        TRACE_ERROR("OSSL_PARAM_BLD_push_octet_string failed\n");
++        rc = CKR_FUNCTION_FAILED;
++        goto out;
++    }
++
++    if (!OSSL_PARAM_BLD_push_BN(tmpl, OSSL_PKEY_PARAM_PRIV_KEY, bn_priv)) {
++        TRACE_ERROR("OSSL_PARAM_BLD_push_BN failed\n");
++        rc = CKR_FUNCTION_FAILED;
++        goto out;
++    }
++
++    rc = build_pkey_from_params(tmpl, EVP_PKEY_KEYPAIR, ec_pkey);
++    if (rc != CKR_OK) {
++        TRACE_ERROR("build_pkey_from_params failed\n");
++        goto out;
++    }
++#endif
++
+ out:
+     if (point != NULL)
+         EC_POINT_free(point);
++#if OPENSSL_VERSION_PREREQ(3, 0)
++    if (group != NULL)
++        EC_GROUP_free(group);
++    if (bn_priv != NULL)
++        BN_free(bn_priv);
++    if (pub_key != NULL)
++        OPENSSL_free(pub_key);
++#endif
+ 
+     return rc;
+ }
+ 
+-static CK_RV make_ec_key_from_template(TEMPLATE *template, EC_KEY **key)
++
++
++static CK_RV make_ec_key_from_template(TEMPLATE *template, EVP_PKEY **pkey)
+ {
+     CK_ATTRIBUTE *attr = NULL;
+     CK_OBJECT_CLASS keyclass;
++    EVP_PKEY *ec_pkey = NULL;
++    int nid;
++#if !OPENSSL_VERSION_PREREQ(3, 0)
+     EC_KEY *ec_key = NULL;
++#else
++    OSSL_PARAM_BLD *tmpl = NULL;
++#endif
+     CK_RV rc;
+ 
+     rc = template_attribute_get_ulong(template, CKA_CLASS, &keyclass);
+@@ -4448,9 +4393,32 @@ static CK_RV make_ec_key_from_template(TEMPLATE *template, EC_KEY **key)
+         goto out;
+     }
+ 
++    nid = curve_nid_from_params(attr->pValue, attr->ulValueLen);
++    if (nid == NID_undef) {
++        TRACE_ERROR("curve not supported by OpenSSL.\n");
++        rc = CKR_CURVE_NOT_SUPPORTED;
++        goto out;
++    }
++
++#if !OPENSSL_VERSION_PREREQ(3, 0)
+     rc = make_ec_key_from_params(attr->pValue, attr->ulValueLen, &ec_key);
+     if (rc != CKR_OK)
+         goto out;
++#else
++    tmpl = OSSL_PARAM_BLD_new();
++    if (tmpl == NULL) {
++        TRACE_ERROR("OSSL_PARAM_BLD_new failed\n");
++        rc = CKR_HOST_MEMORY;
++        goto out;
++    }
++
++    if (!OSSL_PARAM_BLD_push_utf8_string(tmpl, OSSL_PKEY_PARAM_GROUP_NAME,
++                                         OBJ_nid2sn(nid), 0)) {
++        TRACE_ERROR("OSSL_PARAM_BLD_push_utf8_string failed\n");
++        rc = CKR_FUNCTION_FAILED;
++        goto out;
++    }
++#endif
+ 
+     switch (keyclass) {
+     case CKO_PUBLIC_KEY:
+@@ -4460,8 +4428,13 @@ static CK_RV make_ec_key_from_template(TEMPLATE *template, EC_KEY **key)
+             goto out;
+         }
+ 
++#if !OPENSSL_VERSION_PREREQ(3, 0)
+         rc = fill_ec_key_from_pubkey(ec_key, attr->pValue, attr->ulValueLen,
+-                                     FALSE);
++                                     FALSE, nid, &ec_pkey);
++#else
++        rc = fill_ec_key_from_pubkey(tmpl, attr->pValue, attr->ulValueLen,
++                                     FALSE, nid, &ec_pkey);
++#endif
+         if (rc != CKR_OK) {
+             TRACE_DEVEL("fill_ec_key_from_pubkey failed\n");
+             goto out;
+@@ -4475,7 +4448,14 @@ static CK_RV make_ec_key_from_template(TEMPLATE *template, EC_KEY **key)
+             goto out;
+         }
+ 
+-        rc = fill_ec_key_from_privkey(ec_key, attr->pValue, attr->ulValueLen);
++#if !OPENSSL_VERSION_PREREQ(3, 0)
++        rc = fill_ec_key_from_privkey(ec_key, attr->pValue, attr->ulValueLen,
++                                      &ec_pkey);
++#else
++        rc = fill_ec_key_from_privkey(tmpl, attr->pValue, attr->ulValueLen,
++                                      nid, &ec_pkey);
++
++#endif
+         if (rc != CKR_OK) {
+             TRACE_DEVEL("fill_ec_key_from_privkey failed\n");
+             goto out;
+@@ -4487,17 +4467,30 @@ static CK_RV make_ec_key_from_template(TEMPLATE *template, EC_KEY **key)
+         goto out;
+     }
+ 
++#if !OPENSSL_VERSION_PREREQ(3, 0)
++    ec_key = NULL;
++#endif
++
+     rc = CKR_OK;
+ 
+ out:
++#if OPENSSL_VERSION_PREREQ(3, 0)
++    if (tmpl != NULL)
++        OSSL_PARAM_BLD_free(tmpl);
++#endif
++
+     if (rc != CKR_OK) {
++        if (ec_pkey != NULL)
++            EVP_PKEY_free(ec_pkey);
++#if !OPENSSL_VERSION_PREREQ(3, 0)
+         if (ec_key != NULL)
+             EC_KEY_free(ec_key);
++#endif
+ 
+         return rc;
+     }
+ 
+-    *key = ec_key;
++    *pkey = ec_pkey;
+ 
+     return CKR_OK;
+ }
+@@ -4508,10 +4501,17 @@ CK_RV token_specific_ec_generate_keypair(STDLL_TokData_t *tokdata,
+ {
+ 
+     CK_ATTRIBUTE *attr = NULL, *ec_point_attr, *value_attr, *parms_attr;
+-    EC_KEY *ec_key = NULL;
+-    BN_CTX *ctx = NULL;
++#if !OPENSSL_VERSION_PREREQ(3, 0)
++    const EC_KEY *ec_key = NULL;
++    BN_CTX *bnctx = NULL;
++#else
++    BIGNUM *bn_d = NULL;
++#endif
+     CK_BYTE *ecpoint = NULL, *enc_ecpoint = NULL, *d = NULL;
+     CK_ULONG ecpoint_len, enc_ecpoint_len, d_len;
++    EVP_PKEY_CTX *ctx = NULL;
++    EVP_PKEY *ec_pkey = NULL;
++    int nid;
+     CK_RV rc;
+ 
+     UNUSED(tokdata);
+@@ -4520,29 +4520,83 @@ CK_RV token_specific_ec_generate_keypair(STDLL_TokData_t *tokdata,
+     if (rc != CKR_OK)
+         goto out;
+ 
+-    rc = make_ec_key_from_params(attr->pValue, attr->ulValueLen, &ec_key);
+-    if (rc != CKR_OK)
++    nid = curve_nid_from_params(attr->pValue, attr->ulValueLen);
++    if (nid == NID_undef) {
++        TRACE_ERROR("curve not supported by OpenSSL.\n");
++        rc = CKR_CURVE_NOT_SUPPORTED;
+         goto out;
++    }
+ 
+-    if (!EC_KEY_generate_key(ec_key)) {
+-        TRACE_ERROR("Failed to generate an EC key.\n");
++    ctx = EVP_PKEY_CTX_new_id(EVP_PKEY_EC, NULL);
++    if (ctx == NULL) {
++        TRACE_ERROR("EVP_PKEY_CTX_new failed\n");
+         rc = CKR_FUNCTION_FAILED;
+         goto out;
+     }
+ 
+-    ctx = BN_CTX_new();
+-    if (ctx == NULL) {
++    if (EVP_PKEY_keygen_init(ctx) <= 0) {
++        TRACE_ERROR("EVP_PKEY_keygen_init failed\n");
++        rc = CKR_FUNCTION_FAILED;
++        goto out;
++    }
++
++    if (EVP_PKEY_CTX_set_ec_paramgen_curve_nid(ctx, nid) <= 0) {
++        TRACE_ERROR("EVP_PKEY_CTX_set_ec_paramgen_curve_nid failed\n");
++        rc = CKR_CURVE_NOT_SUPPORTED;
++        goto out;
++    }
++
++    if (EVP_PKEY_keygen(ctx, &ec_pkey) <= 0) {
++        TRACE_ERROR("EVP_PKEY_keygen failed\n");
++        rc = CKR_FUNCTION_FAILED;
++        goto out;
++    }
++
++#if !OPENSSL_VERSION_PREREQ(3, 0)
++    ec_key = EVP_PKEY_get0_EC_KEY(ec_pkey);
++    if (ec_key == NULL) {
++       TRACE_ERROR("EVP_PKEY_get0_EC_KEY failed\n");
++       rc = CKR_FUNCTION_FAILED;
++       goto out;
++   }
++
++    bnctx = BN_CTX_new();
++    if (bnctx == NULL) {
+         rc = CKR_HOST_MEMORY;
+         goto out;
+     }
+ 
+     ecpoint_len = EC_KEY_key2buf(ec_key, POINT_CONVERSION_UNCOMPRESSED,
+-                                 &ecpoint, ctx);
++                                 &ecpoint, bnctx);
+     if (ecpoint_len == 0) {
+         TRACE_ERROR("Failed to get the EC Point compressed.\n");
+         rc = CKR_FUNCTION_FAILED;
+         goto out;
+     }
++#else
++    if (!EVP_PKEY_get_octet_string_param(ec_pkey,
++                                         OSSL_PKEY_PARAM_ENCODED_PUBLIC_KEY,
++                                         NULL, 0, &ecpoint_len)) {
++        TRACE_ERROR("EVP_PKEY_get_octet_string_param failed\n");
++        rc = CKR_FUNCTION_FAILED;
++        goto out;
++    }
++
++    ecpoint = OPENSSL_zalloc(ecpoint_len);
++    if (ecpoint == NULL) {
++        TRACE_ERROR("OPENSSL_zalloc failed\n");
++        rc = CKR_HOST_MEMORY;
++        goto out;
++    }
++
++    if (!EVP_PKEY_get_octet_string_param(ec_pkey,
++                                         OSSL_PKEY_PARAM_ENCODED_PUBLIC_KEY,
++                                         ecpoint, ecpoint_len, &ecpoint_len)) {
++        TRACE_ERROR("EVP_PKEY_get_octet_string_param failed\n");
++        rc = CKR_FUNCTION_FAILED;
++        goto out;
++    }
++#endif
+ 
+     rc = ber_encode_OCTET_STRING(FALSE, &enc_ecpoint, &enc_ecpoint_len,
+                                  ecpoint, ecpoint_len);
+@@ -4564,12 +4618,30 @@ CK_RV token_specific_ec_generate_keypair(STDLL_TokData_t *tokdata,
+         goto out;
+     }
+ 
++#if !OPENSSL_VERSION_PREREQ(3, 0)
+     d_len = EC_KEY_priv2buf(ec_key, &d);
+     if (d_len == 0) {
+         TRACE_ERROR("Failed to get the EC private key.\n");
+         rc = CKR_FUNCTION_FAILED;
+         goto out;
+     }
++#else
++    if (!EVP_PKEY_get_bn_param(ec_pkey, OSSL_PKEY_PARAM_PRIV_KEY, &bn_d)) {
++        TRACE_ERROR("EVP_PKEY_get_bn_param failed\n");
++        rc = CKR_FUNCTION_FAILED;
++        goto out;
++    }
++
++    d_len = ec_prime_len_from_nid(nid);
++    d = OPENSSL_zalloc(d_len);
++    if (d == NULL) {
++        TRACE_ERROR("OPENSSL_zalloc failed\n");
++        rc = CKR_HOST_MEMORY;
++        goto out;
++    }
++
++    BN_bn2binpad(bn_d, d, d_len);
++#endif
+ 
+     rc = build_attribute(CKA_VALUE, d, d_len, &value_attr);
+     if (rc != CKR_OK) {
+@@ -4602,10 +4674,17 @@ CK_RV token_specific_ec_generate_keypair(STDLL_TokData_t *tokdata,
+     rc = CKR_OK;
+ 
+ out:
+-    if (ctx)
+-        BN_CTX_free(ctx);
+-    if (ec_key != NULL)
+-        EC_KEY_free(ec_key);
++    if (ctx != NULL)
++        EVP_PKEY_CTX_free(ctx);
++#if !OPENSSL_VERSION_PREREQ(3, 0)
++    if (bnctx != NULL)
++        BN_CTX_free(bnctx);
++#else
++    if (bn_d != NULL)
++        BN_free(bn_d);
++#endif
++    if (ec_pkey != NULL)
++        EVP_PKEY_free(ec_pkey);
+     if (ecpoint != NULL)
+         OPENSSL_free(ecpoint);
+     if (enc_ecpoint != NULL)
+@@ -4621,11 +4700,15 @@ CK_RV token_specific_ec_sign(STDLL_TokData_t *tokdata,  SESSION *sess,
+                              CK_BYTE *out_data, CK_ULONG *out_data_len,
+                              OBJECT *key_obj)
+ {
+-    EC_KEY *ec_key;
+-    ECDSA_SIG *sig;
++    EVP_PKEY *ec_key;
++    ECDSA_SIG *sig = NULL;
+     const BIGNUM *r, *s;
+     CK_ULONG privlen, n;
+     CK_RV rc = CKR_OK;
++    EVP_PKEY_CTX *ctx = NULL;
++    size_t siglen;
++    CK_BYTE *sigbuf = NULL;
++    const unsigned char *p;
+ 
+     UNUSED(tokdata);
+     UNUSED(sess);
+@@ -4636,16 +4719,54 @@ CK_RV token_specific_ec_sign(STDLL_TokData_t *tokdata,  SESSION *sess,
+     if (rc != CKR_OK)
+         return rc;
+ 
+-    sig = ECDSA_do_sign(in_data, in_data_len, ec_key);
++    ctx = EVP_PKEY_CTX_new(ec_key, NULL);
++    if (ctx == NULL) {
++        TRACE_ERROR("EVP_PKEY_CTX_new failed\n");
++        rc = CKR_FUNCTION_FAILED;
++        goto out;
++    }
++
++    if (EVP_PKEY_sign_init(ctx) <= 0) {
++        TRACE_ERROR("EVP_PKEY_sign_init failed\n");
++        rc = CKR_FUNCTION_FAILED;
++        goto out;
++    }
++
++    if (EVP_PKEY_sign(ctx, NULL, &siglen, in_data, in_data_len) <= 0) {
++        TRACE_ERROR("EVP_PKEY_sign failed\n");
++        rc = CKR_FUNCTION_FAILED;
++        goto out;
++    }
++
++    sigbuf = malloc(siglen);
++    if (sigbuf == NULL) {
++        TRACE_ERROR("malloc failed\n");
++        rc = CKR_HOST_MEMORY;
++        goto out;
++    }
++
++    if (EVP_PKEY_sign(ctx, sigbuf, &siglen, in_data, in_data_len) <= 0) {
++        TRACE_ERROR("EVP_PKEY_sign failed\n");
++        rc = CKR_FUNCTION_FAILED;
++        goto out;
++    }
++
++    p = sigbuf;
++    sig = d2i_ECDSA_SIG(NULL, &p, siglen);
+     if (sig == NULL) {
+-        TRACE_ERROR("ECDSA_do_sign failed\n");
++        TRACE_ERROR("d2i_ECDSA_SIG failed\n");
+         rc = CKR_FUNCTION_FAILED;
+         goto out;
+     }
+ 
+     ECDSA_SIG_get0(sig, &r, &s);
+ 
+-    privlen = (EC_GROUP_order_bits(EC_KEY_get0_group(ec_key)) + 7) / 8;
++    privlen = ec_prime_len_from_pkey(ec_key);
++    if (privlen <= 0) {
++        TRACE_ERROR("ec_prime_len_from_pkey failed\n");
++        rc = CKR_FUNCTION_FAILED;
++        goto out;
++    }
+ 
+     /* Insert leading 0x00's if r or s shorter than privlen */
+     n = privlen - BN_num_bytes(r);
+@@ -4662,7 +4783,11 @@ out:
+     if (sig != NULL)
+         ECDSA_SIG_free(sig);
+     if (ec_key != NULL)
+-        EC_KEY_free(ec_key);
++        EVP_PKEY_free(ec_key);
++    if (sigbuf != NULL)
++        free(sigbuf);
++    if (ctx != NULL)
++        EVP_PKEY_CTX_free(ctx);
+ 
+     return rc;
+ }
+@@ -4674,11 +4799,14 @@ CK_RV token_specific_ec_verify(STDLL_TokData_t *tokdata,
+                                CK_BYTE *signature,
+                                CK_ULONG signature_len, OBJECT *key_obj)
+ {
+-    EC_KEY *ec_key;
++    EVP_PKEY *ec_key;
+     CK_ULONG privlen;
+     ECDSA_SIG *sig = NULL;
+     BIGNUM *r = NULL, *s = NULL;
+     CK_RV rc = CKR_OK;
++    size_t siglen;
++    CK_BYTE *sigbuf = NULL;
++    EVP_PKEY_CTX *ctx = NULL;
+ 
+     UNUSED(tokdata);
+     UNUSED(sess);
+@@ -4687,7 +4815,12 @@ CK_RV token_specific_ec_verify(STDLL_TokData_t *tokdata,
+     if (rc != CKR_OK)
+         return rc;
+ 
+-    privlen = (EC_GROUP_order_bits(EC_KEY_get0_group(ec_key)) + 7) / 8;
++    privlen = ec_prime_len_from_pkey(ec_key);
++    if (privlen <= 0) {
++        TRACE_ERROR("ec_prime_len_from_pkey failed\n");
++        rc = CKR_FUNCTION_FAILED;
++        goto out;
++    }
+ 
+     if (signature_len < 2 * privlen) {
+         TRACE_ERROR("Signature is too short\n");
+@@ -4715,7 +4848,27 @@ CK_RV token_specific_ec_verify(STDLL_TokData_t *tokdata,
+         goto out;
+     }
+ 
+-    rc = ECDSA_do_verify(in_data, in_data_len, sig, ec_key);
++    siglen = i2d_ECDSA_SIG(sig, &sigbuf);
++    if (siglen <= 0) {
++        TRACE_ERROR("i2d_ECDSA_SIG failed\n");
++        rc = CKR_FUNCTION_FAILED;
++        goto out;
++    }
++
++    ctx = EVP_PKEY_CTX_new(ec_key, NULL);
++    if (ctx == NULL) {
++        TRACE_ERROR("EVP_PKEY_CTX_new failed\n");
++        rc = CKR_FUNCTION_FAILED;
++        goto out;
++    }
++
++    if (EVP_PKEY_verify_init(ctx) <= 0) {
++        TRACE_ERROR("EVP_PKEY_verify_init failed\n");
++        rc = CKR_FUNCTION_FAILED;
++        goto out;
++    }
++
++    rc = EVP_PKEY_verify(ctx, sigbuf, siglen, in_data, in_data_len);
+     switch (rc) {
+     case 0:
+         rc = CKR_SIGNATURE_INVALID;
+@@ -4732,7 +4885,11 @@ out:
+     if (sig != NULL)
+         ECDSA_SIG_free(sig);
+     if (ec_key != NULL)
+-        EC_KEY_free(ec_key);
++        EVP_PKEY_free(ec_key);
++    if (sigbuf != NULL)
++        OPENSSL_free(sigbuf);
++    if (ctx != NULL)
++        EVP_PKEY_CTX_free(ctx);
+ 
+     return rc;
+ }
+@@ -4746,43 +4903,118 @@ CK_RV token_specific_ecdh_pkcs_derive(STDLL_TokData_t *tokdata,
+                                       CK_ULONG *secret_value_len,
+                                       CK_BYTE *oid, CK_ULONG oid_length)
+ {
+-    EC_KEY *ec_pub = NULL, *ec_priv = NULL;
+-    CK_ULONG privlen;
+-    int secret_len;
++#if !OPENSSL_VERSION_PREREQ(3, 0)
++    EC_KEY *pub = NULL, *priv = NULL;
++#else
++    OSSL_PARAM_BLD *tmpl = NULL;
++#endif
++    EVP_PKEY *ec_pub = NULL, *ec_priv = NULL;
++    EVP_PKEY_CTX *ctx = NULL;
++    size_t secret_len;
++    int nid;
+     CK_RV rc;
+ 
+     UNUSED(tokdata);
+ 
+-    rc = make_ec_key_from_params(oid, oid_length, &ec_priv);
++    nid = curve_nid_from_params(oid, oid_length);
++    if (nid == NID_undef) {
++        TRACE_ERROR("curve not supported by OpenSSL.\n");
++        rc = CKR_CURVE_NOT_SUPPORTED;
++        goto out;
++    }
++
++#if !OPENSSL_VERSION_PREREQ(3, 0)
++    rc = make_ec_key_from_params(oid, oid_length, &priv);
+     if (rc != CKR_OK) {
+         TRACE_DEVEL("make_ec_key_from_params failed\n");
+         goto out;
+     }
++#else
++    tmpl = OSSL_PARAM_BLD_new();
++    if (tmpl == NULL) {
++        TRACE_ERROR("OSSL_PARAM_BLD_new failed\n");
++        rc = CKR_HOST_MEMORY;
++        goto out;
++    }
++
++    if (!OSSL_PARAM_BLD_push_utf8_string(tmpl, OSSL_PKEY_PARAM_GROUP_NAME,
++                                         OBJ_nid2sn(nid), 0)) {
++        TRACE_ERROR("OSSL_PARAM_BLD_push_utf8_string failed\n");
++        rc = CKR_FUNCTION_FAILED;
++        goto out;
++    }
++#endif
+ 
+-    rc = fill_ec_key_from_privkey(ec_priv, priv_bytes, priv_length);
++#if !OPENSSL_VERSION_PREREQ(3, 0)
++    rc = fill_ec_key_from_privkey(priv, priv_bytes, priv_length, &ec_priv);
++#else
++    rc = fill_ec_key_from_privkey(tmpl, priv_bytes, priv_length, nid, &ec_priv);
++#endif
+     if (rc != CKR_OK) {
+         TRACE_DEVEL("fill_ec_key_from_privkey failed\n");
+         goto out;
+     }
++#if !OPENSSL_VERSION_PREREQ(3, 0)
++    priv = NULL;
++#else
++    OSSL_PARAM_BLD_free(tmpl);
++    tmpl = NULL;
++#endif
+ 
+-    rc = make_ec_key_from_params(oid, oid_length, &ec_pub);
++#if !OPENSSL_VERSION_PREREQ(3, 0)
++    rc = make_ec_key_from_params(oid, oid_length, &pub);
+     if (rc != CKR_OK) {
+         TRACE_DEVEL("make_ec_key_from_params failed\n");
+         goto out;
+     }
++#else
++    tmpl = OSSL_PARAM_BLD_new();
++    if (tmpl == NULL) {
++        TRACE_ERROR("OSSL_PARAM_BLD_new failed\n");
++        rc = CKR_HOST_MEMORY;
++        goto out;
++    }
++
++    if (!OSSL_PARAM_BLD_push_utf8_string(tmpl, OSSL_PKEY_PARAM_GROUP_NAME,
++                                         OBJ_nid2sn(nid), 0)) {
++        TRACE_ERROR("OSSL_PARAM_BLD_push_utf8_string failed\n");
++        rc = CKR_FUNCTION_FAILED;
++        goto out;
++    }
++#endif
+ 
+-    rc = fill_ec_key_from_pubkey(ec_pub, pub_bytes, pub_length, TRUE);
++#if !OPENSSL_VERSION_PREREQ(3, 0)
++    rc = fill_ec_key_from_pubkey(pub, pub_bytes, pub_length, TRUE, nid,
++                                 &ec_pub);
++#else
++    rc = fill_ec_key_from_pubkey(tmpl, pub_bytes, pub_length, TRUE, nid,
++                                 &ec_pub);
++#endif
+     if (rc != CKR_OK) {
+         TRACE_DEVEL("fill_ec_key_from_pubkey failed\n");
+         goto out;
+     }
++#if !OPENSSL_VERSION_PREREQ(3, 0)
++    pub = NULL;
++#else
++    OSSL_PARAM_BLD_free(tmpl);
++    tmpl = NULL;
++#endif
+ 
+-    privlen = (EC_GROUP_order_bits(EC_KEY_get0_group(ec_priv)) + 7) / 8;
++    ctx = EVP_PKEY_CTX_new(ec_priv, NULL);
++    if (ctx == NULL) {
++        TRACE_DEVEL("EVP_PKEY_CTX_new failed\n");
++        goto out;
++    }
+ 
+-    secret_len = ECDH_compute_key(secret_value, privlen,
+-                                  EC_KEY_get0_public_key(ec_pub), ec_priv,
+-                                  NULL);
+-    if (secret_len <= 0) {
++    if (EVP_PKEY_derive_init(ctx) <= 0 ||
++        EVP_PKEY_derive_set_peer(ctx, ec_pub) <= 0) {
++        TRACE_DEVEL("EVP_PKEY_derive_init/EVP_PKEY_derive_set_peer failed\n");
++        goto out;
++    }
++
++    secret_len = ec_prime_len_from_nid(nid);
++    if (EVP_PKEY_derive(ctx, secret_value, &secret_len) <= 0) {
+         TRACE_DEVEL("ECDH_compute_key failed\n");
+         rc = CKR_FUNCTION_FAILED;
+         *secret_value_len = 0;
+@@ -4792,10 +5024,21 @@ CK_RV token_specific_ecdh_pkcs_derive(STDLL_TokData_t *tokdata,
+     *secret_value_len = secret_len;
+ 
+ out:
++#if !OPENSSL_VERSION_PREREQ(3, 0)
++    if (priv != NULL)
++        EC_KEY_free(priv);
++    if (pub != NULL)
++        EC_KEY_free(pub);
++#else
++    if (tmpl != NULL)
++        OSSL_PARAM_BLD_free(tmpl);
++#endif
+     if (ec_priv != NULL)
+-        EC_KEY_free(ec_priv);
++        EVP_PKEY_free(ec_priv);
+     if (ec_pub != NULL)
+-        EC_KEY_free(ec_pub);
++        EVP_PKEY_free(ec_pub);
++    if (ctx != NULL)
++        EVP_PKEY_CTX_free(ctx);
+ 
+     return rc;
+ }
+@@ -4807,7 +5050,7 @@ CK_RV token_specific_object_add(STDLL_TokData_t * tokdata, SESSION * sess,
+ {
+     CK_KEY_TYPE keytype;
+ #ifndef NO_EC
+-    EC_KEY *ec_key = NULL;
++    EVP_PKEY *ec_key = NULL;
+ #endif
+     CK_RV rc;
+ 
+@@ -4824,7 +5067,7 @@ CK_RV token_specific_object_add(STDLL_TokData_t * tokdata, SESSION * sess,
+         /* Check if OpenSSL supports the curve */
+         rc = make_ec_key_from_template(obj->template, &ec_key);
+         if (ec_key != NULL)
+-                EC_KEY_free(ec_key);
++                EVP_PKEY_free(ec_key);
+         return rc;
+ #endif
+ 
diff --git a/SOURCES/opencryptoki-openssl3-533cdea6897d1bc0af13490f1c89248c52e7a73b.patch b/SOURCES/opencryptoki-openssl3-533cdea6897d1bc0af13490f1c89248c52e7a73b.patch
new file mode 100644
index 0000000..af4395d
--- /dev/null
+++ b/SOURCES/opencryptoki-openssl3-533cdea6897d1bc0af13490f1c89248c52e7a73b.patch
@@ -0,0 +1,147 @@
+commit 533cdea6897d1bc0af13490f1c89248c52e7a73b
+Author: Ingo Franzki <ifranzki@linux.ibm.com>
+Date:   Wed Jun 30 11:30:00 2021 +0200
+
+    COMMON: utilities.c: Remove deprecated OpenSSL functions
+    
+    Rework functions compute_sha(), compute_sha1(), and compute_md5() to
+    no longer use the mech_sha and mech_md5 routines, but to use the
+    OpenSSL EVP interface directly.
+    
+    Signed-off-by: Ingo Franzki <ifranzki@linux.ibm.com>
+
+diff --git a/usr/lib/common/utility.c b/usr/lib/common/utility.c
+index bcdc15bf..5fc68938 100644
+--- a/usr/lib/common/utility.c
++++ b/usr/lib/common/utility.c
+@@ -849,66 +849,89 @@ CK_RV get_hmac_digest(CK_ULONG mech, CK_ULONG *digest_mech, CK_BBOOL *general)
+     return CKR_OK;
+ }
+ 
+-/* Compute specified SHA using either software or token implementation */
++/* Compute specified SHA or MD5 using software */
+ CK_RV compute_sha(STDLL_TokData_t *tokdata, CK_BYTE *data, CK_ULONG len,
+                   CK_BYTE *hash, CK_ULONG mech)
+ {
+-    DIGEST_CONTEXT ctx;
+-    CK_ULONG hash_len;
+-    CK_RV rv;
++    const EVP_MD *md;
++    unsigned int hash_len;
+ 
+-    memset(&ctx, 0x0, sizeof(ctx));
+-    ctx.mech.mechanism = mech;
++    UNUSED(tokdata);
+ 
+-    rv = get_sha_size(mech, &hash_len);
+-    if (rv != CKR_OK)
+-        return rv;
++    switch (mech) {
++    case CKM_MD5:
++        hash_len = MD5_HASH_SIZE;
++        md = EVP_md5();
++        break;
++    case CKM_SHA_1:
++        hash_len = SHA1_HASH_SIZE;
++        md = EVP_sha1();
++        break;
++    case CKM_SHA224:
++    case CKM_SHA512_224:
++        hash_len = SHA224_HASH_SIZE;
++        md = EVP_sha224();
++        break;
++    case CKM_SHA256:
++    case CKM_SHA512_256:
++        hash_len = SHA256_HASH_SIZE;
++        md = EVP_sha256();
++        break;
++    case CKM_SHA384:
++        hash_len = SHA384_HASH_SIZE;
++        md = EVP_sha384();
++        break;
++    case CKM_SHA512:
++        hash_len = SHA512_HASH_SIZE;
++        md = EVP_sha512();
++        break;
++#ifdef NID_sha3_224
++    case CKM_IBM_SHA3_224:
++        hash_len = SHA3_224_HASH_SIZE;
++        md = EVP_sha3_224();
++        break;
++#endif
++#ifdef NID_sha3_256
++    case CKM_IBM_SHA3_256:
++        hash_len = SHA3_256_HASH_SIZE;
++        md = EVP_sha3_256();
++        break;
++#endif
++#ifdef NID_sha3_384
++    case CKM_IBM_SHA3_384:
++        hash_len = SHA3_384_HASH_SIZE;
++        md = EVP_sha3_384();
++        break;
++#endif
++#ifdef NID_sha3_512
++    case CKM_IBM_SHA3_512:
++        hash_len = SHA3_512_HASH_SIZE;
++        md = EVP_sha3_512();
++        break;
++#endif
++    default:
++        return CKR_MECHANISM_INVALID;
++    }
+ 
+-    rv = sha_init(tokdata, NULL, &ctx, &ctx.mech);
+-    if (rv != CKR_OK) {
+-        TRACE_DEBUG("failed to create digest.\n");
+-        return rv;
++    if (EVP_Digest(data, len, hash, &hash_len, md, NULL) != 1) {
++        TRACE_ERROR("%s EVP_Digest failed\n", __func__);
++        return CKR_FUNCTION_FAILED;
+     }
+-    rv = sha_hash(tokdata, NULL, FALSE, &ctx, data, len, hash, &hash_len);
+ 
+-    digest_mgr_cleanup(&ctx);
+-    return rv;
++    return CKR_OK;
+ }
+ 
+ /* Compute SHA1 using software implementation */
+ CK_RV compute_sha1(STDLL_TokData_t *tokdata, CK_BYTE *data, CK_ULONG len,
+                    CK_BYTE *hash)
+ {
+-    // XXX KEY
+-    DIGEST_CONTEXT ctx;
+-    CK_ULONG hash_len = SHA1_HASH_SIZE;
+-
+-    UNUSED(tokdata);
+-
+-    memset(&ctx, 0x0, sizeof(ctx));
+-
+-    sw_sha1_init(&ctx);
+-    if (ctx.context == NULL)
+-        return CKR_HOST_MEMORY;
+-
+-    return sw_sha1_hash(&ctx, data, len, hash, &hash_len);
++    return compute_sha(tokdata, data, len, hash, CKM_SHA_1);
+ }
+ 
+ CK_RV compute_md5(STDLL_TokData_t *tokdata, CK_BYTE *data, CK_ULONG len,
+                   CK_BYTE *hash)
+ {
+-    DIGEST_CONTEXT ctx;
+-    CK_ULONG hash_len = MD5_HASH_SIZE;
+-
+-    UNUSED(tokdata);
+-
+-    memset(&ctx, 0x0, sizeof(ctx));
+-
+-    sw_md5_init(&ctx);
+-    if (ctx.context == NULL)
+-        return CKR_HOST_MEMORY;
+-
+-    return sw_md5_hash(&ctx, data, len, hash, &hash_len);
++    return compute_sha(tokdata, data, len, hash, CKM_MD5);
+ }
+ 
+ CK_RV get_keytype(STDLL_TokData_t *tokdata, CK_OBJECT_HANDLE hkey,
diff --git a/SOURCES/opencryptoki-openssl3-5377d25a6cbe3d07afcd08276ad7e90f62cad0c9.patch b/SOURCES/opencryptoki-openssl3-5377d25a6cbe3d07afcd08276ad7e90f62cad0c9.patch
new file mode 100644
index 0000000..165a69b
--- /dev/null
+++ b/SOURCES/opencryptoki-openssl3-5377d25a6cbe3d07afcd08276ad7e90f62cad0c9.patch
@@ -0,0 +1,174 @@
+commit 5377d25a6cbe3d07afcd08276ad7e90f62cad0c9
+Author: Ingo Franzki <ifranzki@linux.ibm.com>
+Date:   Wed Jun 30 13:51:02 2021 +0200
+
+    COMMON: mech_sha: Remove deprecated OpenSSL functions
+    
+    All low level SHA functions are deprecated in OpenSSL 3.0.
+    Update the code to not use any of those.
+    
+    Signed-off-by: Ingo Franzki <ifranzki@linux.ibm.com>
+
+diff --git a/usr/lib/common/h_extern.h b/usr/lib/common/h_extern.h
+index 314613a5..b3b965bf 100644
+--- a/usr/lib/common/h_extern.h
++++ b/usr/lib/common/h_extern.h
+@@ -1543,7 +1543,7 @@ CK_RV aes_cfb_decrypt_final(STDLL_TokData_t *tokdata, SESSION *sess,
+ // SHA mechanisms
+ //
+ 
+-void sw_sha1_init(DIGEST_CONTEXT *ctx);
++CK_RV sw_sha1_init(DIGEST_CONTEXT *ctx);
+ 
+ CK_RV sw_sha1_hash(DIGEST_CONTEXT *ctx, CK_BYTE *in_data,
+                    CK_ULONG in_data_len, CK_BYTE *out_data,
+diff --git a/usr/lib/common/mech_sha.c b/usr/lib/common/mech_sha.c
+index 0b9b7b28..1c81abe2 100644
+--- a/usr/lib/common/mech_sha.c
++++ b/usr/lib/common/mech_sha.c
+@@ -38,30 +38,49 @@
+ #include "tok_spec_struct.h"
+ #include "trace.h"
+ 
+-#include <openssl/sha.h>
++#include <openssl/evp.h>
+ #include <openssl/crypto.h>
+ 
+ //
+ // Software SHA-1 implementation (OpenSSL based)
+ //
+ 
+-void sw_sha1_init(DIGEST_CONTEXT *ctx)
++static void sw_sha1_free(STDLL_TokData_t *tokdata, SESSION *sess,
++                         CK_BYTE *context, CK_ULONG context_len)
+ {
+-    ctx->context_len = sizeof(SHA_CTX);
+-    ctx->context = (CK_BYTE *) malloc(sizeof(SHA_CTX));
++    UNUSED(tokdata);
++    UNUSED(sess);
++    UNUSED(context_len);
++
++    EVP_MD_CTX_free((EVP_MD_CTX *)context);
++}
++
++CK_RV sw_sha1_init(DIGEST_CONTEXT *ctx)
++{
++    ctx->context_len = 1;
++    ctx->context = (CK_BYTE *)EVP_MD_CTX_new();
+     if (ctx->context == NULL) {
+         TRACE_ERROR("%s\n", ock_err(ERR_HOST_MEMORY));
+-        // TODO: propagate error up?
+-        return;
++        return CKR_HOST_MEMORY;
++    }
++
++    if (!EVP_DigestInit_ex((EVP_MD_CTX *)ctx->context, EVP_sha1(), NULL)) {
++        TRACE_ERROR("%s\n", ock_err(ERR_FUNCTION_FAILED));
++        EVP_MD_CTX_free((EVP_MD_CTX *)ctx->context);
++        return CKR_FUNCTION_FAILED;
+     }
+ 
+-    SHA1_Init((SHA_CTX *)ctx->context);
++    ctx->state_unsaveable = CK_TRUE;
++    ctx->context_free_func = sw_sha1_free;
++
++    return CKR_OK;
+ }
+ 
+ CK_RV sw_sha1_hash(DIGEST_CONTEXT *ctx, CK_BYTE *in_data,
+                    CK_ULONG in_data_len, CK_BYTE *out_data,
+                    CK_ULONG *out_data_len)
+ {
++    unsigned int len;
+ 
+     if (!ctx || !out_data_len) {
+         TRACE_ERROR("%s received bad argument(s)\n", __func__);
+@@ -76,43 +95,60 @@ CK_RV sw_sha1_hash(DIGEST_CONTEXT *ctx, CK_BYTE *in_data,
+     if (ctx->context == NULL)
+         return CKR_OPERATION_NOT_INITIALIZED;
+ 
+-    SHA1_Update((SHA_CTX *)ctx->context, in_data, in_data_len);
+-    SHA1_Final(out_data, (SHA_CTX *)ctx->context);
+-    *out_data_len = SHA1_HASH_SIZE;
++    len = *out_data_len;
++    if (!EVP_DigestUpdate((EVP_MD_CTX *)ctx->context, in_data, in_data_len) ||
++        !EVP_DigestFinal((EVP_MD_CTX *)ctx->context, out_data, &len)) {
++        TRACE_ERROR("%s\n", ock_err(ERR_FUNCTION_FAILED));
++        return CKR_FUNCTION_FAILED;
++    }
++
++    *out_data_len = len;
+ 
+-    if (ctx->context_free_func != NULL)
+-        ctx->context_free_func(ctx->context, ctx->context_len);
+-    else
+-        free(ctx->context);
++    EVP_MD_CTX_free((EVP_MD_CTX *)ctx->context);
+     ctx->context = NULL;
++    ctx->context_free_func = NULL;
+ 
+     return CKR_OK;
+ }
+ 
+-CK_RV sw_sha1_update(DIGEST_CONTEXT *ctx, CK_BYTE *in_data,
+-                     CK_ULONG in_data_len)
++static CK_RV sw_sha1_update(DIGEST_CONTEXT *ctx, CK_BYTE *in_data,
++                            CK_ULONG in_data_len)
+ {
+     if (ctx->context == NULL)
+         return CKR_OPERATION_NOT_INITIALIZED;
+ 
+-    SHA1_Update((SHA_CTX *)ctx->context, in_data, in_data_len);
++    if (!EVP_DigestUpdate((EVP_MD_CTX *)ctx->context, in_data, in_data_len)) {
++        TRACE_ERROR("%s\n", ock_err(ERR_FUNCTION_FAILED));
++        return CKR_FUNCTION_FAILED;
++    }
++
+     return CKR_OK;
+ }
+ 
+-CK_RV sw_sha1_final(DIGEST_CONTEXT *ctx, CK_BYTE *out_data,
+-                    CK_ULONG *out_data_len)
++static CK_RV sw_sha1_final(DIGEST_CONTEXT *ctx, CK_BYTE *out_data,
++                           CK_ULONG *out_data_len)
+ {
++    unsigned int len;
++
+     if (ctx->context == NULL)
+         return CKR_OPERATION_NOT_INITIALIZED;
+ 
+-    SHA1_Final(out_data, (SHA_CTX *)ctx->context);
+-    *out_data_len = SHA1_HASH_SIZE;
++    if (*out_data_len < SHA1_HASH_SIZE) {
++        TRACE_ERROR("%s\n", ock_err(ERR_BUFFER_TOO_SMALL));
++        return CKR_BUFFER_TOO_SMALL;
++    }
++
++    len = *out_data_len;
++    if (!EVP_DigestFinal((EVP_MD_CTX *)ctx->context, out_data, &len)) {
++        TRACE_ERROR("%s\n", ock_err(ERR_FUNCTION_FAILED));
++        return CKR_FUNCTION_FAILED;
++    }
++
++    *out_data_len = len;
+ 
+-    if (ctx->context_free_func != NULL)
+-        ctx->context_free_func(ctx->context, ctx->context_len);
+-    else
+-        free(ctx->context);
++    EVP_MD_CTX_free((EVP_MD_CTX *)ctx->context);
+     ctx->context = NULL;
++    ctx->context_free_func = NULL;
+ 
+     return CKR_OK;
+ }
+@@ -134,8 +170,7 @@ CK_RV sha_init(STDLL_TokData_t *tokdata, SESSION *sess, DIGEST_CONTEXT *ctx,
+          *  supported. JML
+          */
+         if (mech->mechanism == CKM_SHA_1) {
+-            sw_sha1_init(ctx);
+-            return CKR_OK;
++            return sw_sha1_init(ctx);
+         } else {
+             return CKR_MECHANISM_INVALID;
+         }
diff --git a/SOURCES/opencryptoki-openssl3-5cceead028ec8e0c244b01d38c9096c96d98f96b.patch b/SOURCES/opencryptoki-openssl3-5cceead028ec8e0c244b01d38c9096c96d98f96b.patch
new file mode 100644
index 0000000..0cfe159
--- /dev/null
+++ b/SOURCES/opencryptoki-openssl3-5cceead028ec8e0c244b01d38c9096c96d98f96b.patch
@@ -0,0 +1,84 @@
+commit 5cceead028ec8e0c244b01d38c9096c96d98f96b
+Author: Ingo Franzki <ifranzki@linux.ibm.com>
+Date:   Mon Jul 5 10:46:52 2021 +0200
+
+    ICSF: Remove support for OpenSSL < v1.1.1
+    
+    Signed-off-by: Ingo Franzki <ifranzki@linux.ibm.com>
+
+diff --git a/usr/lib/icsf_stdll/pbkdf.c b/usr/lib/icsf_stdll/pbkdf.c
+index 4ddd0fd7..6ec4128a 100644
+--- a/usr/lib/icsf_stdll/pbkdf.c
++++ b/usr/lib/icsf_stdll/pbkdf.c
+@@ -82,7 +82,6 @@ CK_RV encrypt_aes(CK_BYTE * inbuf, int inbuflen, CK_BYTE * dkey,
+     const EVP_CIPHER *cipher = EVP_aes_256_cbc();
+     int tmplen;
+ 
+-#if OPENSSL_VERSION_NUMBER >= 0x10100000L
+     EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new();
+ 
+     EVP_EncryptInit_ex(ctx, cipher, NULL, dkey, iv);
+@@ -98,24 +97,6 @@ CK_RV encrypt_aes(CK_BYTE * inbuf, int inbuflen, CK_BYTE * dkey,
+     *outbuflen = (*outbuflen) + tmplen;
+     EVP_CIPHER_CTX_free(ctx);
+ 
+-#else
+-    EVP_CIPHER_CTX ctx;
+-    EVP_CIPHER_CTX_init(&ctx);
+-
+-    EVP_EncryptInit_ex(&ctx, cipher, NULL, dkey, iv);
+-    if (!EVP_EncryptUpdate(&ctx, outbuf, outbuflen, inbuf, inbuflen)) {
+-        TRACE_ERROR("EVP_EncryptUpdate failed.\n");
+-        return CKR_FUNCTION_FAILED;
+-    }
+-    if (!EVP_EncryptFinal_ex(&ctx, outbuf + (*outbuflen), &tmplen)) {
+-        TRACE_ERROR("EVP_EncryptFinal failed.\n");
+-        return CKR_FUNCTION_FAILED;
+-    }
+-
+-    *outbuflen = (*outbuflen) + tmplen;
+-    EVP_CIPHER_CTX_cleanup(&ctx);
+-#endif
+-
+     return CKR_OK;
+ }
+ 
+@@ -125,7 +106,6 @@ CK_RV decrypt_aes(CK_BYTE * inbuf, int inbuflen, CK_BYTE * dkey,
+     int size;
+     const EVP_CIPHER *cipher = EVP_aes_256_cbc();
+ 
+-#if OPENSSL_VERSION_NUMBER >= 0x10100000L
+     EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new();
+ 
+     EVP_DecryptInit_ex(ctx, cipher, NULL, dkey, iv);
+@@ -147,30 +127,6 @@ CK_RV decrypt_aes(CK_BYTE * inbuf, int inbuflen, CK_BYTE * dkey,
+ 
+     EVP_CIPHER_CTX_free(ctx);
+ 
+-#else
+-    EVP_CIPHER_CTX ctx;
+-    EVP_CIPHER_CTX_init(&ctx);
+-
+-    EVP_DecryptInit_ex(&ctx, cipher, NULL, dkey, iv);
+-    if (!EVP_DecryptUpdate(&ctx, outbuf, outbuflen, inbuf, inbuflen)) {
+-        TRACE_ERROR("EVP_DecryptUpdate failed.\n");
+-        return CKR_FUNCTION_FAILED;
+-    }
+-    if (!EVP_DecryptFinal_ex(&ctx, outbuf + (*outbuflen), &size)) {
+-        TRACE_ERROR("EVP_DecryptFinal failed.\n");
+-        return CKR_FUNCTION_FAILED;
+-    }
+-
+-    /* total length of the decrypted data */
+-    *outbuflen = (*outbuflen) + size;
+-
+-    /* EVP_DecryptFinal removes any padding. The final length
+-     * is the length of the decrypted data without padding.
+-     */
+-
+-    EVP_CIPHER_CTX_cleanup(&ctx);
+-#endif
+-
+     return CKR_OK;
+ }
+ 
diff --git a/SOURCES/opencryptoki-openssl3-62fc2bcd98672c5d0ff8a2c926f3103110e91ed7.patch b/SOURCES/opencryptoki-openssl3-62fc2bcd98672c5d0ff8a2c926f3103110e91ed7.patch
new file mode 100644
index 0000000..5721c2f
--- /dev/null
+++ b/SOURCES/opencryptoki-openssl3-62fc2bcd98672c5d0ff8a2c926f3103110e91ed7.patch
@@ -0,0 +1,226 @@
+commit 62fc2bcd98672c5d0ff8a2c926f3103110e91ed7
+Author: Ingo Franzki <ifranzki@linux.ibm.com>
+Date:   Thu Jul 1 13:37:04 2021 +0200
+
+    COMMON: Perform proper context cleanup for 3DES/AES CMAC mechanisms
+    
+    The handling of 3DES/AES CMAC mechanisms use a complex context structure,
+    that contains pointers. Such state can not be saved, and needs a custom
+    context free routine to properly clean up the context.
+    
+    Signed-off-by: Ingo Franzki <ifranzki@linux.ibm.com>
+
+diff --git a/usr/lib/common/mech_aes.c b/usr/lib/common/mech_aes.c
+index ad6af16b..59f82482 100644
+--- a/usr/lib/common/mech_aes.c
++++ b/usr/lib/common/mech_aes.c
+@@ -2691,6 +2691,24 @@ CK_RV aes_mac_verify_final(STDLL_TokData_t *tokdata,
+     return CKR_SIGNATURE_INVALID;
+ }
+ 
++static void aes_cmac_cleanup(STDLL_TokData_t *tokdata, SESSION *sess,
++                             CK_BYTE *context, CK_ULONG context_len)
++{
++    UNUSED(tokdata);
++    UNUSED(sess);
++    UNUSED(context_len);
++
++    if (((AES_CMAC_CONTEXT *)context)->ctx != NULL) {
++        token_specific.t_aes_cmac(tokdata, (CK_BYTE *)"", 0, NULL,
++                                  ((AES_CMAC_CONTEXT *)context)->iv,
++                                  CK_FALSE, CK_TRUE,
++                                  ((AES_CMAC_CONTEXT *)context)->ctx);
++        ((AES_CMAC_CONTEXT *)context)->ctx = NULL;
++    }
++
++    free(context);
++}
++
+ CK_RV aes_cmac_sign(STDLL_TokData_t *tokdata,
+                     SESSION *sess,
+                     CK_BBOOL length_only,
+@@ -2743,6 +2761,8 @@ CK_RV aes_cmac_sign(STDLL_TokData_t *tokdata,
+     if (((AES_CMAC_CONTEXT *)ctx->context)->ctx != NULL)
+         ctx->state_unsaveable = CK_TRUE;
+ 
++    ctx->context_free_func = aes_cmac_cleanup;
++
+     memcpy(out_data, ((AES_CMAC_CONTEXT *) ctx->context)->iv, mac_len);
+     *out_data_len = mac_len;
+ 
+@@ -2816,6 +2836,8 @@ CK_RV aes_cmac_sign_update(STDLL_TokData_t *tokdata,
+ 
+             if (context->ctx != NULL)
+                 ctx->state_unsaveable = CK_TRUE;
++
++            ctx->context_free_func = aes_cmac_cleanup;
+         } else {
+             TRACE_DEVEL("Token specific aes cmac failed.\n");
+         }
+@@ -2882,6 +2904,8 @@ CK_RV aes_cmac_sign_final(STDLL_TokData_t *tokdata,
+     if (context->ctx != NULL)
+         ctx->state_unsaveable = CK_TRUE;
+ 
++    ctx->context_free_func = aes_cmac_cleanup;
++
+     memcpy(out_data, context->iv, mac_len);
+     *out_data_len = mac_len;
+ 
+@@ -2941,6 +2965,8 @@ CK_RV aes_cmac_verify(STDLL_TokData_t *tokdata,
+     if (((AES_CMAC_CONTEXT *)ctx->context)->ctx != NULL)
+         ctx->state_unsaveable = CK_TRUE;
+ 
++    ctx->context_free_func = aes_cmac_cleanup;
++
+     if (CRYPTO_memcmp(out_data, ((AES_CMAC_CONTEXT *) ctx->context)->iv,
+                       out_data_len) == 0) {
+         return CKR_OK;
+@@ -3012,6 +3038,8 @@ CK_RV aes_cmac_verify_update(STDLL_TokData_t *tokdata,
+ 
+             if (context->ctx != NULL)
+                 ctx->state_unsaveable = CK_TRUE;
++
++            ctx->context_free_func = aes_cmac_cleanup;
+         } else {
+             TRACE_DEVEL("Token specific aes cmac failed.\n");
+         }
+@@ -3070,6 +3098,8 @@ CK_RV aes_cmac_verify_final(STDLL_TokData_t *tokdata,
+     if (context->ctx != NULL)
+         ctx->state_unsaveable = CK_TRUE;
+ 
++    ctx->context_free_func = aes_cmac_cleanup;
++
+     if (rc != CKR_OK) {
+         TRACE_DEVEL("Token specific aes mac failed.\n");
+         return rc;
+diff --git a/usr/lib/common/mech_des3.c b/usr/lib/common/mech_des3.c
+index be8d6075..591ad3fa 100644
+--- a/usr/lib/common/mech_des3.c
++++ b/usr/lib/common/mech_des3.c
+@@ -2334,6 +2334,24 @@ CK_RV des3_mac_verify_final(STDLL_TokData_t *tokdata,
+     return CKR_SIGNATURE_INVALID;
+ }
+ 
++static void des3_cmac_cleanup(STDLL_TokData_t *tokdata, SESSION *sess,
++                              CK_BYTE *context, CK_ULONG context_len)
++{
++    UNUSED(tokdata);
++    UNUSED(sess);
++    UNUSED(context_len);
++
++    if (((DES_CMAC_CONTEXT *)context)->ctx != NULL) {
++        token_specific.t_tdes_cmac(tokdata, (CK_BYTE *)"", 0, NULL,
++                                   ((DES_CMAC_CONTEXT *)context)->iv,
++                                   CK_FALSE, CK_TRUE,
++                                   ((DES_CMAC_CONTEXT *)context)->ctx);
++        ((DES_CMAC_CONTEXT *)context)->ctx = NULL;
++    }
++
++    free(context);
++}
++
+ CK_RV des3_cmac_sign(STDLL_TokData_t *tokdata,
+                      SESSION *sess,
+                      CK_BBOOL length_only,
+@@ -2383,6 +2401,8 @@ CK_RV des3_cmac_sign(STDLL_TokData_t *tokdata,
+     if (((DES_CMAC_CONTEXT *)ctx->context)->ctx != NULL)
+         ctx->state_unsaveable = CK_TRUE;
+ 
++    ctx->context_free_func = des3_cmac_cleanup;
++
+     memcpy(out_data, ((DES_CMAC_CONTEXT *) ctx->context)->iv, mac_len);
+ 
+     *out_data_len = mac_len;
+@@ -2456,6 +2476,8 @@ CK_RV des3_cmac_sign_update(STDLL_TokData_t *tokdata,
+ 
+             if (context->ctx != NULL)
+                 ctx->state_unsaveable = CK_TRUE;
++
++            ctx->context_free_func = des3_cmac_cleanup;
+         } else {
+             TRACE_DEVEL("Token specific des3 cmac failed.\n");
+         }
+@@ -2521,6 +2543,8 @@ CK_RV des3_cmac_sign_final(STDLL_TokData_t *tokdata,
+     if (context->ctx != NULL)
+         ctx->state_unsaveable = CK_TRUE;
+ 
++    ctx->context_free_func = des3_cmac_cleanup;
++
+     memcpy(out_data, context->iv, mac_len);
+ 
+     *out_data_len = mac_len;
+@@ -2577,6 +2601,8 @@ CK_RV des3_cmac_verify(STDLL_TokData_t *tokdata,
+     if (((DES_CMAC_CONTEXT *)ctx->context)->ctx != NULL)
+         ctx->state_unsaveable = CK_TRUE;
+ 
++    ctx->context_free_func = des3_cmac_cleanup;
++
+     if (CRYPTO_memcmp(out_data, ((DES_CMAC_CONTEXT *) ctx->context)->iv,
+                       out_data_len) == 0) {
+         return CKR_OK;
+@@ -2646,6 +2672,8 @@ CK_RV des3_cmac_verify_update(STDLL_TokData_t *tokdata,
+ 
+             if (context->ctx != NULL)
+                 ctx->state_unsaveable = CK_TRUE;
++
++            ctx->context_free_func = des3_cmac_cleanup;
+         } else {
+             TRACE_DEVEL("Token specific des3 cmac failed.\n");
+         }
+@@ -2709,6 +2737,8 @@ CK_RV des3_cmac_verify_final(STDLL_TokData_t *tokdata,
+     if (context->ctx != NULL)
+         ctx->state_unsaveable = CK_TRUE;
+ 
++    ctx->context_free_func = des3_cmac_cleanup;
++
+     if (CRYPTO_memcmp(signature, context->iv, signature_len) == 0)
+         return CKR_OK;
+ 
+diff --git a/usr/lib/ica_s390_stdll/ica_specific.c b/usr/lib/ica_s390_stdll/ica_specific.c
+index 77876467..881a430c 100644
+--- a/usr/lib/ica_s390_stdll/ica_specific.c
++++ b/usr/lib/ica_s390_stdll/ica_specific.c
+@@ -713,6 +713,9 @@ CK_RV token_specific_tdes_cmac(STDLL_TokData_t *tokdata, CK_BYTE *message,
+     UNUSED(tokdata);
+     UNUSED(ctx);
+ 
++    if (key == NULL)
++        return CKR_ARGUMENTS_BAD;
++
+     // get the key type
+     rc = template_attribute_get_ulong(key->template, CKA_KEY_TYPE, &keytype);
+     if (rc != CKR_OK) {
+@@ -3621,6 +3624,9 @@ CK_RV token_specific_aes_cmac(STDLL_TokData_t *tokdata, CK_BYTE *message,
+     UNUSED(tokdata);
+     UNUSED(ctx);
+ 
++    if (key == NULL)
++        return CKR_ARGUMENTS_BAD;
++
+     rc = template_attribute_get_non_empty(key->template, CKA_VALUE, &attr);
+     if (rc != CKR_OK) {
+         TRACE_ERROR("Could not find CKA_VALUE for the key.\n");
+diff --git a/usr/lib/soft_stdll/soft_specific.c b/usr/lib/soft_stdll/soft_specific.c
+index aeff39a9..5ca22693 100644
+--- a/usr/lib/soft_stdll/soft_specific.c
++++ b/usr/lib/soft_stdll/soft_specific.c
+@@ -3994,6 +3994,9 @@ CK_RV token_specific_tdes_cmac(STDLL_TokData_t *tokdata, CK_BYTE *message,
+     UNUSED(tokdata);
+ 
+     if (first) {
++        if (key == NULL)
++            return CKR_ARGUMENTS_BAD;
++
+         // get the key type
+         rv = template_attribute_get_ulong(key->template, CKA_KEY_TYPE, &keytype);
+         if (rv != CKR_OK) {
+@@ -4194,6 +4197,9 @@ CK_RV token_specific_aes_cmac(STDLL_TokData_t *tokdata, CK_BYTE *message,
+     UNUSED(tokdata);
+ 
+     if (first) {
++        if (key == NULL)
++            return CKR_ARGUMENTS_BAD;
++
+         // get the key value
+         rc = template_attribute_get_non_empty(key->template, CKA_VALUE, &attr);
+         if (rc != CKR_OK) {
diff --git a/SOURCES/opencryptoki-openssl3-6fee37f08391415cdf8d8610c501516c3d3ed29c.patch b/SOURCES/opencryptoki-openssl3-6fee37f08391415cdf8d8610c501516c3d3ed29c.patch
new file mode 100644
index 0000000..516b513
--- /dev/null
+++ b/SOURCES/opencryptoki-openssl3-6fee37f08391415cdf8d8610c501516c3d3ed29c.patch
@@ -0,0 +1,193 @@
+commit 6fee37f08391415cdf8d8610c501516c3d3ed29c
+Author: Ingo Franzki <ifranzki@linux.ibm.com>
+Date:   Wed Jun 30 13:41:57 2021 +0200
+
+    COMMON: mech_md5: Remove deprecated OpenSSL functions
+    
+    All low level MD5 functions are deprecated in OpenSSL 3.0.
+    Update the code to not use any of those.
+    
+    Signed-off-by: Ingo Franzki <ifranzki@linux.ibm.com>
+
+diff --git a/usr/lib/common/h_extern.h b/usr/lib/common/h_extern.h
+index 47b96ba0..314613a5 100644
+--- a/usr/lib/common/h_extern.h
++++ b/usr/lib/common/h_extern.h
+@@ -1667,7 +1667,7 @@ CK_RV md5_hmac_verify(STDLL_TokData_t *tokdata,
+                       CK_ULONG in_data_len,
+                       CK_BYTE *signature, CK_ULONG sig_len);
+ 
+-void sw_md5_init(DIGEST_CONTEXT *ctx);
++CK_RV sw_md5_init(DIGEST_CONTEXT *ctx);
+ 
+ CK_RV sw_md5_hash(DIGEST_CONTEXT *ctx, CK_BYTE *in_data,
+                   CK_ULONG in_data_len, CK_BYTE *out_data,
+diff --git a/usr/lib/common/mech_md5.c b/usr/lib/common/mech_md5.c
+index 320e2549..65c11def 100644
+--- a/usr/lib/common/mech_md5.c
++++ b/usr/lib/common/mech_md5.c
+@@ -20,30 +20,50 @@
+ #include "tok_spec_struct.h"
+ #include "trace.h"
+ 
+-#include <openssl/md5.h>
++#include <openssl/evp.h>
+ #include <openssl/crypto.h>
+ 
+ //
+ // Software MD5 implementation (OpenSSL based)
+ //
+ 
+-void sw_md5_init(DIGEST_CONTEXT *ctx)
++static void sw_md5_free(STDLL_TokData_t *tokdata, SESSION *sess,
++                        CK_BYTE *context, CK_ULONG context_len)
+ {
+-    ctx->context_len = sizeof(MD5_CTX);
+-    ctx->context = (CK_BYTE *) malloc(sizeof(MD5_CTX));
++    UNUSED(tokdata);
++    UNUSED(sess);
++    UNUSED(context_len);
++
++    EVP_MD_CTX_free((EVP_MD_CTX *)context);
++}
++
++CK_RV sw_md5_init(DIGEST_CONTEXT *ctx)
++{
++    ctx->context_len = 1;
++    ctx->context = (CK_BYTE *)EVP_MD_CTX_new();
+     if (ctx->context == NULL) {
+         TRACE_ERROR("%s\n", ock_err(ERR_HOST_MEMORY));
+-        // TODO: propagate error up?
+-        return;
++        return CKR_HOST_MEMORY;
++    }
++
++    if (!EVP_DigestInit_ex((EVP_MD_CTX *)ctx->context, EVP_md5(), NULL)) {
++        TRACE_ERROR("%s\n", ock_err(ERR_FUNCTION_FAILED));
++        EVP_MD_CTX_free((EVP_MD_CTX *)ctx->context);
++        return CKR_FUNCTION_FAILED;
+     }
+ 
+-    MD5_Init((MD5_CTX *)ctx->context);
++    ctx->state_unsaveable = CK_TRUE;
++    ctx->context_free_func = sw_md5_free;
++
++    return CKR_OK;
+ }
+ 
+ CK_RV sw_md5_hash(DIGEST_CONTEXT *ctx, CK_BYTE *in_data,
+                   CK_ULONG in_data_len, CK_BYTE *out_data,
+                   CK_ULONG *out_data_len)
+ {
++    unsigned int len;
++
+     if (!ctx || !out_data_len) {
+         TRACE_ERROR("%s received bad argument(s)\n", __func__);
+         return CKR_FUNCTION_FAILED;
+@@ -57,43 +77,60 @@ CK_RV sw_md5_hash(DIGEST_CONTEXT *ctx, CK_BYTE *in_data,
+     if (ctx->context == NULL)
+         return CKR_OPERATION_NOT_INITIALIZED;
+ 
+-    MD5_Update((MD5_CTX *)ctx->context, in_data, in_data_len);
+-    MD5_Final(out_data, (MD5_CTX *)ctx->context);
+-    *out_data_len = MD5_HASH_SIZE;
++    len = *out_data_len;
++    if (!EVP_DigestUpdate((EVP_MD_CTX *)ctx->context, in_data, in_data_len) ||
++        !EVP_DigestFinal((EVP_MD_CTX *)ctx->context, out_data, &len)) {
++        TRACE_ERROR("%s\n", ock_err(ERR_FUNCTION_FAILED));
++        return CKR_FUNCTION_FAILED;
++    }
+ 
+-    if (ctx->context_free_func != NULL)
+-        ctx->context_free_func(ctx->context, ctx->context_len);
+-    else
+-        free(ctx->context);
++    *out_data_len = len;
++
++    EVP_MD_CTX_free((EVP_MD_CTX *)ctx->context);
+     ctx->context = NULL;
++    ctx->context_free_func = NULL;
+ 
+     return CKR_OK;
+ }
+ 
+-CK_RV sw_MD5_Update(DIGEST_CONTEXT *ctx, CK_BYTE *in_data,
+-                     CK_ULONG in_data_len)
++static CK_RV sw_md5_update(DIGEST_CONTEXT *ctx, CK_BYTE *in_data,
++                           CK_ULONG in_data_len)
+ {
+     if (ctx->context == NULL)
+         return CKR_OPERATION_NOT_INITIALIZED;
+ 
+-    MD5_Update((MD5_CTX *)ctx->context, in_data, in_data_len);
++    if (!EVP_DigestUpdate((EVP_MD_CTX *)ctx->context, in_data, in_data_len)) {
++        TRACE_ERROR("%s\n", ock_err(ERR_FUNCTION_FAILED));
++        return CKR_FUNCTION_FAILED;
++    }
++
+     return CKR_OK;
+ }
+ 
+-CK_RV sw_MD5_Final(DIGEST_CONTEXT *ctx, CK_BYTE *out_data,
+-                    CK_ULONG *out_data_len)
++static CK_RV sw_md5_final(DIGEST_CONTEXT *ctx, CK_BYTE *out_data,
++                          CK_ULONG *out_data_len)
+ {
++    unsigned int len;
++
+     if (ctx->context == NULL)
+         return CKR_OPERATION_NOT_INITIALIZED;
+ 
+-    MD5_Final(out_data, (MD5_CTX *)ctx->context);
+-    *out_data_len = MD5_HASH_SIZE;
++    if (*out_data_len < MD5_HASH_SIZE) {
++        TRACE_ERROR("%s\n", ock_err(ERR_BUFFER_TOO_SMALL));
++        return CKR_BUFFER_TOO_SMALL;
++    }
+ 
+-    if (ctx->context_free_func != NULL)
+-        ctx->context_free_func(ctx->context, ctx->context_len);
+-    else
+-        free(ctx->context);
++    len = *out_data_len;
++    if (!EVP_DigestFinal((EVP_MD_CTX *)ctx->context, out_data, &len)) {
++        TRACE_ERROR("%s\n", ock_err(ERR_FUNCTION_FAILED));
++        return CKR_FUNCTION_FAILED;
++    }
++
++    *out_data_len = len;
++
++    EVP_MD_CTX_free((EVP_MD_CTX *)ctx->context);
+     ctx->context = NULL;
++    ctx->context_free_func = NULL;
+ 
+     return CKR_OK;
+ }
+@@ -105,8 +142,7 @@ CK_RV md5_init(STDLL_TokData_t *tokdata, SESSION *sess, DIGEST_CONTEXT *ctx,
+     UNUSED(sess);
+ 
+     if (mech->mechanism == CKM_MD5) {
+-        sw_md5_init(ctx);
+-        return CKR_OK;
++        return sw_md5_init(ctx);
+     } else {
+         return CKR_MECHANISM_INVALID;
+     }
+@@ -159,7 +195,7 @@ CK_RV md5_hash_update(STDLL_TokData_t *tokdata, SESSION *sess,
+         return CKR_OK;
+ 
+     if (ctx->mech.mechanism == CKM_MD5)
+-        return sw_MD5_Update(ctx, in_data, in_data_len);
++        return sw_md5_update(ctx, in_data, in_data_len);
+     else
+         return CKR_MECHANISM_INVALID;
+ }
+@@ -188,7 +224,7 @@ CK_RV md5_hash_final(STDLL_TokData_t *tokdata, SESSION *sess,
+     }
+ 
+     if (ctx->mech.mechanism == CKM_MD5)
+-        return sw_MD5_Final(ctx, out_data, out_data_len);
++        return sw_md5_final(ctx, out_data, out_data_len);
+     else
+         return CKR_MECHANISM_INVALID;
+ }
diff --git a/SOURCES/opencryptoki-openssl3-7a23c12214688b287b9591133445e593da633caa.patch b/SOURCES/opencryptoki-openssl3-7a23c12214688b287b9591133445e593da633caa.patch
new file mode 100644
index 0000000..b3c2339
--- /dev/null
+++ b/SOURCES/opencryptoki-openssl3-7a23c12214688b287b9591133445e593da633caa.patch
@@ -0,0 +1,1085 @@
+commit 7a23c12214688b287b9591133445e593da633caa
+Author: Ingo Franzki <ifranzki@linux.ibm.com>
+Date:   Mon Jul 5 12:58:42 2021 +0200
+
+    API: Use own OpenSSL library context for Opencryptoki's use of OpenSSL
+    
+    Create a separate library context for Opencryptoki's use of OpenSSL services
+    and explicitly load the 'default' provider for this context.
+    
+    This prevents call-loops when the calling application has configured a PKCS#11
+    provider that uses Opencryptoki under the covers. This could produce a loop
+    with the following calling tree:
+      Application -> Openssl -> PKCS11-provider -> Opencryptoki -> OpenSSL
+      -> PKCS11-provider -> Opencryptoki -> ...
+    Explicitly using the 'default' provider only for Opencrypoki's OpenSSL usage
+    breaks this loop.
+    
+    Signed-off-by: Ingo Franzki <ifranzki@linux.ibm.com>
+
+diff --git a/usr/include/apictl.h b/usr/include/apictl.h
+index 81c65dad..0f3973b5 100644
+--- a/usr/include/apictl.h
++++ b/usr/include/apictl.h
+@@ -13,12 +13,16 @@
+ #include <local_types.h>
+ #include <stdll.h>
+ #include <slotmgr.h>
+-
+-#include "local_types.h"
++#include <defs.h>
+ 
+ #ifndef _APILOCAL_H
+ #define _APILOCAL_H
+ 
++#if OPENSSL_VERSION_PREREQ(3, 0)
++    #include <openssl/crypto.h>
++    #include <openssl/provider.h>
++#endif
++
+ // SAB Add a linked list of STDLL's loaded to
+ // only load and get list once, but let multiple slots us it.
+ 
+@@ -59,6 +63,10 @@ typedef struct {
+                                             // per slot
+     int socketfd;
+     pthread_t event_thread;
++#if OPENSSL_VERSION_PREREQ(3, 0)
++    OSSL_LIB_CTX *openssl_libctx;
++    OSSL_PROVIDER *openssl_default_provider;
++#endif
+ } API_Proc_Struct_t;
+ 
+ #endif
+diff --git a/usr/lib/api/api.mk b/usr/lib/api/api.mk
+index 630a43b7..282f0017 100644
+--- a/usr/lib/api/api.mk
++++ b/usr/lib/api/api.mk
+@@ -12,7 +12,7 @@ opencryptoki_libopencryptoki_la_CFLAGS =				\
+ 	-DSTDLL_NAME=\"api\"
+ 
+ opencryptoki_libopencryptoki_la_LDFLAGS =				\
+-	-shared	-Wl,-z,defs,-Bsymbolic -lc -ldl -lpthread		\
++	-shared	-Wl,-z,defs,-Bsymbolic -lc -ldl -lpthread -lcrypto	\
+ 	-version-info $(SO_CURRENT):$(SO_REVISION):$(SO_AGE)		\
+ 	-Wl,--version-script=${srcdir}/opencryptoki.map
+ 
+diff --git a/usr/lib/api/api_interface.c b/usr/lib/api/api_interface.c
+index 6517ca6c..ca6aff06 100644
+--- a/usr/lib/api/api_interface.c
++++ b/usr/lib/api/api_interface.c
+@@ -37,6 +37,28 @@
+ 
+ void api_init();
+ 
++#if OPENSSL_VERSION_PREREQ(3, 0)
++#define BEGIN_OPENSSL_LIBCTX(ossl_ctx, rc)                                  \
++        do {                                                                \
++            OSSL_LIB_CTX  *prev_ctx = OSSL_LIB_CTX_set0_default((ossl_ctx));\
++            if (prev_ctx == NULL) {                                         \
++                (rc) = CKR_FUNCTION_FAILED;                                 \
++                TRACE_ERROR("OSSL_LIB_CTX_set0_default failed\n");          \
++                break;                                                      \
++            }
++
++#define END_OPENSSL_LIBCTX(rc)                                              \
++            if (OSSL_LIB_CTX_set0_default(prev_ctx) == NULL) {              \
++                if ((rc) == CKR_OK)                                         \
++                    (rc) = CKR_FUNCTION_FAILED;                             \
++                TRACE_ERROR("OSSL_LIB_CTX_set0_default failed\n");          \
++            }                                                               \
++        } while (0);
++#else
++#define BEGIN_OPENSSL_LIBCTX(ossl_ctx, rc)  do {
++#define END_OPENSSL_LIBCTX(rc)              } while (0);
++#endif
++
+ // NOTES:
+ // In many cases the specificaiton does not allow returns
+ // of CKR_ARGUMENTSB_BAD.  We break the spec, since validation of parameters
+@@ -347,6 +369,7 @@ CK_RV C_CancelFunction(CK_SESSION_HANDLE hSession)
+ 
+ CK_RV C_CloseAllSessions(CK_SLOT_ID slotID)
+ {
++    CK_RV rc = CKR_OK;
+     // Although why does modutil do a close all sessions.  It is a single
+     // application it can only close its sessions...
+     // And all sessions should be closed anyhow.
+@@ -365,9 +388,11 @@ CK_RV C_CloseAllSessions(CK_SLOT_ID slotID)
+     /* for every node in the API-level session tree, if the session's slot
+      * matches slotID, close it
+      */
++    BEGIN_OPENSSL_LIBCTX(Anchor->openssl_libctx, rc)
+     CloseAllSessions(slotID, FALSE);
++    END_OPENSSL_LIBCTX(rc)
+ 
+-    return CKR_OK;
++    return rc;
+ }                               // end of C_CloseAllSessions
+ 
+ //------------------------------------------------------------------------
+@@ -408,9 +433,12 @@ CK_RV C_CloseSession(CK_SESSION_HANDLE hSession)
+         return CKR_TOKEN_NOT_PRESENT;
+     }
+     if (fcn->ST_CloseSession) {
++        BEGIN_OPENSSL_LIBCTX(Anchor->openssl_libctx, rv)
+         // Map the Session to the slot session
+         rv = fcn->ST_CloseSession(sltp->TokData, &rSession, FALSE);
+         TRACE_DEVEL("Called STDLL rv = 0x%lx\n", rv);
++        END_OPENSSL_LIBCTX(rv)
++
+         //  If the STDLL successfully closed the session
+         //  we can free it.. Otherwise we will have to leave it
+         //  lying arround.
+@@ -488,9 +516,12 @@ CK_RV C_CopyObject(CK_SESSION_HANDLE hSession,
+         return CKR_TOKEN_NOT_PRESENT;
+     }
+     if (fcn->ST_CopyObject) {
++        BEGIN_OPENSSL_LIBCTX(Anchor->openssl_libctx, rv)
+         // Map the Session to the slot session
+         rv = fcn->ST_CopyObject(sltp->TokData, &rSession, hObject,
+                                 pTemplate, ulCount, phNewObject);
++        TRACE_DEVEL("fcn->ST_CopyObject returned:0x%lx\n", rv);
++        END_OPENSSL_LIBCTX(rv)
+     } else {
+         TRACE_ERROR("%s\n", ock_err(ERR_FUNCTION_NOT_SUPPORTED));
+         rv = CKR_FUNCTION_NOT_SUPPORTED;
+@@ -558,10 +589,12 @@ CK_RV C_CreateObject(CK_SESSION_HANDLE hSession,
+         return CKR_TOKEN_NOT_PRESENT;
+     }
+     if (fcn->ST_CreateObject) {
++        BEGIN_OPENSSL_LIBCTX(Anchor->openssl_libctx, rv)
+         // Map the Session to the slot session
+         rv = fcn->ST_CreateObject(sltp->TokData, &rSession, pTemplate,
+                                   ulCount, phObject);
+         TRACE_DEVEL("fcn->ST_CreateObject returned:0x%lx\n", rv);
++        END_OPENSSL_LIBCTX(rv)
+     } else {
+         TRACE_ERROR("%s\n", ock_err(ERR_FUNCTION_NOT_SUPPORTED));
+         rv = CKR_FUNCTION_NOT_SUPPORTED;
+@@ -611,10 +644,12 @@ CK_RV C_Decrypt(CK_SESSION_HANDLE hSession,
+         return CKR_TOKEN_NOT_PRESENT;
+     }
+     if (fcn->ST_Decrypt) {
++        BEGIN_OPENSSL_LIBCTX(Anchor->openssl_libctx, rv)
+         // Map the Session to the slot session
+         rv = fcn->ST_Decrypt(sltp->TokData, &rSession, pEncryptedData,
+                              ulEncryptedDataLen, pData, pulDataLen);
+         TRACE_DEVEL("fcn->ST_Decrypt returned:0x%lx\n", rv);
++        END_OPENSSL_LIBCTX(rv)
+     } else {
+         TRACE_ERROR("%s\n", ock_err(ERR_FUNCTION_NOT_SUPPORTED));
+         rv = CKR_FUNCTION_NOT_SUPPORTED;
+@@ -661,11 +696,13 @@ CK_RV C_DecryptDigestUpdate(CK_SESSION_HANDLE hSession,
+         return CKR_TOKEN_NOT_PRESENT;
+     }
+     if (fcn->ST_DecryptDigestUpdate) {
++        BEGIN_OPENSSL_LIBCTX(Anchor->openssl_libctx, rv)
+         // Map the Session to the slot session
+         rv = fcn->ST_DecryptDigestUpdate(sltp->TokData, &rSession,
+                                          pEncryptedPart,
+                                          ulEncryptedPartLen, pPart, pulPartLen);
+         TRACE_DEVEL("fcn->ST_DecryptDigestUpdate returned:0x%lx\n", rv);
++        END_OPENSSL_LIBCTX(rv)
+     } else {
+         TRACE_ERROR("%s\n", ock_err(ERR_FUNCTION_NOT_SUPPORTED));
+         rv = CKR_FUNCTION_NOT_SUPPORTED;
+@@ -714,10 +751,12 @@ CK_RV C_DecryptFinal(CK_SESSION_HANDLE hSession,
+         return CKR_TOKEN_NOT_PRESENT;
+     }
+     if (fcn->ST_DecryptFinal) {
++        BEGIN_OPENSSL_LIBCTX(Anchor->openssl_libctx, rv)
+         // Map the Session to the slot session
+         rv = fcn->ST_DecryptFinal(sltp->TokData, &rSession, pLastPart,
+                                   pulLastPartLen);
+         TRACE_DEVEL("fcn->ST_DecryptFinal returned: 0x%lx\n", rv);
++        END_OPENSSL_LIBCTX(rv)
+     } else {
+         TRACE_ERROR("%s\n", ock_err(ERR_FUNCTION_NOT_SUPPORTED));
+         rv = CKR_FUNCTION_NOT_SUPPORTED;
+@@ -771,9 +810,11 @@ CK_RV C_DecryptInit(CK_SESSION_HANDLE hSession,
+         return CKR_TOKEN_NOT_PRESENT;
+     }
+     if (fcn->ST_DecryptInit) {
++        BEGIN_OPENSSL_LIBCTX(Anchor->openssl_libctx, rv)
+         // Map the Session to the slot session
+         rv = fcn->ST_DecryptInit(sltp->TokData, &rSession, pMechanism, hKey);
+         TRACE_DEVEL("fcn->ST_DecryptInit returned:0x%lx\n", rv);
++        END_OPENSSL_LIBCTX(rv)
+     } else {
+         TRACE_ERROR("%s\n", ock_err(ERR_FUNCTION_NOT_SUPPORTED));
+         rv = CKR_FUNCTION_NOT_SUPPORTED;
+@@ -823,11 +864,13 @@ CK_RV C_DecryptUpdate(CK_SESSION_HANDLE hSession,
+         return CKR_TOKEN_NOT_PRESENT;
+     }
+     if (fcn->ST_DecryptUpdate) {
++        BEGIN_OPENSSL_LIBCTX(Anchor->openssl_libctx, rv)
+         // Map the Session to the slot session
+         rv = fcn->ST_DecryptUpdate(sltp->TokData, &rSession,
+                                    pEncryptedPart, ulEncryptedPartLen,
+                                    pPart, pulPartLen);
+         TRACE_DEVEL("fcn->ST_DecryptUpdate:0x%lx\n", rv);
++        END_OPENSSL_LIBCTX(rv)
+     } else {
+         TRACE_ERROR("%s\n", ock_err(ERR_FUNCTION_NOT_SUPPORTED));
+         rv = CKR_FUNCTION_NOT_SUPPORTED;
+@@ -873,11 +916,13 @@ CK_RV C_DecryptVerifyUpdate(CK_SESSION_HANDLE hSession,
+         return CKR_TOKEN_NOT_PRESENT;
+     }
+     if (fcn->ST_DecryptVerifyUpdate) {
++        BEGIN_OPENSSL_LIBCTX(Anchor->openssl_libctx, rv)
+         // Map the Session to the slot session
+         rv = fcn->ST_DecryptVerifyUpdate(sltp->TokData, &rSession,
+                                          pEncryptedPart, ulEncryptedPartLen,
+                                          pPart, pulPartLen);
+         TRACE_DEVEL("fcn->ST_DecryptVerifyUpdate returned:0x%lx\n", rv);
++        END_OPENSSL_LIBCTX(rv)
+     } else {
+         TRACE_ERROR("%s\n", ock_err(ERR_FUNCTION_NOT_SUPPORTED));
+         rv = CKR_FUNCTION_NOT_SUPPORTED;
+@@ -941,10 +986,12 @@ CK_RV C_DeriveKey(CK_SESSION_HANDLE hSession,
+         return CKR_TOKEN_NOT_PRESENT;
+     }
+     if (fcn->ST_DeriveKey) {
++        BEGIN_OPENSSL_LIBCTX(Anchor->openssl_libctx, rv)
+         // Map the Session to the slot session
+         rv = fcn->ST_DeriveKey(sltp->TokData, &rSession, pMechanism,
+                                hBaseKey, pTemplate, ulAttributeCount, phKey);
+         TRACE_DEVEL("fcn->ST_DeriveKey returned:0x%lx\n", rv);
++        END_OPENSSL_LIBCTX(rv)
+     } else {
+         TRACE_ERROR("%s\n", ock_err(ERR_FUNCTION_NOT_SUPPORTED));
+         rv = CKR_FUNCTION_NOT_SUPPORTED;
+@@ -992,9 +1039,11 @@ CK_RV C_DestroyObject(CK_SESSION_HANDLE hSession, CK_OBJECT_HANDLE hObject)
+         return CKR_TOKEN_NOT_PRESENT;
+     }
+     if (fcn->ST_DestroyObject) {
++        BEGIN_OPENSSL_LIBCTX(Anchor->openssl_libctx, rv)
+         // Map the Session to the slot session
+         rv = fcn->ST_DestroyObject(sltp->TokData, &rSession, hObject);
+         TRACE_DEVEL("fcn->ST_DestroyObject returned:0x%lx\n", rv);
++        END_OPENSSL_LIBCTX(rv)
+     } else {
+         TRACE_ERROR("%s\n", ock_err(ERR_FUNCTION_NOT_SUPPORTED));
+         rv = CKR_FUNCTION_NOT_SUPPORTED;
+@@ -1040,10 +1089,12 @@ CK_RV C_Digest(CK_SESSION_HANDLE hSession,
+         return CKR_TOKEN_NOT_PRESENT;
+     }
+     if (fcn->ST_Digest) {
++        BEGIN_OPENSSL_LIBCTX(Anchor->openssl_libctx, rv)
+         // Map the Session to the slot session
+         rv = fcn->ST_Digest(sltp->TokData, &rSession, pData, ulDataLen,
+                             pDigest, pulDigestLen);
+         TRACE_DEVEL("fcn->ST_Digest:0x%lx\n", rv);
++        END_OPENSSL_LIBCTX(rv)
+     } else {
+         TRACE_ERROR("%s\n", ock_err(ERR_FUNCTION_NOT_SUPPORTED));
+         rv = CKR_FUNCTION_NOT_SUPPORTED;
+@@ -1091,11 +1142,13 @@ CK_RV C_DigestEncryptUpdate(CK_SESSION_HANDLE hSession,
+         return CKR_TOKEN_NOT_PRESENT;
+     }
+     if (fcn->ST_DigestEncryptUpdate) {
++        BEGIN_OPENSSL_LIBCTX(Anchor->openssl_libctx, rv)
+         // Map the Session to the slot session
+         rv = fcn->ST_DigestEncryptUpdate(sltp->TokData, &rSession,
+                                          pPart, ulPartLen,
+                                          pEncryptedPart, pulEncryptedPartLen);
+         TRACE_DEVEL("fcn->ST_DigestEncryptUpdate returned:0x%lx\n", rv);
++        END_OPENSSL_LIBCTX(rv)
+     } else {
+         TRACE_ERROR("%s\n", ock_err(ERR_FUNCTION_NOT_SUPPORTED));
+         rv = CKR_FUNCTION_NOT_SUPPORTED;
+@@ -1139,10 +1192,12 @@ CK_RV C_DigestFinal(CK_SESSION_HANDLE hSession,
+         return CKR_TOKEN_NOT_PRESENT;
+     }
+     if (fcn->ST_DigestFinal) {
++        BEGIN_OPENSSL_LIBCTX(Anchor->openssl_libctx, rv)
+         // Map the Session to the slot session
+         rv = fcn->ST_DigestFinal(sltp->TokData, &rSession, pDigest,
+                                  pulDigestLen);
+         TRACE_DEVEL("fcn->ST_DigestFinal returned:0x%lx\n", rv);
++        END_OPENSSL_LIBCTX(rv)
+     } else {
+         TRACE_ERROR("%s\n", ock_err(ERR_FUNCTION_NOT_SUPPORTED));
+         rv = CKR_FUNCTION_NOT_SUPPORTED;
+@@ -1189,9 +1244,11 @@ CK_RV C_DigestInit(CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechanism)
+         return CKR_TOKEN_NOT_PRESENT;
+     }
+     if (fcn->ST_DigestInit) {
++        BEGIN_OPENSSL_LIBCTX(Anchor->openssl_libctx, rv)
+         // Map the Session to the slot session
+         rv = fcn->ST_DigestInit(sltp->TokData, &rSession, pMechanism);
+         TRACE_DEVEL("fcn->ST_DigestInit returned:0x%lx\n", rv);
++        END_OPENSSL_LIBCTX(rv)
+     } else {
+         TRACE_ERROR("%s\n", ock_err(ERR_FUNCTION_NOT_SUPPORTED));
+         rv = CKR_FUNCTION_NOT_SUPPORTED;
+@@ -1234,9 +1291,11 @@ CK_RV C_DigestKey(CK_SESSION_HANDLE hSession, CK_OBJECT_HANDLE hKey)
+         return CKR_TOKEN_NOT_PRESENT;
+     }
+     if (fcn->ST_DigestKey) {
++        BEGIN_OPENSSL_LIBCTX(Anchor->openssl_libctx, rv)
+         // Map the Session to the slot session
+         rv = fcn->ST_DigestKey(sltp->TokData, &rSession, hKey);
+         TRACE_DEBUG("fcn->ST_DigestKey returned:0x%lx\n", rv);
++        END_OPENSSL_LIBCTX(rv)
+     } else {
+         TRACE_ERROR("%s\n", ock_err(ERR_FUNCTION_NOT_SUPPORTED));
+         rv = CKR_FUNCTION_NOT_SUPPORTED;
+@@ -1280,9 +1339,11 @@ CK_RV C_DigestUpdate(CK_SESSION_HANDLE hSession,
+         return CKR_TOKEN_NOT_PRESENT;
+     }
+     if (fcn->ST_DigestUpdate) {
++        BEGIN_OPENSSL_LIBCTX(Anchor->openssl_libctx, rv)
+         // Map the Session to the slot session
+         rv = fcn->ST_DigestUpdate(sltp->TokData, &rSession, pPart, ulPartLen);
+         TRACE_DEVEL("fcn->ST_DigestUpdate returned:0x%lx\n", rv);
++        END_OPENSSL_LIBCTX(rv)
+     } else {
+         TRACE_ERROR("%s\n", ock_err(ERR_FUNCTION_NOT_SUPPORTED));
+         rv = CKR_FUNCTION_NOT_SUPPORTED;
+@@ -1328,10 +1389,12 @@ CK_RV C_Encrypt(CK_SESSION_HANDLE hSession,
+         return CKR_TOKEN_NOT_PRESENT;
+     }
+     if (fcn->ST_Encrypt) {
++        BEGIN_OPENSSL_LIBCTX(Anchor->openssl_libctx, rv)
+         // Map the Session to the slot session
+         rv = fcn->ST_Encrypt(sltp->TokData, &rSession, pData,
+                              ulDataLen, pEncryptedData, pulEncryptedDataLen);
+         TRACE_DEVEL("fcn->ST_Encrypt returned: 0x%lx\n", rv);
++        END_OPENSSL_LIBCTX(rv)
+     } else {
+         TRACE_ERROR("%s\n", ock_err(ERR_FUNCTION_NOT_SUPPORTED));
+         rv = CKR_FUNCTION_NOT_SUPPORTED;
+@@ -1376,10 +1439,12 @@ CK_RV C_EncryptFinal(CK_SESSION_HANDLE hSession,
+         return CKR_TOKEN_NOT_PRESENT;
+     }
+     if (fcn->ST_EncryptFinal) {
++        BEGIN_OPENSSL_LIBCTX(Anchor->openssl_libctx, rv)
+         // Map the Session to the slot session
+         rv = fcn->ST_EncryptFinal(sltp->TokData, &rSession,
+                                   pLastEncryptedPart, pulLastEncryptedPartLen);
+         TRACE_DEVEL("fcn->ST_EncryptFinal: 0x%lx\n", rv);
++        END_OPENSSL_LIBCTX(rv)
+     } else {
+         TRACE_ERROR("%s\n", ock_err(ERR_FUNCTION_NOT_SUPPORTED));
+         rv = CKR_FUNCTION_NOT_SUPPORTED;
+@@ -1427,9 +1492,11 @@ CK_RV C_EncryptInit(CK_SESSION_HANDLE hSession,
+         return CKR_TOKEN_NOT_PRESENT;
+     }
+     if (fcn->ST_EncryptInit) {
++        BEGIN_OPENSSL_LIBCTX(Anchor->openssl_libctx, rv)
+         // Map the Session to the slot session
+         rv = fcn->ST_EncryptInit(sltp->TokData, &rSession, pMechanism, hKey);
+         TRACE_INFO("fcn->ST_EncryptInit returned:0x%lx\n", rv);
++        END_OPENSSL_LIBCTX(rv)
+     } else {
+         TRACE_ERROR("%s\n", ock_err(ERR_FUNCTION_NOT_SUPPORTED));
+         rv = CKR_FUNCTION_NOT_SUPPORTED;
+@@ -1476,11 +1543,13 @@ CK_RV C_EncryptUpdate(CK_SESSION_HANDLE hSession,
+         return CKR_TOKEN_NOT_PRESENT;
+     }
+     if (fcn->ST_EncryptUpdate) {
++        BEGIN_OPENSSL_LIBCTX(Anchor->openssl_libctx, rv)
+         // Map the Session to the slot session
+         rv = fcn->ST_EncryptUpdate(sltp->TokData, &rSession, pPart,
+                                    ulPartLen, pEncryptedPart,
+                                    pulEncryptedPartLen);
+         TRACE_DEVEL("fcn->ST_EncryptUpdate returned:0x%lx\n", rv);
++        END_OPENSSL_LIBCTX(rv)
+     } else {
+         TRACE_ERROR("%s\n", ock_err(ERR_FUNCTION_NOT_SUPPORTED));
+         rv = CKR_FUNCTION_NOT_SUPPORTED;
+@@ -1543,6 +1612,7 @@ CK_RV C_Finalize(CK_VOID_PTR pReserved)
+     // unload all the STDLL's from the application
+     // This is in case the APP decides to do the re-initialize and
+     // continue on
++    BEGIN_OPENSSL_LIBCTX(Anchor->openssl_libctx, rc)
+     for (slotID = 0; slotID < NUMBER_SLOTS_MANAGED; slotID++) {
+         sltp = &(Anchor->SltList[slotID]);
+         if (slot_loaded[slotID]) {
+@@ -1565,12 +1635,20 @@ CK_RV C_Finalize(CK_VOID_PTR pReserved)
+         if (!in_child_fork_initializer)
+             DL_UnLoad(sltp, slotID);
+     }
++    END_OPENSSL_LIBCTX(rc)
+ 
+     // Un register from Slot D
+     API_UnRegister();
+ 
+     bt_destroy(&Anchor->sess_btree);
+ 
++#if OPENSSL_VERSION_PREREQ(3, 0)
++    if (Anchor->openssl_default_provider != NULL)
++        OSSL_PROVIDER_unload(Anchor->openssl_default_provider);
++    if (Anchor->openssl_libctx != NULL)
++        OSSL_LIB_CTX_free(Anchor->openssl_libctx);
++#endif
++
+     detach_shared_memory(Anchor->SharedMemP);
+     free(Anchor);               // Free API Proc Struct
+     Anchor = NULL;
+@@ -1632,10 +1710,12 @@ CK_RV C_FindObjects(CK_SESSION_HANDLE hSession,
+         return CKR_TOKEN_NOT_PRESENT;
+     }
+     if (fcn->ST_FindObjects) {
++        BEGIN_OPENSSL_LIBCTX(Anchor->openssl_libctx, rv)
+         // Map the Session to the slot session
+         rv = fcn->ST_FindObjects(sltp->TokData, &rSession, phObject,
+                                  ulMaxObjectCount, pulObjectCount);
+         TRACE_DEVEL("fcn->ST_FindObjects returned:0x%lx\n", rv);
++        END_OPENSSL_LIBCTX(rv)
+     } else {
+         TRACE_ERROR("%s\n", ock_err(ERR_FUNCTION_NOT_SUPPORTED));
+         rv = CKR_FUNCTION_NOT_SUPPORTED;
+@@ -1683,9 +1763,11 @@ CK_RV C_FindObjectsFinal(CK_SESSION_HANDLE hSession)
+         return CKR_TOKEN_NOT_PRESENT;
+     }
+     if (fcn->ST_FindObjectsFinal) {
++        BEGIN_OPENSSL_LIBCTX(Anchor->openssl_libctx, rv)
+         // Map the Session to the slot session
+         rv = fcn->ST_FindObjectsFinal(sltp->TokData, &rSession);
+         TRACE_DEVEL("fcn->ST_FindObjectsFinal returned: 0x%lx\n", rv);
++        END_OPENSSL_LIBCTX(rv)
+     } else {
+         TRACE_ERROR("%s\n", ock_err(ERR_FUNCTION_NOT_SUPPORTED));
+         rv = CKR_FUNCTION_NOT_SUPPORTED;
+@@ -1736,10 +1818,12 @@ CK_RV C_FindObjectsInit(CK_SESSION_HANDLE hSession,
+         return CKR_TOKEN_NOT_PRESENT;
+     }
+     if (fcn->ST_FindObjectsInit) {
++        BEGIN_OPENSSL_LIBCTX(Anchor->openssl_libctx, rv)
+         // Map the Session to the slot session
+         rv = fcn->ST_FindObjectsInit(sltp->TokData, &rSession,
+                                      pTemplate, ulCount);
+         TRACE_DEVEL("fcn->ST_FindObjectsInit returned:0x%lx\n", rv);
++        END_OPENSSL_LIBCTX(rv)
+     } else {
+         TRACE_ERROR("%s\n", ock_err(ERR_FUNCTION_NOT_SUPPORTED));
+         rv = CKR_FUNCTION_NOT_SUPPORTED;
+@@ -1794,10 +1878,12 @@ CK_RV C_GenerateKey(CK_SESSION_HANDLE hSession,
+         return CKR_TOKEN_NOT_PRESENT;
+     }
+     if (fcn->ST_GenerateKey) {
++        BEGIN_OPENSSL_LIBCTX(Anchor->openssl_libctx, rv)
+         // Map the Session to the slot session
+         rv = fcn->ST_GenerateKey(sltp->TokData, &rSession, pMechanism,
+                                  pTemplate, ulCount, phKey);
+         TRACE_DEVEL("fcn->ST_GenerateKey returned:0x%lx\n", rv);
++        END_OPENSSL_LIBCTX(rv)
+     } else {
+         TRACE_ERROR("%s\n", ock_err(ERR_FUNCTION_NOT_SUPPORTED));
+         rv = CKR_FUNCTION_NOT_SUPPORTED;
+@@ -1861,6 +1947,7 @@ CK_RV C_GenerateKeyPair(CK_SESSION_HANDLE hSession,
+         return CKR_TOKEN_NOT_PRESENT;
+     }
+     if (fcn->ST_GenerateKeyPair) {
++        BEGIN_OPENSSL_LIBCTX(Anchor->openssl_libctx, rv)
+         // Map the Session to the slot session
+         rv = fcn->ST_GenerateKeyPair(sltp->TokData, &rSession,
+                                      pMechanism,
+@@ -1870,6 +1957,7 @@ CK_RV C_GenerateKeyPair(CK_SESSION_HANDLE hSession,
+                                      ulPrivateKeyAttributeCount,
+                                      phPublicKey, phPrivateKey);
+         TRACE_DEVEL("fcn->ST_GenerateKeyPair returned:0x%lx\n", rv);
++        END_OPENSSL_LIBCTX(rv)
+     } else {
+         TRACE_ERROR("%s\n", ock_err(ERR_FUNCTION_NOT_SUPPORTED));
+         rv = CKR_FUNCTION_NOT_SUPPORTED;
+@@ -1917,10 +2005,12 @@ CK_RV C_GenerateRandom(CK_SESSION_HANDLE hSession,
+         return CKR_TOKEN_NOT_PRESENT;
+     }
+     if (fcn->ST_GenerateRandom) {
++        BEGIN_OPENSSL_LIBCTX(Anchor->openssl_libctx, rv)
+         // Map the Session to the slot session
+         rv = fcn->ST_GenerateRandom(sltp->TokData, &rSession,
+                                     RandomData, ulRandomLen);
+         TRACE_DEVEL("fcn->ST_GenerateRandom returned:0x%lx\n", rv);
++        END_OPENSSL_LIBCTX(rv)
+     } else {
+         TRACE_ERROR("%s\n", ock_err(ERR_FUNCTION_NOT_SUPPORTED));
+         rv = CKR_FUNCTION_NOT_SUPPORTED;
+@@ -1977,10 +2067,12 @@ CK_RV C_GetAttributeValue(CK_SESSION_HANDLE hSession,
+         return CKR_TOKEN_NOT_PRESENT;
+     }
+     if (fcn->ST_GetAttributeValue) {
++        BEGIN_OPENSSL_LIBCTX(Anchor->openssl_libctx, rv)
+         // Map the Session to the slot session
+         rv = fcn->ST_GetAttributeValue(sltp->TokData, &rSession,
+                                        hObject, pTemplate, ulCount);
+         TRACE_DEVEL("fcn->ST_GetAttributeValue returned:0x%lx\n", rv);
++        END_OPENSSL_LIBCTX(rv)
+     } else {
+         TRACE_ERROR("%s\n", ock_err(ERR_FUNCTION_NOT_SUPPORTED));
+         rv = CKR_FUNCTION_NOT_SUPPORTED;
+@@ -2098,8 +2190,10 @@ CK_RV C_GetMechanismInfo(CK_SLOT_ID slotID,
+         return CKR_TOKEN_NOT_PRESENT;
+     }
+     if (fcn->ST_GetMechanismInfo) {
++        BEGIN_OPENSSL_LIBCTX(Anchor->openssl_libctx, rv)
+         rv = fcn->ST_GetMechanismInfo(sltp->TokData, slotID, type, pInfo);
+         TRACE_DEVEL("fcn->ST_GetMechanismInfo returned:0x%lx\n", rv);
++        END_OPENSSL_LIBCTX(rv)
+     } else {
+         TRACE_ERROR("%s\n", ock_err(ERR_FUNCTION_NOT_SUPPORTED));
+         rv = CKR_FUNCTION_NOT_SUPPORTED;
+@@ -2156,9 +2250,11 @@ CK_RV C_GetMechanismList(CK_SLOT_ID slotID,
+         return CKR_TOKEN_NOT_PRESENT;
+     }
+     if (fcn->ST_GetMechanismList) {
++        BEGIN_OPENSSL_LIBCTX(Anchor->openssl_libctx, rv)
+         rv = fcn->ST_GetMechanismList(sltp->TokData, slotID,
+                                       pMechanismList, pulCount);
+         TRACE_DEVEL("fcn->ST_GetMechanismList returned: 0x%lx\n", rv);
++        END_OPENSSL_LIBCTX(rv)
+     } else {
+         TRACE_ERROR("%s\n", ock_err(ERR_FUNCTION_NOT_SUPPORTED));
+         rv = CKR_FUNCTION_NOT_SUPPORTED;
+@@ -2220,9 +2316,11 @@ CK_RV C_GetObjectSize(CK_SESSION_HANDLE hSession,
+         return CKR_TOKEN_NOT_PRESENT;
+     }
+     if (fcn->ST_GetObjectSize) {
++        BEGIN_OPENSSL_LIBCTX(Anchor->openssl_libctx, rv)
+         // Map the Session to the slot session
+         rv = fcn->ST_GetObjectSize(sltp->TokData, &rSession, hObject, pulSize);
+         TRACE_DEVEL("fcn->ST_GetObjectSize retuned: 0x%lx\n", rv);
++        END_OPENSSL_LIBCTX(rv)
+     } else {
+         TRACE_ERROR("%s\n", ock_err(ERR_FUNCTION_NOT_SUPPORTED));
+         rv = CKR_FUNCTION_NOT_SUPPORTED;
+@@ -2272,10 +2370,12 @@ CK_RV C_GetOperationState(CK_SESSION_HANDLE hSession,
+         return CKR_TOKEN_NOT_PRESENT;
+     }
+     if (fcn->ST_GetOperationState) {
++        BEGIN_OPENSSL_LIBCTX(Anchor->openssl_libctx, rv)
+         // Map the Session to the slot session
+         rv = fcn->ST_GetOperationState(sltp->TokData, &rSession,
+                                        pOperationState, pulOperationStateLen);
+         TRACE_DEVEL("fcn->ST_GetOperationState returned:0x%lx\n", rv);
++        END_OPENSSL_LIBCTX(rv)
+     } else {
+         TRACE_ERROR("%s\n", ock_err(ERR_FUNCTION_NOT_SUPPORTED));
+         rv = CKR_FUNCTION_NOT_SUPPORTED;
+@@ -2328,6 +2428,7 @@ CK_RV C_GetSessionInfo(CK_SESSION_HANDLE hSession, CK_SESSION_INFO_PTR pInfo)
+         return CKR_TOKEN_NOT_PRESENT;
+     }
+     if (fcn->ST_GetSessionInfo) {
++        BEGIN_OPENSSL_LIBCTX(Anchor->openssl_libctx, rv)
+         // Map the Session to the slot session
+         rv = fcn->ST_GetSessionInfo(sltp->TokData, &rSession, pInfo);
+ 
+@@ -2335,6 +2436,7 @@ CK_RV C_GetSessionInfo(CK_SESSION_HANDLE hSession, CK_SESSION_INFO_PTR pInfo)
+         TRACE_DEVEL("Slot %lu  State %lx  Flags %lx DevErr %lx\n",
+                     pInfo->slotID, pInfo->state, pInfo->flags,
+                     pInfo->ulDeviceError);
++        END_OPENSSL_LIBCTX(rv)
+ 
+     } else {
+         TRACE_ERROR("%s\n", ock_err(ERR_FUNCTION_NOT_SUPPORTED));
+@@ -2650,11 +2752,13 @@ CK_RV C_GetTokenInfo(CK_SLOT_ID slotID, CK_TOKEN_INFO_PTR pInfo)
+         return CKR_TOKEN_NOT_PRESENT;
+     }
+     if (fcn->ST_GetTokenInfo) {
++        BEGIN_OPENSSL_LIBCTX(Anchor->openssl_libctx, rv)
+         rv = fcn->ST_GetTokenInfo(sltp->TokData, slotID, pInfo);
+         if (rv == CKR_OK) {
+             get_sess_count(slotID, &(pInfo->ulSessionCount));
+         }
+         TRACE_DEVEL("rv %lu CK_TOKEN_INFO Flags %lx\n", rv, pInfo->flags);
++        END_OPENSSL_LIBCTX(rv)
+     } else {
+         TRACE_ERROR("%s\n", ock_err(ERR_FUNCTION_NOT_SUPPORTED));
+         rv = CKR_FUNCTION_NOT_SUPPORTED;
+@@ -2814,6 +2918,35 @@ CK_RV C_Initialize(CK_VOID_PTR pVoid)
+     bt_init(&Anchor->sess_btree, free);
+     Anchor->Pid = getpid();
+ 
++#if OPENSSL_VERSION_PREREQ(3, 0)
++    /*
++     * OpenSSL >= 3.0:
++     * Create a separate library context for Opencryptoki's use of OpenSSL
++     * services and explicitly load the 'default' provider for this context.
++     * This prevents call loops when the calling application has configured a
++     * PKCS#11 provider that uses Opencryptoki under the covers. This could
++     * produce a loop with the following calling tree:
++     *   Application -> Openssl -> PKCS11-provider -> Opencryptoki -> OpenSSL
++     *   -> PKCS11-provider -> Opencryptoki -> ...
++     * Explicitly using the 'default' provider only for Opencrypoki's OpenSSL
++     * usage breaks this loop.
++     */
++    Anchor->openssl_libctx = OSSL_LIB_CTX_new();
++    if (Anchor->openssl_libctx == NULL) {
++        TRACE_ERROR("OSSL_LIB_CTX_new failed.\n");
++        rc = CKR_FUNCTION_FAILED;
++        goto error;
++    }
++
++    Anchor->openssl_default_provider =
++                    OSSL_PROVIDER_load(Anchor->openssl_libctx, "default");
++    if (Anchor->openssl_default_provider == NULL) {
++        TRACE_ERROR("OSSL_PROVIDER_load for 'default' failed.\n");
++        rc = CKR_FUNCTION_FAILED;
++        goto error;
++    }
++#endif
++
+     // Get shared memory
+     if ((Anchor->SharedMemP = attach_shared_memory()) == NULL) {
+         OCK_SYSLOG(LOG_ERR, "C_Initialize: Module failed to attach to "
+@@ -2870,10 +3003,14 @@ CK_RV C_Initialize(CK_VOID_PTR pVoid)
+     }
+     //
+     // load all the slot DLL's here
++    BEGIN_OPENSSL_LIBCTX(Anchor->openssl_libctx, rc)
+     for (slotID = 0; slotID < NUMBER_SLOTS_MANAGED; slotID++) {
+         sltp = &(Anchor->SltList[slotID]);
+         slot_loaded[slotID] = DL_Load_and_Init(sltp, slotID);
+     }
++    END_OPENSSL_LIBCTX(rc)
++    if (rc != CKR_OK)
++        goto error_shm;
+ 
+     /* Start event receiver thread */
+     if ((Anchor->SocketDataP.flags & FLAG_EVENT_SUPPORT_DISABLED) == 0 &&
+@@ -2883,6 +3020,7 @@ CK_RV C_Initialize(CK_VOID_PTR pVoid)
+         // unload all the STDLL's from the application
+         // This is in case the APP decides to do the re-initialize and
+         // continue on
++        BEGIN_OPENSSL_LIBCTX(Anchor->openssl_libctx, rc)
+         for (slotID = 0; slotID < NUMBER_SLOTS_MANAGED; slotID++) {
+             sltp = &(Anchor->SltList[slotID]);
+             if (slot_loaded[slotID]) {
+@@ -2895,6 +3033,7 @@ CK_RV C_Initialize(CK_VOID_PTR pVoid)
+             }
+             DL_UnLoad(sltp, slotID);
+         }
++        END_OPENSSL_LIBCTX(rc)
+ 
+         API_UnRegister();
+ 
+@@ -2913,6 +3052,13 @@ error:
+     if (Anchor->socketfd >= 0)
+         close(Anchor->socketfd);
+ 
++#if OPENSSL_VERSION_PREREQ(3, 0)
++    if (Anchor->openssl_default_provider != NULL)
++        OSSL_PROVIDER_unload(Anchor->openssl_default_provider);
++    if (Anchor->openssl_libctx != NULL)
++        OSSL_LIB_CTX_free(Anchor->openssl_libctx);
++#endif
++
+     free((void *) Anchor);
+     Anchor = NULL;
+ 
+@@ -2974,9 +3120,11 @@ CK_RV C_InitPIN(CK_SESSION_HANDLE hSession, CK_CHAR_PTR pPin, CK_ULONG ulPinLen)
+         return CKR_TOKEN_NOT_PRESENT;
+     }
+     if (fcn->ST_InitPIN) {
++        BEGIN_OPENSSL_LIBCTX(Anchor->openssl_libctx, rv)
+         // Map the Session to the slot session
+         rv = fcn->ST_InitPIN(sltp->TokData, &rSession, pPin, ulPinLen);
+         TRACE_DEVEL("fcn->ST_InitPIN returned: 0x%lx\n", rv);
++        END_OPENSSL_LIBCTX(rv)
+     } else {
+         TRACE_ERROR("%s\n", ock_err(ERR_FUNCTION_NOT_SUPPORTED));
+         rv = CKR_FUNCTION_NOT_SUPPORTED;
+@@ -3042,8 +3190,10 @@ CK_RV C_InitToken(CK_SLOT_ID slotID,
+         return CKR_TOKEN_NOT_PRESENT;
+     }
+     if (fcn->ST_InitToken) {
++        BEGIN_OPENSSL_LIBCTX(Anchor->openssl_libctx, rv)
+         rv = fcn->ST_InitToken(sltp->TokData, slotID, pPin, ulPinLen, pLabel);
+         TRACE_DEVEL("fcn->ST_InitToken returned: 0x%lx\n", rv);
++        END_OPENSSL_LIBCTX(rv)
+     } else {
+         TRACE_ERROR("%s\n", ock_err(ERR_FUNCTION_NOT_SUPPORTED));
+         rv = CKR_FUNCTION_NOT_SUPPORTED;
+@@ -3097,9 +3247,11 @@ CK_RV C_Login(CK_SESSION_HANDLE hSession,
+         return CKR_TOKEN_NOT_PRESENT;
+     }
+     if (fcn->ST_Login) {
++        BEGIN_OPENSSL_LIBCTX(Anchor->openssl_libctx, rv)
+         // Map the Session to the slot session
+         rv = fcn->ST_Login(sltp->TokData, &rSession, userType, pPin, ulPinLen);
+         TRACE_DEVEL("fcn->ST_Login returned:0x%lx\n", rv);
++        END_OPENSSL_LIBCTX(rv)
+     } else {
+         TRACE_ERROR("%s\n", ock_err(ERR_FUNCTION_NOT_SUPPORTED));
+         rv = CKR_FUNCTION_NOT_SUPPORTED;
+@@ -3147,9 +3299,11 @@ CK_RV C_Logout(CK_SESSION_HANDLE hSession)
+         return CKR_TOKEN_NOT_PRESENT;
+     }
+     if (fcn->ST_Logout) {
++        BEGIN_OPENSSL_LIBCTX(Anchor->openssl_libctx, rv)
+         // Map the Session to the slot session
+         rv = fcn->ST_Logout(sltp->TokData, &rSession);
+         TRACE_DEVEL("fcn->ST_Logout returned:0x%lx\n", rv);
++        END_OPENSSL_LIBCTX(rv)
+     } else {
+         TRACE_ERROR("%s\n", ock_err(ERR_FUNCTION_NOT_SUPPORTED));
+         rv = CKR_FUNCTION_NOT_SUPPORTED;
+@@ -3223,9 +3377,11 @@ CK_RV C_OpenSession(CK_SLOT_ID slotID,
+     }
+ 
+     if (fcn->ST_OpenSession) {
++        BEGIN_OPENSSL_LIBCTX(Anchor->openssl_libctx, rv)
+         rv = fcn->ST_OpenSession(sltp->TokData, slotID, flags,
+                                  &(apiSessp->sessionh));
+         TRACE_DEVEL("fcn->ST_OpenSession returned: 0x%lx\n", rv);
++        END_OPENSSL_LIBCTX(rv)
+ 
+         // If the session allocation is successful, then we need to
+         // complete the API session block and  return.  Otherwise
+@@ -3237,10 +3393,12 @@ CK_RV C_OpenSession(CK_SLOT_ID slotID,
+              */
+             *phSession = AddToSessionList(apiSessp);
+             if (*phSession == 0) {
++                BEGIN_OPENSSL_LIBCTX(Anchor->openssl_libctx, rv)
+                 /* failed to add the object to the API-level tree, close the
+                  * STDLL-level session and return failure
+                  */
+                 fcn->ST_CloseSession(sltp->TokData, apiSessp, FALSE);
++                END_OPENSSL_LIBCTX(rv)
+                 free(apiSessp);
+                 rv = CKR_HOST_MEMORY;
+                 goto done;
+@@ -3310,9 +3468,11 @@ CK_RV C_SeedRandom(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pSeed,
+         return CKR_TOKEN_NOT_PRESENT;
+     }
+     if (fcn->ST_SeedRandom) {
++        BEGIN_OPENSSL_LIBCTX(Anchor->openssl_libctx, rv)
+         // Map the Session to the slot session
+         rv = fcn->ST_SeedRandom(sltp->TokData, &rSession, pSeed, ulSeedLen);
+         TRACE_DEVEL("fcn->ST_SeedRandom returned: 0x%lx\n", rv);
++        END_OPENSSL_LIBCTX(rv)
+     } else {
+         TRACE_ERROR("%s\n", ock_err(ERR_FUNCTION_NOT_SUPPORTED));
+         rv = CKR_FUNCTION_NOT_SUPPORTED;
+@@ -3371,10 +3531,12 @@ CK_RV C_SetAttributeValue(CK_SESSION_HANDLE hSession,
+         return CKR_TOKEN_NOT_PRESENT;
+     }
+     if (fcn->ST_SetAttributeValue) {
++        BEGIN_OPENSSL_LIBCTX(Anchor->openssl_libctx, rv)
+         // Map the Session to the slot session
+         rv = fcn->ST_SetAttributeValue(sltp->TokData, &rSession,
+                                        hObject, pTemplate, ulCount);
+         TRACE_DEVEL("fcn->ST_SetAttributeValue returned:0x%lx\n", rv);
++        END_OPENSSL_LIBCTX(rv)
+     } else {
+         TRACE_ERROR("%s\n", ock_err(ERR_FUNCTION_NOT_SUPPORTED));
+         rv = CKR_FUNCTION_NOT_SUPPORTED;
+@@ -3426,12 +3588,14 @@ CK_RV C_SetOperationState(CK_SESSION_HANDLE hSession,
+         return CKR_TOKEN_NOT_PRESENT;
+     }
+     if (fcn->ST_SetOperationState) {
++        BEGIN_OPENSSL_LIBCTX(Anchor->openssl_libctx, rv)
+         // Map the Session to the slot session
+         rv = fcn->ST_SetOperationState(sltp->TokData, &rSession,
+                                        pOperationState,
+                                        ulOperationStateLen,
+                                        hEncryptionKey, hAuthenticationKey);
+         TRACE_DEVEL("fcn->ST_SetOperationState returned:0x%lx\n", rv);
++        END_OPENSSL_LIBCTX(rv)
+     } else {
+         TRACE_ERROR("%s\n", ock_err(ERR_FUNCTION_NOT_SUPPORTED));
+         rv = CKR_FUNCTION_NOT_SUPPORTED;
+@@ -3486,10 +3650,12 @@ CK_RV C_SetPIN(CK_SESSION_HANDLE hSession,
+         return CKR_TOKEN_NOT_PRESENT;
+     }
+     if (fcn->ST_SetPIN) {
++        BEGIN_OPENSSL_LIBCTX(Anchor->openssl_libctx, rv)
+         // Map the Session to the slot session
+         rv = fcn->ST_SetPIN(sltp->TokData, &rSession, pOldPin,
+                             ulOldLen, pNewPin, ulNewLen);
+         TRACE_DEVEL("fcn->ST_SetPIN returned: 0x%lx\n", rv);
++        END_OPENSSL_LIBCTX(rv)
+     } else {
+         TRACE_ERROR("%s\n", ock_err(ERR_FUNCTION_NOT_SUPPORTED));
+         rv = CKR_FUNCTION_NOT_SUPPORTED;
+@@ -3540,10 +3706,12 @@ CK_RV C_Sign(CK_SESSION_HANDLE hSession,
+         return CKR_TOKEN_NOT_PRESENT;
+     }
+     if (fcn->ST_Sign) {
++        BEGIN_OPENSSL_LIBCTX(Anchor->openssl_libctx, rv)
+         // Map the Session to the slot session
+         rv = fcn->ST_Sign(sltp->TokData, &rSession, pData, ulDataLen,
+                           pSignature, pulSignatureLen);
+         TRACE_DEVEL("fcn->ST_Sign returned: 0x%lx\n", rv);
++        END_OPENSSL_LIBCTX(rv)
+     } else {
+         TRACE_ERROR("%s\n", ock_err(ERR_FUNCTION_NOT_SUPPORTED));
+         rv = CKR_FUNCTION_NOT_SUPPORTED;
+@@ -3590,11 +3758,13 @@ CK_RV C_SignEncryptUpdate(CK_SESSION_HANDLE hSession,
+         return CKR_TOKEN_NOT_PRESENT;
+     }
+     if (fcn->ST_SignEncryptUpdate) {
++        BEGIN_OPENSSL_LIBCTX(Anchor->openssl_libctx, rv)
+         // Map the Session to the slot session
+         rv = fcn->ST_SignEncryptUpdate(sltp->TokData, &rSession, pPart,
+                                        ulPartLen, pEncryptedPart,
+                                        pulEncryptedPartLen);
+         TRACE_DEVEL("fcn->ST_SignEncryptUpdate return: 0x%lx\n", rv);
++        END_OPENSSL_LIBCTX(rv)
+     } else {
+         TRACE_ERROR("%s\n", ock_err(ERR_FUNCTION_NOT_SUPPORTED));
+         rv = CKR_FUNCTION_NOT_SUPPORTED;
+@@ -3642,10 +3812,12 @@ CK_RV C_SignFinal(CK_SESSION_HANDLE hSession,
+         return CKR_TOKEN_NOT_PRESENT;
+     }
+     if (fcn->ST_SignFinal) {
++        BEGIN_OPENSSL_LIBCTX(Anchor->openssl_libctx, rv)
+         // Map the Session to the slot session
+         rv = fcn->ST_SignFinal(sltp->TokData, &rSession, pSignature,
+                                pulSignatureLen);
+         TRACE_DEVEL("fcn->ST_SignFinal returned: 0x%lx\n", rv);
++        END_OPENSSL_LIBCTX(rv)
+     } else {
+         TRACE_ERROR("%s\n", ock_err(ERR_FUNCTION_NOT_SUPPORTED));
+         rv = CKR_FUNCTION_NOT_SUPPORTED;
+@@ -3697,9 +3869,11 @@ CK_RV C_SignInit(CK_SESSION_HANDLE hSession,
+         return CKR_TOKEN_NOT_PRESENT;
+     }
+     if (fcn->ST_SignInit) {
++        BEGIN_OPENSSL_LIBCTX(Anchor->openssl_libctx, rv)
+         // Map the Session to the slot session
+         rv = fcn->ST_SignInit(sltp->TokData, &rSession, pMechanism, hKey);
+         TRACE_DEVEL("fcn->ST_SignInit returned: 0x%lx\n", rv);
++        END_OPENSSL_LIBCTX(rv)
+     } else {
+         TRACE_ERROR("%s\n", ock_err(ERR_FUNCTION_NOT_SUPPORTED));
+         rv = CKR_FUNCTION_NOT_SUPPORTED;
+@@ -3745,10 +3919,12 @@ CK_RV C_SignRecover(CK_SESSION_HANDLE hSession,
+         return CKR_TOKEN_NOT_PRESENT;
+     }
+     if (fcn->ST_SignRecover) {
++        BEGIN_OPENSSL_LIBCTX(Anchor->openssl_libctx, rv)
+         // Map the Session to the slot session
+         rv = fcn->ST_SignRecover(sltp->TokData, &rSession, pData,
+                                  ulDataLen, pSignature, pulSignatureLen);
+         TRACE_DEVEL("fcn->ST_SignRecover returned:0x%lx\n", rv);
++        END_OPENSSL_LIBCTX(rv)
+     } else {
+         TRACE_ERROR("%s\n", ock_err(ERR_FUNCTION_NOT_SUPPORTED));
+         rv = CKR_FUNCTION_NOT_SUPPORTED;
+@@ -3796,10 +3972,12 @@ CK_RV C_SignRecoverInit(CK_SESSION_HANDLE hSession,
+         return CKR_TOKEN_NOT_PRESENT;
+     }
+     if (fcn->ST_SignRecoverInit) {
++        BEGIN_OPENSSL_LIBCTX(Anchor->openssl_libctx, rv)
+         // Map the Session to the slot session
+         rv = fcn->ST_SignRecoverInit(sltp->TokData, &rSession,
+                                      pMechanism, hKey);
+         TRACE_DEVEL("fcn->ST_SignRecoverInit returned: 0x%lx\n", rv);
++        END_OPENSSL_LIBCTX(rv)
+     } else {
+         TRACE_ERROR("%s\n", ock_err(ERR_FUNCTION_NOT_SUPPORTED));
+         rv = CKR_FUNCTION_NOT_SUPPORTED;
+@@ -3847,9 +4025,11 @@ CK_RV C_SignUpdate(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pPart,
+         return CKR_TOKEN_NOT_PRESENT;
+     }
+     if (fcn->ST_SignUpdate) {
++        BEGIN_OPENSSL_LIBCTX(Anchor->openssl_libctx, rv)
+         // Map the Session to the slot session
+         rv = fcn->ST_SignUpdate(sltp->TokData, &rSession, pPart, ulPartLen);
+         TRACE_DEVEL("fcn->ST_SignUpdate returned: 0x%lx\n", rv);
++        END_OPENSSL_LIBCTX(rv)
+     } else {
+         TRACE_ERROR("%s\n", ock_err(ERR_FUNCTION_NOT_SUPPORTED));
+         rv = CKR_FUNCTION_NOT_SUPPORTED;
+@@ -3910,12 +4090,14 @@ CK_RV C_UnwrapKey(CK_SESSION_HANDLE hSession,
+         return CKR_TOKEN_NOT_PRESENT;
+     }
+     if (fcn->ST_UnwrapKey) {
++        BEGIN_OPENSSL_LIBCTX(Anchor->openssl_libctx, rv)
+         // Map the Session to the slot session
+         rv = fcn->ST_UnwrapKey(sltp->TokData, &rSession, pMechanism,
+                                hUnwrappingKey, pWrappedKey,
+                                ulWrappedKeyLen, pTemplate,
+                                ulAttributeCount, phKey);
+         TRACE_DEVEL("fcn->ST_UnwrapKey returned: 0x%lx\n", rv);
++        END_OPENSSL_LIBCTX(rv)
+     } else {
+         TRACE_ERROR("%s\n", ock_err(ERR_FUNCTION_NOT_SUPPORTED));
+         rv = CKR_FUNCTION_NOT_SUPPORTED;
+@@ -3962,10 +4144,12 @@ CK_RV C_Verify(CK_SESSION_HANDLE hSession,
+         return CKR_TOKEN_NOT_PRESENT;
+     }
+     if (fcn->ST_Verify) {
++        BEGIN_OPENSSL_LIBCTX(Anchor->openssl_libctx, rv)
+         // Map the Session to the slot session
+         rv = fcn->ST_Verify(sltp->TokData, &rSession, pData, ulDataLen,
+                             pSignature, ulSignatureLen);
+         TRACE_DEVEL("fcn->ST_Verify returned: 0x%lx\n", rv);
++        END_OPENSSL_LIBCTX(rv)
+     } else {
+         TRACE_ERROR("%s\n", ock_err(ERR_FUNCTION_NOT_SUPPORTED));
+         rv = CKR_FUNCTION_NOT_SUPPORTED;
+@@ -4009,10 +4193,12 @@ CK_RV C_VerifyFinal(CK_SESSION_HANDLE hSession,
+         return CKR_TOKEN_NOT_PRESENT;
+     }
+     if (fcn->ST_VerifyFinal) {
++        BEGIN_OPENSSL_LIBCTX(Anchor->openssl_libctx, rv)
+         // Map the Session to the slot session
+         rv = fcn->ST_VerifyFinal(sltp->TokData, &rSession, pSignature,
+                                  ulSignatureLen);
+         TRACE_DEVEL("fcn->ST_VerifyFinal returned: 0x%lx\n", rv);
++        END_OPENSSL_LIBCTX(rv)
+     } else {
+         TRACE_ERROR("%s\n", ock_err(ERR_FUNCTION_NOT_SUPPORTED));
+         rv = CKR_FUNCTION_NOT_SUPPORTED;
+@@ -4060,9 +4246,11 @@ CK_RV C_VerifyInit(CK_SESSION_HANDLE hSession,
+         return CKR_TOKEN_NOT_PRESENT;
+     }
+     if (fcn->ST_VerifyInit) {
++        BEGIN_OPENSSL_LIBCTX(Anchor->openssl_libctx, rv)
+         // Map the Session to the slot session
+         rv = fcn->ST_VerifyInit(sltp->TokData, &rSession, pMechanism, hKey);
+         TRACE_DEVEL("fcn->ST_VerifyInit returned: 0x%lx\n", rv);
++        END_OPENSSL_LIBCTX(rv)
+     } else {
+         TRACE_ERROR("%s\n", ock_err(ERR_FUNCTION_NOT_SUPPORTED));
+         rv = CKR_FUNCTION_NOT_SUPPORTED;
+@@ -4109,10 +4297,12 @@ CK_RV C_VerifyRecover(CK_SESSION_HANDLE hSession,
+         return CKR_TOKEN_NOT_PRESENT;
+     }
+     if (fcn->ST_VerifyRecover) {
++        BEGIN_OPENSSL_LIBCTX(Anchor->openssl_libctx, rv)
+         // Map the Session to the slot session
+         rv = fcn->ST_VerifyRecover(sltp->TokData, &rSession, pSignature,
+                                    ulSignatureLen, pData, pulDataLen);
+         TRACE_DEVEL("fcn->ST_VerifyRecover returned: 0x%lx\n", rv);
++        END_OPENSSL_LIBCTX(rv)
+     } else {
+         TRACE_ERROR("%s\n", ock_err(ERR_FUNCTION_NOT_SUPPORTED));
+         rv = CKR_FUNCTION_NOT_SUPPORTED;
+@@ -4160,10 +4350,12 @@ CK_RV C_VerifyRecoverInit(CK_SESSION_HANDLE hSession,
+         return CKR_TOKEN_NOT_PRESENT;
+     }
+     if (fcn->ST_VerifyRecoverInit) {
++        BEGIN_OPENSSL_LIBCTX(Anchor->openssl_libctx, rv)
+         // Map the Session to the slot session
+         rv = fcn->ST_VerifyRecoverInit(sltp->TokData, &rSession,
+                                        pMechanism, hKey);
+         TRACE_DEVEL("fcn->ST_VerifyRecoverInit returned:0x%lx\n", rv);
++        END_OPENSSL_LIBCTX(rv)
+     } else {
+         TRACE_ERROR("%s\n", ock_err(ERR_FUNCTION_NOT_SUPPORTED));
+         rv = CKR_FUNCTION_NOT_SUPPORTED;
+@@ -4207,9 +4399,11 @@ CK_RV C_VerifyUpdate(CK_SESSION_HANDLE hSession,
+         return CKR_TOKEN_NOT_PRESENT;
+     }
+     if (fcn->ST_VerifyUpdate) {
++        BEGIN_OPENSSL_LIBCTX(Anchor->openssl_libctx, rv)
+         // Map the Session to the slot session
+         rv = fcn->ST_VerifyUpdate(sltp->TokData, &rSession, pPart, ulPartLen);
+         TRACE_DEVEL("fcn->ST_VerifyUpdate returned: 0x%lx\n", rv);
++        END_OPENSSL_LIBCTX(rv)
+     } else {
+         TRACE_ERROR("%s\n", ock_err(ERR_FUNCTION_NOT_SUPPORTED));
+         rv = CKR_FUNCTION_NOT_SUPPORTED;
+@@ -4407,10 +4601,12 @@ CK_RV C_WrapKey(CK_SESSION_HANDLE hSession,
+         return CKR_TOKEN_NOT_PRESENT;
+     }
+     if (fcn->ST_WrapKey) {
++        BEGIN_OPENSSL_LIBCTX(Anchor->openssl_libctx, rv)
+         // Map the Session to the slot session
+         rv = fcn->ST_WrapKey(sltp->TokData, &rSession, pMechanism,
+                              hWrappingKey, hKey, pWrappedKey, pulWrappedKeyLen);
+         TRACE_DEVEL("fcn->ST_WrapKey returned: 0x%lx\n", rv);
++        END_OPENSSL_LIBCTX(rv)
+     } else {
+         TRACE_ERROR("%s\n", ock_err(ERR_FUNCTION_NOT_SUPPORTED));
+         rv = CKR_FUNCTION_NOT_SUPPORTED;
+@@ -5110,6 +5306,7 @@ CK_RV C_IBM_ReencryptSingle(CK_SESSION_HANDLE hSession,
+         return CKR_TOKEN_NOT_PRESENT;
+     }
+     if (fcn->ST_IBM_ReencryptSingle) {
++        BEGIN_OPENSSL_LIBCTX(Anchor->openssl_libctx, rv)
+         // Map the Session to the slot session
+         rv = fcn->ST_IBM_ReencryptSingle(sltp->TokData, &rSession, pDecrMech,
+                                          hDecrKey, pEncrMech, hEncrKey,
+@@ -5117,6 +5314,7 @@ CK_RV C_IBM_ReencryptSingle(CK_SESSION_HANDLE hSession,
+                                          pReencryptedData,
+                                          pulReencryptedDataLen);
+         TRACE_DEVEL("fcn->ST_IBM_ReencryptSingle returned: 0x%lx\n", rv);
++        END_OPENSSL_LIBCTX(rv)
+     } else {
+         TRACE_ERROR("%s\n", ock_err(ERR_FUNCTION_NOT_SUPPORTED));
+         rv = CKR_FUNCTION_NOT_SUPPORTED;
+diff --git a/usr/lib/api/socket_client.c b/usr/lib/api/socket_client.c
+index e344ddbf..423972a1 100644
+--- a/usr/lib/api/socket_client.c
++++ b/usr/lib/api/socket_client.c
+@@ -245,11 +245,22 @@ static int handle_event(API_Proc_Struct_t *anchor, event_msg_t *event,
+     return 0;
+ }
+ 
++struct cleanup_data {
++    API_Proc_Struct_t *anchor;
++#if OPENSSL_VERSION_PREREQ(3, 0)
++    OSSL_LIB_CTX *prev_libctx;
++#endif
++};
++
+ static void event_thread_cleanup(void *arg)
+ {
+-    API_Proc_Struct_t *anchor = arg;
++    struct cleanup_data *cleanup = arg;
+ 
+-    UNUSED(anchor);
++#if OPENSSL_VERSION_PREREQ(3, 0)
++    OSSL_LIB_CTX_set0_default(cleanup->prev_libctx);
++#else
++    UNUSED(cleanup);
++#endif
+ 
+     TRACE_DEVEL("Event thread %lu terminating\n", pthread_self());
+ }
+@@ -257,6 +268,10 @@ static void event_thread_cleanup(void *arg)
+ static void *event_thread(void *arg)
+ {
+     API_Proc_Struct_t *anchor = arg;
++    struct cleanup_data cleanup;
++#if OPENSSL_VERSION_PREREQ(3, 0)
++    OSSL_LIB_CTX *prev_libctx;
++#endif
+     int oldstate, oldtype;
+     struct pollfd pollfd;
+     event_msg_t event;
+@@ -275,10 +290,24 @@ static void *event_thread(void *arg)
+         return NULL;
+     }
+ 
++#if OPENSSL_VERSION_PREREQ(3, 0)
++    /* Ensure that the event thread uses Opencryptoki's own library context */
++    prev_libctx = OSSL_LIB_CTX_set0_default(Anchor->openssl_libctx);
++    if (prev_libctx == NULL) {
++        TRACE_ERROR("OSSL_LIB_CTX_set0_default failed\n");
++        TRACE_DEVEL("Event thread %lu terminating\n", pthread_self());
++        return NULL;
++    }
++#endif
++
+     /* Enable cancellation */
+     pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, &oldstate);
+     pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, &oldtype);
+-    pthread_cleanup_push(event_thread_cleanup, anchor);
++    cleanup.anchor = anchor;
++#if OPENSSL_VERSION_PREREQ(3, 0)
++    cleanup.prev_libctx = prev_libctx;
++#endif
++    pthread_cleanup_push(event_thread_cleanup, &cleanup);
+ 
+     pollfd.fd = anchor->socketfd;
+     pollfd.events = POLLIN | POLLHUP | POLLERR;
+@@ -395,6 +424,10 @@ static void *event_thread(void *arg)
+     close(anchor->socketfd);
+     anchor->socketfd = -1;
+ 
++#if OPENSSL_VERSION_PREREQ(3, 0)
++    OSSL_LIB_CTX_set0_default(prev_libctx);
++#endif
++
+     pthread_cleanup_pop(1);
+     return NULL;
+ }
diff --git a/SOURCES/opencryptoki-openssl3-7b4177e8557887d196ce77a129d457e817f8cc59.patch b/SOURCES/opencryptoki-openssl3-7b4177e8557887d196ce77a129d457e817f8cc59.patch
new file mode 100644
index 0000000..104a5f4
--- /dev/null
+++ b/SOURCES/opencryptoki-openssl3-7b4177e8557887d196ce77a129d457e817f8cc59.patch
@@ -0,0 +1,870 @@
+commit 7b4177e8557887d196ce77a129d457e817f8cc59
+Author: Ingo Franzki <ifranzki@linux.ibm.com>
+Date:   Wed Jun 30 10:47:28 2021 +0200
+
+    TPM: Remove deprecated OpenSSL functions
+    
+    All low level RSA functions are deprecated in OpenSSL 3.0.
+    Update the code to not use any of those, and only use the EVP
+    interface.
+    
+    Also remove support for OpenSSL < v1.1.1. This code used even more
+    low level RSA, DES, and AES functions.
+    
+    Signed-off-by: Ingo Franzki <ifranzki@linux.ibm.com>
+
+diff --git a/usr/lib/tpm_stdll/tpm_openssl.c b/usr/lib/tpm_stdll/tpm_openssl.c
+index 94ef9a62..0ccc543d 100644
+--- a/usr/lib/tpm_stdll/tpm_openssl.c
++++ b/usr/lib/tpm_stdll/tpm_openssl.c
+@@ -39,50 +39,33 @@
+ 
+ #include "tpm_specific.h"
+ 
+-/*
+- * In order to make opencryptoki compatible with
+- * OpenSSL 1.1 API Changes and backward compatible
+- * we need to check for its version
+- */
+-#if OPENSSL_VERSION_NUMBER < 0x10100000L
+-#define OLDER_OPENSSL
++#if OPENSSL_VERSION_PREREQ(3, 0)
++#include <openssl/core_names.h>
+ #endif
+ 
+ #ifdef DEBUG
+ void openssl_print_errors()
+ {
++#if !OPENSSL_VERSION_PREREQ(3, 0)
+     ERR_load_ERR_strings();
++#endif
+     ERR_load_crypto_strings();
+     ERR_print_errors_fp(stderr);
+ }
+ #endif
+ 
+-RSA *openssl_gen_key(STDLL_TokData_t *tokdata)
++EVP_PKEY *openssl_gen_key(STDLL_TokData_t *tokdata)
+ {
+-    RSA *rsa = NULL;
+     int rc = 0, counter = 0;
+     char buf[32];
+-#ifndef OLDER_OPENSSL
+     EVP_PKEY *pkey = NULL;
+     EVP_PKEY_CTX *ctx = NULL;
+     BIGNUM *bne = NULL;
+-#endif
+ 
+     token_specific_rng(tokdata, (CK_BYTE *) buf, 32);
+     RAND_seed(buf, 32);
+ 
+ regen_rsa_key:
+-#ifdef OLDER_OPENSSL
+-    rsa = RSA_generate_key(2048, 65537, NULL, NULL);
+-    if (rsa == NULL) {
+-        fprintf(stderr, "Error generating user's RSA key\n");
+-        ERR_load_crypto_strings();
+-        ERR_print_errors_fp(stderr);
+-        goto err;
+-    }
+-
+-    rc = RSA_check_key(rsa);
+-#else
+     bne = BN_new();
+     rc = BN_set_word(bne, 65537);
+     if (!rc) {
+@@ -98,35 +81,36 @@ regen_rsa_key:
+ 
+     if (EVP_PKEY_keygen_init(ctx) <= 0
+         || EVP_PKEY_CTX_set_rsa_keygen_bits(ctx, 2048) <= 0
++#if !OPENSSL_VERSION_PREREQ(3, 0)
+         || EVP_PKEY_CTX_set_rsa_keygen_pubexp(ctx, bne) <= 0) {
++#else
++        || EVP_PKEY_CTX_set1_rsa_keygen_pubexp(ctx, bne) <= 0) {
++#endif
+         fprintf(stderr, "Error generating user's RSA key\n");
+         ERR_load_crypto_strings();
+         ERR_print_errors_fp(stderr);
+         goto err;
+     }
++#if !OPENSSL_VERSION_PREREQ(3, 0)
+     bne = NULL; // will be freed as part of the context
+-    if (EVP_PKEY_keygen(ctx, &pkey) <= 0
+-        || (rsa = EVP_PKEY_get1_RSA(pkey)) == NULL) {
++#else
++    BN_free(bne);
++    bne = NULL;
++#endif
++    if (EVP_PKEY_keygen(ctx, &pkey) <= 0) {
+         fprintf(stderr, "Error generating user's RSA key\n");
+         ERR_load_crypto_strings();
+         ERR_print_errors_fp(stderr);
+         goto err;
+     }
+-#if OPENSSL_VERSION_NUMBER < 0x10101000L
+-    rc = RSA_check_key(rsa);
+-#else
+     EVP_PKEY_CTX_free(ctx);
+     ctx = EVP_PKEY_CTX_new(pkey, NULL);
+     if (ctx == NULL)
+         goto err;
+     rc = (EVP_PKEY_check(ctx) == 1 ? 1 : 0);
+-#endif
+-#endif
+     switch (rc) {
+     case 0:
+         /* rsa is not a valid RSA key */
+-        RSA_free(rsa);
+-        rsa = NULL;
+         counter++;
+         if (counter == KEYGEN_RETRY) {
+             TRACE_DEVEL("Tried %d times to generate a "
+@@ -145,30 +129,23 @@ regen_rsa_key:
+         break;
+     }
+ 
+-#ifndef OLDER_OPENSSL
+-    if (pkey != NULL)
+-        EVP_PKEY_free(pkey);
+     if (ctx != NULL)
+         EVP_PKEY_CTX_free(ctx);
+     if (bne != NULL)
+         BN_free(bne);
+-#endif
+-    return rsa;
++    return pkey;
+ err:
+-    if (rsa != NULL)
+-        RSA_free(rsa);
+-#ifndef OLDER_OPENSSL
+     if (pkey != NULL)
+         EVP_PKEY_free(pkey);
+     if (ctx != NULL)
+         EVP_PKEY_CTX_free(ctx);
+     if (bne != NULL)
+         BN_free(bne);
+-#endif
++
+     return NULL;
+ }
+ 
+-int openssl_write_key(STDLL_TokData_t * tokdata, RSA * rsa, char *filename,
++int openssl_write_key(STDLL_TokData_t * tokdata, EVP_PKEY *pkey, char *filename,
+                       CK_BYTE * pPin)
+ {
+     BIO *b = NULL;
+@@ -193,8 +170,8 @@ int openssl_write_key(STDLL_TokData_t * tokdata, RSA * rsa, char *filename,
+         return -1;
+     }
+ 
+-    if (!PEM_write_bio_RSAPrivateKey(b, rsa,
+-                                     EVP_aes_256_cbc(), NULL, 0, 0, pPin)) {
++    if (!PEM_write_bio_PrivateKey(b, pkey,
++                                  EVP_aes_256_cbc(), NULL, 0, 0, pPin)) {
+         BIO_free(b);
+         TRACE_ERROR("Writing key %s to disk failed.\n", loc);
+         DEBUG_openssl_print_errors();
+@@ -211,10 +188,10 @@ int openssl_write_key(STDLL_TokData_t * tokdata, RSA * rsa, char *filename,
+ }
+ 
+ CK_RV openssl_read_key(STDLL_TokData_t * tokdata, char *filename,
+-                       CK_BYTE * pPin, RSA ** ret)
++                       CK_BYTE * pPin, EVP_PKEY **ret)
+ {
+     BIO *b = NULL;
+-    RSA *rsa = NULL;
++    EVP_PKEY *pkey = NULL;
+     char loc[PATH_MAX];
+     struct passwd *pw = NULL;
+     CK_RV rc = CKR_FUNCTION_FAILED;
+@@ -242,7 +219,7 @@ CK_RV openssl_read_key(STDLL_TokData_t * tokdata, char *filename,
+         return CKR_FILE_NOT_FOUND;
+     }
+ 
+-    if ((rsa = PEM_read_bio_RSAPrivateKey(b, NULL, 0, pPin)) == NULL) {
++    if ((pkey = PEM_read_bio_PrivateKey(b, NULL, 0, pPin)) == NULL) {
+         TRACE_ERROR("Reading key %s from disk failed.\n", loc);
+         DEBUG_openssl_print_errors();
+         if (ERR_GET_REASON(ERR_get_error()) == PEM_R_BAD_DECRYPT) {
+@@ -253,40 +230,54 @@ CK_RV openssl_read_key(STDLL_TokData_t * tokdata, char *filename,
+     }
+ 
+     BIO_free(b);
+-    *ret = rsa;
++    *ret = pkey;
+ 
+     return CKR_OK;
+ }
+ 
+-int openssl_get_modulus_and_prime(RSA * rsa, unsigned int *size_n,
++int openssl_get_modulus_and_prime(EVP_PKEY *pkey, unsigned int *size_n,
+                                   unsigned char *n, unsigned int *size_p,
+                                   unsigned char *p)
+ {
+-#ifndef OLDER_OPENSSL
++#if !OPENSSL_VERSION_PREREQ(3, 0)
+     const BIGNUM *n_tmp, *p_tmp;
++    RSA *rsa;
++#else
++    BIGNUM *n_tmp, *p_tmp;
+ #endif
+ 
++#if !OPENSSL_VERSION_PREREQ(3, 0)
++    rsa = EVP_PKEY_get0_RSA(pkey);
+     /* get the modulus from the RSA object */
+-#ifdef OLDER_OPENSSL
+-    if ((*size_n = BN_bn2bin(rsa->n, n)) <= 0) {
+-#else
+     RSA_get0_key(rsa, &n_tmp, NULL, NULL);
+     if ((*size_n = BN_bn2bin(n_tmp, n)) <= 0) {
+-#endif
+         DEBUG_openssl_print_errors();
+         return -1;
+     }
+ 
+     /* get one of the primes from the RSA object */
+-#ifdef OLDER_OPENSSL
+-    if ((*size_p = BN_bn2bin(rsa->p, p)) <= 0) {
+-#else
+     RSA_get0_factors(rsa, &p_tmp, NULL);
+     if ((*size_p = BN_bn2bin(p_tmp, p)) <= 0) {
+-#endif
+         DEBUG_openssl_print_errors();
+         return -1;
+     }
++#else
++    if (!EVP_PKEY_get_bn_param(pkey, OSSL_PKEY_PARAM_RSA_N, &n_tmp) ||
++        (*size_n = BN_bn2bin(n_tmp, n)) <= 0) {
++        DEBUG_openssl_print_errors();
++        BN_free(n_tmp);
++        return -1;
++    }
++    BN_free(n_tmp);
++
++    if (!EVP_PKEY_get_bn_param(pkey, OSSL_PKEY_PARAM_RSA_FACTOR1, &p_tmp) ||
++        (*size_p = BN_bn2bin(p_tmp, p)) <= 0) {
++        DEBUG_openssl_print_errors();
++        BN_free(p_tmp);
++        return -1;
++    }
++    BN_free(p_tmp);
++#endif
+ 
+     return 0;
+ }
+diff --git a/usr/lib/tpm_stdll/tpm_specific.c b/usr/lib/tpm_stdll/tpm_specific.c
+index 4ebb4a88..45bc4b78 100644
+--- a/usr/lib/tpm_stdll/tpm_specific.c
++++ b/usr/lib/tpm_stdll/tpm_specific.c
+@@ -1451,15 +1451,15 @@ CK_RV token_create_private_tree(STDLL_TokData_t * tokdata, CK_BYTE * pinHash,
+     tpm_private_data_t *tpm_data = (tpm_private_data_t *)tokdata->private_data;
+     CK_RV rc;
+     TSS_RESULT result;
+-    RSA *rsa;
++    EVP_PKEY *pkey;
+     unsigned int size_n, size_p;
+     unsigned char n[256], p[256];
+ 
+     /* all sw generated keys are 2048 bits */
+-    if ((rsa = openssl_gen_key(tokdata)) == NULL)
++    if ((pkey = openssl_gen_key(tokdata)) == NULL)
+         return CKR_HOST_MEMORY;
+ 
+-    if (openssl_get_modulus_and_prime(rsa, &size_n, n, &size_p, p) != 0) {
++    if (openssl_get_modulus_and_prime(pkey, &size_n, n, &size_p, p) != 0) {
+         TRACE_DEVEL("openssl_get_modulus_and_prime failed\n");
+         return CKR_FUNCTION_FAILED;
+     }
+@@ -1473,13 +1473,13 @@ CK_RV token_create_private_tree(STDLL_TokData_t * tokdata, CK_BYTE * pinHash,
+         return rc;
+     }
+ 
+-    if (openssl_write_key(tokdata, rsa, TPMTOK_PRIV_ROOT_KEY_FILE, pPin)) {
++    if (openssl_write_key(tokdata, pkey, TPMTOK_PRIV_ROOT_KEY_FILE, pPin)) {
+         TRACE_DEVEL("openssl_write_key failed.\n");
+-        RSA_free(rsa);
++        EVP_PKEY_free(pkey);
+         return CKR_FUNCTION_FAILED;
+     }
+ 
+-    RSA_free(rsa);
++    EVP_PKEY_free(pkey);
+ 
+     /* store the user base key in a PKCS#11 object internally */
+     rc = token_store_tss_key(tokdata, tpm_data->hPrivateRootKey,
+@@ -1529,15 +1529,15 @@ CK_RV token_create_public_tree(STDLL_TokData_t * tokdata, CK_BYTE * pinHash,
+     tpm_private_data_t *tpm_data = (tpm_private_data_t *)tokdata->private_data;
+     CK_RV rc;
+     TSS_RESULT result;
+-    RSA *rsa;
++    EVP_PKEY *pkey;
+     unsigned int size_n, size_p;
+     unsigned char n[256], p[256];
+ 
+     /* all sw generated keys are 2048 bits */
+-    if ((rsa = openssl_gen_key(tokdata)) == NULL)
++    if ((pkey = openssl_gen_key(tokdata)) == NULL)
+         return CKR_HOST_MEMORY;
+ 
+-    if (openssl_get_modulus_and_prime(rsa, &size_n, n, &size_p, p) != 0) {
++    if (openssl_get_modulus_and_prime(pkey, &size_n, n, &size_p, p) != 0) {
+         TRACE_DEVEL("openssl_get_modulus_and_prime failed\n");
+         return CKR_FUNCTION_FAILED;
+     }
+@@ -1551,13 +1551,13 @@ CK_RV token_create_public_tree(STDLL_TokData_t * tokdata, CK_BYTE * pinHash,
+         return rc;
+     }
+ 
+-    if (openssl_write_key(tokdata, rsa, TPMTOK_PUB_ROOT_KEY_FILE, pPin)) {
++    if (openssl_write_key(tokdata, pkey, TPMTOK_PUB_ROOT_KEY_FILE, pPin)) {
+         TRACE_DEVEL("openssl_write_key\n");
+-        RSA_free(rsa);
++        EVP_PKEY_free(pkey);
+         return CKR_FUNCTION_FAILED;
+     }
+ 
+-    RSA_free(rsa);
++    EVP_PKEY_free(pkey);
+ 
+     result = Tspi_Key_LoadKey(tpm_data->hPublicRootKey, tpm_data->hSRK);
+     if (result) {
+@@ -1602,7 +1602,7 @@ CK_RV token_create_public_tree(STDLL_TokData_t * tokdata, CK_BYTE * pinHash,
+ CK_RV token_migrate(STDLL_TokData_t * tokdata, int key_type, CK_BYTE * pin)
+ {
+     tpm_private_data_t *tpm_data = (tpm_private_data_t *)tokdata->private_data;
+-    RSA *rsa;
++    EVP_PKEY *pkey;
+     char *backup_loc;
+     unsigned int size_n, size_p;
+     unsigned char n[256], p[256];
+@@ -1630,7 +1630,7 @@ CK_RV token_migrate(STDLL_TokData_t * tokdata, int key_type, CK_BYTE * pin)
+     }
+ 
+     /* read the backup key with the old pin */
+-    if ((rc = openssl_read_key(tokdata, backup_loc, pin, &rsa))) {
++    if ((rc = openssl_read_key(tokdata, backup_loc, pin, &pkey))) {
+         if (rc == CKR_FILE_NOT_FOUND)
+             rc = CKR_FUNCTION_FAILED;
+         TRACE_DEVEL("openssl_read_key failed\n");
+@@ -1640,8 +1640,9 @@ CK_RV token_migrate(STDLL_TokData_t * tokdata, int key_type, CK_BYTE * pin)
+     /* So, reading the backup openssl key off disk succeeded with the SOs PIN.
+      * We will now try to re-wrap that key with the current SRK
+      */
+-    if (openssl_get_modulus_and_prime(rsa, &size_n, n, &size_p, p) != 0) {
++    if (openssl_get_modulus_and_prime(pkey, &size_n, n, &size_p, p) != 0) {
+         TRACE_DEVEL("openssl_get_modulus_and_prime failed\n");
++        EVP_PKEY_free(pkey);
+         return CKR_FUNCTION_FAILED;
+     }
+ 
+@@ -1650,10 +1651,10 @@ CK_RV token_migrate(STDLL_TokData_t * tokdata, int key_type, CK_BYTE * pin)
+                            phKey);
+     if (rc != CKR_OK) {
+         TRACE_DEVEL("token_wrap_sw_key failed. rc=0x%lx\n", rc);
+-        RSA_free(rsa);
++        EVP_PKEY_free(pkey);
+         return rc;
+     }
+-    RSA_free(rsa);
++    EVP_PKEY_free(pkey);
+ 
+     result = Tspi_Key_LoadKey(*phKey, tpm_data->hSRK);
+     if (result) {
+@@ -1998,7 +1999,7 @@ CK_RV token_specific_set_pin(STDLL_TokData_t * tokdata, SESSION * sess,
+     tpm_private_data_t *tpm_data = (tpm_private_data_t *)tokdata->private_data;
+     CK_BYTE oldpin_hash[SHA1_HASH_SIZE], newpin_hash[SHA1_HASH_SIZE];
+     CK_RV rc;
+-    RSA *rsa_root;
++    EVP_PKEY *pkey_root;
+     TSS_RESULT result;
+ 
+     if (!sess) {
+@@ -2094,7 +2095,7 @@ CK_RV token_specific_set_pin(STDLL_TokData_t * tokdata, SESSION * sess,
+ 
+         /* read the backup key with the old pin */
+         rc = openssl_read_key(tokdata, TPMTOK_PRIV_ROOT_KEY_FILE, pOldPin,
+-                              &rsa_root);
++                              &pkey_root);
+         if (rc != CKR_OK) {
+             if (rc == CKR_FILE_NOT_FOUND) {
+                 /* If the user has moved his backup PEM file off site, allow a
+@@ -2107,14 +2108,14 @@ CK_RV token_specific_set_pin(STDLL_TokData_t * tokdata, SESSION * sess,
+         }
+ 
+         /* write it out using the new pin */
+-        rc = openssl_write_key(tokdata, rsa_root, TPMTOK_PRIV_ROOT_KEY_FILE,
++        rc = openssl_write_key(tokdata, pkey_root, TPMTOK_PRIV_ROOT_KEY_FILE,
+                                pNewPin);
+         if (rc != CKR_OK) {
+-            RSA_free(rsa_root);
++            EVP_PKEY_free(pkey_root);
+             TRACE_DEVEL("openssl_write_key failed\n");
+             return CKR_FUNCTION_FAILED;
+         }
+-        RSA_free(rsa_root);
++        EVP_PKEY_free(pkey_root);
+     } else if (sess->session_info.state == CKS_RW_SO_FUNCTIONS) {
+         if (tpm_data->not_initialized) {
+             if (memcmp(default_so_pin_sha, oldpin_hash, SHA1_HASH_SIZE)) {
+@@ -2166,7 +2167,7 @@ CK_RV token_specific_set_pin(STDLL_TokData_t * tokdata, SESSION * sess,
+ 
+         /* change auth on the public root key's openssl backup */
+         rc = openssl_read_key(tokdata, TPMTOK_PUB_ROOT_KEY_FILE, pOldPin,
+-                              &rsa_root);
++                              &pkey_root);
+         if (rc != CKR_OK) {
+             if (rc == CKR_FILE_NOT_FOUND) {
+                 /* If the user has moved his backup PEM file off site, allow a
+@@ -2179,14 +2180,14 @@ CK_RV token_specific_set_pin(STDLL_TokData_t * tokdata, SESSION * sess,
+         }
+ 
+         /* write it out using the new pin */
+-        rc = openssl_write_key(tokdata, rsa_root, TPMTOK_PUB_ROOT_KEY_FILE,
++        rc = openssl_write_key(tokdata, pkey_root, TPMTOK_PUB_ROOT_KEY_FILE,
+                                pNewPin);
+         if (rc != CKR_OK) {
+-            RSA_free(rsa_root);
++            EVP_PKEY_free(pkey_root);
+             TRACE_DEVEL("openssl_write_key failed\n");
+             return CKR_FUNCTION_FAILED;
+         }
+-        RSA_free(rsa_root);
++        EVP_PKEY_free(pkey_root);
+     } else {
+         TRACE_ERROR("%s\n", ock_err(ERR_SESSION_READ_ONLY));
+         rc = CKR_SESSION_READ_ONLY;
+@@ -2401,60 +2402,6 @@ CK_RV token_specific_des_ecb(STDLL_TokData_t * tokdata,
+                              CK_ULONG * out_data_len,
+                              OBJECT * key, CK_BYTE encrypt)
+ {
+-#if OPENSSL_VERSION_NUMBER < 0x10100000L
+-    CK_RV rc;
+-    CK_ATTRIBUTE *attr = NULL;
+-
+-    DES_key_schedule des_key2;
+-    const_DES_cblock key_val_SSL, in_key_data;
+-    DES_cblock out_key_data;
+-    unsigned int i, j;
+-
+-    UNUSED(tokdata);
+-
+-    // get the key value
+-    rc = template_attribute_get_non_empty(key->template, CKA_VALUE, &attr);
+-    if (rc != CKR_OK) {
+-        TRACE_ERROR("Could not find CKA_VALUE for the key.\n");
+-        return rc;
+-    }
+-
+-    // Create the key schedule
+-    memcpy(&key_val_SSL, attr->pValue, 8);
+-    DES_set_key_unchecked(&key_val_SSL, &des_key2);
+-
+-    // the des decrypt will only fail if the data length is not evenly divisible
+-    // by 8
+-    if (in_data_len % DES_BLOCK_SIZE) {
+-        TRACE_ERROR("%s\n", ock_err(ERR_DATA_LEN_RANGE));
+-        return CKR_DATA_LEN_RANGE;
+-    }
+-    // Both the encrypt and the decrypt are done 8 bytes at a time
+-    if (encrypt) {
+-        for (i = 0; i < in_data_len; i = i + 8) {
+-            memcpy(in_key_data, in_data + i, 8);
+-            DES_ecb_encrypt(&in_key_data, &out_key_data, &des_key2,
+-                            DES_ENCRYPT);
+-            memcpy(out_data + i, out_key_data, 8);
+-        }
+-
+-        *out_data_len = in_data_len;
+-        rc = CKR_OK;
+-    } else {
+-
+-        for (j = 0; j < in_data_len; j = j + 8) {
+-            memcpy(in_key_data, in_data + j, 8);
+-            DES_ecb_encrypt(&in_key_data, &out_key_data, &des_key2,
+-                            DES_DECRYPT);
+-            memcpy(out_data + j, out_key_data, 8);
+-        }
+-
+-        *out_data_len = in_data_len;
+-        rc = CKR_OK;
+-    }
+-
+-    return rc;
+-#else
+     const EVP_CIPHER *cipher = EVP_des_ecb();
+     EVP_CIPHER_CTX *ctx = NULL;
+     CK_ATTRIBUTE *attr = NULL;
+@@ -2501,7 +2448,6 @@ done:
+     OPENSSL_cleanse(dkey, sizeof(dkey));
+     EVP_CIPHER_CTX_free(ctx);
+     return rc;
+-#endif
+ }
+ 
+ CK_RV token_specific_des_cbc(STDLL_TokData_t * tokdata,
+@@ -2511,50 +2457,6 @@ CK_RV token_specific_des_cbc(STDLL_TokData_t * tokdata,
+                              CK_ULONG * out_data_len,
+                              OBJECT * key, CK_BYTE * init_v, CK_BYTE encrypt)
+ {
+-#if OPENSSL_VERSION_NUMBER < 0x10100000L
+-    CK_RV rc;
+-    CK_ATTRIBUTE *attr = NULL;
+-
+-    DES_cblock ivec;
+-
+-    DES_key_schedule des_key2;
+-    const_DES_cblock key_val_SSL;
+-
+-    UNUSED(tokdata);
+-
+-    // get the key value
+-    rc = template_attribute_get_non_empty(key->template, CKA_VALUE, &attr);
+-    if (rc != CKR_OK) {
+-        TRACE_ERROR("Could not find CKA_VALUE for the key.\n");
+-        return rc;
+-    }
+-
+-    // Create the key schedule
+-    memcpy(&key_val_SSL, attr->pValue, 8);
+-    DES_set_key_unchecked(&key_val_SSL, &des_key2);
+-
+-    memcpy(&ivec, init_v, 8);
+-    // the des decrypt will only fail if the data length is not evenly divisible
+-    // by 8
+-    if (in_data_len % DES_BLOCK_SIZE) {
+-        TRACE_ERROR("%s\n", ock_err(ERR_DATA_LEN_RANGE));
+-        return CKR_DATA_LEN_RANGE;
+-    }
+-
+-
+-    if (encrypt) {
+-        DES_ncbc_encrypt(in_data, out_data, in_data_len, &des_key2, &ivec,
+-                         DES_ENCRYPT);
+-        *out_data_len = in_data_len;
+-        rc = CKR_OK;
+-    } else {
+-        DES_ncbc_encrypt(in_data, out_data, in_data_len, &des_key2, &ivec,
+-                         DES_DECRYPT);
+-        *out_data_len = in_data_len;
+-        rc = CKR_OK;
+-    }
+-    return rc;
+-#else
+     const EVP_CIPHER *cipher = EVP_des_cbc();
+     EVP_CIPHER_CTX *ctx = NULL;
+     CK_ATTRIBUTE *attr = NULL;
+@@ -2601,7 +2503,6 @@ done:
+     OPENSSL_cleanse(dkey, sizeof(dkey));
+     EVP_CIPHER_CTX_free(ctx);
+     return rc;
+-#endif
+ }
+ 
+ CK_RV token_specific_tdes_ecb(STDLL_TokData_t * tokdata,
+@@ -2611,83 +2512,6 @@ CK_RV token_specific_tdes_ecb(STDLL_TokData_t * tokdata,
+                               CK_ULONG * out_data_len,
+                               OBJECT * key, CK_BYTE encrypt)
+ {
+-#if OPENSSL_VERSION_NUMBER < 0x10100000L
+-    CK_RV rc;
+-    CK_ATTRIBUTE *attr = NULL;
+-    CK_KEY_TYPE keytype;
+-    CK_BYTE key_value[3 * DES_KEY_SIZE];
+-
+-    unsigned int k, j;
+-    DES_key_schedule des_key1;
+-    DES_key_schedule des_key2;
+-    DES_key_schedule des_key3;
+-
+-    const_DES_cblock key_SSL1, key_SSL2, key_SSL3, in_key_data;
+-    DES_cblock out_key_data;
+-
+-    UNUSED(tokdata);
+-
+-    // get the key type
+-    rc = template_attribute_get_ulong(key->template, CKA_KEY_TYPE, &keytype);
+-    if (rc != CKR_OK) {
+-        TRACE_ERROR("Could not find CKA_KEY_TYPE for the key\n");
+-        return rc;
+-    }
+-
+-    // get the key value
+-    rc = template_attribute_get_non_empty(key->template, CKA_VALUE, &attr);
+-    if (rc != CKR_OK) {
+-        TRACE_ERROR("Could not find CKA_VALUE for the key\n");
+-        return rc;
+-    }
+-
+-    if (keytype == CKK_DES2) {
+-        memcpy(key_value, attr->pValue, 2 * DES_KEY_SIZE);
+-        memcpy(key_value + (2 * DES_KEY_SIZE), attr->pValue, DES_KEY_SIZE);
+-    } else {
+-        memcpy(key_value, attr->pValue, 3 * DES_KEY_SIZE);
+-    }
+-
+-    // The key as passed is a 24 byte long string containing three des keys
+-    // pick them apart and create the 3 corresponding key schedules
+-    memcpy(&key_SSL1, key_value, 8);
+-    memcpy(&key_SSL2, key_value + 8, 8);
+-    memcpy(&key_SSL3, key_value + 16, 8);
+-    DES_set_key_unchecked(&key_SSL1, &des_key1);
+-    DES_set_key_unchecked(&key_SSL2, &des_key2);
+-    DES_set_key_unchecked(&key_SSL3, &des_key3);
+-
+-    // the des decrypt will only fail if the data length is not evenly divisible
+-    // by 8
+-    if (in_data_len % DES_BLOCK_SIZE) {
+-        TRACE_ERROR("%s\n", ock_err(ERR_DATA_LEN_RANGE));
+-        return CKR_DATA_LEN_RANGE;
+-    }
+-    // the encrypt and decrypt are done 8 bytes at a time
+-    if (encrypt) {
+-        for (k = 0; k < in_data_len; k = k + 8) {
+-            memcpy(in_key_data, in_data + k, 8);
+-            DES_ecb3_encrypt((const_DES_cblock *) & in_key_data,
+-                             (DES_cblock *) & out_key_data,
+-                             &des_key1, &des_key2, &des_key3, DES_ENCRYPT);
+-            memcpy(out_data + k, out_key_data, 8);
+-        }
+-        *out_data_len = in_data_len;
+-        rc = CKR_OK;
+-    } else {
+-        for (j = 0; j < in_data_len; j = j + 8) {
+-            memcpy(in_key_data, in_data + j, 8);
+-            DES_ecb3_encrypt((const_DES_cblock *) & in_key_data,
+-                             (DES_cblock *) & out_key_data,
+-                             &des_key1, &des_key2, &des_key3, DES_DECRYPT);
+-            memcpy(out_data + j, out_key_data, 8);
+-        }
+-        *out_data_len = in_data_len;
+-        rc = CKR_OK;
+-    }
+-
+-    return rc;
+-#else
+     const EVP_CIPHER *cipher = EVP_des_ede3_ecb();
+     EVP_CIPHER_CTX *ctx = NULL;
+     CK_ATTRIBUTE *attr = NULL;
+@@ -2747,7 +2571,6 @@ done:
+     OPENSSL_cleanse(dkey, sizeof(dkey));
+     EVP_CIPHER_CTX_free(ctx);
+     return rc;
+-#endif
+ }
+ 
+ CK_RV token_specific_tdes_cbc(STDLL_TokData_t * tokdata,
+@@ -2757,81 +2580,6 @@ CK_RV token_specific_tdes_cbc(STDLL_TokData_t * tokdata,
+                               CK_ULONG * out_data_len,
+                               OBJECT * key, CK_BYTE * init_v, CK_BYTE encrypt)
+ {
+-#if OPENSSL_VERSION_NUMBER < 0x10100000L
+-    CK_RV rc = CKR_OK;
+-    CK_ATTRIBUTE *attr = NULL;
+-    CK_KEY_TYPE keytype;
+-    CK_BYTE key_value[3 * DES_KEY_SIZE];
+-
+-    DES_key_schedule des_key1;
+-    DES_key_schedule des_key2;
+-    DES_key_schedule des_key3;
+-
+-    const_DES_cblock key_SSL1, key_SSL2, key_SSL3;
+-    DES_cblock ivec;
+-
+-    UNUSED(tokdata);
+-
+-    // get the key type
+-    rc = template_attribute_get_ulong(key->template, CKA_KEY_TYPE, &keytype);
+-    if (rc != CKR_OK) {
+-        TRACE_ERROR("Could not find CKA_KEY_TYPE for the key\n");
+-        return rc;
+-    }
+-
+-    // get the key value
+-    rc = template_attribute_get_non_empty(key->template, CKA_VALUE, &attr);
+-    if (rc != CKR_OK) {
+-        TRACE_ERROR("Could not find CKA_VALUE for the key\n");
+-        return rc;
+-    }
+-
+-    if (keytype == CKK_DES2) {
+-        memcpy(key_value, attr->pValue, 2 * DES_KEY_SIZE);
+-        memcpy(key_value + (2 * DES_KEY_SIZE), attr->pValue, DES_KEY_SIZE);
+-    } else {
+-        memcpy(key_value, attr->pValue, 3 * DES_KEY_SIZE);
+-    }
+-
+-    // The key as passed in is a 24 byte string containing 3 keys
+-    // pick it apart and create the key schedules
+-    memcpy(&key_SSL1, key_value, 8);
+-    memcpy(&key_SSL2, key_value + 8, 8);
+-    memcpy(&key_SSL3, key_value + 16, 8);
+-    DES_set_key_unchecked(&key_SSL1, &des_key1);
+-    DES_set_key_unchecked(&key_SSL2, &des_key2);
+-    DES_set_key_unchecked(&key_SSL3, &des_key3);
+-
+-    memcpy(ivec, init_v, sizeof(ivec));
+-
+-    // the des decrypt will only fail if the data length is not evenly divisible
+-    // by 8
+-    if (in_data_len % DES_BLOCK_SIZE) {
+-        TRACE_ERROR("%s\n", ock_err(ERR_DATA_LEN_RANGE));
+-        return CKR_DATA_LEN_RANGE;
+-    }
+-    // Encrypt or decrypt the data
+-    if (encrypt) {
+-        DES_ede3_cbc_encrypt(in_data,
+-                             out_data,
+-                             in_data_len,
+-                             &des_key1,
+-                             &des_key2, &des_key3, &ivec, DES_ENCRYPT);
+-        *out_data_len = in_data_len;
+-        rc = CKR_OK;
+-    } else {
+-        DES_ede3_cbc_encrypt(in_data,
+-                             out_data,
+-                             in_data_len,
+-                             &des_key1,
+-                             &des_key2, &des_key3, &ivec, DES_DECRYPT);
+-
+-        *out_data_len = in_data_len;
+-        rc = CKR_OK;
+-    }
+-
+-    return rc;
+-#else
+     const EVP_CIPHER *cipher = EVP_des_ede3_cbc();
+     EVP_CIPHER_CTX *ctx = NULL;
+     CK_ATTRIBUTE *attr = NULL;
+@@ -2891,7 +2639,6 @@ done:
+     OPENSSL_cleanse(dkey, sizeof(dkey));
+     EVP_CIPHER_CTX_free(ctx);
+     return rc;
+-#endif
+ }
+ 
+ /* wrap the 20 bytes of auth data @authData and store in an attribute of the two
+@@ -3626,49 +3373,6 @@ CK_RV token_specific_aes_ecb(STDLL_TokData_t * tokdata,
+                              CK_ULONG * out_data_len,
+                              OBJECT * key, CK_BYTE encrypt)
+ {
+-#if OPENSSL_VERSION_NUMBER < 0x10100000L
+-    CK_ATTRIBUTE *attr = NULL;
+-    AES_KEY ssl_aes_key;
+-    unsigned int i;
+-    /* There's a previous check that in_data_len % AES_BLOCK_SIZE == 0,
+-     * so this is fine */
+-    CK_ULONG loops = (CK_ULONG) (in_data_len / AES_BLOCK_SIZE);
+-    CK_RV rc;
+-
+-    UNUSED(tokdata);
+-
+-    // get the key value
+-    rc = template_attribute_get_non_empty(key->template, CKA_VALUE, &attr);
+-    if (rc != CKR_OK) {
+-        TRACE_ERROR("Could not find CKA_VALUE for the key.\n");
+-        return rc;
+-    }
+-
+-    memset(&ssl_aes_key, 0, sizeof(AES_KEY));
+-
+-    // AES_ecb_encrypt encrypts only a single block, so we have to break up the
+-    // input data here
+-    if (encrypt) {
+-        AES_set_encrypt_key((unsigned char *) attr->pValue,
+-                            (attr->ulValueLen * 8), &ssl_aes_key);
+-        for (i = 0; i < loops; i++) {
+-            AES_ecb_encrypt((unsigned char *) in_data + (i * AES_BLOCK_SIZE),
+-                            (unsigned char *) out_data + (i * AES_BLOCK_SIZE),
+-                            &ssl_aes_key, AES_ENCRYPT);
+-        }
+-    } else {
+-        AES_set_decrypt_key((unsigned char *) attr->pValue,
+-                            (attr->ulValueLen * 8), &ssl_aes_key);
+-        for (i = 0; i < loops; i++) {
+-            AES_ecb_encrypt((unsigned char *) in_data + (i * AES_BLOCK_SIZE),
+-                            (unsigned char *) out_data + (i * AES_BLOCK_SIZE),
+-                            &ssl_aes_key, AES_DECRYPT);
+-        }
+-    }
+-    *out_data_len = in_data_len;
+-
+-    return CKR_OK;
+-#else
+     CK_RV rc;
+     int outlen;
+     unsigned char akey[AES_KEY_SIZE_256];
+@@ -3729,7 +3433,6 @@ done:
+     OPENSSL_cleanse(akey, sizeof(akey));
+     EVP_CIPHER_CTX_free(ctx);
+     return rc;
+-#endif
+ }
+ 
+ CK_RV token_specific_aes_cbc(STDLL_TokData_t * tokdata,
+@@ -3739,39 +3442,6 @@ CK_RV token_specific_aes_cbc(STDLL_TokData_t * tokdata,
+                              CK_ULONG * out_data_len,
+                              OBJECT * key, CK_BYTE * init_v, CK_BYTE encrypt)
+ {
+-#if OPENSSL_VERSION_NUMBER < 0x10100000L
+-    AES_KEY ssl_aes_key;
+-    CK_ATTRIBUTE *attr = NULL;
+-    CK_RV rc;
+-
+-    UNUSED(tokdata);
+-
+-    // get the key value
+-    rc = template_attribute_get_non_empty(key->template, CKA_VALUE, &attr);
+-    if (rc != CKR_OK) {
+-        TRACE_ERROR("Could not find CKA_VALUE for the key.\n");
+-        return rc;
+-    }
+-
+-    memset(&ssl_aes_key, 0, sizeof(AES_KEY));
+-
+-    // AES_cbc_encrypt chunks the data into AES_BLOCK_SIZE blocks, unlike
+-    // AES_ecb_encrypt, so no looping required.
+-    if (encrypt) {
+-        AES_set_encrypt_key((unsigned char *) attr->pValue,
+-                            (attr->ulValueLen * 8), &ssl_aes_key);
+-        AES_cbc_encrypt((unsigned char *) in_data, (unsigned char *) out_data,
+-                        in_data_len, &ssl_aes_key, init_v, AES_ENCRYPT);
+-    } else {
+-        AES_set_decrypt_key((unsigned char *) attr->pValue,
+-                            (attr->ulValueLen * 8), &ssl_aes_key);
+-        AES_cbc_encrypt((unsigned char *) in_data, (unsigned char *) out_data,
+-                        in_data_len, &ssl_aes_key, init_v, AES_DECRYPT);
+-    }
+-    *out_data_len = in_data_len;
+-
+-    return CKR_OK;
+-#else
+     CK_RV rc;
+     int outlen;
+     unsigned char akey[AES_KEY_SIZE_256];
+@@ -3832,7 +3502,6 @@ done:
+     OPENSSL_cleanse(akey, sizeof(akey));
+     EVP_CIPHER_CTX_free(ctx);
+     return rc;
+-#endif
+ }
+ 
+ CK_RV token_specific_get_mechanism_list(STDLL_TokData_t * tokdata,
+diff --git a/usr/lib/tpm_stdll/tpm_specific.h b/usr/lib/tpm_stdll/tpm_specific.h
+index 81af2744..2ffd0afc 100644
+--- a/usr/lib/tpm_stdll/tpm_specific.h
++++ b/usr/lib/tpm_stdll/tpm_specific.h
+@@ -56,10 +56,10 @@
+ /* retry count for generating software RSA keys */
+ #define KEYGEN_RETRY    5
+ 
+-RSA *openssl_gen_key(STDLL_TokData_t *);
+-int openssl_write_key(STDLL_TokData_t *, RSA *, char *, CK_BYTE *);
+-CK_RV openssl_read_key(STDLL_TokData_t *, char *, CK_BYTE *, RSA **);
+-int openssl_get_modulus_and_prime(RSA *, unsigned int *, unsigned char *,
++EVP_PKEY *openssl_gen_key(STDLL_TokData_t *);
++int openssl_write_key(STDLL_TokData_t *, EVP_PKEY *, char *, CK_BYTE *);
++CK_RV openssl_read_key(STDLL_TokData_t *, char *, CK_BYTE *, EVP_PKEY **);
++int openssl_get_modulus_and_prime(EVP_PKEY *, unsigned int *, unsigned char *,
+                                   unsigned int *, unsigned char *);
+ int util_set_file_mode(char *, mode_t);
+ CK_BYTE *util_create_id(int);
diff --git a/SOURCES/opencryptoki-openssl3-93588f53d918fe6c7452da076b95081fb6aa9aef.patch b/SOURCES/opencryptoki-openssl3-93588f53d918fe6c7452da076b95081fb6aa9aef.patch
new file mode 100644
index 0000000..a9d436e
--- /dev/null
+++ b/SOURCES/opencryptoki-openssl3-93588f53d918fe6c7452da076b95081fb6aa9aef.patch
@@ -0,0 +1,1847 @@
+commit 93588f53d918fe6c7452da076b95081fb6aa9aef
+Author: Ingo Franzki <ifranzki@linux.ibm.com>
+Date:   Wed Jun 30 13:18:39 2021 +0200
+
+    COMMON: Prevent unsavable operation state to be exported
+    
+    Tokens using OpenSSL 3.0 to implement digest operations (SHA, MD5) are no
+    longer able to store its digest state in the session context in a way
+    that it could be exported via C_GetOperationState(). OpenSSL 3.0 does not
+    provide support to get the digest state. A token must therefore place
+    pointers to OpenSSL digest contexts into the session state structure.
+    Such a state can not be externalized through C_GetOperationState().
+    
+    Also see the discussion in OpenSSL issue "Digest State Serialization":
+    https://github.com/openssl/openssl/issues/14222
+    
+    Allow a token to mark an operation context as 'not saveable', which will
+    cause C_GetOperationState() to return CKR_STATE_UNSAVEABLE if it is tried
+    to save such a state.
+    
+    Also, such operation contexts can not simply be freed that way the common
+    code performs that. Allow a token to use a custom context free function,
+    to cleanup such complex context structures.
+    
+    Signed-off-by: Ingo Franzki <ifranzki@linux.ibm.com>
+
+diff --git a/usr/lib/common/decr_mgr.c b/usr/lib/common/decr_mgr.c
+index 9842302b..fea6c99e 100644
+--- a/usr/lib/common/decr_mgr.c
++++ b/usr/lib/common/decr_mgr.c
+@@ -620,7 +620,8 @@ done:
+ 
+ //
+ //
+-CK_RV decr_mgr_cleanup(ENCR_DECR_CONTEXT *ctx)
++CK_RV decr_mgr_cleanup(STDLL_TokData_t *tokdata, SESSION *sess,
++                       ENCR_DECR_CONTEXT *ctx)
+ {
+     if (!ctx) {
+         TRACE_ERROR("Invalid function argument.\n");
+@@ -635,6 +636,7 @@ CK_RV decr_mgr_cleanup(ENCR_DECR_CONTEXT *ctx)
+     ctx->init_pending = FALSE;
+     ctx->context_len = 0;
+     ctx->pkey_active = FALSE;
++    ctx->state_unsaveable = FALSE;
+ 
+     if (ctx->mech.pParameter) {
+         free(ctx->mech.pParameter);
+@@ -642,9 +644,14 @@ CK_RV decr_mgr_cleanup(ENCR_DECR_CONTEXT *ctx)
+     }
+ 
+     if (ctx->context) {
+-        free(ctx->context);
++        if (ctx->context_free_func != NULL)
++            ctx->context_free_func(tokdata, sess, ctx->context,
++                                   ctx->context_len);
++        else
++            free(ctx->context);
+         ctx->context = NULL;
+     }
++    ctx->context_free_func = NULL;
+ 
+     return CKR_OK;
+ }
+diff --git a/usr/lib/common/dig_mgr.c b/usr/lib/common/dig_mgr.c
+index 77cb60a1..222eee75 100644
+--- a/usr/lib/common/dig_mgr.c
++++ b/usr/lib/common/dig_mgr.c
+@@ -63,7 +63,7 @@ CK_RV digest_mgr_init(STDLL_TokData_t *tokdata,
+         ctx->context = NULL;
+         rc = sha_init(tokdata, sess, ctx, mech);
+         if (rc != CKR_OK) {
+-            digest_mgr_cleanup(ctx);    // to de-initialize context above
++            digest_mgr_cleanup(tokdata, sess, ctx);    // to de-initialize context above
+             TRACE_ERROR("Failed to init sha context.\n");
+             return rc;
+         }
+@@ -76,7 +76,7 @@ CK_RV digest_mgr_init(STDLL_TokData_t *tokdata,
+         ctx->context_len = sizeof(MD2_CONTEXT);
+         ctx->context = (CK_BYTE *) malloc(sizeof(MD2_CONTEXT));
+         if (!ctx->context) {
+-            digest_mgr_cleanup(ctx);    // to de-initialize context above
++            digest_mgr_cleanup(tokdata, sess, ctx);    // to de-initialize context above
+             TRACE_ERROR("%s\n", ock_err(ERR_HOST_MEMORY));
+             return CKR_HOST_MEMORY;
+         }
+@@ -90,7 +90,7 @@ CK_RV digest_mgr_init(STDLL_TokData_t *tokdata,
+           ctx->context = NULL;
+           rc = md5_init(tokdata, sess, ctx, mech);
+           if (rc != CKR_OK) {
+-              digest_mgr_cleanup(ctx);    // to de-initialize context above
++              digest_mgr_cleanup(tokdata, sess, ctx);    // to de-initialize context above
+               TRACE_ERROR("Failed to init md5 context.\n");
+               return rc;
+           }
+@@ -103,7 +103,7 @@ CK_RV digest_mgr_init(STDLL_TokData_t *tokdata,
+     if (mech->ulParameterLen > 0 && mech->pParameter != NULL) {
+         ptr = (CK_BYTE *) malloc(mech->ulParameterLen);
+         if (!ptr) {
+-            digest_mgr_cleanup(ctx);    // to de-initialize context above
++            digest_mgr_cleanup(tokdata, sess, ctx);    // to de-initialize context above
+             TRACE_ERROR("%s\n", ock_err(ERR_HOST_MEMORY));
+             return CKR_HOST_MEMORY;
+         }
+@@ -122,7 +122,8 @@ CK_RV digest_mgr_init(STDLL_TokData_t *tokdata,
+ 
+ //
+ //
+-CK_RV digest_mgr_cleanup(DIGEST_CONTEXT *ctx)
++CK_RV digest_mgr_cleanup(STDLL_TokData_t *tokdata, SESSION *sess,
++                         DIGEST_CONTEXT *ctx)
+ {
+     if (!ctx) {
+         TRACE_ERROR("Invalid function argument.\n");
+@@ -134,6 +135,7 @@ CK_RV digest_mgr_cleanup(DIGEST_CONTEXT *ctx)
+     ctx->multi = FALSE;
+     ctx->active = FALSE;
+     ctx->context_len = 0;
++    ctx->state_unsaveable = FALSE;
+ 
+     if (ctx->mech.pParameter) {
+         free(ctx->mech.pParameter);
+@@ -141,9 +143,14 @@ CK_RV digest_mgr_cleanup(DIGEST_CONTEXT *ctx)
+     }
+ 
+     if (ctx->context != NULL) {
+-        free(ctx->context);
++        if (ctx->context_free_func != NULL)
++            ctx->context_free_func(tokdata, sess, ctx->context,
++                                   ctx->context_len);
++        else
++            free(ctx->context);
+         ctx->context = NULL;
+     }
++    ctx->context_free_func = NULL;
+ 
+     return CKR_OK;
+ }
+@@ -232,7 +239,7 @@ out:
+         // unless it returns CKR_BUFFER_TOO_SMALL or is a successful call (i.e.,
+         // one which returns CKR_OK) to determine the length of the buffer
+         // needed to hold the message digest."
+-        digest_mgr_cleanup(ctx);
++        digest_mgr_cleanup(tokdata, sess, ctx);
+     }
+ 
+     return rc;
+@@ -301,7 +308,7 @@ CK_RV digest_mgr_digest_update(STDLL_TokData_t *tokdata,
+ 
+ out:
+     if (rc != CKR_OK) {
+-        digest_mgr_cleanup(ctx);
++        digest_mgr_cleanup(tokdata, sess, ctx);
+         // "A call to C_DigestUpdate which results in an error
+         // terminates the current digest operation."
+     }
+@@ -373,7 +380,7 @@ CK_RV digest_mgr_digest_key(STDLL_TokData_t *tokdata,
+ 
+ out:
+     if (rc != CKR_OK) {
+-        digest_mgr_cleanup(ctx);
++        digest_mgr_cleanup(tokdata, sess, ctx);
+     }
+ 
+     object_put(tokdata, key_obj, TRUE);
+@@ -451,7 +458,7 @@ out:
+         // operation unless it returns CKR_BUFFER_TOO_SMALL or is a successful
+         // call (i.e., one which returns CKR_OK) to determine the length of the
+         // buffer needed to hold the message digest."
+-        digest_mgr_cleanup(ctx);
++        digest_mgr_cleanup(tokdata, sess, ctx);
+     }
+ 
+     return rc;
+diff --git a/usr/lib/common/encr_mgr.c b/usr/lib/common/encr_mgr.c
+index 3e85ceab..7f7dfbae 100644
+--- a/usr/lib/common/encr_mgr.c
++++ b/usr/lib/common/encr_mgr.c
+@@ -617,7 +617,8 @@ done:
+ 
+ //
+ //
+-CK_RV encr_mgr_cleanup(ENCR_DECR_CONTEXT *ctx)
++CK_RV encr_mgr_cleanup(STDLL_TokData_t *tokdata, SESSION *sess,
++                       ENCR_DECR_CONTEXT *ctx)
+ {
+     if (!ctx) {
+         TRACE_ERROR("Invalid function argument.\n");
+@@ -632,6 +633,7 @@ CK_RV encr_mgr_cleanup(ENCR_DECR_CONTEXT *ctx)
+     ctx->init_pending = FALSE;
+     ctx->context_len = 0;
+     ctx->pkey_active = FALSE;
++    ctx->state_unsaveable = FALSE;
+ 
+     if (ctx->mech.pParameter) {
+         free(ctx->mech.pParameter);
+@@ -639,9 +641,14 @@ CK_RV encr_mgr_cleanup(ENCR_DECR_CONTEXT *ctx)
+     }
+ 
+     if (ctx->context) {
+-        free(ctx->context);
++        if (ctx->context_free_func != NULL)
++            ctx->context_free_func(tokdata, sess, ctx->context,
++                                   ctx->context_len);
++        else
++            free(ctx->context);
+         ctx->context = NULL;
+     }
++    ctx->context_free_func = NULL;
+ 
+     return CKR_OK;
+ }
+@@ -1204,8 +1211,8 @@ done:
+         free(decr_data);
+     }
+ 
+-    decr_mgr_cleanup(decr_ctx);
+-    encr_mgr_cleanup(encr_ctx);
++    decr_mgr_cleanup(tokdata, sess, decr_ctx);
++    encr_mgr_cleanup(tokdata, sess, encr_ctx);
+ 
+     return rc;
+ }
+diff --git a/usr/lib/common/h_extern.h b/usr/lib/common/h_extern.h
+index 5e251d95..47b96ba0 100644
+--- a/usr/lib/common/h_extern.h
++++ b/usr/lib/common/h_extern.h
+@@ -1790,7 +1790,8 @@ CK_RV encr_mgr_init(STDLL_TokData_t *tokdata,
+                     CK_ULONG operation,
+                     CK_MECHANISM *mech, CK_OBJECT_HANDLE key_handle);
+ 
+-CK_RV encr_mgr_cleanup(ENCR_DECR_CONTEXT *ctx);
++CK_RV encr_mgr_cleanup(STDLL_TokData_t *tokdata, SESSION *sess,
++                       ENCR_DECR_CONTEXT *ctx);
+ 
+ CK_RV encr_mgr_encrypt(STDLL_TokData_t *tokdata,
+                        SESSION *sess, CK_BBOOL length_only,
+@@ -1825,7 +1826,8 @@ CK_RV decr_mgr_init(STDLL_TokData_t *tokdata,
+                     CK_ULONG operation,
+                     CK_MECHANISM *mech, CK_OBJECT_HANDLE key_handle);
+ 
+-CK_RV decr_mgr_cleanup(ENCR_DECR_CONTEXT *ctx);
++CK_RV decr_mgr_cleanup(STDLL_TokData_t *tokdata, SESSION *sess,
++                       ENCR_DECR_CONTEXT *ctx);
+ 
+ CK_RV decr_mgr_decrypt(STDLL_TokData_t *tokdata,
+                        SESSION *sess, CK_BBOOL length_only,
+@@ -1866,7 +1868,8 @@ CK_RV decr_mgr_update_des3_cbc(STDLL_TokData_t *tokdata, SESSION *sess,
+ 
+ // digest manager routines
+ //
+-CK_RV digest_mgr_cleanup(DIGEST_CONTEXT *ctx);
++CK_RV digest_mgr_cleanup(STDLL_TokData_t *tokdata, SESSION *sess,
++                         DIGEST_CONTEXT *ctx);
+ 
+ CK_RV digest_mgr_init(STDLL_TokData_t *tokdata,
+                       SESSION *sess,
+@@ -1955,7 +1958,8 @@ CK_RV sign_mgr_init(STDLL_TokData_t *tokdata,
+                     CK_MECHANISM *mech,
+                     CK_BBOOL recover_mode, CK_OBJECT_HANDLE key_handle);
+ 
+-CK_RV sign_mgr_cleanup(SIGN_VERIFY_CONTEXT *ctx);
++CK_RV sign_mgr_cleanup(STDLL_TokData_t *tokdata, SESSION *sess,
++                       SIGN_VERIFY_CONTEXT *ctx);
+ 
+ CK_RV sign_mgr_sign(STDLL_TokData_t *tokdata,
+                     SESSION *sess,
+@@ -1992,7 +1996,8 @@ CK_RV verify_mgr_init(STDLL_TokData_t *tokdata,
+                       CK_MECHANISM *mech,
+                       CK_BBOOL recover_mode, CK_OBJECT_HANDLE key_handle);
+ 
+-CK_RV verify_mgr_cleanup(SIGN_VERIFY_CONTEXT *ctx);
++CK_RV verify_mgr_cleanup(STDLL_TokData_t *tokdata, SESSION *sess,
++                         SIGN_VERIFY_CONTEXT *ctx);
+ 
+ CK_RV verify_mgr_verify(STDLL_TokData_t *tokdata,
+                         SESSION *sess,
+@@ -2036,10 +2041,11 @@ CK_BBOOL session_mgr_so_session_exists(STDLL_TokData_t *tokdata);
+ CK_BBOOL session_mgr_user_session_exists(STDLL_TokData_t *tokdata);
+ CK_BBOOL session_mgr_public_session_exists(STDLL_TokData_t *tokdata);
+ 
+-CK_RV session_mgr_get_op_state(SESSION *sess, CK_BBOOL length_only,
++CK_RV session_mgr_get_op_state(SESSION *sess,
++                               CK_BBOOL length_only,
+                                CK_BYTE *data, CK_ULONG *data_len);
+ 
+-CK_RV session_mgr_set_op_state(SESSION *sess,
++CK_RV session_mgr_set_op_state(STDLL_TokData_t *tokdata, SESSION *sess,
+                                CK_OBJECT_HANDLE encr_key,
+                                CK_OBJECT_HANDLE auth_key, CK_BYTE *data,
+                                CK_ULONG data_len);
+diff --git a/usr/lib/common/host_defs.h b/usr/lib/common/host_defs.h
+index c0b5c83d..41fdb657 100644
+--- a/usr/lib/common/host_defs.h
++++ b/usr/lib/common/host_defs.h
+@@ -21,27 +21,36 @@
+ 
+ #include "local_types.h"
+ 
++struct _SESSION;
++
++typedef void (*context_free_func_t)(STDLL_TokData_t *tokdata, struct _SESSION *sess,
++                                    CK_BYTE *context, CK_ULONG context_len);
++
+ typedef struct _ENCR_DECR_CONTEXT {
+     CK_OBJECT_HANDLE key;
+     CK_MECHANISM mech;
+     CK_BYTE *context;
+     CK_ULONG context_len;
++    context_free_func_t context_free_func;
+     CK_BBOOL multi;
+     CK_BBOOL active;
+     CK_BBOOL init_pending;      // indicate init request pending
+     CK_BBOOL multi_init;        // multi field is initialized
+                                 // on first call *after* init
+     CK_BBOOL pkey_active;
++    CK_BBOOL state_unsaveable;
+ } ENCR_DECR_CONTEXT;
+ 
+ typedef struct _DIGEST_CONTEXT {
+     CK_MECHANISM mech;
+     CK_BYTE *context;
+     CK_ULONG context_len;
++    context_free_func_t context_free_func;
+     CK_BBOOL multi;
+     CK_BBOOL active;
+     CK_BBOOL multi_init;        // multi field is initialized
+                                 // on first call *after* init
++    CK_BBOOL state_unsaveable;
+ } DIGEST_CONTEXT;
+ 
+ typedef struct _SIGN_VERIFY_CONTEXT {
+@@ -49,6 +58,7 @@ typedef struct _SIGN_VERIFY_CONTEXT {
+     CK_MECHANISM mech;          // current sign mechanism
+     CK_BYTE *context;           // temporary work area
+     CK_ULONG context_len;
++    context_free_func_t context_free_func;
+     CK_BBOOL multi;             // is this a multi-part operation?
+     CK_BBOOL recover;           // are we in recover mode?
+     CK_BBOOL active;
+@@ -56,6 +66,7 @@ typedef struct _SIGN_VERIFY_CONTEXT {
+     CK_BBOOL multi_init;        // multi field is initialized
+                                 // on first call *after* init
+     CK_BBOOL pkey_active;
++    CK_BBOOL state_unsaveable;
+ } SIGN_VERIFY_CONTEXT;
+ 
+ 
+diff --git a/usr/lib/common/key_mgr.c b/usr/lib/common/key_mgr.c
+index d9cd1f2f..aea74b7c 100644
+--- a/usr/lib/common/key_mgr.c
++++ b/usr/lib/common/key_mgr.c
+@@ -1011,7 +1011,7 @@ CK_RV key_mgr_wrap_key(STDLL_TokData_t *tokdata,
+         OPENSSL_cleanse(data, data_len);
+         free(data);
+     }
+-    encr_mgr_cleanup(ctx);
++    encr_mgr_cleanup(tokdata, sess, ctx);
+     free(ctx);
+ 
+ done:
+@@ -1259,7 +1259,7 @@ CK_RV key_mgr_unwrap_key(STDLL_TokData_t *tokdata,
+                           FALSE,
+                           ctx, wrapped_key, wrapped_key_len, data, &data_len);
+ 
+-    decr_mgr_cleanup(ctx);
++    decr_mgr_cleanup(tokdata, sess, ctx);
+     free(ctx);
+     ctx = NULL;
+ 
+@@ -1345,7 +1345,7 @@ done:
+         free(data);
+     }
+     if (ctx != NULL) {
+-        decr_mgr_cleanup(ctx);
++        decr_mgr_cleanup(tokdata, sess, ctx);
+         free(ctx);
+     }
+ 
+diff --git a/usr/lib/common/lock_sess_mgr.c b/usr/lib/common/lock_sess_mgr.c
+index 0c7dbedf..0609a6c9 100644
+--- a/usr/lib/common/lock_sess_mgr.c
++++ b/usr/lib/common/lock_sess_mgr.c
+@@ -276,32 +276,62 @@ CK_RV session_mgr_close_session(STDLL_TokData_t *tokdata,
+     if (sess->find_list)
+         free(sess->find_list);
+ 
+-    if (sess->encr_ctx.context)
+-        free(sess->encr_ctx.context);
++    if (sess->encr_ctx.context) {
++        if (sess->encr_ctx.context_free_func != NULL)
++            sess->encr_ctx.context_free_func(tokdata, sess,
++                                             sess->encr_ctx.context,
++                                             sess->encr_ctx.context_len);
++        else
++            free(sess->encr_ctx.context);
++    }
+ 
+     if (sess->encr_ctx.mech.pParameter)
+         free(sess->encr_ctx.mech.pParameter);
+ 
+-    if (sess->decr_ctx.context)
+-        free(sess->decr_ctx.context);
++    if (sess->decr_ctx.context) {
++        if (sess->decr_ctx.context_free_func != NULL)
++            sess->decr_ctx.context_free_func(tokdata, sess,
++                                             sess->decr_ctx.context,
++                                             sess->decr_ctx.context_len);
++        else
++            free(sess->decr_ctx.context);
++    }
+ 
+     if (sess->decr_ctx.mech.pParameter)
+         free(sess->decr_ctx.mech.pParameter);
+ 
+-    if (sess->digest_ctx.context)
+-        free(sess->digest_ctx.context);
++    if (sess->digest_ctx.context) {
++        if (sess->digest_ctx.context_free_func != NULL)
++            sess->digest_ctx.context_free_func(tokdata, sess,
++                                               sess->digest_ctx.context,
++                                               sess->digest_ctx.context_len);
++        else
++            free(sess->digest_ctx.context);
++    }
+ 
+     if (sess->digest_ctx.mech.pParameter)
+         free(sess->digest_ctx.mech.pParameter);
+ 
+-    if (sess->sign_ctx.context)
+-        free(sess->sign_ctx.context);
++    if (sess->sign_ctx.context) {
++        if (sess->sign_ctx.context_free_func != NULL)
++            sess->sign_ctx.context_free_func(tokdata, sess,
++                                             sess->sign_ctx.context,
++                                             sess->sign_ctx.context_len);
++        else
++            free(sess->sign_ctx.context);
++    }
+ 
+     if (sess->sign_ctx.mech.pParameter)
+         free(sess->sign_ctx.mech.pParameter);
+ 
+-    if (sess->verify_ctx.context)
+-        free(sess->verify_ctx.context);
++    if (sess->verify_ctx.context) {
++        if (sess->verify_ctx.context_free_func != NULL)
++            sess->verify_ctx.context_free_func(tokdata, sess,
++                                               sess->verify_ctx.context,
++                                               sess->verify_ctx.context_len);
++        else
++            free(sess->verify_ctx.context);
++    }
+ 
+     if (sess->verify_ctx.mech.pParameter)
+         free(sess->verify_ctx.mech.pParameter);
+@@ -354,32 +384,62 @@ void session_free(STDLL_TokData_t *tokdata, void *node_value,
+     if (sess->find_list)
+         free(sess->find_list);
+ 
+-    if (sess->encr_ctx.context)
+-        free(sess->encr_ctx.context);
++    if (sess->encr_ctx.context) {
++        if (sess->encr_ctx.context_free_func != NULL)
++            sess->encr_ctx.context_free_func(tokdata, sess,
++                                             sess->encr_ctx.context,
++                                             sess->encr_ctx.context_len);
++        else
++            free(sess->encr_ctx.context);
++    }
+ 
+     if (sess->encr_ctx.mech.pParameter)
+         free(sess->encr_ctx.mech.pParameter);
+ 
+-    if (sess->decr_ctx.context)
+-        free(sess->decr_ctx.context);
++    if (sess->decr_ctx.context) {
++        if (sess->decr_ctx.context_free_func != NULL)
++            sess->decr_ctx.context_free_func(tokdata, sess,
++                                             sess->decr_ctx.context,
++                                             sess->decr_ctx.context_len);
++        else
++            free(sess->decr_ctx.context);
++    }
+ 
+     if (sess->decr_ctx.mech.pParameter)
+         free(sess->decr_ctx.mech.pParameter);
+ 
+-    if (sess->digest_ctx.context)
+-        free(sess->digest_ctx.context);
++    if (sess->digest_ctx.context) {
++        if (sess->digest_ctx.context_free_func != NULL)
++            sess->digest_ctx.context_free_func(tokdata, sess,
++                                               sess->digest_ctx.context,
++                                               sess->digest_ctx.context_len);
++        else
++            free(sess->digest_ctx.context);
++    }
+ 
+     if (sess->digest_ctx.mech.pParameter)
+         free(sess->digest_ctx.mech.pParameter);
+ 
+-    if (sess->sign_ctx.context)
+-        free(sess->sign_ctx.context);
++    if (sess->sign_ctx.context) {
++        if (sess->sign_ctx.context_free_func != NULL)
++            sess->sign_ctx.context_free_func(tokdata, sess,
++                                             sess->sign_ctx.context,
++                                             sess->sign_ctx.context_len);
++        else
++            free(sess->sign_ctx.context);
++    }
+ 
+     if (sess->sign_ctx.mech.pParameter)
+         free(sess->sign_ctx.mech.pParameter);
+ 
+-    if (sess->verify_ctx.context)
+-        free(sess->verify_ctx.context);
++    if (sess->verify_ctx.context) {
++        if (sess->verify_ctx.context_free_func != NULL)
++            sess->verify_ctx.context_free_func(tokdata, sess,
++                                               sess->verify_ctx.context,
++                                               sess->verify_ctx.context_len);
++        else
++            free(sess->verify_ctx.context);
++    }
+ 
+     if (sess->verify_ctx.mech.pParameter)
+         free(sess->verify_ctx.mech.pParameter);
+@@ -528,6 +588,10 @@ CK_RV session_mgr_get_op_state(SESSION *sess,
+     active_ops = 0;
+ 
+     if (sess->encr_ctx.active == TRUE) {
++        if (sess->encr_ctx.state_unsaveable) {
++            TRACE_ERROR("%s\n", ock_err(ERR_STATE_UNSAVEABLE));
++            return CKR_STATE_UNSAVEABLE;
++        }
+         active_ops++;
+         if (op_data != NULL) {
+             TRACE_ERROR("%s\n", ock_err(ERR_STATE_UNSAVEABLE));
+@@ -572,6 +636,10 @@ CK_RV session_mgr_get_op_state(SESSION *sess,
+     }
+ 
+     if (sess->decr_ctx.active == TRUE) {
++        if (sess->decr_ctx.state_unsaveable) {
++            TRACE_ERROR("%s\n", ock_err(ERR_STATE_UNSAVEABLE));
++            return CKR_STATE_UNSAVEABLE;
++        }
+         active_ops++;
+         if (op_data != NULL) {
+             TRACE_ERROR("%s\n", ock_err(ERR_STATE_UNSAVEABLE));
+@@ -616,6 +684,10 @@ CK_RV session_mgr_get_op_state(SESSION *sess,
+     }
+ 
+     if (sess->digest_ctx.active == TRUE) {
++        if (sess->digest_ctx.state_unsaveable) {
++            TRACE_ERROR("%s\n", ock_err(ERR_STATE_UNSAVEABLE));
++            return CKR_STATE_UNSAVEABLE;
++        }
+         active_ops++;
+         if (op_data != NULL) {
+             TRACE_ERROR("%s\n", ock_err(ERR_STATE_UNSAVEABLE));
+@@ -660,6 +732,10 @@ CK_RV session_mgr_get_op_state(SESSION *sess,
+     }
+ 
+     if (sess->sign_ctx.active == TRUE) {
++        if (sess->sign_ctx.state_unsaveable) {
++            TRACE_ERROR("%s\n", ock_err(ERR_STATE_UNSAVEABLE));
++            return CKR_STATE_UNSAVEABLE;
++        }
+         active_ops++;
+         if (op_data != NULL) {
+             TRACE_ERROR("%s\n", ock_err(ERR_STATE_UNSAVEABLE));
+@@ -704,6 +780,10 @@ CK_RV session_mgr_get_op_state(SESSION *sess,
+     }
+ 
+     if (sess->verify_ctx.active == TRUE) {
++        if (sess->verify_ctx.state_unsaveable) {
++            TRACE_ERROR("%s\n", ock_err(ERR_STATE_UNSAVEABLE));
++            return CKR_STATE_UNSAVEABLE;
++        }
+         active_ops++;
+         if (op_data != NULL) {
+             TRACE_ERROR("%s\n", ock_err(ERR_STATE_UNSAVEABLE));
+@@ -759,7 +839,7 @@ CK_RV session_mgr_get_op_state(SESSION *sess,
+ 
+ //
+ //
+-CK_RV session_mgr_set_op_state(SESSION *sess,
++CK_RV session_mgr_set_op_state(STDLL_TokData_t *tokdata, SESSION *sess,
+                                CK_OBJECT_HANDLE encr_key,
+                                CK_OBJECT_HANDLE auth_key,
+                                CK_BYTE *data, CK_ULONG data_len)
+@@ -939,19 +1019,19 @@ CK_RV session_mgr_set_op_state(SESSION *sess,
+     // state information looks okay.  cleanup the current session state, first
+     //
+     if (sess->encr_ctx.active)
+-        encr_mgr_cleanup(&sess->encr_ctx);
++        encr_mgr_cleanup(tokdata, sess, &sess->encr_ctx);
+ 
+     if (sess->decr_ctx.active)
+-        decr_mgr_cleanup(&sess->decr_ctx);
++        decr_mgr_cleanup(tokdata, sess, &sess->decr_ctx);
+ 
+     if (sess->digest_ctx.active)
+-        digest_mgr_cleanup(&sess->digest_ctx);
++        digest_mgr_cleanup(tokdata, sess, &sess->digest_ctx);
+ 
+     if (sess->sign_ctx.active)
+-        sign_mgr_cleanup(&sess->sign_ctx);
++        sign_mgr_cleanup(tokdata, sess, &sess->sign_ctx);
+ 
+     if (sess->verify_ctx.active)
+-        verify_mgr_cleanup(&sess->verify_ctx);
++        verify_mgr_cleanup(tokdata, sess, &sess->verify_ctx);
+ 
+ 
+     // copy the new state information
+diff --git a/usr/lib/common/mech_aes.c b/usr/lib/common/mech_aes.c
+index a117487d..ad6af16b 100644
+--- a/usr/lib/common/mech_aes.c
++++ b/usr/lib/common/mech_aes.c
+@@ -2740,6 +2740,9 @@ CK_RV aes_cmac_sign(STDLL_TokData_t *tokdata,
+         goto done;
+     }
+ 
++    if (((AES_CMAC_CONTEXT *)ctx->context)->ctx != NULL)
++        ctx->state_unsaveable = CK_TRUE;
++
+     memcpy(out_data, ((AES_CMAC_CONTEXT *) ctx->context)->iv, mac_len);
+     *out_data_len = mac_len;
+ 
+@@ -2810,6 +2813,9 @@ CK_RV aes_cmac_sign_update(STDLL_TokData_t *tokdata,
+             context->len = remain;
+ 
+             context->initialized = CK_TRUE;
++
++            if (context->ctx != NULL)
++                ctx->state_unsaveable = CK_TRUE;
+         } else {
+             TRACE_DEVEL("Token specific aes cmac failed.\n");
+         }
+@@ -2873,6 +2879,9 @@ CK_RV aes_cmac_sign_final(STDLL_TokData_t *tokdata,
+         goto done;
+     }
+ 
++    if (context->ctx != NULL)
++        ctx->state_unsaveable = CK_TRUE;
++
+     memcpy(out_data, context->iv, mac_len);
+     *out_data_len = mac_len;
+ 
+@@ -2929,6 +2938,9 @@ CK_RV aes_cmac_verify(STDLL_TokData_t *tokdata,
+         return rc;
+     }
+ 
++    if (((AES_CMAC_CONTEXT *)ctx->context)->ctx != NULL)
++        ctx->state_unsaveable = CK_TRUE;
++
+     if (CRYPTO_memcmp(out_data, ((AES_CMAC_CONTEXT *) ctx->context)->iv,
+                       out_data_len) == 0) {
+         return CKR_OK;
+@@ -2997,6 +3009,9 @@ CK_RV aes_cmac_verify_update(STDLL_TokData_t *tokdata,
+             context->len = remain;
+ 
+             context->initialized = CK_TRUE;
++
++            if (context->ctx != NULL)
++                ctx->state_unsaveable = CK_TRUE;
+         } else {
+             TRACE_DEVEL("Token specific aes cmac failed.\n");
+         }
+@@ -3052,6 +3067,9 @@ CK_RV aes_cmac_verify_final(STDLL_TokData_t *tokdata,
+     object_put(tokdata, key_obj, TRUE);
+     key_obj = NULL;
+ 
++    if (context->ctx != NULL)
++        ctx->state_unsaveable = CK_TRUE;
++
+     if (rc != CKR_OK) {
+         TRACE_DEVEL("Token specific aes mac failed.\n");
+         return rc;
+diff --git a/usr/lib/common/mech_des3.c b/usr/lib/common/mech_des3.c
+index 786f9a4a..be8d6075 100644
+--- a/usr/lib/common/mech_des3.c
++++ b/usr/lib/common/mech_des3.c
+@@ -2380,6 +2380,9 @@ CK_RV des3_cmac_sign(STDLL_TokData_t *tokdata,
+     if (rc != CKR_OK)
+         TRACE_DEVEL("Token specific des3 cmac failed.\n");
+ 
++    if (((DES_CMAC_CONTEXT *)ctx->context)->ctx != NULL)
++        ctx->state_unsaveable = CK_TRUE;
++
+     memcpy(out_data, ((DES_CMAC_CONTEXT *) ctx->context)->iv, mac_len);
+ 
+     *out_data_len = mac_len;
+@@ -2450,6 +2453,9 @@ CK_RV des3_cmac_sign_update(STDLL_TokData_t *tokdata,
+             context->len = remain;
+ 
+             context->initialized = CK_TRUE;
++
++            if (context->ctx != NULL)
++                ctx->state_unsaveable = CK_TRUE;
+         } else {
+             TRACE_DEVEL("Token specific des3 cmac failed.\n");
+         }
+@@ -2512,6 +2518,9 @@ CK_RV des3_cmac_sign_final(STDLL_TokData_t *tokdata,
+         goto done;
+     }
+ 
++    if (context->ctx != NULL)
++        ctx->state_unsaveable = CK_TRUE;
++
+     memcpy(out_data, context->iv, mac_len);
+ 
+     *out_data_len = mac_len;
+@@ -2565,6 +2574,9 @@ CK_RV des3_cmac_verify(STDLL_TokData_t *tokdata,
+     object_put(tokdata, key_obj, TRUE);
+     key_obj = NULL;
+ 
++    if (((DES_CMAC_CONTEXT *)ctx->context)->ctx != NULL)
++        ctx->state_unsaveable = CK_TRUE;
++
+     if (CRYPTO_memcmp(out_data, ((DES_CMAC_CONTEXT *) ctx->context)->iv,
+                       out_data_len) == 0) {
+         return CKR_OK;
+@@ -2631,6 +2643,9 @@ CK_RV des3_cmac_verify_update(STDLL_TokData_t *tokdata,
+             context->len = remain;
+ 
+             context->initialized = CK_TRUE;
++
++            if (context->ctx != NULL)
++                ctx->state_unsaveable = CK_TRUE;
+         } else {
+             TRACE_DEVEL("Token specific des3 cmac failed.\n");
+         }
+@@ -2691,6 +2706,9 @@ CK_RV des3_cmac_verify_final(STDLL_TokData_t *tokdata,
+         return rc;
+     }
+ 
++    if (context->ctx != NULL)
++        ctx->state_unsaveable = CK_TRUE;
++
+     if (CRYPTO_memcmp(signature, context->iv, signature_len) == 0)
+         return CKR_OK;
+ 
+diff --git a/usr/lib/common/mech_ec.c b/usr/lib/common/mech_ec.c
+index a0a06302..c338d063 100644
+--- a/usr/lib/common/mech_ec.c
++++ b/usr/lib/common/mech_ec.c
+@@ -414,7 +414,7 @@ CK_RV ec_hash_sign(STDLL_TokData_t *tokdata,
+                            in_data_len, hash, &hash_len);
+     if (rc != CKR_OK) {
+         TRACE_DEVEL("Digest Mgr Digest failed.\n");
+-        digest_mgr_cleanup(&digest_ctx);
++        digest_mgr_cleanup(tokdata, sess, &digest_ctx);
+         return rc;
+     }
+ 
+@@ -434,7 +434,7 @@ CK_RV ec_hash_sign(STDLL_TokData_t *tokdata,
+         TRACE_DEVEL("Sign Mgr Sign failed.\n");
+ 
+ error:
+-    sign_mgr_cleanup(&sign_ctx);
++    sign_mgr_cleanup(tokdata, sess, &sign_ctx);
+ 
+     return rc;
+ }
+@@ -485,6 +485,7 @@ CK_RV ec_hash_sign_update(STDLL_TokData_t *tokdata,
+             return rc;
+         }
+         context->flag = TRUE;
++        ctx->state_unsaveable |= context->hash_context.state_unsaveable;
+     }
+ 
+     rc = digest_mgr_digest_update(tokdata, sess, &context->hash_context,
+@@ -556,12 +557,12 @@ CK_RV ec_hash_sign_final(STDLL_TokData_t *tokdata,
+         TRACE_DEVEL("Sign Mgr Sign failed.\n");
+ 
+     if (length_only == TRUE || rc == CKR_BUFFER_TOO_SMALL) {
+-        sign_mgr_cleanup(&sign_ctx);
++        sign_mgr_cleanup(tokdata, sess, &sign_ctx);
+         return rc;
+     }
+ 
+ done:
+-    sign_mgr_cleanup(&sign_ctx);
++    sign_mgr_cleanup(tokdata, sess, &sign_ctx);
+ 
+     return rc;
+ }
+@@ -627,7 +628,7 @@ CK_RV ec_hash_verify(STDLL_TokData_t *tokdata,
+                            in_data_len, hash, &hash_len);
+     if (rc != CKR_OK) {
+         TRACE_DEVEL("Digest Mgr Digest failed.\n");
+-        digest_mgr_cleanup(&digest_ctx);
++        digest_mgr_cleanup(tokdata, sess, &digest_ctx);
+         return rc;
+     }
+     // Verify the Signed BER-encoded Data block
+@@ -649,7 +650,7 @@ CK_RV ec_hash_verify(STDLL_TokData_t *tokdata,
+     if (rc != CKR_OK)
+         TRACE_DEVEL("Verify Mgr Verify failed.\n");
+ done:
+-    sign_mgr_cleanup(&verify_ctx);
++    sign_mgr_cleanup(tokdata, sess, &verify_ctx);
+ 
+     return rc;
+ }
+@@ -701,6 +702,7 @@ CK_RV ec_hash_verify_update(STDLL_TokData_t *tokdata,
+             return rc;
+         }
+         context->flag = TRUE;
++        ctx->state_unsaveable |= context->hash_context.state_unsaveable;
+     }
+ 
+     rc = digest_mgr_digest_update(tokdata, sess, &context->hash_context,
+@@ -768,7 +770,7 @@ CK_RV ec_hash_verify_final(STDLL_TokData_t *tokdata,
+     if (rc != CKR_OK)
+         TRACE_DEVEL("Verify Mgr Verify failed.\n");
+ done:
+-    verify_mgr_cleanup(&verify_ctx);
++    verify_mgr_cleanup(tokdata, sess, &verify_ctx);
+ 
+     return rc;
+ }
+@@ -823,7 +825,7 @@ CK_RV ckm_kdf(STDLL_TokData_t *tokdata, SESSION *sess, CK_ULONG kdf,
+                            h_len);
+     if (rc != CKR_OK) {
+         TRACE_ERROR("digest_mgr_digest failed with rc = %s\n", ock_err(rc));
+-        digest_mgr_cleanup(&ctx);
++        digest_mgr_cleanup(tokdata, sess, &ctx);
+         return rc;
+     }
+ 
+diff --git a/usr/lib/common/mech_md2.c b/usr/lib/common/mech_md2.c
+index beb84365..91da6259 100644
+--- a/usr/lib/common/mech_md2.c
++++ b/usr/lib/common/mech_md2.c
+@@ -245,7 +245,7 @@ CK_RV md2_hmac_sign(STDLL_TokData_t *tokdata,
+                                attr->pValue, attr->ulValueLen, hash, &hash_len);
+         if (rc != CKR_OK) {
+             TRACE_DEVEL("Digest Mgr Digest failed.\n");
+-            digest_mgr_cleanup(&digest_ctx);
++            digest_mgr_cleanup(tokdata, sess, &digest_ctx);
+             goto done;
+         }
+         memset(&digest_ctx, 0x0, sizeof(DIGEST_CONTEXT));
+diff --git a/usr/lib/common/mech_md5.c b/usr/lib/common/mech_md5.c
+index 6b1281de..320e2549 100644
+--- a/usr/lib/common/mech_md5.c
++++ b/usr/lib/common/mech_md5.c
+@@ -61,7 +61,10 @@ CK_RV sw_md5_hash(DIGEST_CONTEXT *ctx, CK_BYTE *in_data,
+     MD5_Final(out_data, (MD5_CTX *)ctx->context);
+     *out_data_len = MD5_HASH_SIZE;
+ 
+-    free(ctx->context);
++    if (ctx->context_free_func != NULL)
++        ctx->context_free_func(ctx->context, ctx->context_len);
++    else
++        free(ctx->context);
+     ctx->context = NULL;
+ 
+     return CKR_OK;
+@@ -86,7 +89,10 @@ CK_RV sw_MD5_Final(DIGEST_CONTEXT *ctx, CK_BYTE *out_data,
+     MD5_Final(out_data, (MD5_CTX *)ctx->context);
+     *out_data_len = MD5_HASH_SIZE;
+ 
+-    free(ctx->context);
++    if (ctx->context_free_func != NULL)
++        ctx->context_free_func(ctx->context, ctx->context_len);
++    else
++        free(ctx->context);
+     ctx->context = NULL;
+ 
+     return CKR_OK;
+@@ -267,7 +273,7 @@ CK_RV md5_hmac_sign(STDLL_TokData_t *tokdata,
+                                attr->pValue, attr->ulValueLen, hash, &hash_len);
+         if (rc != CKR_OK) {
+             TRACE_DEVEL("Digest Mgr Digest failed.\n");
+-            digest_mgr_cleanup(&digest_ctx);
++            digest_mgr_cleanup(tokdata, sess, &digest_ctx);
+             goto done;
+         }
+ 
+@@ -413,6 +419,6 @@ CK_RV md5_hmac_verify(STDLL_TokData_t *tokdata, SESSION *sess,
+     }
+ 
+ done:
+-    sign_mgr_cleanup(&hmac_ctx);
++    sign_mgr_cleanup(tokdata, sess, &hmac_ctx);
+     return rc;
+ }
+diff --git a/usr/lib/common/mech_rsa.c b/usr/lib/common/mech_rsa.c
+index e35b383c..0a690e78 100644
+--- a/usr/lib/common/mech_rsa.c
++++ b/usr/lib/common/mech_rsa.c
+@@ -1476,7 +1476,7 @@ CK_RV rsa_hash_pss_sign(STDLL_TokData_t *tokdata, SESSION *sess,
+                            in_data, in_data_len, hash, &hlen);
+     if (rc != CKR_OK) {
+         TRACE_DEVEL("Digest Mgr Digest failed.\n");
+-        digest_mgr_cleanup(&digest_ctx);
++        digest_mgr_cleanup(tokdata, sess, &digest_ctx);
+         return rc;
+     }
+ 
+@@ -1497,7 +1497,7 @@ CK_RV rsa_hash_pss_sign(STDLL_TokData_t *tokdata, SESSION *sess,
+         TRACE_DEVEL("Sign Mgr Sign failed.\n");
+ 
+ done:
+-    sign_mgr_cleanup(&sign_ctx);
++    sign_mgr_cleanup(tokdata, sess, &sign_ctx);
+ 
+     return rc;
+ }
+@@ -1546,6 +1546,7 @@ CK_RV rsa_hash_pss_update(STDLL_TokData_t *tokdata, SESSION *sess,
+             TRACE_DEVEL("Digest Mgr Init failed.\n");
+             return rc;
+         }
++        ctx->state_unsaveable |= digest_ctx->state_unsaveable;
+     }
+ 
+     rc = digest_mgr_digest_update(tokdata, sess, digest_ctx, in_data,
+@@ -1613,7 +1614,7 @@ CK_RV rsa_hash_pss_sign_final(STDLL_TokData_t *tokdata, SESSION *sess,
+         TRACE_DEVEL("Sign Mgr Sign failed.\n");
+ 
+ done:
+-    sign_mgr_cleanup(&sign_ctx);
++    sign_mgr_cleanup(tokdata, sess, &sign_ctx);
+ 
+     return rc;
+ }
+@@ -1676,7 +1677,7 @@ CK_RV rsa_hash_pss_verify(STDLL_TokData_t *tokdata, SESSION *sess,
+                            in_data_len, hash, &hlen);
+     if (rc != CKR_OK) {
+         TRACE_DEVEL("Digest Mgr Digest failed.\n");
+-        digest_mgr_cleanup(&digest_ctx);
++        digest_mgr_cleanup(tokdata, sess, &digest_ctx);
+         return rc;
+     }
+ 
+@@ -1698,7 +1699,7 @@ CK_RV rsa_hash_pss_verify(STDLL_TokData_t *tokdata, SESSION *sess,
+         TRACE_DEVEL("Verify Mgr Verify failed.\n");
+ 
+ done:
+-    verify_mgr_cleanup(&verify_ctx);
++    verify_mgr_cleanup(tokdata, sess, &verify_ctx);
+ 
+     return rc;
+ }
+@@ -1760,7 +1761,7 @@ CK_RV rsa_hash_pss_verify_final(STDLL_TokData_t *tokdata, SESSION *sess,
+         TRACE_DEVEL("Verify Mgr Verify failed.\n");
+ 
+ done:
+-    verify_mgr_cleanup(&verify_ctx);
++    verify_mgr_cleanup(tokdata, sess, &verify_ctx);
+ 
+     return rc;
+ }
+@@ -1842,7 +1843,7 @@ CK_RV rsa_hash_pkcs_sign(STDLL_TokData_t *tokdata,
+                            in_data_len, hash, &hash_len);
+     if (rc != CKR_OK) {
+         TRACE_DEVEL("Digest Mgr Digest failed.\n");
+-        digest_mgr_cleanup(&digest_ctx);
++        digest_mgr_cleanup(tokdata, sess, &digest_ctx);
+         return rc;
+     }
+     // build the BER-encodings
+@@ -1885,7 +1886,7 @@ error:
+         free(octet_str);
+     if (ber_data)
+         free(ber_data);
+-    sign_mgr_cleanup(&sign_ctx);
++    sign_mgr_cleanup(tokdata, sess, &sign_ctx);
+ 
+     return rc;
+ }
+@@ -1934,6 +1935,7 @@ CK_RV rsa_hash_pkcs_sign_update(STDLL_TokData_t *tokdata,
+             return rc;
+         }
+         context->flag = TRUE;
++        ctx->state_unsaveable |= context->hash_context.state_unsaveable;
+     }
+ 
+     rc = digest_mgr_digest_update(tokdata, sess, &context->hash_context,
+@@ -2021,7 +2023,7 @@ CK_RV rsa_hash_pkcs_verify(STDLL_TokData_t *tokdata,
+                            in_data_len, hash, &hash_len);
+     if (rc != CKR_OK) {
+         TRACE_DEVEL("Digest Mgr Digest failed.\n");
+-        digest_mgr_cleanup(&digest_ctx);
++        digest_mgr_cleanup(tokdata, sess, &digest_ctx);
+         return rc;
+     }
+     // Build the BER encoding
+@@ -2063,7 +2065,7 @@ done:
+         free(octet_str);
+     if (ber_data)
+         free(ber_data);
+-    sign_mgr_cleanup(&verify_ctx);
++    sign_mgr_cleanup(tokdata, sess, &verify_ctx);
+ 
+     return rc;
+ }
+@@ -2111,6 +2113,7 @@ CK_RV rsa_hash_pkcs_verify_update(STDLL_TokData_t *tokdata,
+             return rc;
+         }
+         context->flag = TRUE;
++        ctx->state_unsaveable |= context->hash_context.state_unsaveable;
+     }
+ 
+     rc = digest_mgr_digest_update(tokdata, sess, &context->hash_context,
+@@ -2236,7 +2239,7 @@ done:
+         free(octet_str);
+     if (ber_data)
+         free(ber_data);
+-    sign_mgr_cleanup(&sign_ctx);
++    sign_mgr_cleanup(tokdata, sess, &sign_ctx);
+ 
+     return rc;
+ }
+@@ -2347,7 +2350,7 @@ done:
+         free(octet_str);
+     if (ber_data)
+         free(ber_data);
+-    verify_mgr_cleanup(&verify_ctx);
++    verify_mgr_cleanup(tokdata, sess, &verify_ctx);
+ 
+     return rc;
+ }
+diff --git a/usr/lib/common/mech_sha.c b/usr/lib/common/mech_sha.c
+index 4037b8f1..0b9b7b28 100644
+--- a/usr/lib/common/mech_sha.c
++++ b/usr/lib/common/mech_sha.c
+@@ -80,7 +80,10 @@ CK_RV sw_sha1_hash(DIGEST_CONTEXT *ctx, CK_BYTE *in_data,
+     SHA1_Final(out_data, (SHA_CTX *)ctx->context);
+     *out_data_len = SHA1_HASH_SIZE;
+ 
+-    free(ctx->context);
++    if (ctx->context_free_func != NULL)
++        ctx->context_free_func(ctx->context, ctx->context_len);
++    else
++        free(ctx->context);
+     ctx->context = NULL;
+ 
+     return CKR_OK;
+@@ -105,7 +108,10 @@ CK_RV sw_sha1_final(DIGEST_CONTEXT *ctx, CK_BYTE *out_data,
+     SHA1_Final(out_data, (SHA_CTX *)ctx->context);
+     *out_data_len = SHA1_HASH_SIZE;
+ 
+-    free(ctx->context);
++    if (ctx->context_free_func != NULL)
++        ctx->context_free_func(ctx->context, ctx->context_len);
++    else
++        free(ctx->context);
+     ctx->context = NULL;
+ 
+     return CKR_OK;
+@@ -421,7 +427,7 @@ CK_RV sha_hmac_sign(STDLL_TokData_t *tokdata,
+                                attr->pValue, attr->ulValueLen, hash, &hash_len);
+         if (rc != CKR_OK) {
+             TRACE_DEVEL("Digest Mgr Digest failed.\n");
+-            digest_mgr_cleanup(&digest_ctx);
++            digest_mgr_cleanup(tokdata, sess, &digest_ctx);
+             goto done;
+         }
+ 
+@@ -607,7 +613,7 @@ CK_RV sha_hmac_verify(STDLL_TokData_t *tokdata, SESSION *sess,
+     }
+ 
+ done:
+-    sign_mgr_cleanup(&hmac_ctx);
++    sign_mgr_cleanup(tokdata, sess, &hmac_ctx);
+     return rc;
+ }
+ 
+diff --git a/usr/lib/common/mech_ssl3.c b/usr/lib/common/mech_ssl3.c
+index 66bdb8f4..566aeee2 100644
+--- a/usr/lib/common/mech_ssl3.c
++++ b/usr/lib/common/mech_ssl3.c
+@@ -289,6 +289,7 @@ CK_RV ssl3_mac_sign_update(STDLL_TokData_t *tokdata,
+             goto done;
+         }
+         context->flag = TRUE;
++        ctx->state_unsaveable |= context->hash_context.state_unsaveable;
+     }
+ 
+ 
+@@ -485,7 +486,7 @@ CK_RV ssl3_mac_verify(STDLL_TokData_t *tokdata,
+         rc = CKR_SIGNATURE_INVALID;
+     }
+ error:
+-    sign_mgr_cleanup(&mac_ctx);
++    sign_mgr_cleanup(tokdata, sess, &mac_ctx);
+ 
+     return rc;
+ }
+@@ -573,6 +574,7 @@ CK_RV ssl3_mac_verify_update(STDLL_TokData_t *tokdata,
+             goto done;
+         }
+         context->flag = TRUE;
++        ctx->state_unsaveable |= context->hash_context.state_unsaveable;
+     }
+ 
+     rc = digest_mgr_digest_update(tokdata, sess, &context->hash_context,
+diff --git a/usr/lib/common/new_host.c b/usr/lib/common/new_host.c
+index a3749d26..d01091f9 100644
+--- a/usr/lib/common/new_host.c
++++ b/usr/lib/common/new_host.c
+@@ -1215,8 +1215,9 @@ CK_RV SC_SetOperationState(STDLL_TokData_t *tokdata,
+         goto done;
+     }
+ 
+-    rc = session_mgr_set_op_state(sess, hEncryptionKey, hAuthenticationKey,
+-                                  pOperationState, ulOperationStateLen);
++    rc = session_mgr_set_op_state(tokdata, sess, hEncryptionKey,
++                                  hAuthenticationKey, pOperationState,
++                                  ulOperationStateLen);
+ 
+     if (rc != CKR_OK)
+         TRACE_DEVEL("session_mgr_set_op_state() failed.\n");
+@@ -2128,7 +2129,7 @@ CK_RV SC_Encrypt(STDLL_TokData_t *tokdata, ST_SESSION_HANDLE *sSession,
+ done:
+     if (rc != CKR_BUFFER_TOO_SMALL && (rc != CKR_OK || length_only != TRUE)) {
+         if (sess)
+-            encr_mgr_cleanup(&sess->encr_ctx);
++            encr_mgr_cleanup(tokdata, sess, &sess->encr_ctx);
+     }
+ 
+     TRACE_INFO("C_Encrypt: rc = 0x%08lx, sess = %ld, amount = %lu\n",
+@@ -2187,7 +2188,7 @@ CK_RV SC_EncryptUpdate(STDLL_TokData_t *tokdata, ST_SESSION_HANDLE *sSession,
+ done:
+     if (rc != CKR_OK && rc != CKR_BUFFER_TOO_SMALL) {
+         if (sess)
+-            encr_mgr_cleanup(&sess->encr_ctx);
++            encr_mgr_cleanup(tokdata, sess, &sess->encr_ctx);
+     }
+ 
+     TRACE_INFO("C_EncryptUpdate: rc = 0x%08lx, sess = %ld, amount = %lu\n",
+@@ -2244,7 +2245,7 @@ CK_RV SC_EncryptFinal(STDLL_TokData_t * tokdata, ST_SESSION_HANDLE * sSession,
+ done:
+     if (rc != CKR_BUFFER_TOO_SMALL && (rc != CKR_OK || length_only != TRUE)) {
+         if (sess)
+-            encr_mgr_cleanup(&sess->encr_ctx);
++            encr_mgr_cleanup(tokdata, sess, &sess->encr_ctx);
+     }
+ 
+     TRACE_INFO("C_EncryptFinal: rc = 0x%08lx, sess = %ld\n",
+@@ -2361,7 +2362,7 @@ CK_RV SC_Decrypt(STDLL_TokData_t *tokdata, ST_SESSION_HANDLE *sSession,
+ done:
+     if (rc != CKR_BUFFER_TOO_SMALL && (rc != CKR_OK || length_only != TRUE)) {
+         if (sess)
+-            decr_mgr_cleanup(&sess->decr_ctx);
++            decr_mgr_cleanup(tokdata, sess, &sess->decr_ctx);
+     }
+ 
+     TRACE_INFO("C_Decrypt: rc = 0x%08lx, sess = %ld, amount = %lu\n",
+@@ -2420,7 +2421,7 @@ CK_RV SC_DecryptUpdate(STDLL_TokData_t *tokdata, ST_SESSION_HANDLE *sSession,
+ done:
+     if (rc != CKR_OK && rc != CKR_BUFFER_TOO_SMALL && sess != NULL) {
+         if (sess)
+-            decr_mgr_cleanup(&sess->decr_ctx);
++            decr_mgr_cleanup(tokdata, sess, &sess->decr_ctx);
+     }
+ 
+     TRACE_INFO("C_DecryptUpdate: rc = 0x%08lx, sess = %ld, amount = %lu\n",
+@@ -2477,7 +2478,7 @@ CK_RV SC_DecryptFinal(STDLL_TokData_t *tokdata, ST_SESSION_HANDLE *sSession,
+ done:
+     if (rc != CKR_BUFFER_TOO_SMALL && (rc != CKR_OK || length_only != TRUE)) {
+         if (sess)
+-            decr_mgr_cleanup(&sess->decr_ctx);
++            decr_mgr_cleanup(tokdata, sess, &sess->decr_ctx);
+     }
+ 
+     TRACE_INFO("C_DecryptFinal: rc = 0x%08lx, sess = %ld, amount = %lu\n",
+@@ -2825,7 +2826,7 @@ CK_RV SC_Sign(STDLL_TokData_t *tokdata, ST_SESSION_HANDLE *sSession,
+ done:
+     if (rc != CKR_BUFFER_TOO_SMALL && (rc != CKR_OK || length_only != TRUE)) {
+         if (sess != NULL)
+-            sign_mgr_cleanup(&sess->sign_ctx);
++            sign_mgr_cleanup(tokdata, sess, &sess->sign_ctx);
+     }
+ 
+     TRACE_INFO("C_Sign: rc = 0x%08lx, sess = %ld, datalen = %lu\n",
+@@ -2875,7 +2876,7 @@ CK_RV SC_SignUpdate(STDLL_TokData_t *tokdata, ST_SESSION_HANDLE *sSession,
+ 
+ done:
+     if (rc != CKR_OK && sess != NULL)
+-        sign_mgr_cleanup(&sess->sign_ctx);
++        sign_mgr_cleanup(tokdata, sess, &sess->sign_ctx);
+ 
+     TRACE_INFO("C_SignUpdate: rc = 0x%08lx, sess = %ld, datalen = %lu\n",
+                rc, (sess == NULL) ? -1 : (CK_LONG) sess->handle, ulPartLen);
+@@ -2930,7 +2931,7 @@ CK_RV SC_SignFinal(STDLL_TokData_t *tokdata, ST_SESSION_HANDLE *sSession,
+ done:
+     if (rc != CKR_BUFFER_TOO_SMALL && (rc != CKR_OK || length_only != TRUE)) {
+         if (sess != NULL)
+-            sign_mgr_cleanup(&sess->sign_ctx);
++            sign_mgr_cleanup(tokdata, sess, &sess->sign_ctx);
+     }
+ 
+     TRACE_INFO("C_SignFinal: rc = 0x%08lx, sess = %ld\n",
+@@ -3045,7 +3046,7 @@ CK_RV SC_SignRecover(STDLL_TokData_t *tokdata, ST_SESSION_HANDLE *sSession,
+ done:
+     if (rc != CKR_BUFFER_TOO_SMALL && (rc != CKR_OK || length_only != TRUE)) {
+         if (sess != NULL)
+-            sign_mgr_cleanup(&sess->sign_ctx);
++            sign_mgr_cleanup(tokdata, sess, &sess->sign_ctx);
+     }
+ 
+     TRACE_INFO("C_SignRecover: rc = 0x%08lx, sess = %ld, datalen = %lu\n",
+@@ -3155,7 +3156,7 @@ CK_RV SC_Verify(STDLL_TokData_t *tokdata, ST_SESSION_HANDLE *sSession,
+ 
+ done:
+     if (sess != NULL)
+-        verify_mgr_cleanup(&sess->verify_ctx);
++        verify_mgr_cleanup(tokdata, sess, &sess->verify_ctx);
+ 
+     TRACE_INFO("C_Verify: rc = 0x%08lx, sess = %ld, datalen = %lu\n",
+                rc, (sess == NULL) ? -1 : (CK_LONG) sess->handle, ulDataLen);
+@@ -3205,7 +3206,7 @@ CK_RV SC_VerifyUpdate(STDLL_TokData_t *tokdata, ST_SESSION_HANDLE *sSession,
+ 
+ done:
+     if (rc != CKR_OK && sess != NULL)
+-        verify_mgr_cleanup(&sess->verify_ctx);
++        verify_mgr_cleanup(tokdata, sess, &sess->verify_ctx);
+ 
+     TRACE_INFO("C_VerifyUpdate: rc = 0x%08lx, sess = %ld, datalen = %lu\n",
+                rc, (sess == NULL) ? -1 : (CK_LONG) sess->handle, ulPartLen);
+@@ -3255,7 +3256,7 @@ CK_RV SC_VerifyFinal(STDLL_TokData_t *tokdata, ST_SESSION_HANDLE *sSession,
+ 
+ done:
+     if (sess != NULL)
+-        verify_mgr_cleanup(&sess->verify_ctx);
++        verify_mgr_cleanup(tokdata, sess, &sess->verify_ctx);
+ 
+     TRACE_INFO("C_VerifyFinal: rc = 0x%08lx, sess = %ld\n",
+                rc, (sess == NULL) ? -1 : (CK_LONG) sess->handle);
+@@ -3372,7 +3373,7 @@ CK_RV SC_VerifyRecover(STDLL_TokData_t *tokdata, ST_SESSION_HANDLE *sSession,
+ done:
+     if (rc != CKR_BUFFER_TOO_SMALL && (rc != CKR_OK || length_only != TRUE)) {
+         if (sess != NULL)
+-            verify_mgr_cleanup(&sess->verify_ctx);
++            verify_mgr_cleanup(tokdata, sess, &sess->verify_ctx);
+     }
+ 
+     TRACE_INFO("C_VerifyRecover: rc = 0x%08lx, sess = %ld, recover len = %lu, "
+diff --git a/usr/lib/common/sess_mgr.c b/usr/lib/common/sess_mgr.c
+index e2da6df5..69c3be3b 100644
+--- a/usr/lib/common/sess_mgr.c
++++ b/usr/lib/common/sess_mgr.c
+@@ -243,32 +243,62 @@ CK_RV session_mgr_close_session(STDLL_TokData_t *tokdata,
+     if (sess->find_list)
+         free(sess->find_list);
+ 
+-    if (sess->encr_ctx.context)
+-        free(sess->encr_ctx.context);
++    if (sess->encr_ctx.context) {
++        if (sess->encr_ctx.context_free_func != NULL)
++            sess->encr_ctx.context_free_func(tokdata, sess,
++                                             sess->encr_ctx.context,
++                                             sess->encr_ctx.context_len);
++        else
++            free(sess->encr_ctx.context);
++    }
+ 
+     if (sess->encr_ctx.mech.pParameter)
+         free(sess->encr_ctx.mech.pParameter);
+ 
+-    if (sess->decr_ctx.context)
+-        free(sess->decr_ctx.context);
++    if (sess->decr_ctx.context) {
++        if (sess->decr_ctx.context_free_func != NULL)
++            sess->decr_ctx.context_free_func(tokdata, sess,
++                                             sess->decr_ctx.context,
++                                             sess->decr_ctx.context_len);
++        else
++            free(sess->decr_ctx.context);
++    }
+ 
+     if (sess->decr_ctx.mech.pParameter)
+         free(sess->decr_ctx.mech.pParameter);
+ 
+-    if (sess->digest_ctx.context)
+-        free(sess->digest_ctx.context);
++    if (sess->digest_ctx.context) {
++        if (sess->digest_ctx.context_free_func != NULL)
++            sess->digest_ctx.context_free_func(tokdata, sess,
++                                               sess->digest_ctx.context,
++                                               sess->digest_ctx.context_len);
++        else
++            free(sess->digest_ctx.context);
++    }
+ 
+     if (sess->digest_ctx.mech.pParameter)
+         free(sess->digest_ctx.mech.pParameter);
+ 
+-    if (sess->sign_ctx.context)
+-        free(sess->sign_ctx.context);
++    if (sess->sign_ctx.context) {
++        if (sess->sign_ctx.context_free_func != NULL)
++            sess->sign_ctx.context_free_func(tokdata, sess,
++                                             sess->sign_ctx.context,
++                                             sess->sign_ctx.context_len);
++        else
++            free(sess->sign_ctx.context);
++    }
+ 
+     if (sess->sign_ctx.mech.pParameter)
+         free(sess->sign_ctx.mech.pParameter);
+ 
+-    if (sess->verify_ctx.context)
+-        free(sess->verify_ctx.context);
++    if (sess->verify_ctx.context) {
++        if (sess->verify_ctx.context_free_func != NULL)
++            sess->verify_ctx.context_free_func(tokdata, sess,
++                                               sess->verify_ctx.context,
++                                               sess->verify_ctx.context_len);
++        else
++            free(sess->verify_ctx.context);
++    }
+ 
+     if (sess->verify_ctx.mech.pParameter)
+         free(sess->verify_ctx.mech.pParameter);
+@@ -323,32 +353,62 @@ void session_free(STDLL_TokData_t *tokdata, void *node_value,
+     if (sess->find_list)
+         free(sess->find_list);
+ 
+-    if (sess->encr_ctx.context)
+-        free(sess->encr_ctx.context);
++    if (sess->encr_ctx.context) {
++        if (sess->encr_ctx.context_free_func != NULL)
++            sess->encr_ctx.context_free_func(tokdata, sess,
++                                             sess->encr_ctx.context,
++                                             sess->encr_ctx.context_len);
++        else
++            free(sess->encr_ctx.context);
++    }
+ 
+     if (sess->encr_ctx.mech.pParameter)
+         free(sess->encr_ctx.mech.pParameter);
+ 
+-    if (sess->decr_ctx.context)
+-        free(sess->decr_ctx.context);
++    if (sess->decr_ctx.context) {
++        if (sess->decr_ctx.context_free_func != NULL)
++            sess->decr_ctx.context_free_func(tokdata, sess,
++                                             sess->decr_ctx.context,
++                                             sess->decr_ctx.context_len);
++        else
++            free(sess->decr_ctx.context);
++    }
+ 
+     if (sess->decr_ctx.mech.pParameter)
+         free(sess->decr_ctx.mech.pParameter);
+ 
+-    if (sess->digest_ctx.context)
+-        free(sess->digest_ctx.context);
++    if (sess->digest_ctx.context) {
++        if (sess->digest_ctx.context_free_func != NULL)
++            sess->digest_ctx.context_free_func(tokdata, sess,
++                                               sess->digest_ctx.context,
++                                               sess->digest_ctx.context_len);
++        else
++            free(sess->digest_ctx.context);
++    }
+ 
+     if (sess->digest_ctx.mech.pParameter)
+         free(sess->digest_ctx.mech.pParameter);
+ 
+-    if (sess->sign_ctx.context)
+-        free(sess->sign_ctx.context);
++    if (sess->sign_ctx.context) {
++        if (sess->sign_ctx.context_free_func != NULL)
++            sess->sign_ctx.context_free_func(tokdata, sess,
++                                             sess->sign_ctx.context,
++                                             sess->sign_ctx.context_len);
++        else
++            free(sess->sign_ctx.context);
++    }
+ 
+     if (sess->sign_ctx.mech.pParameter)
+         free(sess->sign_ctx.mech.pParameter);
+ 
+-    if (sess->verify_ctx.context)
+-        free(sess->verify_ctx.context);
++    if (sess->verify_ctx.context) {
++        if (sess->verify_ctx.context_free_func != NULL)
++            sess->verify_ctx.context_free_func(tokdata, sess,
++                                               sess->verify_ctx.context,
++                                               sess->verify_ctx.context_len);
++        else
++            free(sess->verify_ctx.context);
++    }
+ 
+     if (sess->verify_ctx.mech.pParameter)
+         free(sess->verify_ctx.mech.pParameter);
+@@ -480,6 +540,10 @@ CK_RV session_mgr_get_op_state(SESSION *sess,
+     active_ops = 0;
+ 
+     if (sess->encr_ctx.active == TRUE) {
++        if (sess->encr_ctx.state_unsaveable) {
++            TRACE_ERROR("%s\n", ock_err(ERR_STATE_UNSAVEABLE));
++            return CKR_STATE_UNSAVEABLE;
++        }
+         active_ops++;
+         if (op_data != NULL) {
+             TRACE_ERROR("%s\n", ock_err(ERR_STATE_UNSAVEABLE));
+@@ -524,6 +588,10 @@ CK_RV session_mgr_get_op_state(SESSION *sess,
+     }
+ 
+     if (sess->decr_ctx.active == TRUE) {
++        if (sess->decr_ctx.state_unsaveable) {
++            TRACE_ERROR("%s\n", ock_err(ERR_STATE_UNSAVEABLE));
++            return CKR_STATE_UNSAVEABLE;
++        }
+         active_ops++;
+         if (op_data != NULL) {
+             TRACE_ERROR("%s\n", ock_err(ERR_STATE_UNSAVEABLE));
+@@ -568,6 +636,10 @@ CK_RV session_mgr_get_op_state(SESSION *sess,
+     }
+ 
+     if (sess->digest_ctx.active == TRUE) {
++        if (sess->digest_ctx.state_unsaveable) {
++            TRACE_ERROR("%s\n", ock_err(ERR_STATE_UNSAVEABLE));
++            return CKR_STATE_UNSAVEABLE;
++        }
+         active_ops++;
+         if (op_data != NULL) {
+             TRACE_ERROR("%s\n", ock_err(ERR_STATE_UNSAVEABLE));
+@@ -612,6 +684,10 @@ CK_RV session_mgr_get_op_state(SESSION *sess,
+     }
+ 
+     if (sess->sign_ctx.active == TRUE) {
++        if (sess->sign_ctx.state_unsaveable) {
++            TRACE_ERROR("%s\n", ock_err(ERR_STATE_UNSAVEABLE));
++            return CKR_STATE_UNSAVEABLE;
++        }
+         active_ops++;
+         if (op_data != NULL) {
+             TRACE_ERROR("%s\n", ock_err(ERR_STATE_UNSAVEABLE));
+@@ -656,6 +732,10 @@ CK_RV session_mgr_get_op_state(SESSION *sess,
+     }
+ 
+     if (sess->verify_ctx.active == TRUE) {
++        if (sess->verify_ctx.state_unsaveable) {
++            TRACE_ERROR("%s\n", ock_err(ERR_STATE_UNSAVEABLE));
++            return CKR_STATE_UNSAVEABLE;
++        }
+         active_ops++;
+         if (op_data != NULL) {
+             TRACE_ERROR("%s\n", ock_err(ERR_STATE_UNSAVEABLE));
+@@ -711,7 +791,7 @@ CK_RV session_mgr_get_op_state(SESSION *sess,
+ 
+ //
+ //
+-CK_RV session_mgr_set_op_state(SESSION *sess,
++CK_RV session_mgr_set_op_state(STDLL_TokData_t *tokdata, SESSION *sess,
+                                CK_OBJECT_HANDLE encr_key,
+                                CK_OBJECT_HANDLE auth_key,
+                                CK_BYTE *data, CK_ULONG data_len)
+@@ -891,19 +971,19 @@ CK_RV session_mgr_set_op_state(SESSION *sess,
+     // state information looks okay.  cleanup the current session state, first
+     //
+     if (sess->encr_ctx.active)
+-        encr_mgr_cleanup(&sess->encr_ctx);
++        encr_mgr_cleanup(tokdata, sess, &sess->encr_ctx);
+ 
+     if (sess->decr_ctx.active)
+-        decr_mgr_cleanup(&sess->decr_ctx);
++        decr_mgr_cleanup(tokdata, sess, &sess->decr_ctx);
+ 
+     if (sess->digest_ctx.active)
+-        digest_mgr_cleanup(&sess->digest_ctx);
++        digest_mgr_cleanup(tokdata, sess, &sess->digest_ctx);
+ 
+     if (sess->sign_ctx.active)
+-        sign_mgr_cleanup(&sess->sign_ctx);
++        sign_mgr_cleanup(tokdata, sess, &sess->sign_ctx);
+ 
+     if (sess->verify_ctx.active)
+-        verify_mgr_cleanup(&sess->verify_ctx);
++        verify_mgr_cleanup(tokdata, sess, &sess->verify_ctx);
+ 
+ 
+     // copy the new state information
+diff --git a/usr/lib/common/sign_mgr.c b/usr/lib/common/sign_mgr.c
+index c7268e01..74e3a9e0 100644
+--- a/usr/lib/common/sign_mgr.c
++++ b/usr/lib/common/sign_mgr.c
+@@ -805,7 +805,8 @@ done:
+ 
+ //
+ //
+-CK_RV sign_mgr_cleanup(SIGN_VERIFY_CONTEXT *ctx)
++CK_RV sign_mgr_cleanup(STDLL_TokData_t *tokdata, SESSION *sess,
++                       SIGN_VERIFY_CONTEXT *ctx)
+ {
+     if (!ctx) {
+         TRACE_ERROR("Invalid function argument.\n");
+@@ -821,6 +822,7 @@ CK_RV sign_mgr_cleanup(SIGN_VERIFY_CONTEXT *ctx)
+     ctx->recover = FALSE;
+     ctx->context_len = 0;
+     ctx->pkey_active = FALSE;
++    ctx->state_unsaveable = FALSE;
+ 
+     if (ctx->mech.pParameter) {
+         free(ctx->mech.pParameter);
+@@ -828,9 +830,14 @@ CK_RV sign_mgr_cleanup(SIGN_VERIFY_CONTEXT *ctx)
+     }
+ 
+     if (ctx->context) {
+-        free(ctx->context);
++        if (ctx->context_free_func != NULL)
++            ctx->context_free_func(tokdata, sess, ctx->context,
++                                   ctx->context_len);
++        else
++            free(ctx->context);
+         ctx->context = NULL;
+     }
++    ctx->context_free_func = NULL;
+ 
+     return CKR_OK;
+ }
+diff --git a/usr/lib/common/verify_mgr.c b/usr/lib/common/verify_mgr.c
+index c46a9803..b49fbb49 100644
+--- a/usr/lib/common/verify_mgr.c
++++ b/usr/lib/common/verify_mgr.c
+@@ -798,7 +798,8 @@ done:
+ 
+ //
+ //
+-CK_RV verify_mgr_cleanup(SIGN_VERIFY_CONTEXT *ctx)
++CK_RV verify_mgr_cleanup(STDLL_TokData_t *tokdata, SESSION *sess,
++                         SIGN_VERIFY_CONTEXT *ctx)
+ {
+     if (!ctx) {
+         TRACE_ERROR("Invalid function argument.\n");
+@@ -814,6 +815,7 @@ CK_RV verify_mgr_cleanup(SIGN_VERIFY_CONTEXT *ctx)
+     ctx->recover = FALSE;
+     ctx->context_len = 0;
+     ctx->pkey_active = FALSE;
++    ctx->state_unsaveable = FALSE;
+ 
+     if (ctx->mech.pParameter) {
+         free(ctx->mech.pParameter);
+@@ -821,9 +823,14 @@ CK_RV verify_mgr_cleanup(SIGN_VERIFY_CONTEXT *ctx)
+     }
+ 
+     if (ctx->context) {
+-        free(ctx->context);
++        if (ctx->context_free_func != NULL)
++            ctx->context_free_func(tokdata, sess, ctx->context,
++                                   ctx->context_len);
++        else
++            free(ctx->context);
+         ctx->context = NULL;
+     }
++    ctx->context_free_func = NULL;
+ 
+     return CKR_OK;
+ }
+diff --git a/usr/lib/ep11_stdll/ep11_specific.c b/usr/lib/ep11_stdll/ep11_specific.c
+index fb6055e9..49775d0a 100644
+--- a/usr/lib/ep11_stdll/ep11_specific.c
++++ b/usr/lib/ep11_stdll/ep11_specific.c
+@@ -8091,7 +8091,7 @@ static CK_RV ep11_ende_crypt_init(STDLL_TokData_t * tokdata, SESSION * session,
+         ctx->context_len = ep11_state_l;
+         ctx->pkey_active = FALSE;
+         if (rc != CKR_OK) {
+-            decr_mgr_cleanup(ctx);
++            decr_mgr_cleanup(tokdata, session, ctx);
+             rc = ep11_error_to_pkcs11_error(rc, session);
+             TRACE_ERROR("%s m_DecryptInit rc=0x%lx blob_len=0x%zx "
+                         "mech=0x%lx\n", __func__, rc, blob_len,
+@@ -8124,7 +8124,7 @@ static CK_RV ep11_ende_crypt_init(STDLL_TokData_t * tokdata, SESSION * session,
+         ctx->context_len = ep11_state_l;
+         ctx->pkey_active = FALSE;
+         if (rc != CKR_OK) {
+-            encr_mgr_cleanup(ctx);
++            encr_mgr_cleanup(tokdata, session, ctx);
+             rc = ep11_error_to_pkcs11_error(rc, session);
+             TRACE_ERROR("%s m_EncryptInit rc=0x%lx blob_len=0x%zx "
+                         "mech=0x%lx\n", __func__, rc, blob_len,
+diff --git a/usr/lib/ep11_stdll/new_host.c b/usr/lib/ep11_stdll/new_host.c
+index cd12604e..a0e7517c 100644
+--- a/usr/lib/ep11_stdll/new_host.c
++++ b/usr/lib/ep11_stdll/new_host.c
+@@ -1223,8 +1223,9 @@ CK_RV SC_SetOperationState(STDLL_TokData_t *tokdata,
+         goto done;
+     }
+ 
+-    rc = session_mgr_set_op_state(sess, hEncryptionKey, hAuthenticationKey,
+-                                  pOperationState, ulOperationStateLen);
++    rc = session_mgr_set_op_state(tokdata, sess, hEncryptionKey,
++                                  hAuthenticationKey, pOperationState,
++                                  ulOperationStateLen);
+ 
+     if (rc != CKR_OK)
+         TRACE_DEVEL("session_mgr_set_op_state() failed.\n");
+@@ -2160,7 +2161,7 @@ CK_RV SC_Encrypt(STDLL_TokData_t *tokdata, ST_SESSION_HANDLE *sSession,
+ done:
+     if (rc != CKR_BUFFER_TOO_SMALL && (rc != CKR_OK || length_only != TRUE)) {
+         if (sess)
+-            encr_mgr_cleanup(&sess->encr_ctx);
++            encr_mgr_cleanup(tokdata, sess, &sess->encr_ctx);
+     }
+ 
+     TRACE_INFO("C_Encrypt: rc = 0x%08lx, sess = %ld, amount = %lu\n",
+@@ -2236,7 +2237,7 @@ CK_RV SC_EncryptUpdate(STDLL_TokData_t *tokdata, ST_SESSION_HANDLE *sSession,
+ done:
+     if (rc != CKR_OK && rc != CKR_BUFFER_TOO_SMALL) {
+         if (sess)
+-            encr_mgr_cleanup(&sess->encr_ctx);
++            encr_mgr_cleanup(tokdata, sess, &sess->encr_ctx);
+     }
+ 
+     TRACE_INFO("C_EncryptUpdate: rc = 0x%08lx, sess = %ld, amount = %lu\n",
+@@ -2310,7 +2311,7 @@ CK_RV SC_EncryptFinal(STDLL_TokData_t * tokdata, ST_SESSION_HANDLE * sSession,
+ done:
+     if (rc != CKR_BUFFER_TOO_SMALL && (rc != CKR_OK || length_only != TRUE)) {
+         if (sess)
+-            encr_mgr_cleanup(&sess->encr_ctx);
++            encr_mgr_cleanup(tokdata, sess, &sess->encr_ctx);
+     }
+ 
+     TRACE_INFO("C_EncryptFinal: rc = 0x%08lx, sess = %ld\n",
+@@ -2478,7 +2479,7 @@ CK_RV SC_Decrypt(STDLL_TokData_t *tokdata, ST_SESSION_HANDLE *sSession,
+ done:
+     if (rc != CKR_BUFFER_TOO_SMALL && (rc != CKR_OK || length_only != TRUE)) {
+         if (sess)
+-            decr_mgr_cleanup(&sess->decr_ctx);
++            decr_mgr_cleanup(tokdata, sess, &sess->decr_ctx);
+     }
+ 
+     TRACE_INFO("C_Decrypt: rc = 0x%08lx, sess = %ld, amount = %lu\n",
+@@ -2554,7 +2555,7 @@ CK_RV SC_DecryptUpdate(STDLL_TokData_t *tokdata, ST_SESSION_HANDLE *sSession,
+ done:
+     if (rc != CKR_OK && rc != CKR_BUFFER_TOO_SMALL && sess != NULL) {
+         if (sess)
+-            decr_mgr_cleanup(&sess->decr_ctx);
++            decr_mgr_cleanup(tokdata, sess, &sess->decr_ctx);
+     }
+ 
+     TRACE_INFO("C_DecryptUpdate: rc = 0x%08lx, sess = %ld, amount = %lu\n",
+@@ -2626,7 +2627,7 @@ CK_RV SC_DecryptFinal(STDLL_TokData_t *tokdata, ST_SESSION_HANDLE *sSession,
+ done:
+     if (rc != CKR_BUFFER_TOO_SMALL && (rc != CKR_OK || length_only != TRUE)) {
+         if (sess)
+-            decr_mgr_cleanup(&sess->decr_ctx);
++            decr_mgr_cleanup(tokdata, sess, &sess->decr_ctx);
+     }
+ 
+     TRACE_INFO("C_DecryptFinal: rc = 0x%08lx, sess = %ld, amount = %lu\n",
+@@ -3022,7 +3023,7 @@ CK_RV SC_Sign(STDLL_TokData_t *tokdata, ST_SESSION_HANDLE *sSession,
+ done:
+     if (rc != CKR_BUFFER_TOO_SMALL && (rc != CKR_OK || length_only != TRUE)) {
+         if (sess != NULL)
+-            sign_mgr_cleanup(&sess->sign_ctx);
++            sign_mgr_cleanup(tokdata, sess, &sess->sign_ctx);
+     }
+ 
+     TRACE_INFO("C_Sign: rc = 0x%08lx, sess = %ld, datalen = %lu\n",
+@@ -3104,7 +3105,7 @@ CK_RV SC_SignUpdate(STDLL_TokData_t *tokdata, ST_SESSION_HANDLE *sSession,
+ 
+ done:
+     if (rc != CKR_OK && sess != NULL)
+-        sign_mgr_cleanup(&sess->sign_ctx);
++        sign_mgr_cleanup(tokdata, sess, &sess->sign_ctx);
+ 
+     TRACE_INFO("C_SignUpdate: rc = 0x%08lx, sess = %ld, datalen = %lu\n",
+                rc, (sess == NULL) ? -1 : (CK_LONG) sess->handle, ulPartLen);
+@@ -3185,7 +3186,7 @@ CK_RV SC_SignFinal(STDLL_TokData_t *tokdata, ST_SESSION_HANDLE *sSession,
+ done:
+     if (rc != CKR_BUFFER_TOO_SMALL && (rc != CKR_OK || length_only != TRUE)) {
+         if (sess != NULL)
+-            sign_mgr_cleanup(&sess->sign_ctx);
++            sign_mgr_cleanup(tokdata, sess, &sess->sign_ctx);
+     }
+ 
+     TRACE_INFO("C_SignFinal: rc = 0x%08lx, sess = %ld\n",
+@@ -3406,7 +3407,7 @@ CK_RV SC_Verify(STDLL_TokData_t *tokdata, ST_SESSION_HANDLE *sSession,
+ 
+ done:
+     if (sess != NULL)
+-        verify_mgr_cleanup(&sess->verify_ctx);
++        verify_mgr_cleanup(tokdata, sess, &sess->verify_ctx);
+ 
+     TRACE_INFO("C_Verify: rc = 0x%08lx, sess = %ld, datalen = %lu\n",
+                rc, (sess == NULL) ? -1 : (CK_LONG) sess->handle, ulDataLen);
+@@ -3487,7 +3488,7 @@ CK_RV SC_VerifyUpdate(STDLL_TokData_t *tokdata, ST_SESSION_HANDLE *sSession,
+ 
+ done:
+     if (rc != CKR_OK && sess != NULL)
+-        verify_mgr_cleanup(&sess->verify_ctx);
++        verify_mgr_cleanup(tokdata, sess, &sess->verify_ctx);
+ 
+     TRACE_INFO("C_VerifyUpdate: rc = 0x%08lx, sess = %ld, datalen = %lu\n",
+                rc, (sess == NULL) ? -1 : (CK_LONG) sess->handle, ulPartLen);
+@@ -3562,7 +3563,7 @@ CK_RV SC_VerifyFinal(STDLL_TokData_t *tokdata, ST_SESSION_HANDLE *sSession,
+ 
+ done:
+     if (sess != NULL)
+-        verify_mgr_cleanup(&sess->verify_ctx);
++        verify_mgr_cleanup(tokdata, sess, &sess->verify_ctx);
+ 
+     TRACE_INFO("C_VerifyFinal: rc = 0x%08lx, sess = %ld\n",
+                rc, (sess == NULL) ? -1 : (CK_LONG) sess->handle);
+diff --git a/usr/lib/ica_s390_stdll/ica_specific.c b/usr/lib/ica_s390_stdll/ica_specific.c
+index 7a81145d..77876467 100644
+--- a/usr/lib/ica_s390_stdll/ica_specific.c
++++ b/usr/lib/ica_s390_stdll/ica_specific.c
+@@ -810,8 +810,10 @@ CK_RV token_specific_sha_init(STDLL_TokData_t *tokdata, DIGEST_CONTEXT *ctx,
+     }
+ 
+     /* (re)alloc ctx in one memory area */
+-    if (ctx->context)
++    if (ctx->context) {
+         free(ctx->context);
++        ctx->context_free_func = NULL;
++    }
+     ctx->context_len = 0;
+     ctx->context = malloc(ctxsize + devctxsize);
+     if (ctx->context == NULL) {
+diff --git a/usr/lib/icsf_stdll/new_host.c b/usr/lib/icsf_stdll/new_host.c
+index cfef7425..09e9d27a 100644
+--- a/usr/lib/icsf_stdll/new_host.c
++++ b/usr/lib/icsf_stdll/new_host.c
+@@ -773,8 +773,9 @@ CK_RV SC_SetOperationState(STDLL_TokData_t *tokdata,
+     //set the handle into the session.
+     sess->handle = sSession->sessionh;
+ 
+-    rc = session_mgr_set_op_state(sess, hEncryptionKey, hAuthenticationKey,
+-                                  pOperationState, ulOperationStateLen);
++    rc = session_mgr_set_op_state(tokdata, sess, hEncryptionKey,
++                                  hAuthenticationKey, pOperationState,
++                                  ulOperationStateLen);
+ 
+     if (rc != CKR_OK)
+         TRACE_DEVEL("session_mgr_set_op_state() failed.\n");
+@@ -1556,7 +1557,7 @@ CK_RV SC_Encrypt(STDLL_TokData_t *tokdata, ST_SESSION_HANDLE *sSession,
+ done:
+     if (rc != CKR_BUFFER_TOO_SMALL && (rc != CKR_OK || length_only != TRUE)) {
+         if (sess)
+-            encr_mgr_cleanup(&sess->encr_ctx);
++            encr_mgr_cleanup(tokdata, sess, &sess->encr_ctx);
+     }
+ 
+     TRACE_INFO("C_Encrypt: rc = 0x%08lx, sess = %ld, amount = %lu\n",
+@@ -1612,7 +1613,7 @@ CK_RV SC_EncryptUpdate(STDLL_TokData_t *tokdata, ST_SESSION_HANDLE *sSession,
+ done:
+     if (rc != CKR_OK && rc != CKR_BUFFER_TOO_SMALL) {
+         if (sess)
+-            encr_mgr_cleanup(&sess->encr_ctx);
++            encr_mgr_cleanup(tokdata, sess, &sess->encr_ctx);
+     }
+ 
+     TRACE_INFO("C_EncryptUpdate: rc = 0x%08lx, sess = %ld, amount = %lu\n",
+@@ -1671,7 +1672,7 @@ CK_RV SC_EncryptFinal(STDLL_TokData_t * tokdata, ST_SESSION_HANDLE * sSession,
+ done:
+     if (rc != CKR_BUFFER_TOO_SMALL && (rc != CKR_OK || length_only != TRUE)) {
+         if (sess)
+-            encr_mgr_cleanup(&sess->encr_ctx);
++            encr_mgr_cleanup(tokdata, sess, &sess->encr_ctx);
+     }
+ 
+     TRACE_INFO("C_EncryptFinal: rc = 0x%08lx, sess = %ld\n",
+@@ -1790,7 +1791,7 @@ CK_RV SC_Decrypt(STDLL_TokData_t *tokdata, ST_SESSION_HANDLE *sSession,
+ done:
+     if (rc != CKR_BUFFER_TOO_SMALL && (rc != CKR_OK || length_only != TRUE)) {
+         if (sess)
+-            decr_mgr_cleanup(&sess->decr_ctx);
++            decr_mgr_cleanup(tokdata, sess, &sess->decr_ctx);
+     }
+ 
+     TRACE_INFO("C_Decrypt: rc = 0x%08lx, sess = %ld, amount = %lu\n",
+@@ -1846,7 +1847,7 @@ CK_RV SC_DecryptUpdate(STDLL_TokData_t *tokdata, ST_SESSION_HANDLE *sSession,
+ done:
+     if (rc != CKR_OK && rc != CKR_BUFFER_TOO_SMALL && sess != NULL) {
+         if (sess)
+-            decr_mgr_cleanup(&sess->decr_ctx);
++            decr_mgr_cleanup(tokdata, sess, &sess->decr_ctx);
+     }
+ 
+     TRACE_INFO("C_DecryptUpdate: rc = 0x%08lx, sess = %ld, amount = %lu\n",
+@@ -1903,7 +1904,7 @@ CK_RV SC_DecryptFinal(STDLL_TokData_t *tokdata, ST_SESSION_HANDLE *sSession,
+ done:
+     if (rc != CKR_BUFFER_TOO_SMALL && (rc != CKR_OK || length_only != TRUE)) {
+         if (sess)
+-            decr_mgr_cleanup(&sess->decr_ctx);
++            decr_mgr_cleanup(tokdata, sess, &sess->decr_ctx);
+     }
+ 
+     TRACE_INFO("C_DecryptFinal: rc = 0x%08lx, sess = %ld, amount = %lu\n",
+@@ -2261,7 +2262,7 @@ CK_RV SC_Sign(STDLL_TokData_t *tokdata, ST_SESSION_HANDLE *sSession,
+ done:
+     if (rc != CKR_BUFFER_TOO_SMALL && (rc != CKR_OK || pSignature)) {
+         if (sess != NULL)
+-            sign_mgr_cleanup(&sess->sign_ctx);
++            sign_mgr_cleanup(tokdata, sess, &sess->sign_ctx);
+     }
+ 
+     TRACE_INFO("C_Sign: rc = 0x%08lx, sess = %ld, datalen = %lu\n",
+@@ -2312,7 +2313,7 @@ CK_RV SC_SignUpdate(STDLL_TokData_t *tokdata, ST_SESSION_HANDLE *sSession,
+         TRACE_DEVEL("icsftok_sign_update() failed.\n");
+ done:
+     if (rc != CKR_OK && sess != NULL)
+-        sign_mgr_cleanup(&sess->sign_ctx);
++        sign_mgr_cleanup(tokdata, sess, &sess->sign_ctx);
+ 
+     TRACE_INFO("C_SignUpdate: rc = 0x%08lx, sess = %ld, datalen = %lu\n",
+                rc, (sess == NULL) ? -1 : (CK_LONG) sess->handle, ulPartLen);
+@@ -2364,7 +2365,7 @@ CK_RV SC_SignFinal(STDLL_TokData_t *tokdata, ST_SESSION_HANDLE *sSession,
+ done:
+     if (rc != CKR_BUFFER_TOO_SMALL && (rc != CKR_OK || pSignature)) {
+         if (sess != NULL)
+-            sign_mgr_cleanup(&sess->sign_ctx);
++            sign_mgr_cleanup(tokdata, sess, &sess->sign_ctx);
+     }
+ 
+     TRACE_INFO("C_SignFinal: rc = 0x%08lx, sess = %ld\n",
+@@ -2517,7 +2518,7 @@ CK_RV SC_Verify(STDLL_TokData_t *tokdata, ST_SESSION_HANDLE *sSession,
+ 
+ done:
+     if (sess != NULL)
+-        verify_mgr_cleanup(&sess->verify_ctx);
++        verify_mgr_cleanup(tokdata, sess, &sess->verify_ctx);
+ 
+     TRACE_INFO("C_Verify: rc = 0x%08lx, sess = %ld, datalen = %lu\n",
+                rc, (sess == NULL) ? -1 : (CK_LONG) sess->handle, ulDataLen);
+@@ -2568,7 +2569,7 @@ CK_RV SC_VerifyUpdate(STDLL_TokData_t *tokdata, ST_SESSION_HANDLE *sSession,
+ 
+ done:
+     if (rc != CKR_OK && sess != NULL)
+-        verify_mgr_cleanup(&sess->verify_ctx);
++        verify_mgr_cleanup(tokdata, sess, &sess->verify_ctx);
+ 
+     TRACE_INFO("C_VerifyUpdate: rc = 0x%08lx, sess = %ld, datalen = %lu\n",
+                rc, (sess == NULL) ? -1 : (CK_LONG) sess->handle, ulPartLen);
+@@ -2619,7 +2620,7 @@ CK_RV SC_VerifyFinal(STDLL_TokData_t *tokdata, ST_SESSION_HANDLE *sSession,
+ 
+ done:
+     if (sess != NULL)
+-        verify_mgr_cleanup(&sess->verify_ctx);
++        verify_mgr_cleanup(tokdata, sess, &sess->verify_ctx);
+ 
+     TRACE_INFO("C_VerifyFinal: rc = 0x%08lx, sess = %ld\n",
+                rc, (sess == NULL) ? -1 : (CK_LONG) sess->handle);
diff --git a/SOURCES/opencryptoki-openssl3-ab3fceae6194e8213e9d3ffb7447ccd04d469b9d.patch b/SOURCES/opencryptoki-openssl3-ab3fceae6194e8213e9d3ffb7447ccd04d469b9d.patch
new file mode 100644
index 0000000..091bc8e
--- /dev/null
+++ b/SOURCES/opencryptoki-openssl3-ab3fceae6194e8213e9d3ffb7447ccd04d469b9d.patch
@@ -0,0 +1,115 @@
+commit ab3fceae6194e8213e9d3ffb7447ccd04d469b9d
+Author: Ingo Franzki <ifranzki@linux.ibm.com>
+Date:   Mon Jul 5 10:45:04 2021 +0200
+
+    COMMON: sw_crypt.c: Remove support for OpenSSL < v1.1.1
+    
+    Remove support for OpenSSL < v1.1.1. This code used low level
+    DES/AES functions.
+    
+    Signed-off-by: Ingo Franzki <ifranzki@linux.ibm.com>
+
+diff --git a/usr/lib/common/sw_crypt.c b/usr/lib/common/sw_crypt.c
+index 906a41ab..253b3c26 100644
+--- a/usr/lib/common/sw_crypt.c
++++ b/usr/lib/common/sw_crypt.c
+@@ -32,51 +32,6 @@ CK_RV sw_des3_cbc(CK_BYTE *in_data,
+                   CK_ULONG *out_data_len,
+                   CK_BYTE *init_v, CK_BYTE *key_value, CK_BYTE encrypt)
+ {
+-#if OPENSSL_VERSION_NUMBER < 0x10100000L
+-    DES_key_schedule des_key1;
+-    DES_key_schedule des_key2;
+-    DES_key_schedule des_key3;
+-
+-    const_DES_cblock key_SSL1, key_SSL2, key_SSL3;
+-    DES_cblock ivec;
+-
+-    // the des decrypt will only fail if the data length is not evenly divisible
+-    // by DES_BLOCK_SIZE
+-    if (in_data_len % DES_BLOCK_SIZE) {
+-        TRACE_ERROR("%s\n", ock_err(ERR_DATA_LEN_RANGE));
+-        return CKR_DATA_LEN_RANGE;
+-    }
+-    // The key as passed in is a 24 byte string containing 3 keys
+-    // pick it apart and create the key schedules
+-    memcpy(&key_SSL1, key_value, (size_t) 8);
+-    memcpy(&key_SSL2, key_value + 8, (size_t) 8);
+-    memcpy(&key_SSL3, key_value + 16, (size_t) 8);
+-    DES_set_key_unchecked(&key_SSL1, &des_key1);
+-    DES_set_key_unchecked(&key_SSL2, &des_key2);
+-    DES_set_key_unchecked(&key_SSL3, &des_key3);
+-
+-    memcpy(ivec, init_v, sizeof(ivec));
+-
+-    // Encrypt or decrypt the data
+-    if (encrypt) {
+-        DES_ede3_cbc_encrypt(in_data,
+-                             out_data,
+-                             in_data_len,
+-                             &des_key1,
+-                             &des_key2, &des_key3, &ivec, DES_ENCRYPT);
+-        *out_data_len = in_data_len;
+-    } else {
+-        DES_ede3_cbc_encrypt(in_data,
+-                             out_data,
+-                             in_data_len,
+-                             &des_key1,
+-                             &des_key2, &des_key3, &ivec, DES_DECRYPT);
+-
+-        *out_data_len = in_data_len;
+-    }
+-
+-    return CKR_OK;
+-#else
+     CK_RV rc;
+     int outlen;
+     const EVP_CIPHER *cipher = EVP_des_ede3_cbc();
+@@ -109,7 +64,6 @@ CK_RV sw_des3_cbc(CK_BYTE *in_data,
+ done:
+     EVP_CIPHER_CTX_free(ctx);
+     return rc;
+-#endif
+ }
+ 
+ CK_RV sw_aes_cbc(CK_BYTE *in_data,
+@@ -119,33 +73,6 @@ CK_RV sw_aes_cbc(CK_BYTE *in_data,
+                  CK_BYTE *init_v, CK_BYTE *key_value, CK_ULONG keylen,
+                  CK_BYTE encrypt)
+ {
+-#if OPENSSL_VERSION_NUMBER < 0x10100000L
+-    AES_KEY aes_key;
+-
+-    UNUSED(out_data_len); //XXX can this parameter be removed ?
+-
+-    memset(&aes_key, 0, sizeof(aes_key));
+-
+-    // the aes decrypt will only fail if the data length is not evenly divisible
+-    // by AES_BLOCK_SIZE
+-    if (in_data_len % AES_BLOCK_SIZE) {
+-        TRACE_ERROR("%s\n", ock_err(ERR_DATA_LEN_RANGE));
+-        return CKR_DATA_LEN_RANGE;
+-    }
+-
+-    // Encrypt or decrypt the data
+-    if (encrypt) {
+-        AES_set_encrypt_key(key_value, keylen * 8, &aes_key);
+-        AES_cbc_encrypt(in_data, out_data, in_data_len, &aes_key,
+-                        init_v, AES_ENCRYPT);
+-    } else {
+-        AES_set_decrypt_key(key_value, keylen * 8, &aes_key);
+-        AES_cbc_encrypt(in_data,  out_data, in_data_len, &aes_key,
+-                        init_v, AES_DECRYPT);
+-    }
+-
+-    return CKR_OK;
+-#else
+     CK_RV rc;
+     int outlen;
+     const EVP_CIPHER *cipher = NULL;
+@@ -187,5 +114,4 @@ CK_RV sw_aes_cbc(CK_BYTE *in_data,
+ done:
+     EVP_CIPHER_CTX_free(ctx);
+     return rc;
+-#endif
+ }
diff --git a/SOURCES/opencryptoki-openssl3-c4683eb904238d20cb34a4c7661ffac04901283c.patch b/SOURCES/opencryptoki-openssl3-c4683eb904238d20cb34a4c7661ffac04901283c.patch
new file mode 100644
index 0000000..8a88c26
--- /dev/null
+++ b/SOURCES/opencryptoki-openssl3-c4683eb904238d20cb34a4c7661ffac04901283c.patch
@@ -0,0 +1,37 @@
+commit c4683eb904238d20cb34a4c7661ffac04901283c
+Author: Ingo Franzki <ifranzki@linux.ibm.com>
+Date:   Tue Jun 29 17:35:30 2021 +0200
+
+    COMMON: Add OPENSSL_VERSION_PREREQ macro to check for OpenSSL version
+    
+    Make the OPENSSL_VERSION_PREREQ macro available independent of the
+    used OpenSSL version, so that the code can easily check for the OpenSSL
+    version it is compiled with.
+    
+    Signed-off-by: Ingo Franzki <ifranzki@linux.ibm.com>
+
+diff --git a/usr/lib/common/defs.h b/usr/lib/common/defs.h
+index 22d75d2d..8ab50517 100644
+--- a/usr/lib/common/defs.h
++++ b/usr/lib/common/defs.h
+@@ -17,6 +17,20 @@
+ #ifndef _DEFS_H
+ #define _DEFS_H
+ 
++#include <openssl/opensslv.h>
++
++#ifndef OPENSSL_VERSION_PREREQ
++    #if defined(OPENSSL_VERSION_MAJOR) && defined(OPENSSL_VERSION_MINOR)
++        #define OPENSSL_VERSION_PREREQ(maj, min)        \
++            ((OPENSSL_VERSION_MAJOR << 16) +        \
++            OPENSSL_VERSION_MINOR >= ((maj) << 16) + (min))
++    #else
++        #define OPENSSL_VERSION_PREREQ(maj, min)        \
++            (OPENSSL_VERSION_NUMBER >= (((maj) << 28) | \
++            ((min) << 20)))
++    #endif
++#endif
++
+ #define MAX_SESSION_COUNT     64
+ #define MAX_PIN_LEN           8
+ #define MIN_PIN_LEN           4
diff --git a/SOURCES/opencryptoki-openssl3-dd9cfe2ef89dad185397df46227f9392a6317d35.patch b/SOURCES/opencryptoki-openssl3-dd9cfe2ef89dad185397df46227f9392a6317d35.patch
new file mode 100644
index 0000000..dccf2e8
--- /dev/null
+++ b/SOURCES/opencryptoki-openssl3-dd9cfe2ef89dad185397df46227f9392a6317d35.patch
@@ -0,0 +1,49 @@
+commit dd9cfe2ef89dad185397df46227f9392a6317d35
+Author: Ingo Franzki <ifranzki@linux.ibm.com>
+Date:   Wed Jul 21 13:54:59 2021 +0200
+
+    CONFIGURE: Check that OpenSSL 1.1.1 or later is available
+    
+    Signed-off-by: Ingo Franzki <ifranzki@linux.ibm.com>
+
+diff --git a/configure.ac b/configure.ac
+index 286b7408..f47060d9 100644
+--- a/configure.ac
++++ b/configure.ac
+@@ -277,21 +277,14 @@ if test "x$with_openssl" != "xno"; then
+ 	old_libs="$LIBS"
+ 	CFLAGS="$CFLAGS $OPENSSL_CFLAGS"
+ 	LIBS="$LIBS $OPENSSL_LIBS"
+-	AC_CHECK_HEADER([openssl/ssl.h], [], [
+-		if test "x$with_openssl" != "xcheck"; then
+-			AC_MSG_ERROR([Build with OpenSSL requested but OpenSSL headers couldn't be found])
+-		fi
+-		with_openssl=no
++	AC_CHECK_HEADER([openssl/evp.h], [], [
++		AC_MSG_ERROR([OpenSSL 1.1.1 or later is required but OpenSSL headers couldn't be found])
+ 	])
+ 	if test "x$with_openssl" != "xno"; then
+-		AC_CHECK_LIB([crypto], [RSA_generate_key], [
++		AC_CHECK_LIB([crypto], [EVP_sha3_256], [
+ 			OPENSSL_LIBS="$OPENSSL_LIBS -lcrypto"
+-			with_openssl=yes
+-			], [
+-				if test "x$with_openssl" != "xcheck"; then
+-					AC_MSG_ERROR([Build with OpenSSL requested but OpenSSL libraries couldn't be found])
+-				fi
+-				with_openssl=no
++			with_openssl=yes], [
++			AC_MSG_ERROR([OpenSSL 1.1.1 or later is required but OpenSSL libraries version 1.1.1 or later couldn't be found])
+ 		])
+ 	fi
+ 	if test "x$with_openssl" = "xno"; then
+@@ -299,6 +292,9 @@ if test "x$with_openssl" != "xno"; then
+ 		LIBS="$old_libs"
+ 	fi
+ fi
++if test "x$with_openssl" != "xyes"; then
++	AC_MSG_ERROR([OpenSSL 1.1.1 or later is required but build without OpenSSL was requested])
++fi
+ AC_SUBST([OPENSSL_CFLAGS])
+ AC_SUBST([OPENSSL_LIBS])
+ 
diff --git a/SOURCES/opencryptoki-openssl3-ecf71404e84ae35931cd6c7398c825378ee052b6.patch b/SOURCES/opencryptoki-openssl3-ecf71404e84ae35931cd6c7398c825378ee052b6.patch
new file mode 100644
index 0000000..722c1d6
--- /dev/null
+++ b/SOURCES/opencryptoki-openssl3-ecf71404e84ae35931cd6c7398c825378ee052b6.patch
@@ -0,0 +1,853 @@
+commit ecf71404e84ae35931cd6c7398c825378ee052b6
+Author: Ingo Franzki <ifranzki@linux.ibm.com>
+Date:   Fri Jul 2 11:20:22 2021 +0200
+
+    TESTCASES: Soft: Skip tests with RSA publ.exp. not supported by OpenSSL
+    
+    OpenSSL 3.0 only accepts public exponents of 3 and 65537 for RSA keys.
+    Skip the testcase if another public exponent is used.
+    
+    Also fixed some ugly line breaks within messages.
+    
+    Signed-off-by: Ingo Franzki <ifranzki@linux.ibm.com>
+
+diff --git a/testcases/common/common.c b/testcases/common/common.c
+index bfd486cb..0a64ecf2 100644
+--- a/testcases/common/common.c
++++ b/testcases/common/common.c
+@@ -876,6 +876,16 @@ int is_valid_cca_pubexp(CK_BYTE pubexp[], CK_ULONG pubexp_len)
+         || (pubexp_len == 3 && (!memcmp(pubexp, exp65537, 3)));
+ }
+ 
++/** Returns true if pubexp is valid for Soft Tokens **/
++int is_valid_soft_pubexp(CK_BYTE pubexp[], CK_ULONG pubexp_len)
++{
++    CK_BYTE exp3[] = { 0x03 };  // 3
++    CK_BYTE exp65537[] = { 0x01, 0x00, 0x01 };  // 65537
++
++    return (pubexp_len == 1 && (!memcmp(pubexp, exp3, 1)))
++        || (pubexp_len == 3 && (!memcmp(pubexp, exp65537, 3)));
++}
++
+ /** Returns true if slot_id is an ICSF token
+  ** ICSF token info is not necessarily hard-coded like the other tokens
+  ** so there is no single identifying attribute. So, instead just
+diff --git a/testcases/crypto/rsa_func.c b/testcases/crypto/rsa_func.c
+index 62aa7a76..8739ed37 100644
+--- a/testcases/crypto/rsa_func.c
++++ b/testcases/crypto/rsa_func.c
+@@ -102,8 +102,8 @@ CK_RV do_EncryptDecryptRSA(struct GENERATED_TEST_SUITE_INFO *tsuite)
+ 
+         if (!keysize_supported(slot_id, tsuite->mech.mechanism,
+                                tsuite->tv[i].modbits)) {
+-            testcase_skip("Token in slot %ld cannot be used with "
+-                          "modbits.='%ld'", SLOT_ID, tsuite->tv[i].modbits);
++            testcase_skip("Token in slot %ld cannot be used with modbits.='%ld'",
++                          SLOT_ID, tsuite->tv[i].modbits);
+             free(s);
+             continue;
+         }
+@@ -111,8 +111,7 @@ CK_RV do_EncryptDecryptRSA(struct GENERATED_TEST_SUITE_INFO *tsuite)
+         if (is_ep11_token(slot_id)) {
+             if (!is_valid_ep11_pubexp(tsuite->tv[i].publ_exp,
+                                       tsuite->tv[i].publ_exp_len)) {
+-                testcase_skip("EP11 Token cannot "
+-                              "be used with publ_exp.='%s'", s);
++                testcase_skip("EP11 Token cannot be used with publ_exp.='%s'", s);
+                 free(s);
+                 continue;
+             }
+@@ -124,8 +123,7 @@ CK_RV do_EncryptDecryptRSA(struct GENERATED_TEST_SUITE_INFO *tsuite)
+         if (is_cca_token(slot_id)) {
+             if (!is_valid_cca_pubexp(tsuite->tv[i].publ_exp,
+                                      tsuite->tv[i].publ_exp_len)) {
+-                testcase_skip("CCA Token cannot "
+-                              "be used with publ_exp.='%s'", s);
++                testcase_skip("CCA Token cannot be used with publ_exp.='%s'", s);
+                 free(s);
+                 continue;
+             }
+@@ -148,6 +146,16 @@ CK_RV do_EncryptDecryptRSA(struct GENERATED_TEST_SUITE_INFO *tsuite)
+                  continue;
+              }
+         }
++
++        if (is_soft_token(slot_id)) {
++            if (!is_valid_soft_pubexp(tsuite->tv[i].publ_exp,
++                                      tsuite->tv[i].publ_exp_len)) {
++                testcase_skip("Soft Token cannot be used with publ_exp.='%s'",
++                              s);
++                free(s);
++                continue;
++            }
++        }
+         // tpm special cases:
+         // tpm token can only use public exponent 0x010001 (65537)
+         // so skip test if invalid public exponent is used
+@@ -155,8 +163,7 @@ CK_RV do_EncryptDecryptRSA(struct GENERATED_TEST_SUITE_INFO *tsuite)
+             if ((!is_valid_tpm_pubexp(tsuite->tv[i].publ_exp,
+                                       tsuite->tv[i].publ_exp_len))
+                 || (!is_valid_tpm_modbits(tsuite->tv[i].modbits))) {
+-                testcase_skip("TPM Token cannot " "be used with publ_exp.='%s'",
+-                              s);
++                testcase_skip("TPM Token cannot be used with publ_exp.='%s'", s);
+                 free(s);
+                 continue;
+             }
+@@ -166,8 +173,7 @@ CK_RV do_EncryptDecryptRSA(struct GENERATED_TEST_SUITE_INFO *tsuite)
+             if (!is_valid_icsf_pubexp(tsuite->tv[i].publ_exp,
+                                       tsuite->tv[i].publ_exp_len) ||
+                 (tsuite->tv[i].modbits < 1024)) {
+-                testcase_skip("ICSF Token cannot be used with "
+-                              "publ_exp='%s'.", s);
++                testcase_skip("ICSF Token cannot be used with publ_exp='%s'.", s);
+                 free(s);
+                 continue;
+             }
+@@ -376,8 +382,8 @@ CK_RV do_EncryptDecryptImportRSA(struct PUBLISHED_TEST_SUITE_INFO *tsuite)
+ 
+         if (!keysize_supported(slot_id, tsuite->mech.mechanism,
+                                tsuite->tv[i].mod_len * 8)) {
+-            testcase_skip("Token in slot %ld cannot be used with "
+-                          "modbits.='%ld'", SLOT_ID, tsuite->tv[i].mod_len * 8);
++            testcase_skip("Token in slot %ld cannot be used with modbits.='%ld'",
++                          SLOT_ID, tsuite->tv[i].mod_len * 8);
+             free(s);
+             continue;
+         }
+@@ -385,16 +391,14 @@ CK_RV do_EncryptDecryptImportRSA(struct PUBLISHED_TEST_SUITE_INFO *tsuite)
+         if (is_ep11_token(slot_id)) {
+             if (!is_valid_ep11_pubexp(tsuite->tv[i].pub_exp,
+                                       tsuite->tv[i].pubexp_len)) {
+-                testcase_skip("EP11 Token cannot "
+-                              "be used with publ_exp.='%s'", s);
++                testcase_skip("EP11 Token cannot be used with publ_exp.='%s'", s);
+                 free(s);
+                 continue;
+             }
+             // modulus length must be multiple of 128 byte
+             // skip test if modulus length has unsuported size
+             if ((tsuite->tv[i].mod_len % 128) != 0) {
+-                testcase_skip("EP11 Token cannot be used with "
+-                              "this test vector.");
++                testcase_skip("EP11 Token cannot be used with this test vector.");
+                 free(s);
+                 continue;
+             }
+@@ -416,8 +420,7 @@ CK_RV do_EncryptDecryptImportRSA(struct PUBLISHED_TEST_SUITE_INFO *tsuite)
+                 (tsuite->tv[i].exp2_len >
+                  (tsuite->tv[i].mod_len / 2)) ||
+                 (tsuite->tv[i].coef_len > (tsuite->tv[i].mod_len / 2))) {
+-                testcase_skip("ICA Token cannot be used with "
+-                              "this test vector.");
++                testcase_skip("ICA Token cannot be used with this test vector.");
+                 free(s);
+                 continue;
+             }
+@@ -431,12 +434,21 @@ CK_RV do_EncryptDecryptImportRSA(struct PUBLISHED_TEST_SUITE_INFO *tsuite)
+         if (is_cca_token(slot_id)) {
+             if (!is_valid_cca_pubexp(tsuite->tv[i].pub_exp,
+                                      tsuite->tv[i].pubexp_len)) {
+-                testcase_skip("CCA Token cannot "
+-                              "be used with publ_exp.='%s'", s);
++                testcase_skip("CCA Token cannot be used with publ_exp.='%s'", s);
++                free(s);
++                continue;
++            }
++        }
++
++        if (is_soft_token(slot_id)) {
++            if (!is_valid_soft_pubexp(tsuite->tv[i].pub_exp,
++                                      tsuite->tv[i].pubexp_len)) {
++                testcase_skip("Soft Token cannot be used with publ_exp.='%s'", s);
+                 free(s);
+                 continue;
+             }
+         }
++
+         // tpm special cases:
+         // tpm token can only use public exponent 0x010001 (65537)
+         // so skip test if invalid public exponent is used
+@@ -444,8 +456,7 @@ CK_RV do_EncryptDecryptImportRSA(struct PUBLISHED_TEST_SUITE_INFO *tsuite)
+             if ((!is_valid_tpm_pubexp(tsuite->tv[i].pub_exp,
+                                       tsuite->tv[i].pubexp_len))
+                 || (!is_valid_tpm_modbits(tsuite->tv[i].mod_len * 8))) {
+-                testcase_skip("TPM Token cannot " "be used with publ_exp.='%s'",
+-                              s);
++                testcase_skip("TPM Token cannot be used with publ_exp.='%s'", s);
+                 free(s);
+                 continue;
+             }
+@@ -455,8 +466,7 @@ CK_RV do_EncryptDecryptImportRSA(struct PUBLISHED_TEST_SUITE_INFO *tsuite)
+             if (!is_valid_icsf_pubexp(tsuite->tv[i].pub_exp,
+                                       tsuite->tv[i].pubexp_len) ||
+                 (tsuite->tv[i].mod_len * 8 < 1024)) {
+-                testcase_skip("ICSF Token cannot be used with "
+-                              "publ_exp='%s'.", s);
++                testcase_skip("ICSF Token cannot be used with publ_exp='%s'.", s);
+                 free(s);
+                 continue;
+             }
+@@ -691,8 +701,8 @@ CK_RV do_SignVerifyRSA(struct GENERATED_TEST_SUITE_INFO * tsuite,
+ 
+         if (!keysize_supported(slot_id, tsuite->mech.mechanism,
+                                tsuite->tv[i].modbits)) {
+-            testcase_skip("Token in slot %ld cannot be used with "
+-                          "modbits.='%ld'", SLOT_ID, tsuite->tv[i].modbits);
++            testcase_skip("Token in slot %ld cannot be used with modbits.='%ld'",
++                          SLOT_ID, tsuite->tv[i].modbits);
+             free(s);
+             continue;
+         }
+@@ -700,8 +710,7 @@ CK_RV do_SignVerifyRSA(struct GENERATED_TEST_SUITE_INFO * tsuite,
+         if (is_ep11_token(slot_id)) {
+             if (!is_valid_ep11_pubexp(tsuite->tv[i].publ_exp,
+                                       tsuite->tv[i].publ_exp_len)) {
+-                testcase_skip("EP11 Token cannot "
+-                              "be used with publ_exp.='%s'", s);
++                testcase_skip("EP11 Token cannot be used with publ_exp.='%s'", s);
+                 free(s);
+                 continue;
+             }
+@@ -710,8 +719,16 @@ CK_RV do_SignVerifyRSA(struct GENERATED_TEST_SUITE_INFO * tsuite,
+         if (is_cca_token(slot_id)) {
+             if (!is_valid_cca_pubexp(tsuite->tv[i].publ_exp,
+                                      tsuite->tv[i].publ_exp_len)) {
+-                testcase_skip("CCA Token cannot "
+-                              "be used with publ_exp='%s'.", s);
++                testcase_skip("CCA Token cannot be used with publ_exp='%s'.", s);
++                free(s);
++                continue;
++            }
++        }
++
++        if (is_soft_token(slot_id)) {
++            if (!is_valid_soft_pubexp(tsuite->tv[i].publ_exp,
++                                      tsuite->tv[i].publ_exp_len)) {
++                testcase_skip("Soft Token cannot be used with publ_exp='%s'.", s);
+                 free(s);
+                 continue;
+             }
+@@ -721,8 +738,7 @@ CK_RV do_SignVerifyRSA(struct GENERATED_TEST_SUITE_INFO * tsuite,
+             if ((!is_valid_tpm_pubexp(tsuite->tv[i].publ_exp,
+                                       tsuite->tv[i].publ_exp_len))
+                 || (!is_valid_tpm_modbits(tsuite->tv[i].modbits))) {
+-                testcase_skip("TPM Token cannot " "be used with publ_exp='%s'.",
+-                              s);
++                testcase_skip("TPM Token cannot be used with publ_exp='%s'.", s);
+                 free(s);
+                 continue;
+             }
+@@ -732,8 +748,7 @@ CK_RV do_SignVerifyRSA(struct GENERATED_TEST_SUITE_INFO * tsuite,
+             if (!is_valid_icsf_pubexp(tsuite->tv[i].publ_exp,
+                                       tsuite->tv[i].publ_exp_len) ||
+                 (tsuite->tv[i].modbits < 1024)) {
+-                testcase_skip("ICSF Token cannot be used with "
+-                              "publ_exp='%s'.", s);
++                testcase_skip("ICSF Token cannot be used with publ_exp='%s'.", s);
+                 free(s);
+                 continue;
+             }
+@@ -944,16 +959,23 @@ CK_RV do_SignVerify_RSAPSS(struct GENERATED_TEST_SUITE_INFO * tsuite)
+ 
+         if (!keysize_supported(slot_id, tsuite->mech.mechanism,
+                                tsuite->tv[i].modbits)) {
+-            testcase_skip("Token in slot %ld cannot be used with "
+-                          "modbits.='%ld'", SLOT_ID, tsuite->tv[i].modbits);
++            testcase_skip("Token in slot %ld cannot be used with modbits.='%ld'",
++                          SLOT_ID, tsuite->tv[i].modbits);
+             free(s);
+             continue;
+         }
+         if (is_cca_token(slot_id)) {
+             if (!is_valid_cca_pubexp(tsuite->tv[i].publ_exp,
+                                      tsuite->tv[i].publ_exp_len)) {
+-                testcase_skip("CCA Token cannot "
+-                              "be used with publ_exp='%s'.", s);
++                testcase_skip("CCA Token cannot be used with publ_exp='%s'.", s);
++                free(s);
++                continue;
++            }
++        }
++        if (is_soft_token(slot_id)) {
++            if (!is_valid_soft_pubexp(tsuite->tv[i].publ_exp,
++                                      tsuite->tv[i].publ_exp_len)) {
++                testcase_skip("Soft Token cannot be used with publ_exp='%s'.", s);
+                 free(s);
+                 continue;
+             }
+@@ -1154,8 +1176,8 @@ CK_RV do_WrapUnwrapRSA(struct GENERATED_TEST_SUITE_INFO * tsuite)
+ 
+         if (!keysize_supported(slot_id, tsuite->mech.mechanism,
+                                tsuite->tv[i].modbits)) {
+-            testcase_skip("Token in slot %ld cannot be used with "
+-                          "modbits.='%ld'", SLOT_ID, tsuite->tv[i].modbits);
++            testcase_skip("Token in slot %ld cannot be used with modbits.='%ld'",
++                          SLOT_ID, tsuite->tv[i].modbits);
+             continue;
+         }
+         // get public exponent from test vector
+@@ -1169,8 +1191,7 @@ CK_RV do_WrapUnwrapRSA(struct GENERATED_TEST_SUITE_INFO * tsuite)
+         if (is_ep11_token(slot_id)) {
+             if (!is_valid_ep11_pubexp(tsuite->tv[i].publ_exp,
+                                       tsuite->tv[i].publ_exp_len)) {
+-                testcase_skip("EP11 Token cannot "
+-                              "be used with publ_exp.='%s'", s);
++                testcase_skip("EP11 Token cannot be used with publ_exp.='%s'", s);
+                 free(s);
+                 continue;
+             }
+@@ -1179,8 +1200,7 @@ CK_RV do_WrapUnwrapRSA(struct GENERATED_TEST_SUITE_INFO * tsuite)
+             if (!is_valid_icsf_pubexp(tsuite->tv[i].publ_exp,
+                                       tsuite->tv[i].publ_exp_len) ||
+                 (tsuite->tv[i].modbits < 1024)) {
+-                testcase_skip("ICSF Token cannot be used with "
+-                              "publ_exp='%s'.", s);
++                testcase_skip("ICSF Token cannot be used with publ_exp='%s'.", s);
+                 free(s);
+                 continue;
+             }
+@@ -1189,8 +1209,7 @@ CK_RV do_WrapUnwrapRSA(struct GENERATED_TEST_SUITE_INFO * tsuite)
+             if ((!is_valid_tpm_pubexp(tsuite->tv[i].publ_exp,
+                                       tsuite->tv[i].publ_exp_len)) ||
+                 (!is_valid_tpm_modbits(tsuite->tv[i].modbits))) {
+-                testcase_skip("TPM Token cannot " "be used with publ_exp.='%s'",
+-                              s);
++                testcase_skip("TPM Token cannot be used with publ_exp.='%s'", s);
+                 free(s);
+                 continue;
+            }
+@@ -1198,8 +1217,7 @@ CK_RV do_WrapUnwrapRSA(struct GENERATED_TEST_SUITE_INFO * tsuite)
+         if (is_cca_token(slot_id)) {
+             if (!is_valid_cca_pubexp(tsuite->tv[i].publ_exp,
+                                      tsuite->tv[i].publ_exp_len)) {
+-                testcase_skip("CCA Token cannot "
+-                              "be used with publ_exp='%s'.", s);
++                testcase_skip("CCA Token cannot be used with publ_exp='%s'.", s);
+                 free(s);
+                 continue;
+             }
+@@ -1228,6 +1246,14 @@ CK_RV do_WrapUnwrapRSA(struct GENERATED_TEST_SUITE_INFO * tsuite)
+                  continue;
+              }
+         }
++        if (is_soft_token(slot_id)) {
++            if (!is_valid_soft_pubexp(tsuite->tv[i].publ_exp,
++                                      tsuite->tv[i].publ_exp_len)) {
++                testcase_skip("Soft Token cannot be used with publ_exp='%s'.", s);
++                free(s);
++                continue;
++            }
++        }
+ 
+         // begin test
+         testcase_begin("%s Wrap Unwrap with test vector %d, "
+@@ -1554,8 +1580,7 @@ CK_RV do_SignRSA(struct PUBLISHED_TEST_SUITE_INFO * tsuite)
+                 (tsuite->tv[i].exp2_len >
+                  (tsuite->tv[i].mod_len / 2)) ||
+                 (tsuite->tv[i].coef_len > (tsuite->tv[i].mod_len / 2))) {
+-                testcase_skip("ICA Token cannot be used with "
+-                              "this test vector.");
++                testcase_skip("ICA Token cannot be used with this test vector.");
+                 continue;
+             }
+ 
+@@ -1565,8 +1590,7 @@ CK_RV do_SignRSA(struct PUBLISHED_TEST_SUITE_INFO * tsuite)
+         // skip test if modulus length has unsuported size
+         if (is_ep11_token(slot_id)) {
+             if ((tsuite->tv[i].mod_len % 128) != 0) {
+-                testcase_skip("EP11 Token cannot be used with "
+-                              "this test vector.");
++                testcase_skip("EP11 Token cannot be used with this test vector.");
+                 continue;
+             }
+         }
+@@ -1575,8 +1599,7 @@ CK_RV do_SignRSA(struct PUBLISHED_TEST_SUITE_INFO * tsuite)
+             if ((!is_valid_tpm_pubexp(tsuite->tv[i].pub_exp,
+                                       tsuite->tv[i].pubexp_len))
+                 || (!is_valid_tpm_modbits(tsuite->tv[i].mod_len))) {
+-                testcase_skip("TPM Token cannot "
+-                              "be used with this test vector.");
++                testcase_skip("TPM Token cannot be used with this test vector.");
+                 continue;
+             }
+         }
+@@ -1584,8 +1607,15 @@ CK_RV do_SignRSA(struct PUBLISHED_TEST_SUITE_INFO * tsuite)
+         if (is_cca_token(slot_id)) {
+             if (!is_valid_cca_pubexp(tsuite->tv[i].pub_exp,
+                                      tsuite->tv[i].pubexp_len)) {
+-                testcase_skip("CCA Token cannot "
+-                              "be used with this test vector.");
++                testcase_skip("CCA Token cannot be used with this test vector.");
++                continue;
++            }
++        }
++
++        if (is_soft_token(slot_id)) {
++            if (!is_valid_soft_pubexp(tsuite->tv[i].pub_exp,
++                                      tsuite->tv[i].pubexp_len)) {
++                testcase_skip("Soft Token cannot be used with this test vector.");
+                 continue;
+             }
+         }
+@@ -1735,8 +1765,7 @@ CK_RV do_VerifyRSA(struct PUBLISHED_TEST_SUITE_INFO * tsuite)
+         // skip test if modulus length has unsuported size
+         if (is_ep11_token(slot_id)) {
+             if ((tsuite->tv[i].mod_len % 128) != 0) {
+-                testcase_skip("EP11 Token cannot be used with "
+-                              "this test vector.");
++                testcase_skip("EP11 Token cannot be used with this test vector.");
+                 continue;
+             }
+         }
+@@ -1745,8 +1774,7 @@ CK_RV do_VerifyRSA(struct PUBLISHED_TEST_SUITE_INFO * tsuite)
+             if ((!is_valid_tpm_pubexp(tsuite->tv[i].pub_exp,
+                                       tsuite->tv[i].pubexp_len))
+                 || (!is_valid_tpm_modbits(tsuite->tv[i].mod_len))) {
+-                testcase_skip("TPM Token cannot "
+-                              "be used with this test vector.");
++                testcase_skip("TPM Token cannot be used with this test vector.");
+                 continue;
+             }
+         }
+@@ -1754,8 +1782,15 @@ CK_RV do_VerifyRSA(struct PUBLISHED_TEST_SUITE_INFO * tsuite)
+         if (is_cca_token(slot_id)) {
+             if (!is_valid_cca_pubexp(tsuite->tv[i].pub_exp,
+                                      tsuite->tv[i].pubexp_len)) {
+-                testcase_skip("CCA Token cannot "
+-                              "be used with this test vector.");
++                testcase_skip("CCA Token cannot be used with this test vector.");
++                continue;
++            }
++        }
++
++        if (is_soft_token(slot_id)) {
++            if (!is_valid_soft_pubexp(tsuite->tv[i].pub_exp,
++                                      tsuite->tv[i].pubexp_len)) {
++                testcase_skip("Soft Token cannot be used with this test vector.");
+                 continue;
+             }
+         }
+diff --git a/testcases/crypto/rsaupdate_func.c b/testcases/crypto/rsaupdate_func.c
+index 20611b85..22f8d7e4 100644
+--- a/testcases/crypto/rsaupdate_func.c
++++ b/testcases/crypto/rsaupdate_func.c
+@@ -96,8 +96,8 @@ CK_RV do_SignVerifyUpdateRSA(struct GENERATED_TEST_SUITE_INFO *tsuite)
+ 
+         if (!keysize_supported(slot_id, tsuite->mech.mechanism,
+                                tsuite->tv[i].modbits)) {
+-            testcase_skip("Token in slot %ld cannot be used with "
+-                          "modbits.='%ld'", SLOT_ID, tsuite->tv[i].modbits);
++            testcase_skip("Token in slot %ld cannot be used with modbits.='%ld'",
++                          SLOT_ID, tsuite->tv[i].modbits);
+             free(s);
+             continue;
+         }
+@@ -105,8 +105,7 @@ CK_RV do_SignVerifyUpdateRSA(struct GENERATED_TEST_SUITE_INFO *tsuite)
+         if (is_ep11_token(slot_id)) {
+             if (!is_valid_ep11_pubexp(tsuite->tv[i].publ_exp,
+                                       tsuite->tv[i].publ_exp_len)) {
+-                testcase_skip("EP11 Token cannot "
+-                              "be used with publ_exp.='%s'", s);
++                testcase_skip("EP11 Token cannot be used with publ_exp.='%s'", s);
+                 free(s);
+                 continue;
+             }
+@@ -115,19 +114,27 @@ CK_RV do_SignVerifyUpdateRSA(struct GENERATED_TEST_SUITE_INFO *tsuite)
+         if (is_cca_token(slot_id)) {
+             if (!is_valid_cca_pubexp(tsuite->tv[i].publ_exp,
+                                      tsuite->tv[i].publ_exp_len)) {
+-                testcase_skip("CCA Token cannot "
+-                              "be used with publ_exp='%s'.", s);
++                testcase_skip("CCA Token cannot be used with publ_exp='%s'.", s);
+                 free(s);
+                 continue;
+             }
+         }
+ 
++        if (is_soft_token(slot_id)) {
++            if (!is_valid_soft_pubexp(tsuite->tv[i].publ_exp,
++                                      tsuite->tv[i].publ_exp_len)) {
++                testcase_skip("Soft Token cannot be used with publ_exp='%s'.", s);
++                free(s);
++                continue;
++            }
++        }
++
++
+         if (is_tpm_token(slot_id)) {
+             if ((!is_valid_tpm_pubexp(tsuite->tv[i].publ_exp,
+                                       tsuite->tv[i].publ_exp_len))
+                 || (!is_valid_tpm_modbits(tsuite->tv[i].modbits))) {
+-                testcase_skip("TPM Token cannot " "be used with publ_exp='%s'.",
+-                              s);
++                testcase_skip("TPM Token cannot be used with publ_exp='%s'.", s);
+                 free(s);
+                 continue;
+             }
+@@ -137,8 +144,7 @@ CK_RV do_SignVerifyUpdateRSA(struct GENERATED_TEST_SUITE_INFO *tsuite)
+             if (!is_valid_icsf_pubexp(tsuite->tv[i].publ_exp,
+                                       tsuite->tv[i].publ_exp_len) ||
+                 (tsuite->tv[i].modbits < 1024)) {
+-                testcase_skip("ICSF Token cannot "
+-                              "be used with publ_exp='%s'.", s);
++                testcase_skip("ICSF Token cannot be used with publ_exp='%s'.", s);
+                 free(s);
+                 continue;
+             }
+@@ -162,8 +168,7 @@ CK_RV do_SignVerifyUpdateRSA(struct GENERATED_TEST_SUITE_INFO *tsuite)
+                                        tsuite->tv[i].publ_exp_len,
+                                        &publ_key, &priv_key);
+         if (rc != CKR_OK) {
+-            testcase_error("generate_RSA_PKCS_KeyPair(), "
+-                           "rc=%s", p11_get_ckr(rc));
++            testcase_error("generate_RSA_PKCS_KeyPair(), rc=%s", p11_get_ckr(rc));
+             goto testcase_cleanup;
+         }
+ 
+@@ -367,8 +372,8 @@ CK_RV do_SignVerifyUpdate_RSAPSS(struct GENERATED_TEST_SUITE_INFO * tsuite)
+ 
+         if (!keysize_supported(slot_id, tsuite->mech.mechanism,
+                                tsuite->tv[i].modbits)) {
+-            testcase_skip("Token in slot %ld cannot be used with "
+-                          "modbits.='%ld'", SLOT_ID, tsuite->tv[i].modbits);
++            testcase_skip("Token in slot %ld cannot be used with modbits.='%ld'",
++                          SLOT_ID, tsuite->tv[i].modbits);
+             free(s);
+             continue;
+         }
+@@ -376,8 +381,7 @@ CK_RV do_SignVerifyUpdate_RSAPSS(struct GENERATED_TEST_SUITE_INFO * tsuite)
+         if (is_ep11_token(slot_id)) {
+             if (!is_valid_ep11_pubexp(tsuite->tv[i].publ_exp,
+                                       tsuite->tv[i].publ_exp_len)) {
+-                testcase_skip("EP11 Token cannot "
+-                              "be used with publ_exp.='%s'", s);
++                testcase_skip("EP11 Token cannot be used with publ_exp.='%s'", s);
+                 free(s);
+                 continue;
+             }
+@@ -386,8 +390,16 @@ CK_RV do_SignVerifyUpdate_RSAPSS(struct GENERATED_TEST_SUITE_INFO * tsuite)
+         if (is_cca_token(slot_id)) {
+             if (!is_valid_cca_pubexp(tsuite->tv[i].publ_exp,
+                                      tsuite->tv[i].publ_exp_len)) {
+-                testcase_skip("CCA Token cannot "
+-                              "be used with publ_exp='%s'.", s);
++                testcase_skip("CCA Token cannot be used with publ_exp='%s'.", s);
++                free(s);
++                continue;
++            }
++        }
++
++        if (is_soft_token(slot_id)) {
++            if (!is_valid_soft_pubexp(tsuite->tv[i].publ_exp,
++                                      tsuite->tv[i].publ_exp_len)) {
++                testcase_skip("Soft Token cannot be used with publ_exp='%s'.", s);
+                 free(s);
+                 continue;
+             }
+@@ -412,8 +424,7 @@ CK_RV do_SignVerifyUpdate_RSAPSS(struct GENERATED_TEST_SUITE_INFO * tsuite)
+                                        tsuite->tv[i].publ_exp_len,
+                                        &publ_key, &priv_key);
+         if (rc != CKR_OK) {
+-            testcase_error("generate_RSA_PKCS_KeyPair(), "
+-                           "rc=%s", p11_get_ckr(rc));
++            testcase_error("generate_RSA_PKCS_KeyPair(), rc=%s", p11_get_ckr(rc));
+             goto error;
+         }
+         // generate message
+@@ -639,8 +650,7 @@ CK_RV do_VerifyUpdateRSA(struct PUBLISHED_TEST_SUITE_INFO * tsuite)
+         if (is_ep11_token(slot_id)) {
+             if (!is_valid_ep11_pubexp(tsuite->tv[i].pub_exp,
+                                       tsuite->tv[i].pubexp_len)) {
+-                testcase_skip("EP11 Token cannot "
+-                              "be used with pub_exp.='%s'", s);
++                testcase_skip("EP11 Token cannot be used with pub_exp.='%s'", s);
+                 free(s);
+                 continue;
+             }
+@@ -650,8 +660,7 @@ CK_RV do_VerifyUpdateRSA(struct PUBLISHED_TEST_SUITE_INFO * tsuite)
+             if ((!is_valid_tpm_pubexp(tsuite->tv[i].pub_exp,
+                                       tsuite->tv[i].pubexp_len)) ||
+                 (!is_valid_tpm_modbits(tsuite->tv[i].mod_len))) {
+-                testcase_skip("TPM Token cannot "
+-                              "be used with pub_exp='%s'.", s);
++                testcase_skip("TPM Token cannot be used with pub_exp='%s'.", s);
+                 free(s);
+                 continue;
+             }
+@@ -660,8 +669,16 @@ CK_RV do_VerifyUpdateRSA(struct PUBLISHED_TEST_SUITE_INFO * tsuite)
+         if (is_cca_token(slot_id)) {
+             if (!is_valid_cca_pubexp(tsuite->tv[i].pub_exp,
+                                      tsuite->tv[i].pubexp_len)) {
+-                testcase_skip("CCA Token cannot "
+-                              "be used with publ_exp='%s'.", s);
++                testcase_skip("CCA Token cannot be used with publ_exp='%s'.", s);
++                free(s);
++                continue;
++            }
++        }
++
++        if (is_soft_token(slot_id)) {
++            if (!is_valid_soft_pubexp(tsuite->tv[i].pub_exp,
++                                      tsuite->tv[i].pubexp_len)) {
++                testcase_skip("Soft Token cannot be used with publ_exp='%s'.", s);
+                 free(s);
+                 continue;
+             }
+@@ -826,8 +843,7 @@ CK_RV do_SignUpdateRSA(struct PUBLISHED_TEST_SUITE_INFO * tsuite)
+                 (tsuite->tv[i].exp2_len >
+                  (tsuite->tv[i].mod_len / 2)) ||
+                 (tsuite->tv[i].coef_len > (tsuite->tv[i].mod_len / 2))) {
+-                testcase_skip("ICA Token cannot be used with "
+-                              "this test vector.");
++                testcase_skip("ICA Token cannot be used with this test vector.");
+                 free(s);
+                 continue;
+             }
+@@ -848,8 +864,7 @@ CK_RV do_SignUpdateRSA(struct PUBLISHED_TEST_SUITE_INFO * tsuite)
+         if (is_ep11_token(slot_id)) {
+             if (!is_valid_ep11_pubexp(tsuite->tv[i].pub_exp,
+                                       tsuite->tv[i].pubexp_len)) {
+-                testcase_skip("EP11 Token cannot "
+-                              "be used with publ_exp.='%s'", s);
++                testcase_skip("EP11 Token cannot be used with publ_exp.='%s'", s);
+                 free(s);
+                 continue;
+             }
+@@ -859,8 +874,7 @@ CK_RV do_SignUpdateRSA(struct PUBLISHED_TEST_SUITE_INFO * tsuite)
+             if ((!is_valid_tpm_pubexp(tsuite->tv[i].pub_exp,
+                                       tsuite->tv[i].pubexp_len)) ||
+                 (!is_valid_tpm_modbits(tsuite->tv[i].mod_len))) {
+-                testcase_skip("TPM Token cannot "
+-                              "be used with pub_exp='%s'.", s);
++                testcase_skip("TPM Token cannot be used with pub_exp='%s'.", s);
+                 free(s);
+                 continue;
+             }
+@@ -869,8 +883,16 @@ CK_RV do_SignUpdateRSA(struct PUBLISHED_TEST_SUITE_INFO * tsuite)
+         if (is_cca_token(slot_id)) {
+             if (!is_valid_cca_pubexp(tsuite->tv[i].pub_exp,
+                                      tsuite->tv[i].pubexp_len)) {
+-                testcase_skip("CCA Token cannot "
+-                              "be used with publ_exp='%s'.", s);
++                testcase_skip("CCA Token cannot be used with publ_exp='%s'.", s);
++                free(s);
++                continue;
++            }
++        }
++
++        if (is_soft_token(slot_id)) {
++            if (!is_valid_soft_pubexp(tsuite->tv[i].pub_exp,
++                                      tsuite->tv[i].pubexp_len)) {
++                testcase_skip("Soft Token cannot be used with publ_exp='%s'.", s);
+                 free(s);
+                 continue;
+             }
+diff --git a/testcases/misc_tests/reencrypt.c b/testcases/misc_tests/reencrypt.c
+index a78e1f5a..93fa31bd 100644
+--- a/testcases/misc_tests/reencrypt.c
++++ b/testcases/misc_tests/reencrypt.c
+@@ -361,24 +361,29 @@ CK_RV do_reencrypt(struct mech_info *mech1, struct mech_info *mech2)
+ 
+         if (!keysize_supported(slot_id, mech2->key_gen_mech.mechanism,
+                                mech2->rsa_modbits)) {
+-            testcase_skip("Token in slot %ld cannot be used with "
+-                          "modbits.='%ld'", slot_id, mech2->rsa_modbits);
++            testcase_skip("Token in slot %ld cannot be used with modbits.='%ld'",
++                          slot_id, mech2->rsa_modbits);
+             goto testcase_cleanup;
+         }
+ 
+         if (is_ep11_token(slot_id)) {
+             if (!is_valid_ep11_pubexp(mech2->rsa_publ_exp,
+                                       mech2->rsa_publ_exp_len)) {
+-                testcase_skip("EP11 Token in cannot be used with "
+-                             "publ_exp.='%s'", s);
++                testcase_skip("EP11 Token in cannot be used with publ_exp.='%s'", s);
+                 goto testcase_cleanup;
+             }
+         }
+         if (is_cca_token(slot_id)) {
+             if (!is_valid_cca_pubexp(mech2->rsa_publ_exp,
+                                      mech2->rsa_publ_exp_len)) {
+-                testcase_skip("CCA Token in cannot be used with "
+-                        "     publ_exp.='%s'", s);
++                testcase_skip("CCA Token in cannot be used with publ_exp.='%s'", s);
++                goto testcase_cleanup;
++            }
++        }
++        if (is_soft_token(slot_id)) {
++            if (!is_valid_soft_pubexp(mech2->rsa_publ_exp,
++                                      mech2->rsa_publ_exp_len)) {
++                testcase_skip("Soft Token in cannot be used with publ_exp.='%s'", s);
+                 goto testcase_cleanup;
+             }
+         }
+@@ -386,8 +391,7 @@ CK_RV do_reencrypt(struct mech_info *mech1, struct mech_info *mech2)
+             if (!is_valid_tpm_pubexp(mech2->rsa_publ_exp,
+                                      mech2->rsa_publ_exp_len) ||
+                 !is_valid_tpm_modbits(mech2->rsa_modbits)) {
+-                testcase_skip("TPM Token cannot be used with "
+-                              "publ_exp.='%s'", s);
++                testcase_skip("TPM Token cannot be used with publ_exp.='%s'", s);
+                 goto testcase_cleanup;
+             }
+         }
+@@ -395,8 +399,7 @@ CK_RV do_reencrypt(struct mech_info *mech1, struct mech_info *mech2)
+             if (!is_valid_icsf_pubexp(mech2->rsa_publ_exp,
+                                       mech2->rsa_publ_exp_len) ||
+                 mech2->rsa_modbits < 1024) {
+-                testcase_skip("ICSF Token cannot be used with "
+-                              "publ_exp='%s'.", s);
++                testcase_skip("ICSF Token cannot be used with publ_exp='%s'.", s);
+                 goto testcase_cleanup;
+             }
+         }
+@@ -619,6 +622,14 @@ CK_RV do_encrypt_reencrypt(struct mech_info *mech1)
+                 goto testcase_cleanup;
+             }
+         }
++        if (is_soft_token(slot_id)) {
++            if (!is_valid_soft_pubexp(mech1->rsa_publ_exp,
++                                      mech1->rsa_publ_exp_len)) {
++                testsuite_skip(NUM_REENCRYPT_TESTS, "Soft Token cannot be "
++                               "used with publ_exp.='%s'", s);
++                goto testcase_cleanup;
++            }
++        }
+         if (is_tpm_token(slot_id) ) {
+             if (!is_valid_tpm_pubexp(mech1->rsa_publ_exp,
+                                      mech1->rsa_publ_exp_len) ||
+diff --git a/testcases/misc_tests/tok2tok_transport.c b/testcases/misc_tests/tok2tok_transport.c
+index 9c1dee8f..ebb44760 100644
+--- a/testcases/misc_tests/tok2tok_transport.c
++++ b/testcases/misc_tests/tok2tok_transport.c
+@@ -581,30 +581,35 @@ CK_RV do_wrap_key_test(struct wrapped_mech_info *tsuite,
+ 
+         if (!keysize_supported(slot_id1, tsuite->wrapped_key_gen_mech.mechanism,
+                                tsuite->rsa_modbits)) {
+-            testcase_skip("Token in slot %lu cannot be used with "
+-                          "modbits.='%ld'", slot_id1, tsuite->rsa_modbits);
++            testcase_skip("Token in slot %lu cannot be used with modbits.='%ld'",
++                          slot_id1, tsuite->rsa_modbits);
+             goto testcase_cleanup;
+         }
+         if (!keysize_supported(slot_id2, tsuite->wrapped_key_gen_mech.mechanism,
+                                tsuite->rsa_modbits)) {
+-            testcase_skip("Token in slot %lu cannot be used with "
+-                          "modbits.='%ld'", slot_id2, tsuite->rsa_modbits);
++            testcase_skip("Token in slot %lu cannot be used with modbits.='%ld'",
++                          slot_id2, tsuite->rsa_modbits);
+             goto testcase_cleanup;
+         }
+ 
+         if (is_ep11_token(slot_id1) || is_ep11_token(slot_id2)) {
+             if (!is_valid_ep11_pubexp(tsuite->rsa_publ_exp,
+                                       tsuite->rsa_publ_exp_len)) {
+-                testcase_skip("EP11 Token in cannot be used with "
+-                             "publ_exp.='%s'", s);
++                testcase_skip("EP11 Token in cannot be used with publ_exp.='%s'", s);
+                 goto testcase_cleanup;
+             }
+         }
+         if (is_cca_token(slot_id1) || is_cca_token(slot_id2)) {
+             if (!is_valid_cca_pubexp(tsuite->rsa_publ_exp,
+                                      tsuite->rsa_publ_exp_len)) {
+-                testcase_skip("CCA Token in scannot be used with "
+-                              "publ_exp.='%s'", s);
++                testcase_skip("CCA Token in scannot be used with publ_exp.='%s'", s);
++                goto testcase_cleanup;
++            }
++        }
++        if (is_soft_token(slot_id1) || is_cca_token(slot_id2)) {
++            if (!is_valid_soft_pubexp(tsuite->rsa_publ_exp,
++                                      tsuite->rsa_publ_exp_len)) {
++                testcase_skip("Soft Token in scannot be used with publ_exp.='%s'", s);
+                 goto testcase_cleanup;
+             }
+         }
+@@ -612,8 +617,7 @@ CK_RV do_wrap_key_test(struct wrapped_mech_info *tsuite,
+             if (!is_valid_tpm_pubexp(tsuite->rsa_publ_exp,
+                                      tsuite->rsa_publ_exp_len) ||
+                 !is_valid_tpm_modbits(tsuite->rsa_modbits)) {
+-                testcase_skip("TPM Token cannot " "be used with "
+-                              "publ_exp.='%s'", s);
++                testcase_skip("TPM Token cannot " "be used with publ_exp.='%s'", s);
+                 goto testcase_cleanup;
+             }
+         }
+@@ -621,8 +625,7 @@ CK_RV do_wrap_key_test(struct wrapped_mech_info *tsuite,
+             if (!is_valid_icsf_pubexp(tsuite->rsa_publ_exp,
+                                       tsuite->rsa_publ_exp_len) ||
+                 tsuite->rsa_modbits < 1024) {
+-                testcase_skip("ICSF Token cannot be used with "
+-                              "publ_exp='%s'.", s);
++                testcase_skip("ICSF Token cannot be used with publ_exp='%s'.", s);
+                 goto testcase_cleanup;
+             }
+         }
+@@ -967,31 +970,36 @@ CK_RV do_wrapping_test(struct wrapping_mech_info *tsuite)
+         if (!keysize_supported(slot_id1,
+                                tsuite->wrapping_key_gen_mech.mechanism,
+                                tsuite->rsa_modbits)) {
+-            testcase_skip("Token in slot %ld cannot be used with "
+-                          "modbits.='%ld'", slot_id1, tsuite->rsa_modbits);
++            testcase_skip("Token in slot %ld cannot be used with modbits.='%ld'",
++                          slot_id1, tsuite->rsa_modbits);
+             goto testcase_cleanup;
+         }
+         if (!keysize_supported(slot_id2,
+                                tsuite->wrapping_key_gen_mech.mechanism,
+                                tsuite->rsa_modbits)) {
+-            testcase_skip("Token in slot %ld cannot be used with "
+-                          "modbits.='%ld'", slot_id2, tsuite->rsa_modbits);
++            testcase_skip("Token in slot %ld cannot be used with modbits.='%ld'",
++                          slot_id2, tsuite->rsa_modbits);
+             goto testcase_cleanup;
+         }
+ 
+         if (is_ep11_token(slot_id1) || is_ep11_token(slot_id2)) {
+             if (!is_valid_ep11_pubexp(tsuite->rsa_publ_exp,
+                                       tsuite->rsa_publ_exp_len)) {
+-                testcase_skip("EP11 Token in cannot be used with "
+-                             "publ_exp.='%s'", s);
++                testcase_skip("EP11 Token in cannot be used with publ_exp.='%s'", s);
+                 goto testcase_cleanup;
+             }
+         }
+         if (is_cca_token(slot_id1) || is_cca_token(slot_id2)) {
+             if (!is_valid_cca_pubexp(tsuite->rsa_publ_exp,
+                                      tsuite->rsa_publ_exp_len)) {
+-                testcase_skip("CCA Token in scannot be used with "
+-                              "publ_exp.='%s'", s);
++                testcase_skip("CCA Token in scannot be used with publ_exp.='%s'", s);
++                goto testcase_cleanup;
++            }
++        }
++        if (is_soft_token(slot_id1) || is_soft_token(slot_id2)) {
++            if (!is_valid_soft_pubexp(tsuite->rsa_publ_exp,
++                                      tsuite->rsa_publ_exp_len)) {
++                testcase_skip("Soft Token in scannot be used with publ_exp.='%s'", s);
+                 goto testcase_cleanup;
+             }
+         }
+@@ -999,8 +1007,7 @@ CK_RV do_wrapping_test(struct wrapping_mech_info *tsuite)
+             if (!is_valid_tpm_pubexp(tsuite->rsa_publ_exp,
+                                      tsuite->rsa_publ_exp_len) ||
+                 !is_valid_tpm_modbits(tsuite->rsa_modbits)) {
+-                testcase_skip("TPM Token cannot " "be used with "
+-                              "publ_exp.='%s'", s);
++                testcase_skip("TPM Token cannot " "be used with publ_exp.='%s'", s);
+                 goto testcase_cleanup;
+             }
+         }
+@@ -1008,8 +1015,7 @@ CK_RV do_wrapping_test(struct wrapping_mech_info *tsuite)
+             if (!is_valid_icsf_pubexp(tsuite->rsa_publ_exp,
+                                       tsuite->rsa_publ_exp_len) ||
+                 tsuite->rsa_modbits < 1024) {
+-                testcase_skip("ICSF Token cannot be used with "
+-                              "publ_exp='%s'.", s);
++                testcase_skip("ICSF Token cannot be used with publ_exp='%s'.", s);
+                 goto testcase_cleanup;
+             }
+         }
diff --git a/SOURCES/opencryptoki-pkcsslotd-pidfile.patch b/SOURCES/opencryptoki-pkcsslotd-pidfile.patch
new file mode 100644
index 0000000..8af4d96
--- /dev/null
+++ b/SOURCES/opencryptoki-pkcsslotd-pidfile.patch
@@ -0,0 +1,12 @@
+diff -up opencryptoki-3.16.0/misc/pkcsslotd.service.in.me opencryptoki-3.16.0/misc/pkcsslotd.service.in
+--- opencryptoki-3.16.0/misc/pkcsslotd.service.in.me	2021-06-25 09:25:11.464487847 +0200
++++ opencryptoki-3.16.0/misc/pkcsslotd.service.in	2021-06-25 09:25:38.701225760 +0200
+@@ -4,7 +4,7 @@ After=local-fs.target
+ 
+ [Service]
+ Type=forking
+-PIDFile=/var/run/pkcsslotd.pid
++PIDFile=/run/pkcsslotd.pid
+ ExecStart=@sbindir@/pkcsslotd
+ 
+ [Install]
diff --git a/SOURCES/opencryptoki.module b/SOURCES/opencryptoki.module
new file mode 100644
index 0000000..4720c04
--- /dev/null
+++ b/SOURCES/opencryptoki.module
@@ -0,0 +1,8 @@
+# This file describes how to load the opensc module
+# See: http://p11-glue.freedesktop.org/doc/p11-kit/config.html
+
+# This is a relative path, which means it will be loaded from
+# the p11-kit default path which is usually $(libdir)/pkcs11.
+# Doing it this way allows for packagers to package opensc for
+# 32-bit and 64-bit and make them parallel installable
+module: libopencryptoki.so
diff --git a/SPECS/opencryptoki.spec b/SPECS/opencryptoki.spec
new file mode 100644
index 0000000..6235217
--- /dev/null
+++ b/SPECS/opencryptoki.spec
@@ -0,0 +1,686 @@
+Name:			opencryptoki
+Summary:		Implementation of the PKCS#11 (Cryptoki) specification v2.11
+Version:		3.16.0
+Release:		12%{?dist}
+License:		CPL
+URL:			https://github.com/opencryptoki/opencryptoki
+Source0:		https://github.com/opencryptoki/%{name}/archive/v%{version}/%{name}-%{version}.tar.gz
+Source1:		opencryptoki.module
+# https://bugzilla.redhat.com/show_bug.cgi?id=732756
+Patch0:			opencryptoki-3.11.0-group.patch
+
+# bz#1373833, change tmpfiles snippets from /var/lock/* to /run/lock/*
+Patch1:			opencryptoki-3.11.0-lockdir.patch
+# PIDfile below legacy directory /var/run/
+Patch2:			opencryptoki-pkcsslotd-pidfile.patch
+# Use --no-undefined to debug missing symbols
+#Patch100:			%%{name}-3.2-no-undefined.patch
+
+# upstream patches
+Patch200:	opencryptoki-3.16.0-4e3b43c3d8844402c04a66b55c6c940f965109f0.patch
+Patch201:	opencryptoki-3.16.0-c79e899d77a5724635a9d4451a34a240e2c7e891.patch
+Patch202:	opencryptoki-3.16.0-69244a5e0d9dfec3ef534b19b89a541576bb17dc.patch
+Patch203:	opencryptoki-3.16.0-b07505993dd8b2f367cf3b630f6da186e4e8550d.patch
+Patch204:	opencryptoki-3.16.0-b048be548508dd1958bb7271568f388d0f6cbcf8.patch
+Patch205:	opencryptoki-3.16.0-e9548127edae313da7840bcb87fd0afd04549c2e.patch
+Patch206:	opencryptoki-3.16.0-d929fe8470e99f4dcbbd889e7aa87e147d0d5b48.patch
+Patch207:	opencryptoki-3.16.0-19f56d12b302b87e1dacf613cc61a063ad209d15.patch
+Patch208:	opencryptoki-3.16.0-342dfbeb8275f5ea6ed52dd3f30126614ec1d037.patch
+Patch209:	opencryptoki-3.16.0-fa94a16116d8382a987ddf9e8cdd88027dd1f647.patch
+Patch210:	opencryptoki-3.16.0-d7de5092247a0efc2c397f12977a7c9925420143.patch
+Patch211:	opencryptoki-3.16.0-1fdd0e4497b0078e73e0004e3492db647c7c458b.patch
+Patch212:	opencryptoki-3.16.0-bf812c652c49d7e248b115d121a4f7f6568941a2.patch
+Patch213:	opencryptoki-3.16.0-7b7d83c571ceb3050969359817d4145600f14ae8.patch
+Patch214:	opencryptoki-3.16.0-pkcstok_migrate-detection_if_pkcsslotd_is_still_running.patch
+Patch215:	opencryptoki-3.16.0-5824364d995e5d2418f885ee57e377e11d1b3302.patch
+Patch216:	opencryptoki-3.16.0-e88a9de3128df1c4b89bd4c7312c15bb3eb34593.patch
+Patch217:	opencryptoki-3.16.0-d2f137cce5e6efb123842509352c7c49f889c67f.patch
+Patch218:	opencryptoki-openssl3-dd9cfe2ef89dad185397df46227f9392a6317d35.patch
+Patch219:	opencryptoki-openssl3-93588f53d918fe6c7452da076b95081fb6aa9aef.patch
+Patch220:	opencryptoki-openssl3-62fc2bcd98672c5d0ff8a2c926f3103110e91ed7.patch
+Patch221:	opencryptoki-openssl3-50408fc3ae0f25b256dda2033d538f88c9b4f903.patch
+Patch222:	opencryptoki-openssl3-145a696d478a1694ef314659a3d374f03f75c1b1.patch
+Patch223:	opencryptoki-openssl3-7a23c12214688b287b9591133445e593da633caa.patch
+Patch224:	opencryptoki-openssl3-ecf71404e84ae35931cd6c7398c825378ee052b6.patch
+Patch225:	opencryptoki-openssl3-50e3f06823696c74eea90a77e16b28da1f79cd47.patch
+Patch226:	opencryptoki-openssl3-ab3fceae6194e8213e9d3ffb7447ccd04d469b9d.patch
+Patch227:	opencryptoki-openssl3-5377d25a6cbe3d07afcd08276ad7e90f62cad0c9.patch
+Patch228:	opencryptoki-openssl3-6fee37f08391415cdf8d8610c501516c3d3ed29c.patch
+Patch230:	opencryptoki-openssl3-2c116d49359a5eb91ad7f1483c64650c7874a513.patch
+Patch231:	opencryptoki-openssl3-533cdea6897d1bc0af13490f1c89248c52e7a73b.patch
+Patch232:	opencryptoki-openssl3-5cceead028ec8e0c244b01d38c9096c96d98f96b.patch
+Patch233:	opencryptoki-openssl3-7b4177e8557887d196ce77a129d457e817f8cc59.patch
+Patch234:	opencryptoki-openssl3-11a53055b22d590bd3c197908b0ff63f6fd3c520.patch
+Patch235:	opencryptoki-openssl3-c4683eb904238d20cb34a4c7661ffac04901283c.patch
+Patch236:	opencryptoki-openssl3-11196c4d7e221d29f0d385bd48ae4d6023a6e874.patch
+Patch237:	opencryptoki-openssl3-4dd8a952fc00dd54cce090e4c053de408ba3884b.patch
+Patch238:	opencryptoki-openssl3-376e664f082b66de970b62a81588b034fd560d27.patch
+
+Requires(pre):		coreutils
+Requires: 		(selinux-policy >= 34.1.8-1 if selinux-policy-targeted)
+BuildRequires:		gcc
+BuildRequires:		gcc-c++
+BuildRequires:		openssl-devel
+%if 0%{?tmptok}
+BuildRequires:		trousers-devel
+%endif
+BuildRequires:		openldap-devel
+BuildRequires:		autoconf automake libtool
+BuildRequires:		bison flex
+BuildRequires:		systemd-devel
+BuildRequires:		libitm-devel
+BuildRequires:		expect
+BuildRequires:		make
+%ifarch s390 s390x
+BuildRequires:		libica-devel >= 2.3
+%endif
+Requires(pre):		%{name}-libs%{?_isa} = %{version}-%{release}
+Requires:		%{name}-libs%{?_isa} = %{version}-%{release}
+Requires:		%{name}(token)
+Requires(post):		systemd
+Requires(preun):	systemd
+Requires(postun):	systemd
+
+
+%description
+Opencryptoki implements the PKCS#11 specification v2.11 for a set of
+cryptographic hardware, such as IBM 4764 and 4765 crypto cards, and the
+Trusted Platform Module (TPM) chip. Opencryptoki also brings a software
+token implementation that can be used without any cryptographic
+hardware.
+This package contains the Slot Daemon (pkcsslotd) and general utilities.
+
+
+%package libs
+Summary:		The run-time libraries for opencryptoki package
+Requires(pre):	shadow-utils
+
+%description libs
+Opencryptoki implements the PKCS#11 specification v2.11 for a set of
+cryptographic hardware, such as IBM 4764 and 4765 crypto cards, and the
+Trusted Platform Module (TPM) chip. Opencryptoki also brings a software
+token implementation that can be used without any cryptographic
+hardware.
+This package contains the PKCS#11 library implementation, and requires
+at least one token implementation (packaged separately) to be fully
+functional.
+
+
+%package devel
+Summary:		Development files for openCryptoki
+Requires:		%{name}-libs%{?_isa} = %{version}-%{release}
+
+%description devel
+This package contains the development header files for building
+opencryptoki and PKCS#11 based applications
+
+
+%package swtok
+Summary:		The software token implementation for opencryptoki
+Requires(pre):		%{name}-libs%{?_isa} = %{version}-%{release}
+Requires:		%{name}-libs%{?_isa} = %{version}-%{release}
+Provides:		%{name}(token)
+
+%description swtok
+Opencryptoki implements the PKCS#11 specification v2.11 for a set of
+cryptographic hardware, such as IBM 4764 and 4765 crypto cards, and the
+Trusted Platform Module (TPM) chip. Opencryptoki also brings a software
+token implementation that can be used without any cryptographic
+hardware.
+This package brings the software token implementation to use opencryptoki
+without any specific cryptographic hardware.
+
+
+%package tpmtok
+Summary:		Trusted Platform Module (TPM) device support for opencryptoki
+Requires(pre):		%{name}-libs%{?_isa} = %{version}-%{release}
+Requires:		%{name}-libs%{?_isa} = %{version}-%{release}
+Provides:		%{name}(token)
+
+%description tpmtok
+Opencryptoki implements the PKCS#11 specification v2.11 for a set of
+cryptographic hardware, such as IBM 4764 and 4765 crypto cards, and the
+Trusted Platform Module (TPM) chip. Opencryptoki also brings a software
+token implementation that can be used without any cryptographic
+hardware.
+This package brings the necessary libraries and files to support
+Trusted Platform Module (TPM) devices in the opencryptoki stack.
+
+
+%package icsftok
+Summary:		ICSF token support for opencryptoki
+Requires(pre):		%{name}-libs%{?_isa} = %{version}-%{release}
+Requires:		%{name}-libs%{?_isa} = %{version}-%{release}
+Provides:		%{name}(token)
+
+%description icsftok
+Opencryptoki implements the PKCS#11 specification v2.11 for a set of
+cryptographic hardware, such as IBM 4764 and 4765 crypto cards, and the
+Trusted Platform Module (TPM) chip. Opencryptoki also brings a software
+token implementation that can be used without any cryptographic
+hardware.
+This package brings the necessary libraries and files to support
+ICSF token in the opencryptoki stack.
+
+
+%ifarch s390 s390x
+%package icatok
+Summary:		ICA cryptographic devices (clear-key) support for opencryptoki
+Requires(pre):		%{name}-libs%{?_isa} = %{version}-%{release}
+Requires:		%{name}-libs%{?_isa} = %{version}-%{release}
+Provides:		%{name}(token)
+
+%description icatok
+Opencryptoki implements the PKCS#11 specification v2.11 for a set of
+cryptographic hardware, such as IBM 4764 and 4765 crypto cards, and the
+Trusted Platform Module (TPM) chip. Opencryptoki also brings a software
+token implementation that can be used without any cryptographic
+hardware.
+This package brings the necessary libraries and files to support ICA
+devices in the opencryptoki stack. ICA is an interface to IBM
+cryptographic hardware such as IBM 4764 or 4765 that uses the
+"accelerator" or "clear-key" path.
+
+%package ccatok
+Summary:		CCA cryptographic devices (secure-key) support for opencryptoki
+Requires(pre):		%{name}-libs%{?_isa} = %{version}-%{release}
+Requires:		%{name}-libs%{?_isa} = %{version}-%{release}
+Provides:		%{name}(token)
+
+%description ccatok
+Opencryptoki implements the PKCS#11 specification v2.11 for a set of
+cryptographic hardware, such as IBM 4764 and 4765 crypto cards, and the
+Trusted Platform Module (TPM) chip. Opencryptoki also brings a software
+token implementation that can be used without any cryptographic
+hardware.
+This package brings the necessary libraries and files to support CCA
+devices in the opencryptoki stack. CCA is an interface to IBM
+cryptographic hardware such as IBM 4764 or 4765 that uses the
+"co-processor" or "secure-key" path.
+
+%package ep11tok
+Summary:		CCA cryptographic devices (secure-key) support for opencryptoki
+Requires(pre):		%{name}-libs%{?_isa} = %{version}-%{release}
+Requires:		%{name}-libs%{?_isa} = %{version}-%{release}
+Provides:		%{name}(token)
+
+%description ep11tok
+Opencryptoki implements the PKCS#11 specification v2.11 for a set of
+cryptographic hardware, such as IBM 4764 and 4765 crypto cards, and the
+Trusted Platform Module (TPM) chip. Opencryptoki also brings a software
+token implementation that can be used without any cryptographic
+hardware.
+This package brings the necessary libraries and files to support EP11
+tokens in the opencryptoki stack. The EP11 token is a token that uses
+the IBM Crypto Express adapters (starting with Crypto Express 4S adapters)
+configured with Enterprise PKCS#11 (EP11) firmware.
+%endif
+
+
+%prep
+%autosetup -p1
+
+
+%build
+./bootstrap.sh
+
+%configure --with-systemd=%{_unitdir} --enable-testcases	\
+%if 0%{?tpmtok}
+    --enable-tpmtok \
+%else
+    --disable-tpmtok \
+%endif
+%ifarch s390 s390x
+    --enable-icatok --enable-ccatok --enable-ep11tok --enable-pkcsep11_migrate
+%else
+    --disable-icatok --disable-ccatok --disable-ep11tok --disable-pkcsep11_migrate
+%endif
+
+%make_build CHGRP=/bin/true
+
+
+%install
+%make_install CHGRP=/bin/true
+
+
+%pre libs
+getent group pkcs11 >/dev/null || groupadd -r pkcs11
+exit 0
+
+%post
+%systemd_post pkcsslotd.service
+if test $1 -eq 1; then
+	%tmpfiles_create %{name}.conf
+fi
+
+%preun
+%systemd_preun pkcsslotd.service
+
+%postun
+%systemd_postun_with_restart pkcsslotd.service
+
+
+%files
+%doc ChangeLog FAQ README.md
+%doc doc/opencryptoki-howto.md
+%doc doc/README.token_data
+%dir %{_sysconfdir}/%{name}
+%config(noreplace) %{_sysconfdir}/%{name}/%{name}.conf
+%{_tmpfilesdir}/%{name}.conf
+%{_unitdir}/pkcsslotd.service
+%{_sbindir}/p11sak
+%{_sbindir}/pkcstok_migrate
+%{_sbindir}/pkcsconf
+%{_sbindir}/pkcsslotd
+%{_mandir}/man1/p11sak.1*
+%{_mandir}/man1/pkcstok_migrate.1*
+%{_mandir}/man1/pkcsconf.1*
+%{_mandir}/man5/%{name}.conf.5*
+%{_mandir}/man7/%{name}.7*
+%{_mandir}/man8/pkcsslotd.8*
+%{_libdir}/opencryptoki/methods
+%{_libdir}/pkcs11/methods
+%dir %attr(770,root,pkcs11) %{_sharedstatedir}/%{name}
+%ghost %dir %attr(770,root,pkcs11) %{_rundir}/lock/%{name}
+%ghost %dir %attr(770,root,pkcs11) %{_rundir}/lock/%{name}/*
+%dir %attr(770,root,pkcs11) %{_localstatedir}/log/opencryptoki
+
+%files libs
+%license LICENSE
+%{_sysconfdir}/ld.so.conf.d/*
+# Unversioned .so symlinks usually belong to -devel packages, but opencryptoki
+# needs them in the main package, because:
+#   documentation suggests that programs should dlopen "PKCS11_API.so".
+%dir %{_libdir}/opencryptoki
+%{_libdir}/opencryptoki/libopencryptoki.*
+%{_libdir}/opencryptoki/PKCS11_API.so
+%dir %{_libdir}/opencryptoki/stdll
+%dir %{_libdir}/pkcs11
+%{_libdir}/pkcs11/libopencryptoki.so
+%{_libdir}/pkcs11/PKCS11_API.so
+%{_libdir}/pkcs11/stdll
+
+%files devel
+%{_includedir}/%{name}/
+
+%files swtok
+%{_libdir}/opencryptoki/stdll/libpkcs11_sw.*
+%{_libdir}/opencryptoki/stdll/PKCS11_SW.so
+%dir %attr(770,root,pkcs11) %{_sharedstatedir}/%{name}/swtok/
+%dir %attr(770,root,pkcs11) %{_sharedstatedir}/%{name}/swtok/TOK_OBJ/
+
+%if 0%{?tmptok}
+%files tpmtok
+%doc doc/README.tpm_stdll
+%{_libdir}/opencryptoki/stdll/libpkcs11_tpm.*
+%{_libdir}/opencryptoki/stdll/PKCS11_TPM.so
+%dir %attr(770,root,pkcs11) %{_sharedstatedir}/%{name}/tpm/
+%endif
+
+%files icsftok
+%doc doc/README.icsf_stdll
+%{_sbindir}/pkcsicsf
+%{_mandir}/man1/pkcsicsf.1*
+%{_libdir}/opencryptoki/stdll/libpkcs11_icsf.*
+%{_libdir}/opencryptoki/stdll/PKCS11_ICSF.so
+%dir %attr(770,root,pkcs11) %{_sharedstatedir}/%{name}/icsf/
+
+%ifarch s390 s390x
+%files icatok
+%{_libdir}/opencryptoki/stdll/libpkcs11_ica.*
+%{_libdir}/opencryptoki/stdll/PKCS11_ICA.so
+%dir %attr(770,root,pkcs11) %{_sharedstatedir}/%{name}/lite/
+%dir %attr(770,root,pkcs11) %{_sharedstatedir}/%{name}/lite/TOK_OBJ/
+
+%files ccatok
+%doc doc/README.cca_stdll
+%{_sbindir}/pkcscca
+%{_mandir}/man1/pkcscca.1*
+%{_libdir}/opencryptoki/stdll/libpkcs11_cca.*
+%{_libdir}/opencryptoki/stdll/PKCS11_CCA.so
+%dir %attr(770,root,pkcs11) %{_sharedstatedir}/%{name}/ccatok/
+%dir %attr(770,root,pkcs11) %{_sharedstatedir}/%{name}/ccatok/TOK_OBJ/
+
+%files ep11tok
+%doc doc/README.ep11_stdll
+%config(noreplace) %{_sysconfdir}/%{name}/ep11tok.conf
+%config(noreplace) %{_sysconfdir}/%{name}/ep11cpfilter.conf
+%{_sbindir}/pkcsep11_migrate
+%{_sbindir}/pkcsep11_session
+%{_mandir}/man1/pkcsep11_migrate.1*
+%{_mandir}/man1/pkcsep11_session.1*
+%{_libdir}/opencryptoki/stdll/libpkcs11_ep11.*
+%{_libdir}/opencryptoki/stdll/PKCS11_EP11.so
+%dir %attr(770,root,pkcs11) %{_sharedstatedir}/%{name}/ep11tok/
+%dir %attr(770,root,pkcs11) %{_sharedstatedir}/%{name}/ep11tok/TOK_OBJ/
+%endif
+
+
+%changelog
+* Thu Aug 26 2021 Than Ngo <than@redhat.com> - 3.16.0-12
+- Related: #1989138, Support for OpenSSL 3.0
+
+* Mon Aug 23 2021 Than Ngo <than@redhat.com> - 3.16.0-11
+- Resolves: #1989138, Support for OpenSSL 3.0
+
+* Thu Aug 19 2021 Than Ngo <than@redhat.com> - 3.16.0-10
+- Resolves: #1987186, pkcstok_migrate leaves options with multiple strings in opencryptoki.conf options without double-quotes
+
+* Mon Aug 09 2021 Mohan Boddu <mboddu@redhat.com> - 3.16.0-9
+- Rebuilt for IMA sigs, glibc 2.34, aarch64 flags
+  Related: rhbz#1991688
+
+* Wed Jul 28 2021 Florian Weimer <fweimer@redhat.com> - 3.16.0-8
+- Rebuild to pick up OpenSSL 3.0 Beta ABI (#1984097)
+
+* Fri Jul 16 2021 Than Ngo <than@redhat.com> - 3.16.0-7
+- Resolves: #1974365, Fix detection if pkcsslotd is still running
+
+* Fri Jun 25 2021 Than Ngo <than@redhat.com> - 3.16.0-6
+- Resolves: #1974693, pkcsslotd PIDfile below legacy directory /var/run/
+
+* Wed Jun 16 2021 Mohan Boddu <mboddu@redhat.com> - 3.16.0-5
+- Rebuilt for RHEL 9 BETA for openssl 3.0
+  Related: rhbz#1971065
+
+* Tue Jun 15 2021 Than Ngo <than@redhat.com> - 3.16.0-4
+- Related: #1924120, add conditional requirement on new selinux-policy
+
+* Mon May 17 2021 Than Ngo <than@redhat.com> - 3.16.0-3
+- Resolves: #1959894, Soft token does not check if an EC key is valid
+- Resolves: #1924120, Event Notification Support
+
+* Fri Apr 16 2021 Mohan Boddu <mboddu@redhat.com> - 3.16.0-2
+- Rebuilt for RHEL 9 BETA on Apr 15th 2021. Related: rhbz#1947937
+
+* Wed Mar 31 2021 Dan Horák <dan[at]danny.cz> - 3.16.0-1
+- Rebase to 3.16.0
+
+* Tue Mar 02 2021 Zbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl> - 3.15.1-6
+- Rebuilt for updated systemd-rpm-macros
+  See https://pagure.io/fesco/issue/2583.
+
+* Fri Feb 12 2021 Than Ngo <than@redhat.com> - 3.15.1-5
+- Added upstream patch, a slot ID has nothing to do with the number of slots
+
+* Tue Jan 26 2021 Fedora Release Engineering <releng@fedoraproject.org> - 3.15.1-4
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_34_Mass_Rebuild
+
+* Tue Dec 22 2020 Than Ngo <than@redhat.com> - 3.15.1-3
+- Drop tpm1.2 support by default
+
+* Tue Dec 22 2020 Than Ngo <than@redhat.com> - 3.15.1-2
+- Fix compiling with c++
+- Added error message handling for p11sak remove-key command
+- Add BR on make
+
+* Mon Nov 02 2020 Than Ngo <than@redhat.com> - 3.15.1-1
+- Rebase to 3.15.1
+
+* Mon Oct 19 2020 Dan Horák <dan[at]danny.cz> - 3.15.0-1
+- Rebase to 3.15.0
+
+* Tue Jul 28 2020 Fedora Release Engineering <releng@fedoraproject.org> - 3.14.0-6
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_33_Mass_Rebuild
+
+* Tue Jul 14 2020 Tom Stellard <tstellar@redhat.com> - 3.14.0-5
+- Use make macros
+- https://fedoraproject.org/wiki/Changes/UseMakeBuildInstallMacro
+
+* Wed Jul 08 2020 Than Ngo <than@redhat.com> - 3.14.0-4
+- added PIN conversion tool
+
+* Wed Jul 01 2020 Than Ngo <than@redhat.com> - 3.14.0-3
+- upstream fix - handle early error cases in C_Initialize
+
+* Wed May 27 2020 Than Ngo <than@redhat.com> - 3.14.0-2
+- fix regression, segfault in C_SetPin
+
+* Fri May 15 2020 Dan Horák <dan[at]danny.cz> - 3.14.0-1
+- Rebase to 3.14.0
+
+* Fri Mar 06 2020 Dan Horák <dan[at]danny.cz> - 3.13.0-1
+- Rebase to 3.13.0
+
+* Mon Feb 03 2020 Dan Horák <dan[at]danny.cz> - 3.12.1-3
+- fix build with gcc 10
+
+* Wed Jan 29 2020 Fedora Release Engineering <releng@fedoraproject.org> - 3.12.1-2
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_32_Mass_Rebuild
+
+* Wed Nov 27 2019 Dan Horák <dan[at]danny.cz> - 3.12.1-1
+- Rebase to 3.12.1
+
+* Wed Nov 13 2019 Dan Horák <dan[at]danny.cz> - 3.12.0-1
+- Rebase to 3.12.0
+
+* Sun Sep 22 2019 Dan Horák <dan[at]danny.cz> - 3.11.1-1
+- Rebase to 3.11.1
+
+* Thu Jul 25 2019 Fedora Release Engineering <releng@fedoraproject.org> - 3.11.0-5
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_31_Mass_Rebuild
+
+* Thu Mar 28 2019 Than Ngo <than@redhat.com> - 3.11.0-4
+- enable testcase by default
+- fix URL
+
+* Tue Feb 19 2019 Than Ngo <than@redhat.com> - 3.11.0-3
+- Resolved #1063763 - opencryptoki tools should inform the user that he is not in pkcs11 group
+
+* Fri Feb 01 2019 Fedora Release Engineering <releng@fedoraproject.org> - 3.11.0-2
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_30_Mass_Rebuild
+
+* Thu Jan 31 2019 Than Ngo <than@redhat.com> - 3.11.0-1
+- Updated to 3.11.0
+- Resolved #1341079 - Failed to create directory or subvolume "/var/lock/opencryptoki"
+- Ported root's group membership's patch for 3.11.0
+
+* Fri Jul 13 2018 Fedora Release Engineering <releng@fedoraproject.org> - 3.10.0-2
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_29_Mass_Rebuild
+
+* Tue Jun 12 2018 Dan Horák <dan[at]danny.cz> - 3.10.0-1
+- Rebase to 3.10.0
+
+* Fri Feb 23 2018 Dan Horák <dan[at]danny.cz> - 3.9.0-1
+- Rebase to 3.9.0
+
+* Thu Feb 08 2018 Fedora Release Engineering <releng@fedoraproject.org> - 3.8.2-3
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_28_Mass_Rebuild
+
+* Fri Nov 24 2017 Dan Horák <dan[at]danny.cz> - 3.8.2-2
+- use upstream tmpfiles config
+
+* Thu Nov 23 2017 Dan Horák <dan[at]danny.cz> - 3.8.2-1
+- Rebase to 3.8.2 (#1512678)
+
+* Thu Aug 03 2017 Fedora Release Engineering <releng@fedoraproject.org> - 3.7.0-3
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_27_Binutils_Mass_Rebuild
+
+* Thu Jul 27 2017 Fedora Release Engineering <releng@fedoraproject.org> - 3.7.0-2
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_27_Mass_Rebuild
+
+* Wed May 17 2017 Sinny Kumari <sinny@redhat.com> - 3.7.0-1
+- Rebase to 3.7.0
+- Added libitm-devel as BuildRequires
+
+* Mon Apr 03 2017 Sinny Kumari <sinny@redhat.com> - 3.6.2-1
+- Rebase to 3.6.2
+- RHBZ#1424017 - opencryptoki: FTBFS in rawhide
+
+* Sat Feb 11 2017 Fedora Release Engineering <releng@fedoraproject.org> - 3.5.1-2
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_26_Mass_Rebuild
+
+* Thu Sep 01 2016 Jakub Jelen <jjelen@redhat.com> - 3.5.1-1
+- New upstream release
+
+* Tue May 03 2016 Jakub Jelen <jjelen@redhat.com> - 3.5-1
+- New upstream release
+
+* Thu Feb 04 2016 Fedora Release Engineering <releng@fedoraproject.org> - 3.4.1-2
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_24_Mass_Rebuild
+
+* Mon Dec 07 2015 Jakub Jelen <jjelen@redhat.com> 3.4.1-1
+- New bugfix upstream release
+
+* Wed Nov 18 2015 Jakub Jelen <jjelen@redhat.com> 3.4-1
+- New upstream release
+- Adding post-release patch fixing compile warnings
+
+* Thu Aug 27 2015 Jakub Jelen <jjelen@redhat.com> 3.3-1.1
+- New upstream release
+- Correct dependencies for group creation
+
+* Wed Jun 17 2015 Fedora Release Engineering <rel-eng@lists.fedoraproject.org> - 3.2-4
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_23_Mass_Rebuild
+
+* Thu May 07 2015 Jakub Jelen <jjelen@redhat.com> 3.2-3
+- Few more undefined symbols fixed for s390(x) specific targets
+- Do not require --no-undefined, because s390(x) requires some
+
+* Mon May 04 2015 Jakub Jelen <jjelen@redhat.com> 3.2-2
+- Fix missing sources and libraries in makefiles causing undefined symbols (#1193560)
+- Make inline function compatible for GCC5
+
+* Wed Sep 10 2014 Petr Lautrbach <plautrba@redhat.com> 3.2-1
+- new upstream release 3.2
+- add new sub-package opencryptoki-ep11tok on s390x
+
+* Sun Aug 17 2014 Fedora Release Engineering <rel-eng@lists.fedoraproject.org> - 3.1-2
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_21_22_Mass_Rebuild
+
+* Thu Jul 24 2014 Petr Lautrbach <plautrba@redhat.com> 3.1-1
+- new upstream release 3.1
+
+* Sat Jun 07 2014 Fedora Release Engineering <rel-eng@lists.fedoraproject.org> - 3.0-11
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_21_Mass_Rebuild
+
+* Mon Feb 17 2014 Petr Lautrbach <plautrba@redhat.com> 3.0-10
+- create the right lock directory for cca tokens (#1054442)
+
+* Wed Jan 29 2014 Petr Lautrbach <plautrba@redhat.com> 3.0-9
+- use Requires(pre): opencryptoki-libs for subpackages
+
+* Mon Jan 20 2014 Dan Horák <dan[at]danny.cz> - 3.0-8
+- include token specific directories (#1013017, #1045775, #1054442)
+- fix pkcsconf crash for non-root users (#10054661)
+- the libs subpackage must care of creating the pkcs11 group, it's the first to be installed
+
+* Tue Dec 03 2013 Dan Horák <dan[at]danny.cz> - 3.0-7
+- fix build with -Werror=format-security (#1037228)
+
+* Fri Nov 22 2013 Dan Horák <dan[at]danny.cz> - 3.0-6
+- apply post-3.0 fixes (#1033284)
+
+* Tue Nov 19 2013 Dan Horák <dan[at]danny.cz> - 3.0-5
+- update opencryptoki man page (#1001729)
+
+* Fri Aug 23 2013 Dan Horák <dan[at]danny.cz> - 3.0-4
+- update unit file (#995002)
+
+* Sat Aug 03 2013 Fedora Release Engineering <rel-eng@lists.fedoraproject.org> - 3.0-3
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_20_Mass_Rebuild
+
+* Tue Jul 23 2013 Dan Horák <dan[at]danny.cz> - 3.0-2
+- update pkcsconf man page (#948460)
+
+* Mon Jul 22 2013 Dan Horák <dan[at]danny.cz> - 3.0-1
+- new upstream release 3.0
+
+* Tue Jun 25 2013 Dan Horák <dan[at]danny.cz> - 2.4.3.1-1
+- new upstream release 2.4.3.1
+
+* Fri May 03 2013 Dan Horák <dan[at]danny.cz> - 2.4.3-1
+- new upstream release 2.4.3
+
+* Thu Apr 04 2013 Dan Horák <dan[at]danny.cz> - 2.4.2-4
+- enable hardened build
+- switch to systemd macros in scriptlets (#850240)
+
+* Mon Jan 28 2013 Dan Horák <dan[at]danny.cz> - 2.4.2-3
+- add virtual opencryptoki(token) Provides to token modules and as Requires
+  to main package (#904986)
+
+* Fri Jul 20 2012 Fedora Release Engineering <rel-eng@lists.fedoraproject.org> - 2.4.2-2
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_18_Mass_Rebuild
+
+* Thu Jun 21 2012 Dan Horák <dan[at]danny.cz> - 2.4.2-1
+- new upstream release 2.4.2
+- add pkcs_slot man page
+- don't add root to the pkcs11 group
+
+* Mon Jun 11 2012 Dan Horák <dan[at]danny.cz> - 2.4.1-2
+- fix unresolved symbols in TPM module (#830129)
+
+* Sat Feb 25 2012 Dan Horák <dan[at]danny.cz> - 2.4.1-1
+- new upstream release 2.4.1
+- convert from initscript to systemd unit
+- import fixes from RHEL-6 about root's group membership (#732756, #730903)
+
+* Fri Jan 13 2012 Fedora Release Engineering <rel-eng@lists.fedoraproject.org> - 2.4-2
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_17_Mass_Rebuild
+
+* Thu Jul 07 2011 Dan Horák <dan[at]danny.cz> - 2.4-1
+- new upstream release 2.4
+
+* Tue Feb 08 2011 Fedora Release Engineering <rel-eng@lists.fedoraproject.org> - 2.3.3-2
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_15_Mass_Rebuild
+
+* Mon Jan 17 2011 Dan Horák <dan[at]danny.cz> 2.3.3-1
+- new upstream release 2.3.3
+
+* Tue Nov 09 2010 Michal Schmidt <mschmidt@redhat.com> 2.3.2-2
+- Apply Obsoletes to package names, not provides.
+
+* Tue Sep 14 2010 Dan Horák <dan[at]danny.cz> 2.3.2-1
+- new upstream release 2.3.2
+- put STDLLs in separate packages to match upstream package design
+
+* Thu Jul 08 2010 Michal Schmidt <mschmidt@redhat.com> 2.3.1-7
+- Move the LICENSE file to the -libs subpackage.
+
+* Tue Jun 29 2010 Dan Horák <dan[at]danny.cz> 2.3.1-6
+- rebuilt with CCA enabled (#604287)
+- fixed issues from #546274
+
+* Fri Apr 30 2010 Dan Horák <dan[at]danny.cz> 2.3.1-5
+- fixed one more issue in the initscript (#547324)
+
+* Mon Apr 26 2010 Dan Horák <dan[at]danny.cz> 2.3.1-4
+- fixed pidfile creating and usage (#547324)
+
+* Mon Feb 08 2010 Michal Schmidt <mschmidt@redhat.com> 2.3.1-3
+- Also list 'reload' and 'force-reload' in "Usage: ...".
+
+* Mon Feb 08 2010 Michal Schmidt <mschmidt@redhat.com> 2.3.1-2
+- Support 'force-reload' in the initscript.
+
+* Wed Jan 27 2010 Michal Schmidt <mschmidt@redhat.com> 2.3.1-1
+- New upstream release 2.3.1.
+- opencryptoki-2.3.0-fix-nss-breakage.patch was merged.
+
+* Fri Jan 22 2010 Dan Horák <dan[at]danny.cz> 2.3.0-5
+- made pkcsslotd initscript LSB compliant (#522149)
+
+* Mon Sep 07 2009 Michal Schmidt <mschmidt@redhat.com> 2.3.0-4
+- Added opencryptoki-2.3.0-fix-nss-breakage.patch on upstream request.
+
+* Fri Aug 21 2009 Tomas Mraz <tmraz@redhat.com> - 2.3.0-3
+- rebuilt with new openssl
+
+* Sun Aug 16 2009 Michal Schmidt <mschmidt@redhat.com> 2.3.0-2
+- Require libica-2.0.
+
+* Fri Aug 07 2009 Michal Schmidt <mschmidt@redhat.com> 2.3.0-1
+- New upstream release 2.3.0:
+  - adds support for RSA 4096 bit keys in the ICA token.
+
+* Tue Jul 21 2009 Michal Schmidt <mschmidt@redhat.com> - 2.2.8-5
+- Require arch-specific dependency on -libs.
+
+* Tue Jul 21 2009 Michal Schmidt <mschmidt@redhat.com> - 2.2.8-4
+- Return support for crypto hw on s390.
+- Renamed to opencryptoki.
+- Simplified multilib by putting libs in subpackage as suggested by Dan Horák.
+
+* Tue Jul 21 2009 Michal Schmidt <mschmidt@redhat.com> - 2.2.8-2
+- Fedora package based on RHEL-5 package.