diff --git a/.gitignore b/.gitignore
index 3e095fa..7229299 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1 +1 @@
-SOURCES/opencryptoki-3.12.1.tar.gz
+SOURCES/opencryptoki-3.14.0.tar.gz
diff --git a/.opencryptoki.metadata b/.opencryptoki.metadata
index 5177ce9..9259d12 100644
--- a/.opencryptoki.metadata
+++ b/.opencryptoki.metadata
@@ -1 +1 @@
-8cb8804fe7bbd306d16ca714f62c54927fc3c3d8 SOURCES/opencryptoki-3.12.1.tar.gz
+9ddd1bbe34992707b20b314645fd92d35cb298ef SOURCES/opencryptoki-3.14.0.tar.gz
diff --git a/SOURCES/opencryptoki-3.14.0-cd40f4b7cb1b502ca754b9bfb307d934285709a9-PIN-conversion-tool.patch b/SOURCES/opencryptoki-3.14.0-cd40f4b7cb1b502ca754b9bfb307d934285709a9-PIN-conversion-tool.patch
new file mode 100644
index 0000000..ef02e85
--- /dev/null
+++ b/SOURCES/opencryptoki-3.14.0-cd40f4b7cb1b502ca754b9bfb307d934285709a9-PIN-conversion-tool.patch
@@ -0,0 +1,3575 @@
+diff --git a/configure.ac b/configure.ac
+index 289c5052..c1f24eb9 100644
+--- a/configure.ac
++++ b/configure.ac
+@@ -189,6 +189,12 @@ AC_ARG_ENABLE([p11sak],
+ 	[],
+ 	[enable_p11sak=yes])
+ 
++dnl --- pkcstok_migrate
++AC_ARG_ENABLE([pkcstok_migrate],
++	AS_HELP_STRING([--enable-pkcstok_migrate],[build pkcstok_migrate tool @<:@default=enabled@:>@]),
++	[],
++	[enable_pkcstok_migrate=yes])
++
+ dnl ---
+ dnl --- Check for external software
+ dnl --- Define what to check based on enabled features
+@@ -592,6 +598,9 @@ AM_CONDITIONAL([ENABLE_PKCSEP11_SESSION], [test "x$enable_pkcsep11_session" = "x
+ dnl --- enable_p11sak
+ AM_CONDITIONAL([ENABLE_P11SAK], [test "x$enable_p11sak" = "xyes"])
+ 
++dnl --- enable_pkcstok_migrate
++AM_CONDITIONAL([ENABLE_PKCSTOK_MIGRATE], [test "x$enable_pkcstok_migrate" = "xyes"])
++
+ dnl --- enable_locks
+ if test "x$enable_locks" != "xno"; then
+ 	enable_locks=yes
+@@ -625,6 +634,7 @@ AC_CONFIG_FILES([Makefile				\
+ 		 man/man1/p11sak.1			\
+ 		 man/man1/pkcsep11_migrate.1		\
+ 		 man/man1/pkcsep11_session.1		\
++		 man/man1/pkcstok_migrate.1		\
+ 		 man/man5/opencryptoki.conf.5		\
+ 		 man/man7/opencryptoki.7		\
+ 		 man/man8/pkcsslotd.8])
+@@ -639,6 +649,7 @@ echo "	Library build:		$enable_library"
+ echo "	Systemd service:	$enable_systemd"
+ echo "	Build with locks:	$enable_locks"
+ echo "	Build p11sak tool:	$enable_p11sak"
++echo "	token migrate tool:	$enable_pkcstok_migrate"
+ echo
+ echo "Enabled token types:"
+ echo "	ICA token:		$enable_icatok"
+diff --git a/man/man1/man1.mk b/man/man1/man1.mk
+index 51d23d39..6102340f 100644
+--- a/man/man1/man1.mk
++++ b/man/man1/man1.mk
+@@ -1,5 +1,9 @@
+ man1_MANS += man/man1/pkcsconf.1 man/man1/pkcsicsf.1
+ 
++if ENABLE_PKCSTOK_MIGRATE
++man1_MANS += man/man1/pkcstok_migrate.1
++endif
++
+ if ENABLE_PKCSEP11_MIGRATE
+ man1_MANS += man/man1/pkcsep11_migrate.1
+ endif
+diff --git a/man/man1/pkcstok_migrate.1.in b/man/man1/pkcstok_migrate.1.in
+new file mode 100644
+index 00000000..b17d10b8
+--- /dev/null
++++ b/man/man1/pkcstok_migrate.1.in
+@@ -0,0 +1,69 @@
++.\" pkcstok_migrate.1
++.\"
++.\" Copyright IBM Corp. 2020
++.\" See LICENSE for details.
++.\"
++.TH PKCSTOK_MIGRATE 1 "June 2020" "@PACKAGE_VERSION@" "openCryptoki"
++.SH NAME
++pkcstok_migrate \- utility to migrate an ICA, CCA, Soft, or EP11 token repository
++to the FIPS compliant format introduced with openCryptoki 3.12.
++
++.SH SYNOPSIS
++\fBpkcstok_migrate\fP [\fB-h\fP]
++.br
++\fBpkcstok_migrate\fP \fB--slotid\fP \fIslot-number\fP \fB--datastore\fP \fIdatastore\fP
++\fB--confdir\fP \fIconfdir\fP [\fB--sopin\fP \fIsopin\fP] [\fB--userpin\fP
++\fIuserpin\fP] [\fB--verbose\fP \fIlevel\fP]
++
++.SH DESCRIPTION
++Convert all objects inside a token repository to the new format introduced with
++version 3.12.  All encrypted data inside the new format is stored using FIPS
++compliant methods. The new format affects the token's master key files (MK_SO
++and MK_USER), the NVTOK.DAT, and the token object files in the TOK_OBJ folder.
++
++While using this tool no process using the token to be migrated must be running.
++Especially the pkcsslotd must be stopped before running this tool.
++
++The tool creates a backup of the token repository to be migrated, and performs
++all migration actions on this backup, leaving the original repository folder
++completely untouched. The backup folder is located in the same directory as the
++original repository and is suffixed with _PKCSTOK_MIGRATE_TMP.
++
++After a successful migration, the original repository is renamed with a suffix
++of _BAK and the backup folder is renamed to the original repository name, so
++that the migrated repository can immediately be used. The old folder may be
++deleted by the user manually later.
++
++After a successful migration, the tool adds parameter 'tokversion = 3.12' to the
++token's slot configuration in the opencryptoki.conf file. The original config
++file is still available as opencryptoki.conf_BAK and may be removed by the user
++manually.
++
++After an unsuccessful migration, the original repository is still available
++unchanged. 
++
++.SH "OPTIONS SUMMARY"
++.IP "\fB--slotid -s\fP \fISLOT-NUMBER\fP" 10
++specifies the token slot number of the token repository to be migrated
++.IP "\fB--datastore -d\fP \fIDATASTORE\fP" 10
++specifies the directory of the token repository to be migrated.
++.IP "\fB--confdir -c\fP \fICONFDIR\fP" 10
++specifies the directory where the opencryptoki.conf file is located.
++.IP "\fB--sopin -p\fP \fISOPIN\fP" 10
++specifies the SO pin. If not specified, the SO pin is prompted.
++.IP "\fB--userpin -u\fP \fIUSERPIN\fP" 10
++specifies the user pin. If not specified, the user pin is prompted.
++.IP "\fB--verbose -v\fP \fILEVEL\fP" 10
++specifies the verbose level: \fInone\fP, error, warn, info, devel, debug
++.IP "\fB--help -h\fP" 10
++show usage information
++
++.SH SEE ALSO
++.PD 0
++.TP
++\fBpkcsconf\fP(1),
++.TP
++\fBopencryptoki\fP(7),
++.TP
++\fBpkcsslotd\fP(8).
++.PD
+diff --git a/rpm/opencryptoki.spec b/rpm/opencryptoki.spec
+index ae563406..64142e23 100644
+--- a/rpm/opencryptoki.spec
++++ b/rpm/opencryptoki.spec
+@@ -239,8 +239,10 @@ exit 0
+ %{_sbindir}/pkcsconf
+ %{_sbindir}/pkcsslotd
+ %{_sbindir}/p11sak
++%{_sbindir}/pkcstok_migrate
+ %{_mandir}/man1/pkcsconf.1*
+ %{_mandir}/man1/p11sak.1*
++%{_mandir}/man1/pkcstok_migrate.1*
+ %{_mandir}/man5/%{name}.conf.5*
+ %{_mandir}/man7/%{name}.7*
+ %{_mandir}/man8/pkcsslotd.8*
+diff --git a/usr/lib/common/loadsave.c b/usr/lib/common/loadsave.c
+index c30dd1ab..068fdf36 100644
+--- a/usr/lib/common/loadsave.c
++++ b/usr/lib/common/loadsave.c
+@@ -1701,7 +1701,7 @@ static CK_RV aes_256_gcm_seal(unsigned char *out,
+         || EVP_CipherInit_ex(ctx, NULL, NULL, key, iv, 1) != 1
+         || EVP_CipherUpdate(ctx, NULL, &outlen, aad, aadlen) != 1
+         || EVP_CipherUpdate(ctx, out, &outlen, in, inlen) != 1
+-        || EVP_CipherFinal_ex(ctx, out, &outlen) != 1
++        || EVP_CipherFinal_ex(ctx, out + outlen, &outlen) != 1
+         || EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_GET_TAG, 16, tag) != 1) {
+         TRACE_ERROR("%s\n", ock_err(ERR_GENERAL_ERROR));
+         rc = ERR_GENERAL_ERROR;
+@@ -1741,7 +1741,7 @@ static CK_RV aes_256_gcm_unseal(unsigned char *out,
+         || EVP_CipherInit_ex(ctx, NULL, NULL, key, iv, 0) != 1
+         || EVP_CipherUpdate(ctx, NULL, &outlen, aad, aadlen) != 1
+         || EVP_CipherUpdate(ctx, out, &outlen, in, inlen) != 1
+-        || EVP_CipherFinal_ex(ctx, out, &outlen) != 1) {
++        || EVP_CipherFinal_ex(ctx, out + outlen, &outlen) != 1) {
+         TRACE_ERROR("%s\n", ock_err(ERR_GENERAL_ERROR));
+         rc = ERR_GENERAL_ERROR;
+         goto done;
+@@ -1759,6 +1759,7 @@ static CK_RV aes_256_wrap(unsigned char out[40],
+ {
+     CK_RV rc;
+     int outlen;
++    unsigned char buffer[40 + EVP_MAX_BLOCK_LENGTH];
+ 
+     EVP_CIPHER_CTX *ctx = NULL;
+ 
+@@ -1772,13 +1773,14 @@ static CK_RV aes_256_wrap(unsigned char out[40],
+     EVP_CIPHER_CTX_set_flags(ctx, EVP_CIPHER_CTX_FLAG_WRAP_ALLOW);
+ 
+     if (EVP_CipherInit_ex(ctx, EVP_aes_256_wrap(), NULL, kek, NULL, 1) != 1
+-        || EVP_CipherUpdate(ctx, out, &outlen, in, 32) != 1
+-        || EVP_CipherFinal_ex(ctx, out, &outlen) != 1) {
++        || EVP_CipherUpdate(ctx, buffer, &outlen, in, 32) != 1
++        || EVP_CipherFinal_ex(ctx, buffer + outlen, &outlen) != 1) {
+         TRACE_ERROR("%s\n", ock_err(ERR_GENERAL_ERROR));
+         rc = ERR_GENERAL_ERROR;
+         goto done;
+     }
+ 
++    memcpy(out, buffer, 40);
+     rc = CKR_OK;
+ done:
+     EVP_CIPHER_CTX_free(ctx);
+@@ -1791,6 +1793,7 @@ static CK_RV aes_256_unwrap(unsigned char key[32],
+ {
+     CK_RV rc;
+     int outlen;
++    unsigned char buffer[32 + EVP_MAX_BLOCK_LENGTH];
+ 
+     EVP_CIPHER_CTX *ctx = NULL;
+ 
+@@ -1804,13 +1807,14 @@ static CK_RV aes_256_unwrap(unsigned char key[32],
+     EVP_CIPHER_CTX_set_flags(ctx, EVP_CIPHER_CTX_FLAG_WRAP_ALLOW);
+ 
+     if (EVP_CipherInit_ex(ctx, EVP_aes_256_wrap(), NULL, kek, NULL, 0) != 1
+-        || EVP_CipherUpdate(ctx, key, &outlen, in, 40) != 1
+-        || EVP_CipherFinal_ex(ctx, key, &outlen) != 1) {
++        || EVP_CipherUpdate(ctx, buffer, &outlen, in, 40) != 1
++        || EVP_CipherFinal_ex(ctx, buffer + outlen, &outlen) != 1) {
+         TRACE_ERROR("%s\n", ock_err(ERR_GENERAL_ERROR));
+         rc = ERR_GENERAL_ERROR;
+         goto done;
+     }
+ 
++    memcpy(key, buffer, 32);
+     rc = CKR_OK;
+ done:
+     EVP_CIPHER_CTX_free(ctx);
+diff --git a/usr/lib/common/obj_mgr.c b/usr/lib/common/obj_mgr.c
+index 3e4ba3b6..1bdbe64c 100644
+--- a/usr/lib/common/obj_mgr.c
++++ b/usr/lib/common/obj_mgr.c
+@@ -1751,6 +1751,12 @@ CK_RV object_mgr_set_attribute_values(STDLL_TokData_t *tokdata,
+         save_token_object(tokdata, obj);
+ 
+         if (priv_obj) {
++            if (tokdata->global_shm->num_priv_tok_obj == 0) {
++                TRACE_DEVEL("%s\n", ock_err(ERR_OBJECT_HANDLE_INVALID));
++                rc = CKR_OBJECT_HANDLE_INVALID;
++                XProcUnLock(tokdata);
++                goto done;
++            }
+             rc = object_mgr_search_shm_for_obj(tokdata->global_shm->
+                                                priv_tok_objs, 0,
+                                                tokdata->global_shm->
+@@ -1765,6 +1771,12 @@ CK_RV object_mgr_set_attribute_values(STDLL_TokData_t *tokdata,
+ 
+             entry = &tokdata->global_shm->priv_tok_objs[index];
+         } else {
++            if (tokdata->global_shm->num_publ_tok_obj == 0) {
++                TRACE_DEVEL("%s\n", ock_err(ERR_OBJECT_HANDLE_INVALID));
++                rc = CKR_OBJECT_HANDLE_INVALID;
++                XProcUnLock(tokdata);
++                goto done;
++            }
+             rc = object_mgr_search_shm_for_obj(tokdata->global_shm->
+                                                publ_tok_objs, 0,
+                                                tokdata->global_shm->
+@@ -1846,6 +1858,10 @@ CK_RV object_mgr_del_from_shm(OBJECT *obj, LW_SHM_TYPE *global_shm)
+     priv = object_is_private(obj);
+ 
+     if (priv) {
++        if (global_shm->num_priv_tok_obj == 0) {
++            TRACE_DEVEL("%s\n", ock_err(ERR_OBJECT_HANDLE_INVALID));
++            return CKR_OBJECT_HANDLE_INVALID;
++        }
+         rc = object_mgr_search_shm_for_obj(global_shm->priv_tok_objs,
+                                            0, global_shm->num_priv_tok_obj - 1,
+                                            obj, &index);
+@@ -1886,6 +1902,10 @@ CK_RV object_mgr_del_from_shm(OBJECT *obj, LW_SHM_TYPE *global_shm)
+                    sizeof(TOK_OBJ_ENTRY));
+         }
+     } else {
++        if (global_shm->num_publ_tok_obj == 0) {
++            TRACE_DEVEL("%s\n", ock_err(ERR_OBJECT_HANDLE_INVALID));
++            return CKR_OBJECT_HANDLE_INVALID;
++        }
+         rc = object_mgr_search_shm_for_obj(global_shm->publ_tok_objs,
+                                            0, global_shm->num_publ_tok_obj - 1,
+                                            obj, &index);
+diff --git a/usr/lib/common/pkcs_utils.c b/usr/lib/common/pkcs_utils.c
+new file mode 100644
+index 00000000..d3074d5c
+--- /dev/null
++++ b/usr/lib/common/pkcs_utils.c
+@@ -0,0 +1,477 @@
++/*
++ * COPYRIGHT (c) International Business Machines Corp. 2020
++ *
++ * 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
++ */
++
++/*
++ * Some routines that are shared between the pkcs utilities in usr/sbin.
++ */
++
++#define _GNU_SOURCE
++#include <dlfcn.h>
++#include <errno.h>
++#include <string.h>
++#include <stdio.h>
++#include <stdlib.h>
++#include <termios.h>
++#include <unistd.h>
++#include <sys/stat.h>
++#include <fcntl.h>
++#include <dirent.h>
++#include <grp.h>
++#include <openssl/evp.h>
++#include <pkcs11types.h>
++
++#include "defs.h"
++#include "host_defs.h"
++
++#define OCK_TOOL
++#include "pkcs_utils.h"
++
++extern pkcs_trace_level_t trace_level;
++
++void pkcs_trace(pkcs_trace_level_t level, const char *file, int line,
++                const char *fmt, ...)
++{
++    va_list ap;
++    const char *fmt_pre;
++    char buf[1024];
++    char *pbuf;
++    int buflen, len;
++
++    if (level > trace_level)
++        return;
++
++    pbuf = buf;
++    buflen = sizeof(buf);
++
++    /* add file line */
++    switch (level) {
++    case TRACE_LEVEL_NONE:
++        fmt_pre = "";
++        break;
++    case TRACE_LEVEL_ERROR:
++        fmt_pre = "[%s:%d] ERROR: ";
++        break;
++    case TRACE_LEVEL_WARNING:
++        fmt_pre = "[%s:%d] WARN: ";
++        break;
++    case TRACE_LEVEL_INFO:
++        fmt_pre = "[%s:%d] INFO: ";
++        break;
++    case TRACE_LEVEL_DEVEL:
++        fmt_pre = "[%s:%d] DEVEL: ";
++        break;
++    case TRACE_LEVEL_DEBUG:
++        fmt_pre = "[%s:%d] DEBUG: ";
++        break;
++    default:
++        return;
++    }
++    snprintf(pbuf, buflen, fmt_pre, file, line);
++
++    len = strlen(buf);
++    pbuf = buf + len;
++    buflen = sizeof(buf) - len;
++
++    va_start(ap, fmt);
++    vsnprintf(pbuf, buflen, fmt, ap);
++    va_end(ap);
++
++    printf("%s", buf);
++}
++
++int compute_hash(int hash_type, int buf_size, char *buf, char *digest)
++{
++    EVP_MD_CTX *md_ctx = NULL;
++    unsigned int result_size;
++    int rc;
++
++    md_ctx = EVP_MD_CTX_create();
++
++    switch (hash_type) {
++    case HASH_SHA1:
++        rc = EVP_DigestInit(md_ctx, EVP_sha1());
++        break;
++    case HASH_MD5:
++        rc = EVP_DigestInit(md_ctx, EVP_md5());
++        break;
++    default:
++        rc = -1;
++        goto done;
++    }
++
++    if (rc != 1) {
++        TRACE_ERROR("EVP_DigestInit() failed: rc = %d\n", rc);
++        rc = -1;
++        goto done;
++    }
++
++    rc = EVP_DigestUpdate(md_ctx, buf, buf_size);
++    if (rc != 1) {
++    	TRACE_ERROR("EVP_DigestUpdate() failed: rc = %d\n", rc);
++        rc = -1;
++        goto done;
++    }
++
++    result_size = EVP_MD_CTX_size(md_ctx);
++    rc = EVP_DigestFinal(md_ctx, (unsigned char *) digest, &result_size);
++    if (rc != 1) {
++    	TRACE_ERROR("EVP_DigestFinal() failed: rc = %d\n", rc);
++        rc = -1;
++        goto done;
++    }
++
++    rc = 0;
++
++done:
++
++    EVP_MD_CTX_destroy(md_ctx);
++
++    return rc;
++}
++
++CK_RV local_rng(CK_BYTE *output, CK_ULONG bytes)
++{
++    int ranfd;
++    int rlen;
++    unsigned int totallen = 0;
++
++    ranfd = open("/dev/prandom", 0);
++    if (ranfd < 0)
++        ranfd = open("/dev/urandom", 0);
++    if (ranfd >= 0) {
++        do {
++            rlen = read(ranfd, output + totallen, bytes - totallen);
++            totallen += rlen;
++        } while (totallen < bytes);
++        close(ranfd);
++        return CKR_OK;
++    }
++
++    return CKR_FUNCTION_FAILED;
++}
++
++CK_RV aes_256_wrap(unsigned char out[40], const unsigned char in[32],
++                   const unsigned char kek[32])
++{
++    CK_RV rc;
++    int outlen;
++    unsigned char buffer[40 + EVP_MAX_BLOCK_LENGTH];
++
++    EVP_CIPHER_CTX *ctx = NULL;
++
++    ctx = EVP_CIPHER_CTX_new();
++    if (ctx == NULL) {
++        TRACE_ERROR("EVP_CIPHER_CTX_new failed.\n");
++        rc = CKR_HOST_MEMORY;
++        goto done;
++    }
++
++    EVP_CIPHER_CTX_set_flags(ctx, EVP_CIPHER_CTX_FLAG_WRAP_ALLOW);
++
++    if (EVP_CipherInit_ex(ctx, EVP_aes_256_wrap(), NULL, kek, NULL, 1) != 1
++        || EVP_CipherUpdate(ctx, buffer, &outlen, in, 32) != 1
++        || EVP_CipherFinal_ex(ctx, buffer + outlen, &outlen) != 1) {
++        TRACE_ERROR("EVP_Cipher funcs failed\n");
++        rc = CKR_FUNCTION_FAILED;
++        goto done;
++    }
++
++    memcpy(out, buffer, 40);
++    rc = CKR_OK;
++done:
++    EVP_CIPHER_CTX_free(ctx);
++    return rc;
++}
++
++CK_RV aes_256_unwrap(unsigned char key[32], const unsigned char in[40],
++                     const unsigned char kek[32])
++{
++    CK_RV rc;
++    int outlen;
++    unsigned char buffer[32 + EVP_MAX_BLOCK_LENGTH];
++
++    EVP_CIPHER_CTX *ctx = NULL;
++
++    ctx = EVP_CIPHER_CTX_new();
++    if (ctx == NULL) {
++        TRACE_ERROR("EVP_CIPHER_CTX_new failed\n");
++        rc = CKR_HOST_MEMORY;
++        goto done;
++    }
++
++    EVP_CIPHER_CTX_set_flags(ctx, EVP_CIPHER_CTX_FLAG_WRAP_ALLOW);
++
++    if (EVP_CipherInit_ex(ctx, EVP_aes_256_wrap(), NULL, kek, NULL, 0) != 1
++        || EVP_CipherUpdate(ctx, buffer, &outlen, in, 40) != 1
++        || EVP_CipherFinal_ex(ctx, buffer + outlen, &outlen) != 1) {
++        rc = CKR_FUNCTION_FAILED;
++        goto done;
++    }
++
++    memcpy(key, buffer, 32);
++    rc = CKR_OK;
++done:
++    EVP_CIPHER_CTX_free(ctx);
++    return rc;
++}
++
++CK_RV aes_256_gcm_seal(unsigned char *out, unsigned char tag[16],
++                       const unsigned char *aad, size_t aadlen,
++                       const unsigned char *in, size_t inlen,
++                       const unsigned char key[32],
++                       const unsigned char iv[12])
++{
++    CK_RV rc;
++    int outlen;
++
++    EVP_CIPHER_CTX *ctx = NULL;
++
++    ctx = EVP_CIPHER_CTX_new();
++    if (ctx == NULL) {
++        TRACE_ERROR("EVP_CIPHER_CTX_new failed\n");
++        rc = CKR_HOST_MEMORY;
++        goto done;
++    }
++
++    if (EVP_CipherInit_ex(ctx, EVP_aes_256_gcm(), NULL, NULL, NULL, -1) != 1
++        || EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_IVLEN, 12, NULL) != 1
++        || EVP_CipherInit_ex(ctx, NULL, NULL, key, iv, 1) != 1
++        || EVP_CipherUpdate(ctx, NULL, &outlen, aad, aadlen) != 1
++        || EVP_CipherUpdate(ctx, out, &outlen, in, inlen) != 1
++        || EVP_CipherFinal_ex(ctx, out + outlen, &outlen) != 1
++        || EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_GET_TAG, 16, tag) != 1) {
++        TRACE_ERROR("EVP_Cipher funcs failed\n");
++        rc = CKR_FUNCTION_FAILED;
++        goto done;
++    }
++
++    rc = CKR_OK;
++done:
++    EVP_CIPHER_CTX_free(ctx);
++    return rc;
++}
++
++int get_pin(char **pin, size_t *pinlen)
++{
++    struct termios old, new;
++    int nread;
++    char *buff = NULL;
++    size_t buflen;
++    int rc = 0;
++
++    /* turn echoing off */
++    if (tcgetattr(fileno(stdin), &old) != 0)
++        return -1;
++
++    new = old;
++    new.c_lflag &= ~ECHO;
++    if (tcsetattr(fileno(stdin), TCSAFLUSH, &new) != 0)
++        return -1;
++
++    /* read the pin
++     * Note: getline will allocate memory for buff. free it when done.
++     */
++    nread = getline(&buff, &buflen, stdin);
++    if (nread == -1) {
++        rc = -1;
++        goto done;
++    }
++
++    /* Restore terminal */
++    (void) tcsetattr(fileno(stdin), TCSAFLUSH, &old);
++
++    /* start a newline */
++    printf("\n");
++    fflush(stdout);
++
++    /* Allocate  PIN.
++     * Note: nread includes carriage return.
++     * Replace with terminating NULL.
++     */
++    *pin = (char *) malloc(nread);
++    if (*pin == NULL) {
++        rc = -ENOMEM;
++        goto done;
++    }
++
++    /* strip the carriage return since not part of pin. */
++    buff[nread - 1] = '\0';
++    memcpy(*pin, buff, nread);
++    /* don't include the terminating null in the pinlen */
++    *pinlen = nread - 1;
++
++done:
++    if (buff)
++        free(buff);
++
++    return rc;
++}
++
++/**
++ * Verify that SO PIN and user PIN are correct by comparing their SHA-1
++ * values with the stored hashes in NVTOK.DAT.
++ */
++int verify_pins(char *data_store, char *sopin, unsigned long sopinlen,
++                  char *userpin, unsigned long userpinlen)
++{
++    TOKEN_DATA td;
++    char fname[PATH_MAX];
++    char pin_sha[SHA1_HASH_SIZE];
++    FILE *fp = NULL;
++    int ret;
++    int tdnew;
++    struct stat stbuf;
++    size_t tdlen;
++    int fd;
++
++    /* read the NVTOK.DAT */
++    snprintf(fname, PATH_MAX, "%s/NVTOK.DAT", data_store);
++    fp = fopen((char *) fname, "r");
++    if (!fp) {
++        TRACE_ERROR("Cannot not open %s: %s\n", fname, strerror(errno));
++        return -1;
++    }
++
++    fd = fileno(fp);
++    if ((fstat(fd, &stbuf) != 0) || (!S_ISREG(stbuf.st_mode))) {
++        ret = -1;
++        goto done;
++    }
++
++    if (stbuf.st_size == sizeof(TOKEN_DATA_OLD)) {
++        /* old data store/pin format */
++        tdnew = 0;
++        tdlen = sizeof(TOKEN_DATA_OLD);
++    } else if (stbuf.st_size == sizeof(TOKEN_DATA)) {
++        /* new data store/pin format */
++        tdnew = 1;
++        tdlen = sizeof(TOKEN_DATA);
++    } else {
++        TRACE_ERROR("%s has an invalid size of %ld bytes. Neither old nor new token format.\n",
++                  fname, stbuf.st_size);
++        ret = -1;
++        goto done;
++    }
++
++    ret = fread(&td, tdlen, 1, fp);
++    if (ret != 1) {
++        TRACE_ERROR("Could not read %s: %s\n", fname, strerror(errno));
++        ret = -1;
++        goto done;
++    }
++
++    if (tdnew == 0) {
++        /* Now compute the SHAs for the SO and USER pins entered.
++         * Compare with the SHAs for SO and USER PINs saved in
++         * NVTOK.DAT to verify.
++         */
++
++        if (sopin != NULL) {
++            ret = compute_sha1(sopin, sopinlen, pin_sha);
++            if (ret) {
++                TRACE_ERROR("Failed to compute sha for SO.\n");
++                goto done;
++            }
++
++            if (memcmp(td.so_pin_sha, pin_sha, SHA1_HASH_SIZE) != 0) {
++                TRACE_ERROR("SO PIN is incorrect.\n");
++                ret = -1;
++                goto done;
++            }
++        }
++
++        if (userpin != NULL) {
++            ret = compute_sha1(userpin, userpinlen, pin_sha);
++            if (ret) {
++                TRACE_ERROR("Failed to compute sha for USER.\n");
++                goto done;
++            }
++
++            if (memcmp(td.user_pin_sha, pin_sha, SHA1_HASH_SIZE) != 0) {
++                TRACE_ERROR("USER PIN is incorrect.\n");
++                ret = -1;
++                goto done;
++            }
++        }
++    } else if (tdnew == 1) {
++        if (sopin != NULL) {
++            unsigned char so_login_key[32];
++
++            ret = PKCS5_PBKDF2_HMAC(sopin, sopinlen,
++                                    td.dat.so_login_salt, 64,
++                                    td.dat.so_login_it, EVP_sha512(),
++                                    256 / 8, so_login_key);
++            if (ret != 1) {
++                TRACE_ERROR("PBKDF2 failed.\n");
++                goto done;
++            }
++
++            if (CRYPTO_memcmp(td.dat.so_login_key, so_login_key, 32) != 0) {
++                TRACE_ERROR("USER PIN is incorrect.\n");
++                ret = -1;
++                goto done;
++            }
++        }
++        if (userpin != NULL) {
++            unsigned char user_login_key[32];
++
++            ret = PKCS5_PBKDF2_HMAC(userpin, userpinlen,
++                                    td.dat.user_login_salt, 64,
++                                    td.dat.user_login_it, EVP_sha512(),
++                                    256 / 8, user_login_key);
++            if (ret != 1) {
++                TRACE_ERROR("PBKDF2 failed.\n");
++                goto done;
++            }
++
++            if (CRYPTO_memcmp(td.dat.user_login_key, user_login_key, 32) != 0) {
++                TRACE_ERROR("USER PIN is incorrect.\n");
++                ret = -1;
++                goto done;
++            }
++        }
++    } else {
++        TRACE_ERROR("Unknown token format.\n");
++        ret = -1;
++        goto done;
++    }
++    ret = 0;
++
++done:
++    /* clear out the hash */
++    memset(pin_sha, 0, SHA1_HASH_SIZE);
++    if (fp)
++        fclose(fp);
++
++    return ret;
++}
++
++void set_perm(int file)
++{
++    struct group *grp;
++
++    // Set absolute permissions or rw-rw----
++    fchmod(file, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP);
++
++    grp = getgrnam("pkcs11"); // Obtain the group id
++    if (grp) {
++        // set ownership to root, and pkcs11 group
++        if (fchown(file, getuid(), grp->gr_gid) != 0) {
++            goto error;
++        }
++    } else {
++        goto error;
++    }
++
++    return;
++
++error:
++    TRACE_DEVEL("Unable to set permissions on file.\n");
++}
+diff --git a/usr/lib/common/pkcs_utils.h b/usr/lib/common/pkcs_utils.h
+new file mode 100644
+index 00000000..248559cc
+--- /dev/null
++++ b/usr/lib/common/pkcs_utils.h
+@@ -0,0 +1,81 @@
++/*
++ * COPYRIGHT (c) International Business Machines Corp. 2020
++ *
++ * 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 PKCS_UTILS_H
++#define PKCS_UTILS_H
++
++#include "pkcs11types.h"
++
++#define MASTER_KEY_SIZE           24
++#define MASTER_KEY_SIZE_CCA       64
++#define MAX_MASTER_KEY_SIZE       MASTER_KEY_SIZE_CCA
++
++#define MK_FILE_SIZE_00           48
++#define MK_FILE_SIZE_00_CCA       88
++
++#define HASH_SHA1   1
++#define HASH_MD5    2
++
++#define compute_sha1(a,b,c)     compute_hash((HASH_SHA1),(b),(a),(c))
++#define compute_md5(a,b,c)      compute_hash(HASH_MD5,(b),(a),(c))
++
++int compute_hash(int hash_type, int buf_size, char *buf, char *digest);
++
++CK_RV local_rng(CK_BYTE *output, CK_ULONG bytes);
++
++CK_RV aes_256_wrap(unsigned char out[40], const unsigned char in[32],
++                   const unsigned char kek[32]);
++
++CK_RV aes_256_unwrap(unsigned char key[32], const unsigned char in[40],
++                     const unsigned char kek[32]);
++
++CK_RV aes_256_gcm_seal(unsigned char *out, unsigned char tag[16],
++                       const unsigned char *aad, size_t aadlen,
++                       const unsigned char *in, size_t inlen,
++                       const unsigned char key[32],
++                       const unsigned char iv[12]);
++
++int get_pin(char **pin, size_t *pinlen);
++
++int verify_pins(char *data_store, char *sopin, unsigned long sopinlen,
++                char *userpin, unsigned long userpinlen);
++
++void set_perm(int file);
++
++#ifdef OCK_TOOL
++/* Log levels */
++typedef enum {
++    TRACE_LEVEL_NONE = 0,
++    TRACE_LEVEL_ERROR,
++    TRACE_LEVEL_WARNING,
++    TRACE_LEVEL_INFO,
++    TRACE_LEVEL_DEVEL,
++    TRACE_LEVEL_DEBUG
++} pkcs_trace_level_t;
++
++void pkcs_trace(pkcs_trace_level_t level, const char * file, int line,
++                const char *fmt, ...)
++                __attribute__ ((format(printf, 4, 5)));
++
++#define TRACE_NONE(...)    \
++    pkcs_trace(TRACE_LEVEL_NONE, __FILE__, __LINE__, __VA_ARGS__)
++#define TRACE_ERROR(...)    \
++    pkcs_trace(TRACE_LEVEL_ERROR, __FILE__, __LINE__, __VA_ARGS__)
++#define TRACE_WARN(...)    \
++    pkcs_trace(TRACE_LEVEL_WARNING, __FILE__, __LINE__, __VA_ARGS__)
++#define TRACE_INFO(...)    \
++    pkcs_trace(TRACE_LEVEL_INFO, __FILE__, __LINE__, __VA_ARGS__)
++#define TRACE_DEVEL(...)    \
++    pkcs_trace(TRACE_LEVEL_DEVEL, __FILE__, __LINE__, __VA_ARGS__)
++#define TRACE_DEBUG(...)    \
++    pkcs_trace(TRACE_LEVEL_DEBUG, __FILE__, __LINE__, __VA_ARGS__)
++#endif /* OCK_TOOL */
++
++#endif
+diff --git a/usr/sbin/pkcstok_migrate/pkcstok_migrate.c b/usr/sbin/pkcstok_migrate/pkcstok_migrate.c
+new file mode 100644
+index 00000000..e90a5c91
+--- /dev/null
++++ b/usr/sbin/pkcstok_migrate/pkcstok_migrate.c
+@@ -0,0 +1,2686 @@
++/*
++ * COPYRIGHT (c) International Business Machines Corp. 2020
++ *
++ * 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
++ */
++
++/*
++ * pkcstok_migrate - A tool for migrating ICA, CCA, Soft, and EP11 token
++ * repositories to 3.12 format.
++ *
++ */
++
++#define _GNU_SOURCE
++#include <dlfcn.h>
++#include <err.h>
++#include <errno.h>
++#include <getopt.h>
++#include <memory.h>
++#include <linux/limits.h>
++#include <openssl/evp.h>
++#include <string.h>
++#include <stdio.h>
++#include <stdlib.h>
++#include <sys/stat.h>
++#include <sys/types.h>
++#include <grp.h>
++#include <termios.h>
++#include <unistd.h>
++#include <dirent.h>
++#include <pkcs11types.h>
++
++#include "sw_crypt.h"
++#include "defs.h"
++#include "host_defs.h"
++#include "local_types.h"
++#include "h_extern.h"
++
++#define OCK_TOOL
++#include "pkcs_utils.h"
++
++
++#define TOKVERSION_00         0x00000000
++#define TOKVERSION_312        0x0003000C
++
++#define INVALID_TOKEN         "unknown/unsupported"
++
++#define HEADER_LEN            64
++#define FOOTER_LEN            16
++
++#define PKCSTOK_MIGRATE_MAX_PATH_LEN    (PATH_MAX - 200)
++
++pkcs_trace_level_t trace_level = TRACE_LEVEL_NONE;
++
++/**
++ * Make a 3.12 format OBJECT_PUB:
++ *
++ *   struct OBJECT_PUB {
++ *       //--------------        <--+
++ *       u32 tokversion;            | 16-byte header
++ *       u8 private_flag;           |
++ *       u8 reserved[7];            |
++ *       u32 object_len;            |
++ *       //--------------        <--+
++ *       u8 object[object_len];     | body
++ *       //--------------        <--+
++ *   };
++ */
++static CK_RV make_OBJECT_PUB_312(char **obj_new, unsigned int *obj_new_len,
++                                 unsigned char *clear, unsigned int clear_len)
++{
++    struct {
++        uint32_t tokversion;
++        uint8_t private_flag;
++        uint8_t reserved[7];
++        uint32_t object_len;
++    } header;
++    uint32_t total_len;
++    char *object;
++    CK_RV ret;
++
++    *obj_new = NULL;
++    *obj_new_len = 0;
++
++    /* Check parms */
++    if (!clear || clear_len == 0) {
++        TRACE_ERROR("Error in parms.\n");
++        ret = CKR_ARGUMENTS_BAD;
++        goto done;
++    }
++
++    /* Allocate memory for new OBJECT_PUB */
++    total_len = sizeof(header) + clear_len;
++    object = malloc(total_len);
++    if (object == NULL) {
++        TRACE_ERROR("cannot malloc %d bytes.\n", total_len);
++        ret = CKR_HOST_MEMORY;
++        goto done;
++    }
++
++    /* Setup object */
++    memset(&header, 0, sizeof(header));
++    header.tokversion = 0x0003000C;
++    header.private_flag = 0x00;
++    header.object_len = clear_len;
++    memcpy(object, &header, sizeof(header));
++    memcpy(object + sizeof(header), clear, clear_len);
++
++    *obj_new = object;
++    *obj_new_len = total_len;
++
++    ret = CKR_OK;
++
++done:
++
++    return ret;
++}
++
++/**
++ * This function migrates the public obj to the current format.
++ */
++static CK_RV migrate_public_token_object(const char *data_store, const char *name,
++                                         unsigned char *data, unsigned long len)
++{
++    const char *tokobj = "TOK_OBJ";
++    char fname[PATH_MAX + 1 + strlen(tokobj) + 1 + strlen(name) + 1];
++    char *obj_new = NULL;
++    unsigned int obj_new_len;
++    FILE *fp = NULL;
++    CK_RV ret = 0;
++
++    /* Create new public object */
++    ret = make_OBJECT_PUB_312(&obj_new, &obj_new_len, data, len);
++    if (ret != CKR_OK) {
++        TRACE_ERROR("Cannot create an OBJECT_PUB_312, ret=%08lX.\n", ret);
++        goto done;
++    }
++
++    /* Setup file name for new object */
++    sprintf(fname, "%s/%s/%s", data_store, tokobj, name);
++    fp = fopen((char *) fname, "w");
++    if (!fp) {
++        TRACE_ERROR("fopen(%s) failed, errno=%s\n", fname, strerror(errno));
++        ret = CKR_FUNCTION_FAILED;
++        goto done;
++    }
++    set_perm(fileno(fp));
++
++    /* Save new object */
++    if (fwrite(obj_new, obj_new_len, 1, fp) != 1) {
++        TRACE_ERROR("fwrite(%s) failed, errno=%s\n", fname, strerror(errno));
++        ret = CKR_FUNCTION_FAILED;
++        goto done;
++    }
++
++    ret = CKR_OK;
++
++done:
++
++    free(obj_new);
++    if (fp)
++        fclose(fp);
++
++    return ret;
++}
++
++/**
++ * Make a 3.12 format OBJECT_PRIV:
++ *
++ *   struct OBJECT_PRIV {
++ *       u32 total_len;
++ *       --- auth -------           <--+
++ *       u32 tokversion                | 64-byte header
++ *       u8  private_flag              |
++ *       u8  reserved[3]               |
++ *       u8  key_wrapped[40]           |
++ *       u8  iv[12]                    |
++ *       u32 object_len                |
++ *       --- auth+enc ---           <--+
++ *       u8  object[object_len]        | body
++ *       ----------------           <--+
++ *       u8 tag[16]                    | 16-byte footer
++ *       ----------------           <--+
++ *   }
++ */
++static CK_RV make_OBJECT_PRIV_312(unsigned char **obj_new, unsigned int *obj_new_len,
++                                  const char *name, unsigned char *clear,
++                                  unsigned int clear_len, const CK_BYTE *masterkey)
++{
++    struct {
++        uint32_t tokversion;
++        uint8_t private_flag;
++        uint8_t reserved[3];
++        uint8_t key_wrapped[40];
++        uint8_t iv[12];
++        uint32_t object_len;
++    } header;
++    unsigned char *object;
++    CK_BYTE obj_key[32];
++    uint32_t total_len;
++    CK_RV ret;
++
++    *obj_new = NULL;
++    *obj_new_len = 0;
++
++    /* Check parms */
++    if (!name || !clear || clear_len == 0 || !masterkey) {
++        TRACE_ERROR("Error in parms.\n");
++        ret = CKR_ARGUMENTS_BAD;
++        goto done;
++    }
++
++    /* An obj name has by definition 8 chars */
++    if (strlen(name) != 8) {
++        TRACE_ERROR("obj name %s does not have 8 chars, OBJ.IDX probably corrupted.\n",
++                    name);
++        ret = CKR_ARGUMENTS_BAD;
++        goto done;
++    }
++
++    /* Allocate memory for new OBJECT_PRIV */
++    total_len = sizeof(header) + clear_len + FOOTER_LEN;
++    object = malloc(total_len);
++    if (object == NULL) {
++        TRACE_ERROR("cannot malloc %d bytes.\n", total_len);
++        ret = CKR_HOST_MEMORY;
++        goto done;
++    }
++
++    /* Create new object key */
++    ret = local_rng(obj_key, 32);
++    if (ret != CKR_OK) {
++        TRACE_ERROR("local_rng failed with ret=%08lX.\n", ret);
++        goto done;
++    }
++
++    /* Setup header */
++    memset(&header, 0, sizeof(header));
++    header.tokversion = 0x0003000C;
++    header.private_flag = 0x01;
++    ret = aes_256_wrap(header.key_wrapped, obj_key, masterkey);
++    if (ret != CKR_OK) {
++        TRACE_ERROR("aes_256_wrap failed with ret=%08lX.\n", ret);
++        goto done;
++    }
++
++    memcpy(header.iv, name, 8);
++    header.iv[8] = 0;
++    header.iv[9] = 0;
++    header.iv[10] = 0;
++    header.iv[11] = 1;
++    header.object_len = clear_len;
++    memcpy(object, &header, HEADER_LEN);
++
++    /* Encrypt body */
++    ret = aes_256_gcm_seal(object + HEADER_LEN, /* ciphertext */
++                           object + HEADER_LEN + clear_len, /* tag */
++                           object, HEADER_LEN, /* aad */
++                           clear, clear_len, /* plaintext */
++                           obj_key, /* key */
++                           header.iv /* iv */);
++    if (ret != CKR_OK) {
++        TRACE_ERROR("aes_256_gcm_seal failed with rc=%08lX.\n", ret);
++        goto done;
++    }
++
++    *obj_new = object;
++    *obj_new_len = total_len;
++
++    ret = CKR_OK;
++
++done:
++
++    return ret;
++}
++
++/**
++ * Decrypts the given version 0.0 private object with given old masterkey.
++ *
++ *   struct OBJECT_PRIV {
++ *      u32 total_len;
++ *      u8 private_flag;
++ *      //--- enc ---        <- enc_old starts here
++ *      u32 object_len;
++ *      u8 object[object_len];
++ *      u8 sha1[20];
++ *      u8 padding[padding_len];
++ *   };
++ */
++static CK_RV decrypt_OBJECT_PRIV_00(unsigned char **clear, unsigned int *clear_len,
++                                    unsigned char *enc_old, unsigned int enc_len,
++                                    const CK_BYTE *masterkey_old)
++{
++    CK_ULONG_32 obj_data_len_32;
++    CK_BYTE hash_sha[SHA1_HASH_SIZE];
++    CK_BYTE des3_key[MAX_MASTER_KEY_SIZE];
++    unsigned char *tmp_clear, *raw_clear;
++    CK_ULONG tmp_clear_len;
++    CK_RV ret;
++
++    *clear = NULL;
++    *clear_len = 0;
++
++    /* Allocate storage for clear output */
++    tmp_clear = malloc(enc_len);
++    if (!tmp_clear) {
++        TRACE_ERROR("Cannot malloc %d bytes, errno=%s.\n",
++                    enc_len, strerror(errno));
++        ret = CKR_HOST_MEMORY;
++        goto done;
++    }
++
++    /* Decrypt old object */
++    memcpy(des3_key, masterkey_old, MAX_MASTER_KEY_SIZE);
++    ret  = sw_des3_cbc_decrypt(enc_old, enc_len, tmp_clear, &tmp_clear_len,
++                               (CK_BYTE *)"10293847", des3_key);
++    if (ret) {
++        TRACE_ERROR("sw_des3_cbc_decrypt failed with ret=%08lX\n", ret);
++        goto done;
++    }
++
++    /* Validate the length */
++    memcpy(&obj_data_len_32, tmp_clear, sizeof(CK_ULONG_32));
++    if (obj_data_len_32 >= enc_len) {
++        TRACE_ERROR("Decrypted object data length %d inconsistent\n",
++                obj_data_len_32);
++        ret = CKR_FUNCTION_FAILED;
++        goto done;
++    }
++
++    /* Validate the hash */
++    ret = compute_sha1((char *)(tmp_clear + sizeof(CK_ULONG_32)),
++                       obj_data_len_32, (char *)hash_sha);
++    if (ret != CKR_OK) {
++        TRACE_ERROR("compute_sha1 failed with ret=%08lX\n", ret);
++        goto done;
++    }
++
++    if (memcmp(tmp_clear + sizeof(CK_ULONG_32) + obj_data_len_32, hash_sha,
++               SHA1_HASH_SIZE) != 0) {
++        TRACE_ERROR("Stored hash does not match with newly calculated hash.\n");
++        ret = CKR_FUNCTION_FAILED;
++        goto done;
++    }
++
++    /* At this point, tmp_clear points to the full decrypted obj data:
++     *  | 4 bytes len | clear obj[obj_data_len_32] | 20 bytes sha | padding |
++     * But the caller only wants clear obj[obj_data_len_32].
++     */
++    raw_clear = malloc(obj_data_len_32);
++    if (!raw_clear) {
++        TRACE_ERROR("Cannot malloc %d bytes, errno=%s.\n", enc_len, strerror(errno));
++        ret = CKR_HOST_MEMORY;
++        goto done;
++    }
++    memcpy(raw_clear, tmp_clear + sizeof(CK_ULONG_32), obj_data_len_32);
++
++    *clear = raw_clear;
++    *clear_len = (unsigned int)obj_data_len_32;
++
++    ret = CKR_OK;
++
++done:
++
++    free(tmp_clear);
++    return ret;
++}
++
++/**
++ * This function migrates the private obj to the current format.
++ */
++static CK_RV migrate_private_token_object(const char *data_store, const char *name,
++                                          unsigned char *data, unsigned long len,
++                                          const CK_BYTE *masterkey_old,
++                                          const CK_BYTE *masterkey_new)
++{
++    const char *tokobj = "TOK_OBJ";
++    char fname[PATH_MAX + 1 + strlen(tokobj) + 1 + strlen(name) + 1];
++    unsigned char *clear = NULL;
++    unsigned int clear_len;
++    unsigned char *obj_new = NULL;
++    unsigned int obj_new_len;
++    FILE *fp = NULL;
++    CK_RV ret;
++
++    /* Decrypt old object */
++    ret = decrypt_OBJECT_PRIV_00(&clear, &clear_len, data, len, masterkey_old);
++    if (ret != 0) {
++        TRACE_ERROR("Cannot decrypt old object with old masterkey, ret=%08lX.\n", ret);
++        goto done;
++    }
++
++    /* Create new object */
++    ret = make_OBJECT_PRIV_312(&obj_new, &obj_new_len, name, clear, clear_len,
++                               masterkey_new);
++    if (ret != 0) {
++        TRACE_ERROR("make_OBJECT_PRIV_312 failed with ret=%08lX.\n", ret);
++        goto done;
++    }
++
++    /* Create file name for new object */
++    sprintf(fname, "%s/%s/%s", data_store, tokobj, name);
++    fp = fopen((char *)fname, "w");
++    if (!fp) {
++        TRACE_ERROR("fopen(%s) failed, errno=%s\n", fname, strerror(errno));
++        ret = CKR_FUNCTION_FAILED;
++        goto done;
++    }
++    set_perm(fileno(fp));
++
++    /* Save new object */
++    if (fwrite(obj_new, obj_new_len, 1, fp) != 1) {
++        TRACE_ERROR("fwrite(%s) failed, errno=%s\n", fname, strerror(errno));
++        ret = CKR_FUNCTION_FAILED;
++        goto done;
++    }
++
++    ret = CKR_OK;
++
++done:
++
++    if (fp) {
++        fclose(fp);
++        fp = NULL;
++    }
++    free(clear);
++    free(obj_new);
++
++    return ret;
++}
++
++/**
++ * Reads a format 0.0 object:
++ *
++ *   struct OBJECT {
++ *      u32 total_len;   <- indicates old or new format
++ *      u8 private_flag;
++ *      u8 object;       <- can be public or private
++ *   };
++ *
++ * The total_len field has been already read to decide whether this
++ * object is old or new. Its value is passed via the size parm.
++ */
++static CK_RV read_object_00(FILE *fp, const char *name, unsigned int size,
++                            unsigned char **obj, unsigned int *obj_len,
++                            CK_BBOOL *obj_priv)
++{
++    CK_BBOOL priv;
++    size_t read_size;
++    unsigned char *buf = NULL;
++    CK_RV ret;
++
++    *obj = NULL;
++    *obj_len = 0;
++
++    /* Check parms */
++    if (!fp || !name) {
++        TRACE_ERROR("Arguments bad.\n");
++        ret = CKR_ARGUMENTS_BAD;
++        goto done;
++    }
++
++    /* Read 1-char private flag */
++    read_size = fread(&priv, sizeof(CK_BBOOL), 1, fp);
++    if (read_size != 1) {
++        TRACE_ERROR("Cannot read private flag from old object %s.\n", name);
++        ret = CKR_FUNCTION_FAILED;
++        goto done;
++    }
++
++    /* Allocate buffer for obj */
++    size = size - sizeof(CK_ULONG_32) - sizeof(CK_BBOOL);
++    buf = malloc(size);
++    if (!buf) {
++        TRACE_ERROR("Cannot malloc %d bytes for object %s.\n", size, name);
++        ret = CKR_HOST_MEMORY;
++        goto done;
++    }
++
++    /* Read obj into buf */
++    read_size = fread((char *)buf, 1, size, fp);
++    if (read_size != size) {
++        TRACE_ERROR("Cannot read old object %s.\n", name);
++        ret = CKR_FUNCTION_FAILED;
++        goto done;
++    }
++
++    *obj = buf;
++    *obj_len = size;
++    *obj_priv = priv;
++
++    ret = CKR_OK;
++
++done:
++
++    return ret;
++}
++
++/**
++ * Reads the token object given by name from given data_store and returns
++ * pointers to the object, its length, and indications whether the object
++ * is in 0.0 or 3.12 format and whether it's private.
++ */
++static CK_RV read_object(const char *data_store, const char *name,
++                         unsigned char **obj, unsigned int *obj_len,
++                         CK_ULONG *version, CK_BBOOL *obj_priv)
++{
++    char fname[PATH_MAX];
++    unsigned int size = 0;
++    size_t read_size;
++    FILE *fp;
++    CK_RV ret;
++
++    *obj = NULL;
++    *obj_len = 0;
++
++    /* Open token object file */
++    snprintf((char *) fname, sizeof(fname), "%s/TOK_OBJ/", data_store);
++    strcat((char *) fname, (char *) name);
++    fp = fopen((char *) fname, "r");
++    if (!fp) {
++        TRACE_ERROR("fopen(%s) failed, errno=%s\n", fname, strerror(errno));
++        ret = CKR_FUNCTION_FAILED;
++        goto done;
++    }
++
++    /* Read 32-bit size field */
++    read_size = fread(&size, sizeof(CK_ULONG_32), 1, fp);
++    if (read_size != 1) {
++        TRACE_ERROR("Cannot read %ld bytes from %s, read_size = %ld. "
++                    "Object probably empty or corrupted.\n",
++                    sizeof(CK_ULONG_32), name, read_size);
++        ret = CKR_FUNCTION_FAILED;
++        goto done;
++    }
++
++    /* Check if this object is old or current */
++    if (size == TOKVERSION_312) {
++        TRACE_INFO("%s is already in current format, nothing to do.\n", name);
++        ret = CKR_OK;
++        *version = TOKVERSION_312;
++        goto done;
++    }
++
++    /* Read old object */
++    *version = TOKVERSION_00;
++    ret = read_object_00(fp, name, size, obj, obj_len, obj_priv);
++    if (ret != 0) {
++        TRACE_ERROR("Cannot read old object %s.\n", name);
++        ret = CKR_FUNCTION_FAILED;
++        goto done;
++    }
++
++    ret = CKR_OK;
++
++done:
++
++    if (fp)
++        fclose(fp);
++
++    return ret;
++}
++
++/**
++ * Migrate the token objects from old to new format. Some of the token objects
++ * may be in old and some may be in new format if a previous migration run
++ * was interrupted.
++ */
++static CK_RV migrate_token_objects(const char *data_store, const CK_BYTE *masterkey_old,
++                                   const CK_BYTE *masterkey_new,
++                                   const CK_BYTE *so_wrap_key,
++                                   const CK_BYTE *user_wrap_key)
++{
++    const char *tokobj = "TOK_OBJ";
++    const char *objidx = "OBJ.IDX";
++    FILE *fp = NULL;
++    unsigned char *obj = NULL;
++    unsigned int obj_len;
++    char tmp[PATH_MAX];
++    char iname[PATH_MAX + 1 + strlen(tokobj) + 1 + strlen(objidx) + 1];
++    CK_BBOOL priv;
++    CK_ULONG version;
++    int count = 0, scount = 0;
++    CK_RV ret;
++
++    /* Check parms */
++    if (!data_store || !masterkey_old || !masterkey_new || !so_wrap_key
++        || !user_wrap_key) {
++        TRACE_ERROR("Invalid parms.\n");
++        ret = CKR_ARGUMENTS_BAD;
++        goto done;
++    }
++
++    /* Open index file OBJ.IDX */
++    sprintf(iname, "%s/%s/%s", data_store, tokobj, objidx);
++    fp = fopen((char *)iname, "r");
++    if (!fp) {
++        TRACE_ERROR("fopen(%s) failed, errno=%s\n", iname, strerror(errno));
++        ret = CKR_FUNCTION_FAILED;
++        goto done;
++    }
++
++    /* Migrate items from OBJ.IDX */
++    while (fgets(tmp, PATH_MAX, fp)) {
++        tmp[strlen(tmp) - 1] = 0;
++        ret = read_object(data_store, tmp, &obj, &obj_len, &version, &priv);
++        if (ret == 0 && version == TOKVERSION_00) {
++            if (priv) {
++                ret = migrate_private_token_object(data_store, tmp,
++                                   obj, obj_len, masterkey_old, masterkey_new);
++                if (ret != CKR_OK) {
++                    TRACE_ERROR("Cannot migrate private object %s, continuing ... \n", tmp);
++                } else
++                    scount++;
++            } else {
++                ret = migrate_public_token_object(data_store, tmp,
++                                                  obj, obj_len);
++                if (ret != CKR_OK) {
++                    TRACE_ERROR("Cannot migrate public object %s, continuing ... \n", tmp);
++                } else
++                    scount++;
++            }
++        }
++
++        if (obj) {
++            free(obj);
++            obj = NULL;
++        }
++        count++;
++    }
++
++    /* OBJ.IDX must be at eof here */
++    if (!feof(fp)) {
++        TRACE_WARN("OBJ.IDX is not at eof after object %s, should not happen.\n",
++                   tmp);
++    }
++
++    /* Close OBJ.IDX */
++    fclose(fp);
++
++    ret = CKR_OK;
++
++    TRACE_NONE("Migrated %d object(s) out of %d object(s).\n", scount, count);
++
++done:
++
++    return ret;
++}
++
++/**
++ * loads the new aes256 masterkey.
++ * The new format defines the MK to be an AES-256 key. Its unencrypted format
++ * are just the 32 key bytes. Its encrypted format is a 40 byte key blob
++ */
++static CK_RV load_masterkey_312(const char *data_store, const char *mkfile,
++                                const char *pin, TOKEN_DATA *tokdata,
++                                CK_BYTE *masterkey)
++{
++    FILE *fp = NULL;
++    CK_RV ret;
++    int rc;
++    char fname[PATH_MAX];
++    unsigned char inbuf[40];
++    unsigned char wrap_key[32];
++
++    /* Open file */
++    memset(fname, 0, PATH_MAX);
++    snprintf(fname, PATH_MAX, "%s/%s", data_store, mkfile);
++    fp = fopen(fname, "r");
++    if (!fp) {
++        TRACE_ERROR("fopen(%s) failed, errno=%s\n", fname, strerror(errno));
++        ret = CKR_FUNCTION_FAILED;
++        goto done;
++    }
++
++    /* Read wrapped key from file */
++    rc = fread(inbuf, sizeof(inbuf), 1, fp);
++    if (rc != 1) {
++        TRACE_ERROR("Cannot read %ld bytes from %s.\n", sizeof(inbuf), fname);
++        ret = CKR_FUNCTION_FAILED;
++        goto done;
++    }
++
++    /* Derive wrapping key from pin and public info in TOKEN_DATA */
++    if (strstr(mkfile,"MK_SO")) {
++        rc = PKCS5_PBKDF2_HMAC(pin, strlen(pin),
++                               tokdata->dat.so_wrap_salt, 64,
++                               tokdata->dat.so_wrap_it, EVP_sha512(),
++                               256 / 8, wrap_key);
++    } else {
++        rc = PKCS5_PBKDF2_HMAC(pin, strlen(pin),
++                               tokdata->dat.user_wrap_salt, 64,
++                               tokdata->dat.user_wrap_it, EVP_sha512(),
++                               256 / 8, wrap_key);
++    }
++    if (rc != 1) {
++        TRACE_INFO("PKCS5_PBKDF2_HMAC returned rc=%08X.\n", rc);
++        ret = CKR_FUNCTION_FAILED;
++        goto done;
++    }
++
++    /* Decrypt buffer with pin-related wrapping key */
++    rc = aes_256_unwrap(masterkey, inbuf, wrap_key);
++    if (rc != CKR_OK) {
++        TRACE_ERROR("aes_256_unwrap failed with rc=%08X.\n", rc);
++        ret = CKR_FUNCTION_FAILED;
++        goto done;
++    }
++
++    ret = CKR_OK;
++
++done:
++
++    if (fp)
++        fclose(fp);
++
++    return ret;
++}
++
++/**
++ * loads the old des3 masterkey from given file with given PIN.
++ * The format is:
++ *
++ *   struct MK {
++ *     u8 MK [24 or 64 (cca)];
++ *     u8 sha1 [20];
++ *     u8 padding[4];
++ *   };
++ */
++static CK_RV load_masterkey_00(const char *mkfile, const char *pin,
++                               CK_BYTE *masterkey)
++{
++    CK_BYTE des3_key[3 * DES_KEY_SIZE];
++    char hash_sha[SHA1_HASH_SIZE];
++    char pin_md5_hash[MD5_HASH_SIZE];
++    unsigned char *cipher = NULL;
++    unsigned char *clear = NULL;
++    unsigned long cipher_len, clear_len;
++    CK_ULONG master_key_len = 0L;
++    int file_size = 0;
++
++    CK_RV ret;
++    int rc;
++    FILE *fp = NULL;
++
++    fp = fopen(mkfile, "r");
++    if (!fp) {
++        TRACE_ERROR("fopen(%s) failed, errno=%s\n", mkfile, strerror(errno));
++        return CKR_FUNCTION_FAILED;
++    }
++
++    /* Determine the master key length */
++    fseek(fp, 0L, SEEK_END);
++    file_size = ftell(fp);
++    switch (file_size) {
++    case MK_FILE_SIZE_00_CCA: /* CCA token */
++        master_key_len = MASTER_KEY_SIZE_CCA;
++        break;
++    case MK_FILE_SIZE_00: /* All other tokens */
++        master_key_len = MASTER_KEY_SIZE;
++        break;
++    default:
++        /* Unknown MK format, should not occur. */
++        TRACE_ERROR("%s has an unknown file size of %d bytes. Should be either 48 or 88 bytes.\n",
++                    mkfile, file_size);
++        ret = CKR_FUNCTION_FAILED;
++        goto done;
++    }
++    rewind(fp);
++
++    /* Read file contents */
++    clear_len = cipher_len =
++            (master_key_len + SHA1_HASH_SIZE +
++            (DES_BLOCK_SIZE - 1)) & ~(DES_BLOCK_SIZE - 1);
++
++    cipher = malloc(cipher_len);
++    clear = malloc(clear_len);
++    if (cipher == NULL || clear == NULL) {
++        ret = CKR_HOST_MEMORY;
++        goto done;
++    }
++
++    rc = fread(cipher, cipher_len, 1, fp);
++    if (rc != 1) {
++        TRACE_ERROR("Cannot read %ld bytes from %s\n", cipher_len, mkfile);
++        ret = CKR_FUNCTION_FAILED;
++        goto done;
++    }
++
++    /* Decrypt the masterkey */
++    ret = compute_md5((char *)pin, strlen(pin), pin_md5_hash);
++    if (ret) {
++        TRACE_ERROR("Error calculating MD5 of PIN, ret=%08lX\n", ret);
++        goto done;
++    }
++
++    memcpy(des3_key, pin_md5_hash, MD5_HASH_SIZE);
++    memcpy(des3_key + MD5_HASH_SIZE, pin_md5_hash, DES_KEY_SIZE);
++
++    ret = sw_des3_cbc_decrypt(cipher, cipher_len, clear,
++                              &clear_len, (unsigned char *) "12345678",
++                              des3_key);
++    if (ret != CKR_OK) {
++        TRACE_ERROR("failed to decrypt master key file after read, ret=%08lX\n", ret);
++        ret = CKR_FUNCTION_FAILED;
++        goto done;
++    }
++
++    /* compare the hashes to verify integrity */
++    ret = compute_sha1((char *)clear, master_key_len, hash_sha);
++    if (ret) {
++        TRACE_ERROR("Failed to compute sha1 for masterkey, ret=%08lX\n", ret);
++        goto done;
++    }
++
++    if (memcmp(hash_sha, clear + master_key_len, SHA1_HASH_SIZE) != 0) {
++        TRACE_ERROR("%s appears to be tampered! Cannot migrate.\n", mkfile);
++        ret = CKR_FUNCTION_FAILED;
++        goto done;
++    }
++
++    memcpy(masterkey, clear, master_key_len);
++    ret = 0;
++
++done:
++    if (fp)
++        fclose(fp);
++    free(clear);
++    free(cipher);
++
++    return ret;
++}
++
++/**
++ * Remove any spaces from given string
++ */
++static void remove_spaces(char *s)
++{
++    int i, count = 0;
++
++    for (i = 0; s[i]; i++) {
++        if (s[i] != ' ')
++            s[count++] = s[i];
++    }
++
++    s[count] = '\0';
++}
++
++/**
++ * gets the stdll name from the given opencryptoki.conf file.
++ */
++static CK_RV get_stdll_name(FILE *fp, char *dll_name)
++{
++    char line[PATH_MAX];
++
++    while (fgets(line, sizeof(line), fp)) {
++        remove_spaces(line);
++        if (strstr(line, "stdll=")) {
++            sscanf(line, "stdll=%s", dll_name);
++            return CKR_OK;
++        }
++    }
++
++    return CKR_FUNCTION_FAILED;
++}
++
++/**
++ * Check if the given conf_dir exists and contains the opencryptoki.conf.
++ */
++static CK_BBOOL conffile_exists(const char *conf_dir)
++{
++    char fname[PATH_MAX];
++    struct stat statbuf;
++    DIR *dir;
++
++    TRACE_INFO("Checking if config file exists in %s ...\n", conf_dir);
++    dir = opendir(conf_dir);
++    if (dir == NULL) {
++        TRACE_INFO("Cannot open %s.\n", conf_dir);
++        return CK_FALSE;
++    }
++
++    /* Check if opencryptoki.conf exists */
++    memset(fname, 0, PATH_MAX);
++    snprintf(fname, PATH_MAX, "%s/opencryptoki.conf", conf_dir);
++    if (stat(fname, &statbuf) != 0) {
++        TRACE_INFO("Cannot find %s.\n", fname);
++        closedir(dir);
++        return CK_FALSE;
++    }
++    closedir(dir);
++
++    return CK_TRUE;
++}
++
++/**
++ * Check if the given data_store directory exists.
++ */
++static CK_BBOOL datastore_exists(const char *data_store)
++{
++    DIR *dir;
++
++    TRACE_INFO("Checking if datastore %s exists ...\n", data_store);
++    dir = opendir(data_store);
++    if (dir == NULL) {
++        TRACE_INFO("Cannot open %s.\n", data_store);
++        return CK_FALSE;
++    }
++    closedir(dir);
++
++    return CK_TRUE;
++}
++
++/**
++ * Check if the data store is empty, which is the case when
++ * there are no entries in OBJ.IDX.
++ */
++static CK_BBOOL datastore_empty(const char *data_store)
++{
++    char fname[PATH_MAX];
++    struct stat statbuf;
++
++    TRACE_INFO("Checking if data store is empty ...\n");
++
++    /* Check if OBJ.IDX exists */
++    memset(fname, 0, PATH_MAX);
++    snprintf(fname, PATH_MAX, "%s/TOK_OBJ/OBJ.IDX", data_store);
++    if (stat(fname, &statbuf) != 0) {
++        TRACE_INFO("Cannot find %s, data store probably empty.\n", fname);
++        return CK_TRUE;
++    }
++
++    /* Check if OBJ.IDX is empty */
++    if (statbuf.st_size == 0) {
++        TRACE_INFO("OBJ.IDX file is empty. Thus no objects to migrate.\n");
++        return CK_TRUE;
++    }
++
++    return CK_FALSE;
++}
++
++/**
++ *
++ */
++static CK_RV load_MK_SO_00(const char *data_store, const char *sopin,
++                           CK_BYTE *masterkey)
++{
++    const char *mkso = "MK_SO";
++    char fname[PATH_MAX + 1 + strlen(mkso) + 1];
++    CK_RV ret;
++
++    /* Get masterkey from MK_SO. This also verifies SO PIN is correct */
++    memset(masterkey, 0, MAX_MASTER_KEY_SIZE);
++    sprintf(fname, "%s/%s", data_store, mkso);
++    ret = load_masterkey_00(fname, sopin, masterkey);
++    if (ret != CKR_OK) {
++        TRACE_ERROR("Cannot load old masterkey from MK_SO, ret=%08lX.\n", ret);
++        // We cannot do anything more here, even when some objs are still old.
++        // We would need the old key in order to open an old obj.
++    }
++
++    return ret;
++}
++
++/**
++ *
++ */
++static CK_RV load_MK_USER_00(const char *data_store, const char *userpin,
++                             CK_BYTE *masterkey)
++{
++    const char *mkuser = "MK_USER";
++    char fname[PATH_MAX + 1 + strlen(mkuser) + 1];
++    CK_RV ret;
++
++    /* Get masterkey from MK_USER. This also verifies user PIN is correct */
++    memset(masterkey, 0, MAX_MASTER_KEY_SIZE);
++    sprintf(fname, "%s/%s", data_store, mkuser);
++    ret = load_masterkey_00(fname, userpin, masterkey);
++    if (ret != CKR_OK) {
++        TRACE_ERROR("Cannot load old masterkey from MK_USER, ret=%08lX.\n", ret);
++    }
++
++    return ret;
++}
++
++/**
++ *
++ */
++static CK_RV load_MK_SO_312(const char *data_store, const char *sopin,
++                            TOKEN_DATA *tokdata, CK_BYTE *masterkey)
++{
++    CK_RV ret;
++
++    /* Get masterkey from MK_SO_312. This also verifies SO PIN is correct */
++    memset(masterkey, 0, MAX_MASTER_KEY_SIZE);
++    ret = load_masterkey_312(data_store, "MK_SO_312", sopin, tokdata, masterkey);
++    if (ret != CKR_OK) {
++        TRACE_ERROR("Cannot load masterkey from MK_SO_312, ret=%08lX.\n", ret);
++    }
++
++    return ret;
++}
++
++/**
++ *
++ */
++static CK_RV load_MK_USER_312(const char *data_store, const char *userpin,
++                              TOKEN_DATA *tokdata, CK_BYTE *masterkey)
++{
++    CK_RV ret;
++
++    /* Get masterkey from MK_USER_312. This also verifies user PIN is correct */
++    memset(masterkey, 0, MAX_MASTER_KEY_SIZE);
++    ret = load_masterkey_312(data_store, "MK_USER_312", userpin, tokdata, masterkey);
++    if (ret != CKR_OK) {
++        TRACE_ERROR("Cannot load masterkey from MK_USER_312, ret=%08lX.\n", ret);
++    }
++
++    return ret;
++}
++
++/**
++ * Loads the NVTOK.DAT and returns the TOKEN_DATA struct.
++ */
++static CK_RV load_NVTOK_DAT(const char *data_store, const char *nvtok_name,
++                            TOKEN_DATA *td)
++{
++    char fname[PATH_MAX];
++    struct stat stbuf;
++    int fd;
++    size_t tdlen;
++    FILE *fp = NULL;
++    CK_RV ret;
++
++    /* Check parms */
++    if (!data_store || !nvtok_name || !td) {
++        ret = CKR_ARGUMENTS_BAD;
++        goto done;
++    }
++
++    /* Read the NVTOK.DAT */
++    snprintf(fname, PATH_MAX, "%s/%s", data_store, nvtok_name);
++    fp = fopen((char *) fname, "r");
++    if (!fp) {
++        TRACE_ERROR("fopen(%s) failed, errno=%s\n", fname, strerror(errno));
++        return CKR_FUNCTION_FAILED;
++    }
++
++    fd = fileno(fp);
++    if ((fstat(fd, &stbuf) != 0) || (!S_ISREG(stbuf.st_mode))) {
++        ret = CKR_FUNCTION_FAILED;
++        goto done;
++    }
++
++    /* Check if this NVTOK.DAT is old or new */
++    if (stbuf.st_size == sizeof(TOKEN_DATA_OLD)) {
++        /* old data store/pin format */
++        tdlen = sizeof(TOKEN_DATA_OLD);
++    } else if (stbuf.st_size == sizeof(TOKEN_DATA)) {
++        /* new data store/pin format */
++        tdlen = sizeof(TOKEN_DATA);
++    } else {
++        TRACE_ERROR("%s has an invalid size of %ld bytes. Neither old nor new token format.\n",
++                    fname, stbuf.st_size);
++        ret = CKR_FUNCTION_FAILED;
++        goto done;
++    }
++
++    /* Read TOKEN_DATA */
++    ret = fread(td, tdlen, 1, fp);
++    if (ret != 1) {
++        TRACE_ERROR("Cannot read %s, errno=%s\n", fname, strerror(errno));
++        ret = CKR_FUNCTION_FAILED;
++        goto done;
++    }
++
++    ret = CKR_OK;
++
++done:
++
++    if (fp)
++        fclose(fp);
++
++    return ret;
++}
++
++/**
++ * Strip trailing chars from given string.
++ */
++static char *strip_trailing_chars(char *s, int slen, char c)
++{
++    int i;
++
++    if (!s || slen == 0)
++        return s;
++
++    for (i = slen - 1; i >= 0; i--) {
++        if (s[i] == c)
++            s[i] = '\0';
++        else
++            break;
++    }
++
++    return s;
++}
++
++/**
++ * Read the token info from NVTOK.DAT.
++ */
++static CK_RV get_token_info(const char *data_store, CK_TOKEN_INFO_32 *tokinfo)
++{
++    TOKEN_DATA tokdata;
++    CK_RV ret;
++
++    TRACE_INFO("Reading token info from NVTOK.DAT ...\n");
++
++    ret = load_NVTOK_DAT(data_store, "NVTOK.DAT", &tokdata);
++    if (ret != CKR_OK) {
++        TRACE_ERROR("Cannot load NVTOK.DAT, datastore inconsistent.\n");
++        return ret;
++    }
++
++    memcpy(tokinfo, &(tokdata.token_info), sizeof(CK_TOKEN_INFO_32));
++
++    return CKR_OK;
++}
++
++/**
++ * returns true, if the given string begins with the given char,
++ * false otherwise. The string may contain leading spaces.
++ */
++static CK_BBOOL begins_with(const char *str, const char c)
++{
++    size_t i;
++
++    for (i = 0; i < strlen(str); i++) {
++        if (str[i] == ' ')
++            continue;
++        else if (str[i] == c)
++            return CK_TRUE;
++        else
++            return CK_FALSE;
++    }
++
++    return CK_FALSE;
++}
++
++/**
++ * Identify the token that belongs to the given slot ID.
++ */
++static CK_RV identify_token(CK_SLOT_ID slot_id, char *conf_dir, char *dll_name)
++{
++    char conf_file[PATH_MAX];
++    char line[80], parm[80];
++    FILE *fp;
++    CK_RV ret;
++
++    TRACE_INFO("Identifying the token that belongs to slot %ld ...\n", slot_id);
++
++    /* Open conf file */
++    snprintf(conf_file, PATH_MAX, "%s/%s", conf_dir, "opencryptoki.conf");
++    fp = fopen(conf_file, "r");
++    if (!fp) {
++        TRACE_ERROR("fopen(%s) failed, errno=%s\n", conf_file, strerror(errno));
++        ret = CKR_FUNCTION_FAILED;
++        goto done;
++    }
++
++    /* Get stdll name */
++    ret = CKR_FUNCTION_FAILED;
++    snprintf(parm, sizeof(parm), "slot %ld", slot_id);
++    while (fgets(line, sizeof(line), fp)) {
++        if (!begins_with(line, '#') && strstr(line, parm)) {
++            ret = get_stdll_name(fp, dll_name);
++            goto done;
++        }
++    }
++
++done:
++
++    fclose(fp);
++
++    return ret;
++}
++
++/**
++ * derives the SO wrap key from the given SO pin and given public
++ * info in NVTOK.DAT.
++ */
++static CK_RV derive_so_wrap_key_312(const char *sopin, TOKEN_DATA *tokdata,
++                                    CK_BYTE *so_wrap_key)
++{
++    CK_RV ret;
++
++    ret = PKCS5_PBKDF2_HMAC(sopin, strlen(sopin),
++                            tokdata->dat.so_wrap_salt, 64,
++                            tokdata->dat.so_wrap_it, EVP_sha512(),
++                            256 / 8, so_wrap_key);
++    if (ret != 1) {
++        TRACE_ERROR("PBKDF2 failed.\n");
++        ret = CKR_FUNCTION_FAILED;
++        goto done;
++    }
++
++    ret = CKR_OK;
++
++done:
++
++    return ret;
++}
++
++/**
++ * derives the user wrap key from the given user pin and given public
++ * info in NVTOK.DAT.
++ */
++static CK_RV derive_user_wrap_key_312(const char *userpin, TOKEN_DATA *tokdata,
++                                      CK_BYTE *user_wrap_key)
++{
++    CK_RV ret;
++
++    ret = PKCS5_PBKDF2_HMAC(userpin, strlen(userpin),
++                            tokdata->dat.user_wrap_salt, 64,
++                            tokdata->dat.user_wrap_it, EVP_sha512(),
++                            256 / 8, user_wrap_key);
++    if (ret != 1) {
++        TRACE_ERROR("PBKDF2 failed.\n");
++        ret = CKR_FUNCTION_FAILED;
++        goto done;
++    }
++
++    ret = CKR_OK;
++
++done:
++
++    return ret;
++}
++
++/**
++ * Activates the new repository by deleting the old MK_USER, MK_SO, and
++ * NVTOK.DAT, and renaming the new MK_SO_312, MK_USER_312, NVTOK.DAT_312 to
++ * their normal names.
++ */
++static CK_RV cleanup_repository_backup(const char *data_store)
++{
++    static char *names[] = { "MK_SO", "MK_USER", "NVTOK.DAT" };
++    int num_names = sizeof(names) / sizeof(char *);
++    char fname1[PATH_MAX + 9 + 1]; // satisfy compiler warning
++    char fname2[PATH_MAX + 1 + 1]; // satisfy compiler warning
++    int i, rc;
++    CK_RV ret;
++
++    /* Delete old files */
++    for (i = 0; i < num_names; i++) {
++        snprintf(fname1, sizeof(fname1), "%s/%s", data_store, names[i]);
++        rc = remove(fname1);
++        if (rc) {
++            TRACE_ERROR("Cannot delete old file %s.\n", fname1);
++            ret = CKR_FUNCTION_FAILED;
++            goto done;
++        }
++    }
++
++    /* Rename new files */
++    for (i = 0; i < num_names; i++) {
++        snprintf(fname1, sizeof(fname1), "%s/%s_312", data_store, names[i]);
++        snprintf(fname2, sizeof(fname2), "%s/%s", data_store, names[i]);
++        rc = rename(fname1, fname2);
++        if (rc) {
++            TRACE_ERROR("Cannot rename new file %s.\n", fname1);
++            ret = CKR_FUNCTION_FAILED;
++            goto done;
++        }
++    }
++
++    ret = CKR_OK;
++
++done:
++
++    return ret;
++}
++
++/**
++ * Migrates the repository. This process may be interrupted at any time and
++ * must be able to resume until the complete repository is successfully
++ * migrated. This especially requires to keep the old keys until
++ * everything is done.
++ */
++static CK_RV migrate_repository(const char *data_store, const char *sopin,
++                         const char *userpin)
++{
++    CK_BYTE so_masterkey_old[MAX_MASTER_KEY_SIZE];
++    CK_BYTE so_masterkey_new[MAX_MASTER_KEY_SIZE];
++    CK_BYTE user_masterkey_old[MAX_MASTER_KEY_SIZE];
++    CK_BYTE user_masterkey_new[MAX_MASTER_KEY_SIZE];
++    CK_BYTE so_wrap_key[32];
++    CK_BYTE user_wrap_key[32];
++    CK_RV ret;
++    TOKEN_DATA tokdata;
++
++    TRACE_INFO("Migrating the repository ...\n");
++
++    /* Load NVTOK.DAT_312, which was either created before or exists from a
++     * previous interrupted run. So tokdata definitely contains the 3.12
++     * extension.
++     */
++    ret = load_NVTOK_DAT(data_store, "NVTOK.DAT_312", &tokdata);
++    if (ret != CKR_OK) {
++        TRACE_ERROR("Cannot load NVTOK.DAT_312, ret=%08lX.\n", ret);
++        goto done;
++    }
++
++    ret = load_MK_SO_00(data_store, sopin, so_masterkey_old);
++    if (ret != CKR_OK) {
++        TRACE_ERROR("Cannot load old masterkey, ret=%08lX.\n", ret);
++        goto done;
++    }
++
++    ret = load_MK_USER_00(data_store, userpin, user_masterkey_old);
++    if (ret != CKR_OK) {
++        TRACE_ERROR("Cannot load old masterkey, ret=%08lX.\n", ret);
++        goto done;
++    }
++
++    if (memcmp(so_masterkey_old, user_masterkey_old, MAX_MASTER_KEY_SIZE) != 0) {
++        TRACE_ERROR("MK_SO and MK_USER are inconsistent, got different MKs.\n");
++        ret = CKR_FUNCTION_FAILED;
++        goto done;
++    }
++
++    ret = load_MK_SO_312(data_store, sopin, &tokdata, so_masterkey_new);
++    if (ret != CKR_OK) {
++        TRACE_ERROR("Cannot load new masterkey from MK_SO_312, ret=%08lX.\n", ret);
++        goto done;
++    }
++
++    ret = load_MK_USER_312(data_store, userpin, &tokdata, user_masterkey_new);
++    if (ret != CKR_OK) {
++        TRACE_ERROR("Cannot load new masterkey from MK_USER_312, ret=%08lX.\n", ret);
++        goto done;
++    }
++
++    if (memcmp(so_masterkey_new, user_masterkey_new, MAX_MASTER_KEY_SIZE) != 0) {
++        TRACE_ERROR("MK_SO_312 and MK_USER_312 are inconsistent, got different MKs.\n");
++        ret = CKR_FUNCTION_FAILED;
++        goto done;
++    }
++
++    /* This function needs a new NVTOK.DAT with the public salt and icount */
++    ret = derive_so_wrap_key_312(sopin, &tokdata, so_wrap_key);
++    if (ret != CKR_OK) {
++        TRACE_ERROR("Cannot create new so_wrap_key, ret=%08lX.\n", ret);
++        goto done;
++    }
++
++    /* This function needs a new NVTOK.DAT with the public salt and icount */
++    ret = derive_user_wrap_key_312(userpin, &tokdata, user_wrap_key);
++    if (ret != CKR_OK) {
++        TRACE_ERROR("Cannot create new user_wrap_key, ret=%08lX.\n", ret);
++        goto done;
++    }
++
++    /* Now do the migration */
++    ret = migrate_token_objects(data_store, so_masterkey_old, so_masterkey_new,
++                                so_wrap_key, user_wrap_key);
++    if (ret != CKR_OK) {
++        TRACE_ERROR("Migrating token objects failed with ret=%08lX.\n", ret);
++        goto done;
++    }
++
++    /* Remove temp files in backup */
++    ret = cleanup_repository_backup(data_store);
++    if (ret != CKR_OK) {
++        TRACE_ERROR("Cleanup repository backup failed with ret=%08lX.\n", ret);
++        goto done;
++    }
++
++done:
++
++    return ret;
++}
++
++/**
++ * creates a MK_USER_312 file containing the new user MK.
++ */
++static CK_RV create_MK_USER_312(const char *data_store, const char *userpin,
++                                const CK_BYTE *masterkey,
++                                TOKEN_DATA *tokdata)
++{
++    const char *mkuser = "MK_USER_312";
++    char fname[PATH_MAX + 1 + strlen(mkuser) + 1];
++    CK_BYTE user_wrap_key[32];
++    CK_BYTE outbuf[40];
++    FILE *fp = NULL;
++    size_t rv;
++    CK_RV ret;
++
++    /* Create user wrap key */
++    ret = derive_user_wrap_key_312(userpin, tokdata, (unsigned char *)&user_wrap_key);
++    if (ret != CKR_OK) {
++        TRACE_ERROR("Cannot derive user wrap key, ret=%08lX.\n", ret);
++        ret = CKR_FUNCTION_FAILED;
++        goto done;
++    }
++
++    /* Wrap user MK with user_wrap_key */
++    ret = aes_256_wrap(outbuf, masterkey, user_wrap_key);
++    if (ret != CKR_OK)
++        goto done;
++
++    /* Create file MK_USER_312 */
++    sprintf(fname, "%s/%s", data_store, mkuser);
++    fp = fopen(fname, "w");
++    if (!fp) {
++        TRACE_ERROR("fopen(%s) failed, errno=%s\n", fname, strerror(errno));
++        ret = CKR_FUNCTION_FAILED;
++        goto done;
++    }
++    set_perm(fileno(fp));
++
++    rv = fwrite(outbuf, sizeof(outbuf), 1, fp);
++    if (rv != 1) {
++        TRACE_ERROR("fwrite(%s) failed, errno=%s.\n", fname, strerror(errno));
++        ret = CKR_FUNCTION_FAILED;
++        goto done;
++    }
++
++    ret = CKR_OK;
++
++done:
++
++    if (fp)
++        fclose(fp);
++
++    return ret;
++}
++
++/**
++ * creates a MK_SO_312 file containing the new SO MK.
++ */
++static CK_RV create_MK_SO_312(const char *data_store, const char *sopin,
++                              const CK_BYTE *masterkey,
++                              TOKEN_DATA *tokdata)
++{
++    const char *mkso = "MK_SO_312";
++    char fname[PATH_MAX + 1 + strlen(mkso) + 1];
++    CK_BYTE outbuf[40];
++    CK_BYTE so_wrap_key[32];
++    FILE *fp = NULL;
++    size_t rv;
++    CK_RV ret;
++
++    /* Derive so wrap key from sopin and tokdata */
++    ret = derive_so_wrap_key_312(sopin, tokdata, so_wrap_key);
++    if (ret != CKR_OK) {
++        TRACE_ERROR("Cannot derive new so wrap key, ret=%08lX.\n", ret);
++        ret = CKR_FUNCTION_FAILED;
++        goto done;
++    }
++
++    /* Wrap masterkey with SO_wrap_key */
++    ret = aes_256_wrap(outbuf, masterkey, so_wrap_key);
++    if (ret != CKR_OK) {
++        TRACE_ERROR("Cannot wrap masterkey with so wrap key, ret=%08lX.\n", ret);
++        goto done;
++    }
++
++    /* Create file MK_SO_312 */
++    sprintf(fname, "%s/%s", data_store, mkso);
++    fp = fopen(fname, "w");
++    if (!fp) {
++        TRACE_ERROR("fopen(%s) failed, errno=%s\n", fname, strerror(errno));
++        rv = CKR_FUNCTION_FAILED;
++        goto done;
++    }
++    set_perm(fileno(fp));
++
++    rv = fwrite(outbuf, sizeof(outbuf), 1, fp);
++    if (rv != 1) {
++        TRACE_ERROR("fwrite(%s) failed, errno=%s.\n", fname, strerror(errno));
++        ret = CKR_FUNCTION_FAILED;
++        goto done;
++    }
++
++    ret = CKR_OK;
++
++done:
++
++    if (fp)
++        fclose(fp);
++
++    return ret;
++}
++
++/**
++ * reads the old NVTOK.DAT and returns its contents:
++ */
++static CK_RV read_NVTOK_DAT_00(const char *data_store, TOKEN_DATA *tokdata)
++{
++    FILE *fp;
++    const char *nvtok = "NVTOK.DAT";
++    char fname[PATH_MAX + 1 + strlen(nvtok) + 1];
++    struct stat stbuf;
++    int fd;
++    CK_RV ret;
++
++    /* Check parms */
++    if (!data_store || !tokdata) {
++        return CKR_ARGUMENTS_BAD;
++    }
++
++    /* Read the old NVTOK.DAT */
++    sprintf(fname, "%s/%s", data_store, nvtok);
++    fp = fopen((char *) fname, "r");
++    if (!fp) {
++        TRACE_ERROR("fopen(%s) failed, errno=%s\n", fname, strerror(errno));
++        return CKR_FUNCTION_FAILED;
++    }
++
++    fd = fileno(fp);
++    if ((fstat(fd, &stbuf) != 0) || (!S_ISREG(stbuf.st_mode))) {
++        ret = CKR_FUNCTION_FAILED;
++        goto done;
++    }
++
++    /* Ensure that this NVTOK.DAT is in fact old */
++    if (stbuf.st_size != sizeof(TOKEN_DATA_OLD)) {
++        TRACE_ERROR("%s has an invalid size of %ld bytes.\n", fname, stbuf.st_size);
++        ret = CKR_FUNCTION_FAILED;
++        goto done;
++    }
++
++    /* Read TOKEN_DATA_OLD */
++    ret = fread(tokdata, sizeof(TOKEN_DATA_OLD), 1, fp);
++    if (ret != 1) {
++        TRACE_ERROR("Cannot read %s, errno=%s\n", fname, strerror(errno));
++        ret = CKR_FUNCTION_FAILED;
++        goto done;
++    }
++
++    ret = CKR_OK;
++
++done:
++
++    if (fp)
++        fclose(fp);
++
++    return ret;
++}
++
++/**
++ * creates the additions for new format.
++ */
++static CK_RV create_TOKEN_DATA_VERSION(const char *sopin, const char *userpin,
++                                       TOKEN_DATA *tokdata)
++{
++    CK_RV ret;
++    int rc;
++
++    tokdata->dat.version = TOKVERSION_312;
++
++    tokdata->dat.so_login_it = SO_KDF_LOGIN_IT;
++    memcpy(tokdata->dat.so_login_salt, SO_KDF_LOGIN_PURPOSE, 32);
++    ret = local_rng(tokdata->dat.so_login_salt + 32, 32);
++    if (ret != CKR_OK) {
++        TRACE_ERROR("local_rng returned %lX\n", ret);
++        goto done;
++    }
++    rc = PKCS5_PBKDF2_HMAC(sopin, strlen(sopin),
++                           tokdata->dat.so_login_salt, 64,
++                           tokdata->dat.so_login_it, EVP_sha512(),
++                           256 / 8, tokdata->dat.so_login_key);
++    if (rc != 1) {
++        TRACE_ERROR("Error: PKCS5_PBKDF2_HMAC\n");
++        ret = CKR_FUNCTION_FAILED;
++        goto done;
++    }
++
++    tokdata->dat.so_wrap_it = SO_KDF_WRAP_IT;
++    memcpy(tokdata->dat.so_wrap_salt, SO_KDF_WRAP_PURPOSE, 32);
++    ret = local_rng(tokdata->dat.so_wrap_salt + 32, 32);
++    if (ret != CKR_OK) {
++        TRACE_ERROR("local_rng returned %lX\n", ret);
++        goto done;
++    }
++
++    tokdata->dat.user_login_it = USER_KDF_LOGIN_IT;
++    memcpy(tokdata->dat.user_login_salt, USER_KDF_LOGIN_PURPOSE, 32);
++    ret = local_rng(tokdata->dat.user_login_salt + 32, 32);
++    if (ret != CKR_OK) {
++        TRACE_ERROR("local_rng returned %lX\n", ret);
++        goto done;
++    }
++    rc = PKCS5_PBKDF2_HMAC(userpin, strlen(userpin),
++                           tokdata->dat.user_login_salt, 64,
++                           tokdata->dat.user_login_it, EVP_sha512(),
++                           256 / 8, tokdata->dat.user_login_key);
++    if (rc != 1) {
++        TRACE_ERROR("Error: PKCS5_PBKDF2_HMAC\n");
++        ret = CKR_FUNCTION_FAILED;
++        goto done;
++    }
++
++    tokdata->dat.user_wrap_it = USER_KDF_WRAP_IT;
++    memcpy(tokdata->dat.user_wrap_salt, USER_KDF_WRAP_PURPOSE, 32);
++    ret = local_rng(tokdata->dat.user_wrap_salt + 32, 32);
++    if (ret != CKR_OK) {
++        TRACE_ERROR("local_rng returned %lX\n", ret);
++        goto done;
++    }
++
++    ret = CKR_OK;
++
++done:
++
++    return ret;
++}
++
++/**
++ * Creates the new NVTOK.DAT which now contains the public salt and iteration
++ * count values that are necessary for re-deriving the pin-related
++ * wrapping keys.
++ */
++static CK_RV create_NVTOK_DAT_312(const char *data_store, const char *sopin,
++                                  const char *userpin, TOKEN_DATA *tokdata)
++{
++    const char *nvtok = "NVTOK.DAT_312";
++    char fname[PATH_MAX + 1 + strlen(nvtok) + 1];
++    FILE *fp = NULL;
++    CK_RV ret;
++    size_t rc;
++
++    /* Check parms */
++    if (!data_store || !sopin || !userpin || !tokdata) {
++        TRACE_ERROR("invalid parms.\n");
++        ret = CKR_ARGUMENTS_BAD;
++        goto done;
++    }
++
++    /* Create new file NVTOK.DAT_312 */
++    sprintf(fname, "%s/%s", data_store, nvtok);
++    fp = fopen(fname, "w");
++    if (!fp) {
++        TRACE_ERROR("fopen(%s) failed, errno=%s\n", fname, strerror(errno));
++        ret = CKR_FUNCTION_FAILED;
++        goto done;
++    }
++    set_perm(fileno(fp));
++
++    /* Get contents from old NVTOK.DAT */
++    ret = read_NVTOK_DAT_00(data_store, tokdata);
++    if (ret != CKR_OK) {
++        TRACE_ERROR("Cannot read old NVTOK.DAT, ret=%08lX\n", ret);
++        goto done;
++    }
++
++    /* Write old part into NVTOK.DAT_312 */
++    rc = fwrite(tokdata, sizeof(TOKEN_DATA_OLD), 1, fp);
++    if (rc != 1) {
++        TRACE_ERROR("fwrite(%s) failed, errno=%s.\n", fname, strerror(errno));
++        ret = CKR_FUNCTION_FAILED;
++        goto done;
++    }
++
++    /* Create additions for new format */
++    ret = create_TOKEN_DATA_VERSION(sopin, userpin, tokdata);
++    if (ret != CKR_OK) {
++        TRACE_ERROR("Cannot create TOKEN_DATA_VERSION struct, ret=%08lX\n", ret);
++        goto done;
++    }
++
++    /* Append TOKEN_DATA_VERSION to NVTOK.DAT_312 */
++    rc = fwrite(&(tokdata->dat), sizeof(TOKEN_DATA_VERSION), 1, fp);
++    if (rc != 1) {
++        TRACE_ERROR("fwrite(%s) failed, errno=%s.\n", fname, strerror(errno));
++        ret = CKR_FUNCTION_FAILED;
++        goto done;
++    }
++
++    ret = CKR_OK;
++
++done:
++
++    if (fp)
++        fclose(fp);
++
++    return ret;
++}
++
++/**
++ * Creates new token keys MK_USER_312 and MK_SO_312. The old keys in
++ * MK_USER and MK_SO are kept until the migration is fully completed.
++ * Then the old keys are deleted and the new keys are renamed.
++ */
++static CK_RV create_token_keys_312(const char *data_store, const char *sopin,
++                                   const char *userpin)
++{
++    unsigned char masterkey[32];
++    TOKEN_DATA tokdata;
++    CK_RV ret = CKR_OK;
++
++    TRACE_INFO("Creating new v3.12 MK_SO, MK_USER, and NVTOK.DAT ...\n");
++
++    /* Create master key */
++    ret = local_rng(masterkey, 32);
++    if (ret != CKR_OK) {
++        TRACE_ERROR("Cannot create master key, ret=%08lX\n", ret);
++        goto done;
++    }
++
++    ret = create_NVTOK_DAT_312(data_store, sopin, userpin, &tokdata);
++    if (ret != CKR_OK) {
++        TRACE_ERROR("Cannot create NVTOK.DAT_312, ret=%08lX\n", ret);
++        goto done;
++    }
++
++    ret = create_MK_SO_312(data_store, sopin, masterkey, &tokdata);
++    if (ret != CKR_OK) {
++        TRACE_ERROR("Cannot create MK_SO_312, ret=%08lX\n", ret);
++        goto done;
++    }
++
++    ret = create_MK_USER_312(data_store, userpin, masterkey, &tokdata);
++    if (ret != CKR_OK) {
++        TRACE_ERROR("Cannot create MK_USER_312, ret=%08lX\n", ret);
++        goto done;
++    }
++
++    ret = CKR_OK;
++
++done:
++
++    return ret;
++}
++
++/**
++ * Count the objs in the data_store and return the number of total objs
++ * and number of old objs.
++ */
++static CK_RV count_objects(const char *data_store, unsigned int *num_objs,
++                           unsigned int *num_old_objs)
++{
++    char tmp[PATH_MAX], iname[PATH_MAX];
++    unsigned char *obj = NULL;
++    unsigned int obj_len;
++    CK_ULONG version;
++    CK_BBOOL priv;
++    FILE *fp;
++    CK_RV ret;
++
++    *num_objs = 0;
++    *num_old_objs = 0;
++
++    /* Open index file OBJ.IDX */
++    snprintf(iname, sizeof(iname), "%s/TOK_OBJ/OBJ.IDX", data_store);
++    fp = fopen((char *) iname, "r");
++    if (!fp) {
++        TRACE_INFO("Cannot open %s, datastore probably empty.\n", iname);
++        ret = CKR_OK;
++        goto done;
++    }
++
++    /* Count objects and old objects */
++    while (fgets(tmp, PATH_MAX, fp)) {
++        tmp[strlen(tmp) - 1] = 0;
++        (*num_objs)++;
++        ret = read_object(data_store, tmp, &obj, &obj_len, &version, &priv);
++        if (ret == 0 && version == TOKVERSION_00)
++            (*num_old_objs)++;
++        if (obj) {
++            free(obj);
++            obj = NULL;
++        }
++    }
++
++    /* OBJ.IDX must be at eof here */
++    if (!feof(fp)) {
++        TRACE_WARN("OBJ.IDX is not at eof after object %s, should not happen.\n",
++                   tmp);
++    }
++
++    fclose(fp);
++    ret = CKR_OK;
++
++done:
++
++    return ret;
++}
++
++/**
++ * Set parameter "*new" to true if the NVTOK.DAT in the given data store
++ * is on 3.12 level, or false otherwise.
++ */
++static CK_RV NVTOK_DAT_is_312(const char *data_store, CK_BBOOL *new)
++{
++    CK_RV ret;
++    char fname[PATH_MAX];
++    struct stat stbuf;
++    int fd;
++    FILE *fp = NULL;
++
++    *new = CK_FALSE;
++
++    /* Read the NVTOK.DAT */
++    snprintf(fname, PATH_MAX, "%s/NVTOK.DAT", data_store);
++    fp = fopen((char *)fname, "r");
++    if (!fp) {
++        TRACE_ERROR("Cannot open %s, errno=%s\n", fname, strerror(errno));
++        return CKR_FUNCTION_FAILED;
++    }
++
++    fd = fileno(fp);
++    if ((fstat(fd, &stbuf) != 0) || (!S_ISREG(stbuf.st_mode))) {
++        ret = CKR_FUNCTION_FAILED;
++        goto done;
++    }
++
++    /* Check if this NVTOK.DAT is old or new */
++    if (stbuf.st_size == sizeof(TOKEN_DATA_OLD)) {
++        *new = CK_FALSE;
++    } else if (stbuf.st_size == sizeof(TOKEN_DATA)) {
++        *new = CK_TRUE;
++    } else {
++        TRACE_ERROR("%s has an invalid size of %ld bytes. Neither old nor new token format.\n",
++                    fname, stbuf.st_size);
++        ret = CKR_FUNCTION_FAILED;
++        goto done;
++    }
++
++    ret = CKR_OK;
++
++done:
++
++    return ret;
++}
++
++/**
++ * Check if the data store is in 3.12 format.
++ */
++static CK_RV datastore_is_312(const char *data_store, const char *sopin,
++                              const char *userpin, CK_BBOOL *new)
++{
++    CK_RV ret;
++    CK_BYTE masterkey_so[32];
++    CK_BYTE masterkey_user[32];
++    unsigned int num_objs = 0, num_old_objs = 0;
++    TOKEN_DATA tokdata;
++
++    *new = CK_FALSE;
++
++    TRACE_INFO("Checking if data store is already in 3.12 format ...\n");
++
++    /* Check if NVTOK.DAT is new */
++    ret = NVTOK_DAT_is_312(data_store, new);
++    if (ret != CKR_OK) {
++        warnx("Cannot determine if NVTOK.DAT has an old or new format.");
++        warnx("Note that generic token formats cannot be migrated.");
++        ret = CKR_FUNCTION_FAILED;
++        goto done;
++    }
++
++    if (*new == CK_FALSE) {
++        ret = CKR_OK;
++        goto done;
++    }
++
++    /* NVTOK.DAT is already new, now check if we can read the keys */
++    ret = load_NVTOK_DAT(data_store, "NVTOK.DAT", &tokdata);
++    if (ret != CKR_OK) {
++        TRACE_ERROR("Cannot load NVTOK.DAT, datastore inconsistent, ret=%08lX\n", ret);
++        ret = CKR_FUNCTION_FAILED;
++        goto done;
++    }
++
++    ret = load_masterkey_312(data_store, "MK_SO", sopin, &tokdata, masterkey_so);
++    if (ret != CKR_OK) {
++        TRACE_INFO("Cannot load new MK from MK_SO, datastore probably old.\n");
++        goto done;
++    }
++
++    ret = load_masterkey_312(data_store, "MK_USER", userpin, &tokdata, masterkey_user);
++    if (ret != CKR_OK) {
++        TRACE_INFO("Cannot load new MK from MK_USER, datastore probably old.\n");
++        goto done;
++    }
++
++    if (memcmp(masterkey_so, masterkey_user, 32) != 0) {
++        TRACE_ERROR("MKs from MK_SO and MK_USER don't match, datastore inconsistent.\n");
++        ret = CKR_FUNCTION_FAILED;
++        goto done;
++    }
++
++    ret = count_objects(data_store, &num_objs, &num_old_objs);
++    if (ret != CKR_OK) {
++        TRACE_ERROR("cannot count objects in %s.\n", data_store);
++        ret = CKR_FUNCTION_FAILED;
++        goto done;
++    }
++
++    TRACE_INFO("Found %d objects total, %d old objects.\n", num_objs, num_old_objs);
++    if (num_old_objs > 0)
++        TRACE_WARN("Note that the old objects are not usable anymore, because "
++                   "we don't have the corresponding old masterkey!\n");
++
++    if (num_objs > 0 && num_old_objs == 0)
++        *new = CK_TRUE;
++
++    ret = CKR_OK;
++
++done:
++
++    return ret;
++}
++
++/**
++ * Switch to new repository by deleting the old repository and renaming
++ * the backup folder to the original data store name.
++ */
++static CK_RV switch_to_new_repository(const char *data_store_old,
++                                      const char *data_store_new)
++{
++    char fname1[PATH_MAX];
++    CK_RV ret;
++    int rc = -1;
++
++    TRACE_INFO("Switching to new repository ...\n");
++
++    /* Rename original repository folder */
++    snprintf(fname1, sizeof(fname1), "%s_BAK", data_store_old);
++    rc = rename(data_store_old, fname1);
++    if (rc) {
++        TRACE_ERROR("Cannot rename %s, errno=%s.\n", data_store_old, strerror(rc));
++        ret = CKR_FUNCTION_FAILED;
++        goto done;
++    }
++
++    /* Rename backup folder */
++    rc = rename(data_store_new, data_store_old);
++    if (rc) {
++        TRACE_ERROR("Cannot rename %s, errno=%s.\n", data_store_new, strerror(rc));
++        ret = CKR_FUNCTION_FAILED;
++        goto done;
++    }
++
++    ret = CKR_OK;
++
++done:
++
++    return ret;
++}
++
++/**
++ * Inserts the new tokversion parm in the token's slot configuration, e.g.
++ *
++ *   slot 2
++ *   {
++ *     stdll = libpkcs11_cca.so
++ *     tokversion = 3.12
++ *   }
++ */
++static CK_RV update_opencryptoki_conf(CK_SLOT_ID slot_id, char *location)
++{
++    const char *parm = "tokversion = 3.12\n";
++    char dst_file[PATH_MAX], src_file[PATH_MAX], fname[PATH_MAX+20], line[PATH_MAX];
++    char slot[32];
++    FILE *fp_r = NULL, *fp_w = NULL;
++    CK_RV ret;
++    int rc;
++
++    TRACE_INFO("Updating config file ...\n");
++
++    /* Open current conf file for read */
++    snprintf(src_file, PATH_MAX, "%s/%s", location, "opencryptoki.conf");
++    fp_r = fopen(src_file, "r");
++    if (!fp_r) {
++        TRACE_ERROR("fopen(%s) failed, errno=%s\n", src_file, strerror(errno));
++        ret = CKR_FUNCTION_FAILED;
++        goto done;
++    }
++
++    /* Open new conf file for write */
++    snprintf(dst_file, PATH_MAX, "%s/%s", location, "opencryptoki.conf_new");
++    fp_w = fopen(dst_file, "w");
++    if (!fp_w) {
++        TRACE_ERROR("fopen(%s) failed, errno=%s\n", dst_file, strerror(errno));
++        ret = CKR_FUNCTION_FAILED;
++        goto done;
++    }
++    set_perm(fileno(fp_w));
++
++    /* Insert/replace tokversion parm in new file */
++    snprintf(slot, sizeof(slot), "slot %ld", slot_id);
++    while (fgets(line, sizeof(line), fp_r)) {
++        if (!begins_with(line, '#') && strstr(line, slot)) {
++            fputs(line, fp_w); // write "slot n"
++            /* insert tokversion before '}' */
++            while (fgets(line, sizeof(line), fp_r)) {
++                if (strstr(line, "}")) {
++                    fputs(parm, fp_w);
++                    fputs(line, fp_w);
++                    break;
++                }
++                /* Don't write existing tokversion to new file */
++                if (!strstr(line, "tokversion"))
++                    fputs(line, fp_w);
++            }
++        } else
++            fputs(line, fp_w);
++    }
++
++    fclose(fp_r);
++    fclose(fp_w);
++    fp_r = NULL;
++    fp_w = NULL;
++
++    /* Rename old conf file */
++    snprintf(fname, sizeof(fname), "%s_BAK", src_file);
++    rc = rename(src_file, fname);
++    if (rc) {
++        TRACE_ERROR("Cannot rename %s\n", src_file);
++        ret = CKR_FUNCTION_FAILED;
++        goto done;
++    }
++
++    /* Rename new file */
++    rc = rename(dst_file, src_file);
++    if (rc) {
++        TRACE_ERROR("Cannot rename %s.\n", dst_file);
++        ret = CKR_FUNCTION_FAILED;
++        goto done;
++    }
++
++    ret = CKR_OK;
++
++done:
++    if (fp_r)
++        fclose(fp_r);
++    if (fp_w)
++        fclose(fp_w);
++
++    return ret;
++
++}
++
++/**
++ * Copy a file given by name from a src folder to a dst folder.
++ */
++static CK_RV file_copy(char *dst, const char *src, const char *name)
++{
++    char dst_file[PATH_MAX], src_file[PATH_MAX], buf[4096];
++    FILE *fp_r = NULL, *fp_w = NULL;
++    size_t written;
++    CK_RV ret;
++
++    snprintf(dst_file, PATH_MAX, "%s/%s", dst, name);
++    snprintf(src_file, PATH_MAX, "%s/%s", src, name);
++
++    fp_r = fopen(src_file, "r");
++    if (!fp_r) {
++        warnx("fopen(%s) failed, errno=%s", src_file, strerror(errno));
++        ret = CKR_FUNCTION_FAILED;
++        goto done;
++    }
++
++    fp_w = fopen(dst_file, "w");
++    if (!fp_w) {
++        warnx("fopen(%s) failed, errno=%s", dst_file, strerror(errno));
++        ret = CKR_FUNCTION_FAILED;
++        goto done;
++    }
++    set_perm(fileno(fp_w));
++
++    while (!feof(fp_r)) {
++        size_t bytes = fread(buf, 1, sizeof(buf), fp_r);
++        if (bytes) { // can be zero, if file empty
++            written = fwrite(buf, 1, bytes, fp_w);
++            if (written != bytes) {
++                warnx("fwrite(%s) failed, errno=%s", dst_file,
++                      strerror(errno));
++                ret = CKR_FUNCTION_FAILED;
++                goto done;
++            }
++        }
++    }
++
++    ret = CKR_OK;
++
++done:
++
++    if (fp_r)
++        fclose(fp_r);
++    if (fp_w)
++        fclose(fp_w);
++
++    return ret;
++}
++
++/**
++ * Change the group owner of the given directory to 'pkcs11'.
++ */
++static CK_RV change_owner(char *dir)
++{
++    struct group* grp;
++    CK_RV ret;
++
++    /* Set group owner */
++    grp = getgrnam("pkcs11");
++    if (grp) {
++        if (chown(dir, -1, grp->gr_gid)) {
++            ret = CKR_FUNCTION_FAILED;
++            goto done;
++        }
++    } else {
++        ret = CKR_FUNCTION_FAILED;
++        goto done;
++    }
++
++    /* Fix group permissions (see man 2 mkdir for details) */
++    if (chmod(dir, 0770)) {
++        ret = CKR_FUNCTION_FAILED;
++        goto done;
++    }
++
++    ret = CKR_OK;
++
++done:
++
++    return ret;
++}
++
++/**
++ * Copy the given src folder to the given dst folder including all
++ * subdirectories and files.
++ */
++static CK_RV folder_copy(char *dst, const char *src)
++{
++    char d[PATH_MAX], s[PATH_MAX];
++    struct dirent *entry;
++    CK_RV ret;
++    DIR *dir;
++
++    /* Open src */
++    dir = opendir(src);
++    if (dir == NULL) {
++        TRACE_ERROR("Cannot open %s\n", src);
++        return CKR_FUNCTION_FAILED;
++    }
++
++    /* Create dst */
++    if (mkdir(dst, 0) != 0) {
++        TRACE_ERROR("Cannot create %s\n", dst);
++        ret = CKR_FUNCTION_FAILED;
++        goto done;
++    }
++
++    /* Change group owner and set permissions */
++    ret = change_owner(dst);
++    if (ret != CKR_OK) {
++        TRACE_ERROR("Cannot change owner and permissions for %s\n", dst);
++        ret = CKR_FUNCTION_FAILED;
++        goto done;
++    }
++
++    /* Copy folder recursively, skip the "." and ".." entries */
++    while ((entry = readdir(dir)) != NULL) {
++        if (entry->d_type == DT_DIR) {
++            if (strncmp(entry->d_name, ".", 1) != 0) {
++                snprintf(d, PATH_MAX, "%s/%s", dst, entry->d_name);
++                snprintf(s, PATH_MAX, "%s/%s", src, entry->d_name);
++                ret = folder_copy(d, s);
++                if (ret != CKR_OK)
++                    goto done;
++            }
++        } else {
++            ret = file_copy(dst, src, entry->d_name);
++            if (ret != CKR_OK)
++                goto done;
++        }
++    }
++
++    ret = CKR_OK;
++
++done:
++
++    closedir(dir);
++
++    return ret;
++}
++
++/**
++ * Remove the given folder and all of its contents.
++ */
++static CK_RV folder_delete(const char *folder)
++{
++    DIR *dir;
++    char fname[PATH_MAX];
++    size_t len, path_len;
++    struct stat statbuf;
++    struct dirent *ent;
++    CK_RV ret = CKR_OK;
++
++    dir = opendir(folder);
++    if (!dir) {
++        TRACE_INFO("Folder %s doesn't exist.\n", folder);
++        return CKR_OK;
++    }
++
++    path_len = strlen(folder);
++    while (!ret && (ent = readdir(dir))) {
++        if (!strcmp(ent->d_name, ".") || !strcmp(ent->d_name, ".."))
++            continue;
++        len = path_len + strlen(ent->d_name) + 2;
++        snprintf(fname, len, "%s/%s", folder, ent->d_name);
++        if (!stat(fname, &statbuf)) {
++            if (S_ISDIR(statbuf.st_mode)) {
++                ret = folder_delete(fname);
++                if (ret != CKR_OK)
++                    goto done;
++            } else {
++                ret = remove(fname);
++                if (ret != CKR_OK)
++                    goto done;
++            }
++        } else {
++            /* stat failed */
++            TRACE_ERROR("Cannot stat %s, errno=%s.\n", fname, strerror(errno));
++            ret = CKR_FUNCTION_FAILED;
++            goto done;
++        }
++    }
++
++    ret = CKR_OK;
++
++done:
++
++    closedir(dir);
++    if (ret == CKR_OK)
++        rmdir(folder);
++
++    return ret;
++}
++
++/**
++ * Backs up the given data_store to data_store_PKCSTOK_MIGRATE_TMP.
++ * All folders and files are recursively created and copied.
++ * Remove the backup if it already exists so that we always have
++ * a clean backup.
++ * The calling routine ensures that data_store does not end with a '/' !
++ */
++static CK_RV backup_repository(const char *data_store)
++{
++    char dst[PATH_MAX];
++    CK_RV ret = CKR_OK;
++
++    TRACE_INFO("Creating data store backup ...\n");
++
++    memset(dst, 0, PATH_MAX);
++    snprintf(dst, PATH_MAX, "%s_PKCSTOK_MIGRATE_TMP", data_store);
++    ret = folder_delete(dst);
++    if (ret != CKR_OK) {
++        warnx("Fatal error: cannot delete old backup: %s", dst);
++        return CKR_FUNCTION_FAILED;
++    }
++
++    return folder_copy(dst, data_store);
++}
++
++/**
++ * Checks if the pkcsslotd is running.
++ */
++static CK_BBOOL pkcsslotd_running(void)
++{
++    DIR *dir;
++    FILE *fp;
++    struct dirent* ent;
++    char* endptr;
++    char buf[PATH_MAX];
++    char fname[PATH_MAX];
++
++    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;
++    }
++
++    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;
++        }
++
++        /* 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;
++        }
++
++        /* 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;
++            }
++        }
++        fclose(fp);
++    }
++
++    closedir(dir);
++    return CK_FALSE;
++}
++
++/**
++ *
++ */
++static CK_BBOOL token_invalid(const char *dll)
++{
++    if (strcmp(dll, INVALID_TOKEN) == 0)
++        return CK_TRUE;
++    else
++        return CK_FALSE;
++}
++
++/**
++ * returns the token name related to the given stdll name for the
++ * 4 supported tokens.
++ */
++static const char *dll2name(const char *dll)
++{
++    static char *dlls[] = {
++        "libpkcs11_ica.so", "libpkcs11_cca.so",
++        "libpkcs11_sw.so", "libpkcs11_ep11.so"
++    };
++    static char *names[] = {
++        "ICA", "CCA", "Soft", "EP11"
++    };
++    int i, num_tokens = sizeof(names) / sizeof(char *);
++
++    for (i = 0; i < num_tokens; i++) {
++        if (strcmp(dll, dlls[i]) == 0)
++            return names[i];
++    }
++
++    return INVALID_TOKEN;
++}
++
++/**
++ * translates the given verbose level string into a numeric verbose level.
++ * Returns -1 if the string is invalid.
++ */
++static int verbose_str2level(char *str)
++{
++    const char *tlevel[] = {"none", "error", "warn", "info", "devel", "debug"};
++    const int num = sizeof(tlevel) / sizeof(char *);
++    int i;
++
++    for (i = 0; i < num; i++) {
++        if (strcmp(str, tlevel[i]) == 0) {
++            return i;
++        }
++    }
++
++    return -1;
++}
++
++static void usage(char *progname)
++{
++    printf(" Help:\t\t\t\t%s -h\n", progname);
++    printf(" -h, --help \t\t\tShow this help\n\n");
++    printf(" Options:\n");
++    printf(" -s, --slotid SLOTID\t\tPKCS slot number (required)\n");
++    printf(" -d, --datastore DATASTORE\ttoken datastore location (required)\n");
++    printf(" -c, --confdir CONFDIR\t\tlocation of opencryptoki.conf (required)\n");
++    printf(" -u, --userpin USERPIN\t\ttoken user pin (prompted if not specified)\n");
++    printf(" -p, --sopin SOPIN\t\ttoken SO pin (prompted if not specified)\n");
++    printf(" -v, --verbose LEVEL\t\tset verbose level (optional):\n");
++    printf("\t\t\t\tnone (default), error, warn, info, devel, debug\n");
++    return;
++}
++
++int main(int argc, char **argv)
++{
++    CK_RV ret = 0;
++    int opt = 0, vlevel = -1;
++    CK_SLOT_ID slot_id = 0;
++    CK_BBOOL slot_id_specified = CK_FALSE;
++    size_t sopinlen, userpinlen, buflen = 0;
++    ssize_t num_chars;
++    char *data_store = NULL, *data_store_old = NULL, *conf_dir = NULL;
++    char *sopin = NULL, *userpin = NULL, *verbose = NULL;
++    char *buff = NULL;
++    char dll_name[PATH_MAX];
++    CK_TOKEN_INFO_32 tokinfo;
++    CK_BBOOL new;
++
++    static const struct option long_opts[] = {
++        {"datastore", required_argument, NULL, 'd'},
++        {"confdir", required_argument, NULL, 'c'},
++        {"slotid", required_argument, NULL, 's'},
++        {"userpin", required_argument, NULL, 'u'},
++        {"sopin", required_argument, NULL, 'p'},
++        {"verbose", required_argument, NULL, 'v'},
++        {"help", no_argument, NULL, 'h'},
++        {0, 0, 0, 0}
++    };
++
++    while ((opt = getopt_long(argc, argv, "d:c:s:u:p:v:h", long_opts, NULL)) != -1) {
++        switch (opt) {
++        case 'd':
++            data_store = strdup(optarg);
++            break;
++        case 'c':
++            conf_dir = strdup(optarg);
++            break;
++        case 's':
++            slot_id = atoi(optarg);
++            slot_id_specified = CK_TRUE;
++            break;
++        case 'u':
++            userpin = strdup(optarg);
++            userpinlen = strlen(userpin);
++            break;
++        case 'p':
++            sopin = strdup(optarg);
++            sopinlen = strlen(sopin);
++            break;
++        case 'v':
++            verbose = strdup(optarg);
++            vlevel = verbose_str2level(verbose);
++            if (vlevel < 0) {
++                warnx("Invalid verbose level '%s' specified.", verbose);
++                usage(argv[0]);
++                exit(1);
++            }
++            break;
++        case 'h':
++            usage(argv[0]);
++            exit(0);
++        default:
++            warnx("pkcstok_migrate: Parameters are required.");
++            usage(argv[0]);
++            exit(1);
++        }
++    }
++
++    if (argc == 1) {
++        usage(argv[0]);
++        exit(1);
++    }
++
++    printf("\npkcstok_migrate:\n");
++    printf("Summary of input parameters:\n");
++    if (data_store) {
++        strip_trailing_chars(data_store, strlen(data_store), '/');
++        printf("  datastore = %s \n", data_store);
++    }
++    if (conf_dir) {
++        strip_trailing_chars(conf_dir, strlen(conf_dir), '/');
++        printf("  confdir = %s \n", conf_dir);
++    }
++    if (slot_id_specified)
++        printf("  slot ID = %ld\n", slot_id);
++    if (userpin)
++        printf("  user PIN specified\n");
++    if (sopin)
++        printf("  SO PIN specified\n");
++    if (vlevel >= 0) {
++        trace_level = vlevel;
++        printf("  verbose level = %s\n", verbose);
++    }
++    printf("\n");
++
++    /* Slot ID must be given */
++    if (!slot_id_specified) {
++        warnx("Slot ID must be specified.");
++        goto done;
++    }
++
++    /* Datastore must be given */
++    if (data_store == NULL) {
++        warnx("Data store path must be specified.");
++        goto done;
++    }
++
++    /* Limit datastore path length because of appended suffixes */
++    if (strlen(data_store) > PKCSTOK_MIGRATE_MAX_PATH_LEN) {
++        warnx("Datastore path (%ld characters) is too long (max = %d).\n",
++              strlen(data_store), PKCSTOK_MIGRATE_MAX_PATH_LEN);
++        ret = CKR_FUNCTION_FAILED;
++        goto done;
++    }
++
++    /* Location of opencryptoki.conf must be specified. */
++    if (conf_dir == NULL) {
++        warnx("Location of opencryptoki.conf must be specified.");
++        goto done;
++    }
++
++    /* Limit path to config file because of appended suffixes */
++    if (strlen(conf_dir) > PKCSTOK_MIGRATE_MAX_PATH_LEN) {
++        warnx("Path to config file (%ld characters) is too long (max = %d).\n",
++              strlen(conf_dir), PKCSTOK_MIGRATE_MAX_PATH_LEN);
++        ret = CKR_FUNCTION_FAILED;
++        goto done;
++    }
++
++    /* Check if given data_store exists */
++    if (!datastore_exists(data_store)) {
++        ret = CKR_FUNCTION_FAILED;
++        warnx("Datastore %s does not exist.", data_store);
++        goto done;
++    }
++
++    /* Check if given conf_dir exists and contains opencryptoki.conf */
++    if (!conffile_exists(conf_dir)) {
++        ret = CKR_FUNCTION_FAILED;
++        warnx("%s does not exist or does not contain opencryptoki.conf", conf_dir);
++        goto done;
++    }
++
++    /* Check if pkcsslotd is running */
++    if (pkcsslotd_running()) {
++        warnx("Please stop pkcsslotd before running this utility.");
++        ret = CKR_FUNCTION_FAILED;
++        goto done;
++    }
++
++    /* Identify token related to given slot ID */
++    ret = identify_token(slot_id, conf_dir, dll_name);
++    if (ret != CKR_OK) {
++        warnx("Cannot identify a token related to given slot ID %ld", slot_id);
++        goto done;
++    }
++
++    /* Check if DLL name from conf file is a known and migratable token */
++    printf("Slot ID %ld points to DLL name %s, which is a %s token.\n",
++           slot_id, dll_name, dll2name(dll_name));
++    if (token_invalid(dll2name(dll_name))) {
++        warnx("Please check your input.");
++        goto done;
++    }
++
++    /* Check if there are token objects to migrate */
++    if (datastore_empty(data_store)) {
++        ret = CKR_OK;
++        warnx("Datastore %s is empty, no objects to migrate.", data_store);
++        goto done;
++    }
++
++    /* Get token info from NVTOK.DAT */
++    ret = get_token_info(data_store, &tokinfo);
++    if (ret != CKR_OK) {
++        warnx("Cannot get the token label from NVTOK.DAT");
++        goto done;
++    }
++
++    /* Check with user if ok to migrate this token, or quit if token not migratable */
++    printf("Data store %s points to this token info:\n", data_store);
++    printf("  label           : %.*s\n", 32, tokinfo.label);
++    printf("  manufacturerID  : %.*s\n", 32, tokinfo.manufacturerID);
++    printf("  model           : %.*s\n", 16, tokinfo.model);
++    printf("  serialNumber    : %.*s\n", 16, tokinfo.serialNumber);
++    printf("  hardwareVersion : %i.%i\n", tokinfo.hardwareVersion.major, tokinfo.hardwareVersion.minor);
++    printf("  firmwareVersion : %i.%i\n", tokinfo.firmwareVersion.major, tokinfo.firmwareVersion.minor);
++    printf("Migrate this token with given slot ID? y/n\n");
++    num_chars = getline(&buff, &buflen, stdin);
++    if (num_chars < 0 || strncmp(buff, "y", 1) != 0) {
++        printf("ok, let's quit.\n");
++        goto done;
++    }
++
++    /* Get the SO pin to authorize migration */
++    if (!sopin) {
++        printf("Enter the SO PIN: ");
++        fflush(stdout);
++        ret = get_pin(&sopin, &sopinlen);
++        if (ret != 0) {
++            warnx("Could not get SO PIN.");
++            goto done;
++        }
++    }
++
++    /* Get the USER pin to authorize migration */
++    if (!userpin) {
++        printf("Enter the USER PIN: ");
++        fflush(stdout);
++        ret = get_pin(&userpin, &userpinlen);
++        if (ret != 0) {
++            warnx("Could not get USER PIN.");
++            goto done;
++        }
++    }
++
++    /* Verify the SO and USER PINs entered against NVTOK.DAT. */
++    ret = verify_pins(data_store, sopin, sopinlen, userpin, userpinlen);
++    if (ret) {
++        warnx("Could not verify pins.");
++        goto done;
++    }
++
++    /* Check if data store is already new */
++    ret = datastore_is_312(data_store, sopin, userpin, &new);
++    if (ret == 0 && new) {
++        printf("Data store %s is already in new format.\n", data_store);
++        ret = update_opencryptoki_conf(slot_id, conf_dir);
++        if (ret != CKR_OK)
++            warnx("Failed to update opencryptoki.conf, you must do this manually.");
++        goto done;
++    }
++
++    /* Backup repository if not already done */
++    ret = backup_repository(data_store);
++    if (ret != CKR_OK) {
++        warnx("Failed to create backup.");
++        goto done;
++    }
++
++    /* Perform all actions on the backup */
++    char data_store_new[PATH_MAX];
++    data_store_old = data_store;
++    snprintf(data_store_new, PATH_MAX, "%s_PKCSTOK_MIGRATE_TMP", data_store_old);
++
++    /* Create new temp token keys, which exist in parallel to the old ones
++     * until the migration is fully completed. */
++    ret = create_token_keys_312(data_store_new, sopin, userpin);
++    if (ret != CKR_OK) {
++        warnx("Failed to create new token keys.");
++        goto done;
++    }
++
++    /* Migrate repository */
++    ret = migrate_repository(data_store_new, sopin, userpin);
++    if (ret != CKR_OK) {
++        warnx("Failed to migrate repository.");
++        goto done;
++    }
++
++    /* Switch to new repository */
++    ret = switch_to_new_repository(data_store_old, data_store_new);
++    if (ret != CKR_OK) {
++        warnx("Switch to new repository failed.");
++        goto done;
++    }
++
++    /* Now insert new 'tokversion=3.12' parm in opencryptoki.conf */
++    ret = update_opencryptoki_conf(slot_id, conf_dir);
++    if (ret != CKR_OK) {
++        warnx("Failed to update opencryptoki.conf, you must do this manually.");
++        goto done;
++    }
++
++    printf("Pre-migration data backed up at '%s_BAK'\n", data_store_old);
++    printf("Config file backed up at '%s/opencryptoki.conf_BAK'\n", conf_dir);
++    printf("Remove these backups manually after testing the new repository.\n");
++
++    ret = CKR_OK;
++
++done:
++
++    free(buff);
++    free(sopin);
++    free(userpin);
++    free(data_store);
++    free(conf_dir);
++    free(verbose);
++
++    if (ret == CKR_OK) {
++        printf("pkcstok_migrate finished successfully.\n");
++        return EXIT_SUCCESS;
++    } else {
++        printf("pkcstok_migrate finished with warnings/errors.\n");
++        return EXIT_FAILURE;
++    }
++}
+diff --git a/usr/sbin/pkcstok_migrate/pkcstok_migrate.mk b/usr/sbin/pkcstok_migrate/pkcstok_migrate.mk
+new file mode 100644
+index 00000000..dc4582e5
+--- /dev/null
++++ b/usr/sbin/pkcstok_migrate/pkcstok_migrate.mk
+@@ -0,0 +1,22 @@
++sbin_PROGRAMS += usr/sbin/pkcstok_migrate/pkcstok_migrate
++noinst_HEADERS += misc/mech_types.h
++noinst_HEADERS += usr/lib/common/defs.h
++noinst_HEADERS += usr/lib/common/host_defs.h
++noinst_HEADERS += usr/include/local_types.h
++noinst_HEADERS += usr/lib/common/h_extern.h
++noinst_HEADERS += usr/lib/common/pkcs_utils.h
++
++usr_sbin_pkcstok_migrate_pkcstok_migrate_LDFLAGS = -lcrypto -ldl
++
++usr_sbin_pkcstok_migrate_pkcstok_migrate_CFLAGS  =		\
++	-DSTDLL_NAME=\"pkcstok_migrate\"			\
++	-I${srcdir}/usr/include 				\
++	-I${srcdir}/usr/lib/common				\
++	-I${srcdir}/usr/sbin/pkcstok_migrate
++
++usr_sbin_pkcstok_migrate_pkcstok_migrate_SOURCES =		\
++	usr/lib/common/p11util.c 			\
++	usr/lib/common/sw_crypt.c			\
++	usr/lib/common/trace.c 				\
++	usr/lib/common/pkcs_utils.c			\
++	usr/sbin/pkcstok_migrate/pkcstok_migrate.c
+diff --git a/usr/sbin/sbin.mk b/usr/sbin/sbin.mk
+index 640308f5..01e152df 100644
+--- a/usr/sbin/sbin.mk
++++ b/usr/sbin/sbin.mk
+@@ -13,6 +13,9 @@ endif
+ if ENABLE_P11SAK
+ include usr/sbin/p11sak/p11sak.mk
+ endif
++if ENABLE_PKCSTOK_MIGRATE
++include usr/sbin/pkcstok_migrate/pkcstok_migrate.mk
++endif
+ 
+ include usr/sbin/pkcsslotd/pkcsslotd.mk
+ include usr/sbin/pkcsconf/pkcsconf.mk
diff --git a/SOURCES/opencryptoki-3.14.0-crash-in-c_setpin.patch b/SOURCES/opencryptoki-3.14.0-crash-in-c_setpin.patch
new file mode 100644
index 0000000..0ea8ccd
--- /dev/null
+++ b/SOURCES/opencryptoki-3.14.0-crash-in-c_setpin.patch
@@ -0,0 +1,63 @@
+diff -up opencryptoki-3.14.0/usr/lib/tpm_stdll/tpm_openssl.c.me opencryptoki-3.14.0/usr/lib/tpm_stdll/tpm_openssl.c
+--- opencryptoki-3.14.0/usr/lib/tpm_stdll/tpm_openssl.c.me	2020-05-26 08:51:32.714189399 -0400
++++ opencryptoki-3.14.0/usr/lib/tpm_stdll/tpm_openssl.c	2020-05-26 08:52:16.429412060 -0400
+@@ -57,7 +57,7 @@ void openssl_print_errors()
+ }
+ #endif
+ 
+-RSA *openssl_gen_key()
++RSA *openssl_gen_key(STDLL_TokData_t *tokdata)
+ {
+     RSA *rsa;
+     int rc, counter = 0;
+@@ -66,7 +66,7 @@ RSA *openssl_gen_key()
+     BIGNUM *bne;
+ #endif
+ 
+-    token_specific_rng(NULL, (CK_BYTE *) buf, 32);
++    token_specific_rng(tokdata, (CK_BYTE *) buf, 32);
+     RAND_seed(buf, 32);
+ 
+ regen_rsa_key:
+diff -up opencryptoki-3.14.0/usr/lib/tpm_stdll/tpm_specific.c.me opencryptoki-3.14.0/usr/lib/tpm_stdll/tpm_specific.c
+--- opencryptoki-3.14.0/usr/lib/tpm_stdll/tpm_specific.c.me	2020-05-26 08:52:26.351235628 -0400
++++ opencryptoki-3.14.0/usr/lib/tpm_stdll/tpm_specific.c	2020-05-26 08:53:15.928354051 -0400
+@@ -159,8 +159,6 @@ CK_RV token_specific_rng(STDLL_TokData_t
+     TSS_HTPM hTPM;
+     BYTE *random_bytes = NULL;
+ 
+-    UNUSED(tokdata);
+-
+     rc = Tspi_Context_GetTpmObject(tpm_data->tspContext, &hTPM);
+     if (rc) {
+         TRACE_ERROR("Tspi_Context_GetTpmObject: %x\n", rc);
+@@ -1389,7 +1387,7 @@ CK_RV token_create_private_tree(STDLL_To
+     unsigned char n[256], p[256];
+ 
+     /* all sw generated keys are 2048 bits */
+-    if ((rsa = openssl_gen_key()) == NULL)
++    if ((rsa = openssl_gen_key(tokdata)) == NULL)
+         return CKR_HOST_MEMORY;
+ 
+     if (openssl_get_modulus_and_prime(rsa, &size_n, n, &size_p, p) != 0) {
+@@ -1467,7 +1465,7 @@ CK_RV token_create_public_tree(STDLL_Tok
+     unsigned char n[256], p[256];
+ 
+     /* all sw generated keys are 2048 bits */
+-    if ((rsa = openssl_gen_key()) == NULL)
++    if ((rsa = openssl_gen_key(tokdata)) == NULL)
+         return CKR_HOST_MEMORY;
+ 
+     if (openssl_get_modulus_and_prime(rsa, &size_n, n, &size_p, p) != 0) {
+diff -up opencryptoki-3.14.0/usr/lib/tpm_stdll/tpm_specific.h.me opencryptoki-3.14.0/usr/lib/tpm_stdll/tpm_specific.h
+--- opencryptoki-3.14.0/usr/lib/tpm_stdll/tpm_specific.h.me	2020-05-26 08:53:20.281276648 -0400
++++ opencryptoki-3.14.0/usr/lib/tpm_stdll/tpm_specific.h	2020-05-26 08:54:08.356421779 -0400
+@@ -56,7 +56,7 @@
+ /* retry count for generating software RSA keys */
+ #define KEYGEN_RETRY    5
+ 
+-RSA *openssl_gen_key();
++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 *,
diff --git a/SOURCES/opencryptoki-3.14.0-missing-p11sak-tool-a94436937b6364c53219fb3c7922439f403e8d5e.patch b/SOURCES/opencryptoki-3.14.0-missing-p11sak-tool-a94436937b6364c53219fb3c7922439f403e8d5e.patch
new file mode 100644
index 0000000..5c7bddf
--- /dev/null
+++ b/SOURCES/opencryptoki-3.14.0-missing-p11sak-tool-a94436937b6364c53219fb3c7922439f403e8d5e.patch
@@ -0,0 +1,22 @@
+commit a94436937b6364c53219fb3c7922439f403e8d5e
+Author: Harald Freudenberger <freude@linux.ibm.com>
+Date:   Wed May 27 07:30:33 2020 +0200
+
+    Fix missing entries for p11sak tool in template spec file
+    
+    Signed-off-by: Harald Freudenberger <freude@linux.ibm.com>
+
+diff --git a/rpm/opencryptoki.spec b/rpm/opencryptoki.spec
+index fa4b9899..ae563406 100644
+--- a/rpm/opencryptoki.spec
++++ b/rpm/opencryptoki.spec
+@@ -238,7 +238,9 @@ exit 0
+ %{_unitdir}/pkcsslotd.service
+ %{_sbindir}/pkcsconf
+ %{_sbindir}/pkcsslotd
++%{_sbindir}/p11sak
+ %{_mandir}/man1/pkcsconf.1*
++%{_mandir}/man1/p11sak.1*
+ %{_mandir}/man5/%{name}.conf.5*
+ %{_mandir}/man7/%{name}.7*
+ %{_mandir}/man8/pkcsslotd.8*
diff --git a/SOURCES/opencryptoki-50a8a8806059647a3e446fd129995af61ec54867.patch b/SOURCES/opencryptoki-50a8a8806059647a3e446fd129995af61ec54867.patch
deleted file mode 100644
index 3032fb6..0000000
--- a/SOURCES/opencryptoki-50a8a8806059647a3e446fd129995af61ec54867.patch
+++ /dev/null
@@ -1,33 +0,0 @@
-commit 50a8a8806059647a3e446fd129995af61ec54867
-Author: Ingo Franzki <ifranzki@linux.ibm.com>
-Date:   Tue Dec 3 14:58:26 2019 +0100
-
-    EP11: Fix EC-uncompress buffer length
-    
-    Function ec_uncompress_public_key() expects the size of the output
-    buffer in out_pubkey to be specified in the out_len parameter.
-    However, variable pubkey_len is uninitialized when calling
-    ec_uncompress_public_key(), so this may result in CKR_BUFFER_TOO_SMALL
-    dependent on the value of pubkey_len.
-    Fix this by setting pubkey_len to the size of the public key buffer
-    allocated above.
-    
-    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 38b6708f..10dfe4e0 100644
---- a/usr/lib/ep11_stdll/ep11_specific.c
-+++ b/usr/lib/ep11_stdll/ep11_specific.c
-@@ -2034,9 +2034,10 @@ static CK_RV import_EC_key(STDLL_TokData_t * tokdata, SESSION * sess,
-         rc = get_ecsiglen(ec_key_obj, &privkey_len);
-         if (rc != CKR_OK)
-             goto import_EC_key_end;
--        privkey_len /= 2; /* Public key is half the size of an EC signature */
-+        privkey_len /= 2; /* private key is half the size of an EC signature */
- 
--        pubkey = (CK_BYTE *)malloc(1 + 2 * privkey_len);
-+        pubkey_len = 1 + 2 * privkey_len;
-+        pubkey = (CK_BYTE *)malloc(pubkey_len);
-         if (pubkey == NULL) {
-             rc = CKR_HOST_MEMORY;
-             goto import_EC_key_end;
diff --git a/SPECS/opencryptoki.spec b/SPECS/opencryptoki.spec
index e739bb5..9b65dfe 100644
--- a/SPECS/opencryptoki.spec
+++ b/SPECS/opencryptoki.spec
@@ -1,7 +1,7 @@
 Name:			opencryptoki
 Summary:		Implementation of the PKCS#11 (Cryptoki) specification v2.11
-Version:		3.12.1
-Release:		2%{?dist}
+Version:		3.14.0
+Release:		3%{?dist}
 License:		CPL
 Group:			System Environment/Base
 URL:			https://github.com/opencryptoki/opencryptoki
@@ -10,7 +10,12 @@ Source0:		https://github.com/opencryptoki/%{name}/archive/v%{version}/%{name}-%{
 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
-Patch2:			opencryptoki-50a8a8806059647a3e446fd129995af61ec54867.patch
+# bz#1780293, fix regression, segfault in C_SetPin
+Patch2:			opencryptoki-3.14.0-crash-in-c_setpin.patch
+# Fix missing entries for p11sak tool in template spec file
+Patch3:			opencryptoki-3.14.0-missing-p11sak-tool-a94436937b6364c53219fb3c7922439f403e8d5e.patch
+# bz#1780294, PIN conversion tool
+Patch4:			opencryptoki-3.14.0-cd40f4b7cb1b502ca754b9bfb307d934285709a9-PIN-conversion-tool.patch
 Requires(pre):		coreutils
 BuildRequires:		gcc
 BuildRequires:		openssl-devel
@@ -244,8 +249,12 @@ fi
 %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*
@@ -327,6 +336,19 @@ fi
 
 
 %changelog
+* Mon Jun 15 2020 Than Ngo <than@redhat.com> - 3.14.0-3
+- Resolves: #1780294, PIN conversion tool
+
+* Tue May 26 2020 Than Ngo <than@redhat.com> - 3.14.0-2
+- Related: #1780293, fix regression, segfault in C_SetPin
+
+* Tue May 19 2020 Than Ngo <than@redhat.com> - 3.14.0-1
+- Resolves: #1723863 - ep11 token: Enhanced Support
+- Resolves: #1780285 - ep11 token: Support for new IBM Z hardware z15
+- Resolves: #1780293 - rebase to 3.14.0
+- Resolves: #1800549 - key management tool: list keys function
+ -Resolves: #1800555 - key management tool: random key generation function
+
 * Fri Dec 13 2019 Than Ngo <than@redhat.com> - 3.12.1-2
 - Resolves: #1782445, EP11: Fix EC-uncompress buffer length