/*
* COPYRIGHT (c) International Business Machines Corp. 2014-2017
*
* 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
*/
/*
* pkcscca - A tool for PKCS#11 CCA token.
* Currently, only migrates CCA private token objects from CCA cipher
* to using a software cipher.
*
*/
#define _GNU_SOURCE
#include <dlfcn.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 <termios.h>
#include <unistd.h>
#include <unistd.h>
#include <pkcs11types.h>
#include "sw_crypt.h"
#include "pkcscca.h"
int v_flag = 0;
void *p11_lib = NULL;
void (*CSNDKTC) ();
void (*CSNBKTC) ();
void (*CSNBKTC2) ();
void (*CSNBDEC) ();
void *lib_csulcca;
static struct algo aes = {(CK_BYTE *)"RTCMK AES ", (CK_BYTE *)"AES", 2 };
static struct algo des = {(CK_BYTE *)"RTCMK ", (CK_BYTE *)"DES", 1 };
static struct algo hmac = {(CK_BYTE *)"RTCMK HMAC ", (CK_BYTE *)"HMAC", 2 };
static struct algo ecc = {(CK_BYTE *)"RTCMK ECC ", (CK_BYTE *)"ECC", 2 };
static struct algo rsa = {(CK_BYTE *)"RTCMK ", (CK_BYTE *)"RSA", 1 };
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:
EVP_MD_CTX_destroy(md_ctx);
return -1;
break;
}
if (rc != 1) {
fprintf(stderr, "EVP_DigestInit() failed: rc = %d\n", rc);
return -1;
}
rc = EVP_DigestUpdate(md_ctx, buf, buf_size);
if (rc != 1) {
fprintf(stderr, "EVP_DigestUpdate() failed: rc = %d\n", rc);
return -1;
}
result_size = EVP_MD_CTX_size(md_ctx);
rc = EVP_DigestFinal(md_ctx, (unsigned char *) digest, &result_size);
if (rc != 1) {
fprintf(stderr, "EVP_DigestFinal() failed: rc = %d\n", rc);
return -1;
}
EVP_MD_CTX_destroy(md_ctx);
return 0;
}
int cca_decrypt(unsigned char *in_data, unsigned long in_data_len,
unsigned char *out_data, unsigned long *out_data_len,
unsigned char *init_v, unsigned char *key_value)
{
long return_code, reason_code, rule_array_count, length;
unsigned char chaining_vector[18];
unsigned char rule_array[256];
length = in_data_len;
rule_array_count = 1;
memcpy(rule_array, "CBC ", 8);
CSNBDEC(&return_code, &reason_code, NULL, NULL, key_value,
&length, in_data, init_v, &rule_array_count,
rule_array, chaining_vector, out_data);
if (return_code != 0) {
fprintf(stderr,
"CSNBDEC (DES3 DECRYPT) failed: "
"return_code=%ld reason_code=%ld\n",
return_code, reason_code);
return -1;
}
*out_data_len = length;
return 0;
}
// Function: dlist_remove_node()
//
// Attempts to remove the specified node from the list. The caller is
// responsible for freeing the data associated with the node prior to
// calling this routine
//
DL_NODE *dlist_remove_node(DL_NODE *list, DL_NODE *node)
{
DL_NODE *temp = list;
if (!list || !node)
return NULL;
// special case: removing head of the list
//
if (list == node) {
temp = list->next;
if (temp)
temp->prev = NULL;
free(list);
return temp;
}
// we have no guarantee that the node is in the list
// so search through the list to find it
//
while ((temp != NULL) && (temp->next != node))
temp = temp->next;
if (temp != NULL) {
DL_NODE *next = node->next;
temp->next = next;
if (next)
next->prev = temp;
free(node);
}
return list;
}
// Function: dlist_add_as_first()
//
// Adds the specified node to the start of the list
//
// Returns: pointer to the start of the list
//
DL_NODE *dlist_add_as_first(DL_NODE *list, void *data)
{
DL_NODE *node = NULL;
if (!data)
return list;
node = (DL_NODE *) malloc(sizeof(DL_NODE));
if (!node)
return NULL;
node->data = data;
node->prev = NULL;
node->next = list;
if (list)
list->prev = node;
return node;
}
CK_ULONG dlist_length(DL_NODE *list)
{
DL_NODE *temp = list;
CK_ULONG len = 0;
while (temp) {
len++;
temp = temp->next;
}
return len;
}
/* template_free() */
CK_RV template_free(TEMPLATE *tmpl)
{
if (!tmpl)
return CKR_OK;
while (tmpl->attribute_list) {
CK_ATTRIBUTE *attr = (CK_ATTRIBUTE *) tmpl->attribute_list->data;
if (attr)
free(attr);
tmpl->attribute_list = dlist_remove_node(tmpl->attribute_list,
tmpl->attribute_list);
}
free(tmpl);
return CKR_OK;
}
/* template_update_attribute()
*
* modifies an existing attribute or adds a new attribute to the template
*
* Returns: TRUE on success, FALSE on failure
*/
CK_RV template_update_attribute(TEMPLATE *tmpl, CK_ATTRIBUTE *new_attr)
{
DL_NODE *node = NULL;
CK_ATTRIBUTE *attr = NULL;
if (!tmpl || !new_attr) {
fprintf(stderr, "Invalid function arguments.\n");
return CKR_FUNCTION_FAILED;
}
node = tmpl->attribute_list;
/* if the attribute already exists in the list, remove it.
* this algorithm will limit an attribute to appearing at most
* once in the list
*/
while (node != NULL) {
attr = (CK_ATTRIBUTE *) node->data;
if (new_attr->type == attr->type) {
free(attr);
tmpl->attribute_list =
dlist_remove_node(tmpl->attribute_list, node);
break;
}
node = node->next;
}
/* add the new attribute */
tmpl->attribute_list = dlist_add_as_first(tmpl->attribute_list, new_attr);
return CKR_OK;
}
/* Modified version of template_unflatten that checks
* that buf isn't overread. buf_size=-1 turns off checking
* (for backwards compatability)
*/
CK_RV template_unflatten_withSize(TEMPLATE **new_tmpl, CK_BYTE *buf,
CK_ULONG count, int buf_size)
{
TEMPLATE *tmpl = NULL;
CK_ATTRIBUTE *a2 = NULL;
CK_BYTE *ptr = NULL;
CK_ULONG i, len;
CK_RV rc;
CK_ULONG_32 long_len = sizeof(CK_ULONG);
CK_ULONG_32 attr_ulong_32;
CK_ULONG attr_ulong;
CK_ATTRIBUTE *a1_64 = NULL;
CK_ATTRIBUTE_32 *a1 = NULL;
if (!new_tmpl || !buf) {
fprintf(stderr, "Invalid function arguments.\n");
return CKR_FUNCTION_FAILED;
}
tmpl = (TEMPLATE *) malloc(sizeof(TEMPLATE));
if (!tmpl) {
fprintf(stderr, "Failed to allocate template\n");
return CKR_HOST_MEMORY;
}
memset(tmpl, 0x0, sizeof(TEMPLATE));
ptr = buf;
for (i = 0; i < count; i++) {
if (buf_size >= 0 &&
((ptr + sizeof(CK_ATTRIBUTE)) > (buf + buf_size))) {
template_free(tmpl);
return CKR_FUNCTION_FAILED;
}
if (long_len == 4) {
a1_64 = (CK_ATTRIBUTE *) ptr;
len = sizeof(CK_ATTRIBUTE) + a1_64->ulValueLen;
a2 = (CK_ATTRIBUTE *) malloc(len);
if (!a2) {
template_free(tmpl);
fprintf(stderr, "Failed to allocate attribute\n");
return CKR_HOST_MEMORY;
}
/* if a buffer size is given, make sure it
* doesn't get overrun
*/
if (buf_size >= 0 &&
(((unsigned char *) a1_64 + len)
> ((unsigned char *) buf + buf_size))) {
free(a2);
template_free(tmpl);
return CKR_FUNCTION_FAILED;
}
memcpy(a2, a1_64, len);
} else {
a1 = (CK_ATTRIBUTE_32 *) ptr;
if ((a1->type == CKA_CLASS || a1->type == CKA_KEY_TYPE
|| a1->type == CKA_MODULUS_BITS
|| a1->type == CKA_VALUE_BITS
|| a1->type == CKA_CERTIFICATE_TYPE
|| a1->type == CKA_VALUE_LEN)
&& a1->ulValueLen != 0) {
len = sizeof(CK_ATTRIBUTE) + sizeof(CK_ULONG);
} else {
len = sizeof(CK_ATTRIBUTE) + a1->ulValueLen;
}
a2 = (CK_ATTRIBUTE *) malloc(len);
if (!a2) {
template_free(tmpl);
fprintf(stderr, "Failed to allocate attribute\n");
return CKR_HOST_MEMORY;
}
a2->type = a1->type;
if ((a1->type == CKA_CLASS || a1->type == CKA_KEY_TYPE
|| a1->type == CKA_MODULUS_BITS
|| a1->type == CKA_VALUE_BITS
|| a1->type == CKA_CERTIFICATE_TYPE
|| a1->type == CKA_VALUE_LEN)
&& a1->ulValueLen != 0) {
a2->ulValueLen = sizeof(CK_ULONG);
{
CK_ULONG_32 *p32;
CK_BYTE *pb2;
pb2 = (CK_BYTE *) a1;
pb2 += sizeof(CK_ATTRIBUTE_32);
p32 = (CK_ULONG_32 *) pb2;
attr_ulong_32 = *p32;
}
attr_ulong = attr_ulong_32;
{
CK_BYTE *pb2;
pb2 = (CK_BYTE *) a2;
pb2 += sizeof(CK_ATTRIBUTE);
memcpy(pb2, (CK_BYTE *) & attr_ulong, sizeof(CK_ULONG));
}
} else {
CK_BYTE *pb2, *pb;
a2->ulValueLen = a1->ulValueLen;
pb2 = (CK_BYTE *) a2;
pb2 += sizeof(CK_ATTRIBUTE);
pb = (CK_BYTE *) a1;
pb += sizeof(CK_ATTRIBUTE_32);
/* if a buffer size is given, make sure it
* doesn't get overrun
*/
if (buf_size >= 0 && (pb + a1->ulValueLen) > (buf + buf_size)) {
free(a2);
template_free(tmpl);
return CKR_FUNCTION_FAILED;
}
memcpy(pb2, pb, a1->ulValueLen);
}
}
if (a2->ulValueLen != 0)
a2->pValue = (CK_BYTE *) a2 + sizeof(CK_ATTRIBUTE);
else
a2->pValue = NULL;
rc = template_update_attribute(tmpl, a2);
if (rc != CKR_OK) {
free(a2);
template_free(tmpl);
return rc;
}
if (long_len == 4)
ptr += len;
else
ptr += sizeof(CK_ATTRIBUTE_32) + a1->ulValueLen;
}
*new_tmpl = tmpl;
return CKR_OK;
}
/* template_flatten()
* this still gets used when saving token objects to disk
*/
CK_RV template_flatten(TEMPLATE *tmpl, CK_BYTE *dest)
{
DL_NODE *node = NULL;
CK_BYTE *ptr = NULL;
CK_ULONG_32 long_len;
CK_ATTRIBUTE_32 *attr_32 = NULL;
CK_ULONG Val;
CK_ULONG_32 Val_32;
CK_ULONG *pVal;
long_len = sizeof(CK_ULONG);
if (!tmpl || !dest) {
fprintf(stderr, "Invalid function arguments.\n");
return CKR_FUNCTION_FAILED;
}
ptr = dest;
node = tmpl->attribute_list;
while (node) {
CK_ATTRIBUTE *attr = (CK_ATTRIBUTE *) node->data;
if (long_len == 4) {
memcpy(ptr, attr, sizeof(CK_ATTRIBUTE) + attr->ulValueLen);
ptr += sizeof(CK_ATTRIBUTE) + attr->ulValueLen;
} else {
attr_32 = malloc(sizeof(CK_ATTRIBUTE_32));
if (!attr_32) {
fprintf(stderr, "Failed to allocate attribute\n");
return CKR_HOST_MEMORY;
}
attr_32->type = attr->type;
attr_32->pValue = 0x00;
if ((attr->type == CKA_CLASS ||
attr->type == CKA_KEY_TYPE ||
attr->type == CKA_MODULUS_BITS ||
attr->type == CKA_VALUE_BITS ||
attr->type == CKA_CERTIFICATE_TYPE ||
attr->type == CKA_VALUE_LEN) && attr->ulValueLen != 0) {
attr_32->ulValueLen = sizeof(CK_ULONG_32);
memcpy(ptr, attr_32, sizeof(CK_ATTRIBUTE_32));
ptr += sizeof(CK_ATTRIBUTE_32);
pVal = (CK_ULONG *) attr->pValue;
Val = *pVal;
Val_32 = (CK_ULONG_32) Val;
memcpy(ptr, &Val_32, sizeof(CK_ULONG_32));
ptr += sizeof(CK_ULONG_32);
} else {
attr_32->ulValueLen = attr->ulValueLen;
memcpy(ptr, attr_32, sizeof(CK_ATTRIBUTE_32));
ptr += sizeof(CK_ATTRIBUTE_32);
if (attr->ulValueLen != 0) {
memcpy(ptr, attr->pValue, attr->ulValueLen);
ptr += attr->ulValueLen;
}
}
free(attr_32);
}
node = node->next;
}
return CKR_OK;
}
CK_ULONG template_get_count(TEMPLATE *tmpl)
{
if (tmpl == NULL)
return 0;
return dlist_length(tmpl->attribute_list);
}
CK_ULONG template_get_compressed_size(TEMPLATE *tmpl)
{
DL_NODE *node;
CK_ULONG size = 0;
if (tmpl == NULL)
return 0;
node = tmpl->attribute_list;
while (node) {
CK_ATTRIBUTE *attr = (CK_ATTRIBUTE *) node->data;
size += sizeof(CK_ATTRIBUTE_32);
if ((attr->type == CKA_CLASS || attr->type == CKA_KEY_TYPE
|| attr->type == CKA_MODULUS_BITS
|| attr->type == CKA_VALUE_BITS
|| attr->type == CKA_CERTIFICATE_TYPE
|| attr->type == CKA_VALUE_LEN)
&& attr->ulValueLen != 0) {
size += sizeof(CK_ULONG_32);
} else {
size += attr->ulValueLen;
}
node = node->next;
}
return size;
}
/* template_get_class */
CK_BBOOL template_get_class(TEMPLATE *tmpl, CK_ULONG *class,
CK_ULONG *subclass)
{
DL_NODE *node;
CK_BBOOL found = FALSE;
if (!tmpl || !class || !subclass)
return FALSE;
node = tmpl->attribute_list;
/* have to iterate through all attributes. no early exits */
while (node) {
CK_ATTRIBUTE *attr = (CK_ATTRIBUTE *) node->data;
if (attr->type == CKA_CLASS) {
*class = *(CK_OBJECT_CLASS *) attr->pValue;
found = TRUE;
}
/* underneath, these guys are both CK_ULONG so we
* could combine this
*/
if (attr->type == CKA_CERTIFICATE_TYPE)
*subclass = *(CK_CERTIFICATE_TYPE *) attr->pValue;
if (attr->type == CKA_KEY_TYPE)
*subclass = *(CK_KEY_TYPE *) attr->pValue;
node = node->next;
}
return found;
}
/* template_attribute_find()
*
* find the attribute in the list and return its value
*/
CK_BBOOL template_attribute_find(TEMPLATE *tmpl, CK_ATTRIBUTE_TYPE type,
CK_ATTRIBUTE **attr)
{
DL_NODE *node = NULL;
CK_ATTRIBUTE *a = NULL;
if (!tmpl || !attr)
return FALSE;
node = tmpl->attribute_list;
while (node != NULL) {
a = (CK_ATTRIBUTE *) node->data;
if (type == a->type) {
*attr = a;
return TRUE;
}
node = node->next;
}
*attr = NULL;
return FALSE;
}
CK_RV build_attribute(CK_ATTRIBUTE_TYPE type,
CK_BYTE *data, CK_ULONG data_len, CK_ATTRIBUTE **attrib)
{
CK_ATTRIBUTE *attr = NULL;
attr = (CK_ATTRIBUTE *) malloc(sizeof(CK_ATTRIBUTE) + data_len);
if (!attr) {
fprintf(stderr, "Failed to allocate attribute\n");
return CKR_HOST_MEMORY;
}
attr->type = type;
attr->ulValueLen = data_len;
if (data_len > 0) {
attr->pValue = (CK_BYTE *) attr + sizeof(CK_ATTRIBUTE);
memcpy(attr->pValue, data, data_len);
} else {
attr->pValue = NULL;
}
*attrib = attr;
return CKR_OK;
}
// object_free()
//
// does what it says...
//
void object_free(OBJECT * obj)
{
/* refactorization here to do actual free - fix from coverity scan */
if (obj) {
if (obj->template)
template_free(obj->template);
free(obj);
}
}
//Modified object_restore to prevent buffer overflow
//If data_size=-1, won't do bounds checking
CK_RV object_restore_withSize(CK_BYTE * data, OBJECT ** new_obj,
CK_BBOOL replace, int data_size)
{
TEMPLATE *tmpl = NULL;
OBJECT *obj = NULL;
CK_ULONG offset = 0;
CK_ULONG_32 count = 0;
CK_RV rc;
CK_OBJECT_CLASS_32 class32;
if (!data || !new_obj) {
fprintf(stderr, "Invalid function arguments.\n");
return CKR_FUNCTION_FAILED;
}
obj = (OBJECT *) malloc(sizeof(OBJECT));
if (!obj) {
fprintf(stderr, "Failed to allocate object\n");
rc = CKR_HOST_MEMORY;
goto error;
}
memset(obj, 0x0, sizeof(OBJECT));
memcpy( &class32, data + offset, sizeof(CK_OBJECT_CLASS_32) );
obj->class = class32;
offset += sizeof(CK_OBJECT_CLASS_32);
memcpy(&count, data + offset, sizeof(CK_ULONG_32));
offset += sizeof(CK_ULONG_32);
memcpy(&obj->name, data + offset, 8);
offset += 8;
rc = template_unflatten_withSize(&tmpl, data + offset, count, data_size);
if (rc != CKR_OK) {
fprintf(stderr, "template_unflatten_withSize failed rc=%lx.\n", rc);
goto error;
}
obj->template = tmpl;
if (replace == FALSE) {
*new_obj = obj;
} else {
template_free((*new_obj)->template);
memcpy(*new_obj, obj, sizeof(OBJECT));
free(obj); // don't want to do object_free() here!
}
return CKR_OK;
error:
if (obj)
object_free(obj);
if (tmpl)
template_free(tmpl);
return rc;
}
// object_flatten() - this is still used when saving token objects
//
CK_RV object_flatten(OBJECT * obj, CK_BYTE ** data, CK_ULONG * len)
{
CK_BYTE *buf = NULL;
CK_ULONG tmpl_len, total_len;
CK_ULONG offset;
CK_ULONG_32 count;
CK_OBJECT_CLASS_32 class32;
long rc;
if (!obj) {
fprintf(stderr, "Invalid function arguments.\n");
return CKR_FUNCTION_FAILED;
}
count = template_get_count(obj->template);
tmpl_len = template_get_compressed_size(obj->template);
total_len = tmpl_len + sizeof(CK_OBJECT_CLASS_32) + sizeof(CK_ULONG_32) + 8;
buf = (CK_BYTE *) malloc(total_len);
if (!buf) { // SAB XXX FIXME This was DATA
fprintf(stderr, "Failed to allocate buffer\n");
return CKR_HOST_MEMORY;
}
memset((CK_BYTE *) buf, 0x0, total_len);
offset = 0;
class32 = obj->class;
memcpy( buf + offset, &class32, sizeof(CK_OBJECT_CLASS_32) );
offset += sizeof(CK_OBJECT_CLASS_32);
memcpy(buf + offset, &count, sizeof(CK_ULONG_32));
offset += sizeof(CK_ULONG_32);
memcpy(buf + offset, &obj->name, sizeof(CK_BYTE) * 8);
offset += 8;
rc = template_flatten(obj->template, buf + offset);
if (rc != CKR_OK) {
free(buf);
return rc;
}
*data = buf;
*len = total_len;
return CKR_OK;
}
CK_RV add_pkcs_padding(CK_BYTE *ptr,
CK_ULONG block_size, CK_ULONG data_len,
CK_ULONG total_len)
{
CK_ULONG i, pad_len;
CK_BYTE pad_value;
pad_len = block_size - (data_len % block_size);
pad_value = (CK_BYTE) pad_len;
if (data_len + pad_len > total_len) {
fprintf(stderr, "The total length is too small to add padding.\n");
return CKR_FUNCTION_FAILED;
}
for (i = 0; i < pad_len; i++)
ptr[i] = pad_value;
return CKR_OK;
}
#define CKR_IBM_NOT_TOUCHED -1
int adjust_secret_key_attributes(OBJECT *obj, CK_ULONG key_type)
{
CK_RV rc;
CK_ATTRIBUTE *attr = NULL;
CK_ATTRIBUTE *value_attr = NULL;
CK_ATTRIBUTE *ibm_opaque_attr = NULL;
CK_ULONG key_size;
struct secaeskeytoken *aes_token;
CK_BYTE *zero = NULL;
if (key_type != CKK_AES) {
/* DES/3DES keys are already contained in CKA_IBM_OPAQUE */
return CKR_IBM_NOT_TOUCHED;
}
/* Don't touch if object already has an IBM_OPAQUE attribute */
if (template_attribute_find(obj->template, CKA_IBM_OPAQUE, &attr))
return CKR_IBM_NOT_TOUCHED;
if (!template_attribute_find(obj->template, CKA_VALUE, &value_attr)) {
fprintf(stderr, "No CKA_VALUE attribute found\n");
return CKR_TEMPLATE_INCOMPLETE;
}
aes_token = (struct secaeskeytoken *)value_attr->pValue;
if (value_attr->ulValueLen != sizeof(struct secaeskeytoken) ||
aes_token->type != 0x01 ||
aes_token->version != 0x04) {
fprintf(stderr, "CKA_VALUE does not contain a CCA secure key\n");
return CKR_IBM_NOT_TOUCHED;
}
/* Move CKA_VALUE to CKA_IBM_OPAQUE */
rc = build_attribute(CKA_IBM_OPAQUE, value_attr->pValue,
value_attr->ulValueLen, &ibm_opaque_attr);
if (rc != CKR_OK)
goto cleanup;
rc = template_update_attribute(obj->template, ibm_opaque_attr);
if (rc != CKR_OK)
goto cleanup;
/* Provide dummy CKA_VAUE attribute in (clear) key size */
key_size = aes_token->bitsize / 8;
zero = (CK_BYTE *)calloc(key_size, 1);
if (zero == NULL) {
fprintf(stderr, "Failed to allocate zero value\n");
rc = CKR_HOST_MEMORY;
goto cleanup;
}
rc = build_attribute(CKA_VALUE, zero, key_size, &value_attr);
if (rc != CKR_OK)
goto cleanup;
rc = template_update_attribute(obj->template, value_attr);
if (rc != CKR_OK)
goto cleanup;
free(zero);
return CKR_OK;
cleanup:
if (ibm_opaque_attr)
free(ibm_opaque_attr);
if (zero)
free(zero);
return rc;
}
/*
* OCK version 2.x create AES key objects with the CCA secure key stored
* in CKA_VALUE. OCK 3.x requires the secure in CKA_IBM_OPAQUE instead.
* Note: Other key types, such as DES/3DES keys as well as symmetric
* keys (RSA, EC, etc) already store the key in CKA_IBM_OPAQUE in OCK 2.x
*
* This function moves the CCA AES key from CKA_VALUE to CKA_IBM_OPAQUE
* and supplies a dummy (all zero) key in CKA_VALUE.
*/
int adjust_key_object_attributes(unsigned char *data, unsigned long data_len,
unsigned char **new_data,
unsigned long *new_data_len)
{
int rc;
OBJECT *obj = NULL;
CK_ULONG class, subclass = 0;
*new_data = NULL;
*new_data_len = 0;
/* Now unflatten the OBJ */
rc = object_restore_withSize(data, &obj, CK_FALSE, data_len);
if (rc)
goto cleanup;
if (!template_get_class(obj->template, &class, &subclass)) {
fprintf(stderr, "No CKA_CLASS attribute found\n");
rc = CKR_TEMPLATE_INCOMPLETE;
goto cleanup;
}
switch(class) {
case CKO_SECRET_KEY:
rc = adjust_secret_key_attributes(obj, subclass);
if (rc == CKR_IBM_NOT_TOUCHED) {
rc = CKR_OK;
goto cleanup;
}
break;
default:
/* no need to modify the object */
rc = CKR_OK;
goto cleanup;
}
if (rc != CKR_OK)
goto cleanup;
/* flatten the object */
rc = object_flatten(obj, new_data, new_data_len);
if (rc)
goto cleanup;
cleanup:
if (obj)
object_free(obj);
return rc;
}
int reencrypt_private_token_object(unsigned char *data, unsigned long len,
unsigned char *new_cipher,
unsigned long *new_cipher_len,
unsigned char *masterkey)
{
unsigned char *clear = NULL;
unsigned char des3_key[64];
unsigned char sw_des3_key[3 * DES_KEY_SIZE];
unsigned long clear_len;
unsigned char *new_obj_data = NULL;
unsigned long new_obj_data_len;
CK_ULONG_32 obj_data_len_32;
CK_ULONG padded_len;
CK_ULONG block_size = DES_BLOCK_SIZE;
CK_BYTE *ptr = NULL;
CK_BYTE hash_sha[SHA1_HASH_SIZE];
CK_RV rc;
int ret;
/* cca wants 8 extra bytes for padding purposes */
clear_len = len + 8;
clear = (unsigned char *) malloc(clear_len);
if (!clear) {
fprintf(stderr, "malloc() failed: %s.\n", strerror(errno));
ret = -1;
goto done;
}
/* decrypt using cca des3 */
memcpy(des3_key, masterkey, MASTER_KEY_SIZE);
ret = cca_decrypt(data, len, clear, &clear_len, (CK_BYTE *)"10293847",
des3_key);
if (ret)
goto done;
/* Validate the hash */
memcpy(&obj_data_len_32, clear, sizeof(CK_ULONG_32));
if (obj_data_len_32 >= clear_len) {
fprintf(stderr, "Decrypted object data is inconsistent. Possibly already migrated?\n");
ret = CKR_FUNCTION_FAILED;
goto done;
}
ret = compute_sha1((char *)(clear + sizeof(CK_ULONG_32)),
obj_data_len_32, (char *)hash_sha);
if (ret != CKR_OK) {
goto done;
}
if (memcmp(clear + sizeof(CK_ULONG_32) + obj_data_len_32, hash_sha,
SHA1_HASH_SIZE) != 0) {
fprintf(stderr, "Stored hash does not match restored data hash.\n");
ret = CKR_FUNCTION_FAILED;
goto done;
}
/* Adjust the key object attributes */
ret = adjust_key_object_attributes(clear + sizeof(CK_ULONG_32),
obj_data_len_32,
&new_obj_data, &new_obj_data_len);
if (ret)
goto done;
if (new_obj_data != NULL) {
free(clear);
/* build data to be encrypted */
clear_len = sizeof(CK_ULONG_32) + new_obj_data_len + SHA1_HASH_SIZE;
padded_len = block_size * (clear_len / block_size + 1);
clear = malloc(padded_len);
if (!clear) {
fprintf(stderr, "Failed to allocate buffer\n");
goto done;
}
ptr = clear;
obj_data_len_32 = new_obj_data_len;
memcpy(ptr, &obj_data_len_32, sizeof(CK_ULONG_32));
ptr += sizeof(CK_ULONG_32);
memcpy(ptr, new_obj_data, obj_data_len_32);
ptr += obj_data_len_32;
compute_sha1((char *)new_obj_data, new_obj_data_len, (char *)hash_sha);
memcpy(ptr, hash_sha, SHA1_HASH_SIZE);
add_pkcs_padding(clear + clear_len, block_size, clear_len,
padded_len);
clear_len = padded_len;
}
/* now encrypt using software des3 */
memcpy(sw_des3_key, masterkey, 3 * DES_KEY_SIZE);
rc = sw_des3_cbc_encrypt(clear, clear_len, new_cipher, new_cipher_len,
(CK_BYTE *)"10293847", sw_des3_key);
if (rc != CKR_OK)
ret = -1;
done:
if (clear)
free(clear);
if (new_obj_data)
free(new_obj_data);
return ret;
}
int load_token_objects(unsigned char *data_store,
unsigned char *masterkey)
{
FILE *fp1 = NULL, *fp2 = NULL;
unsigned char *buf = NULL;
char tmp[PATH_MAX], fname[PATH_MAX], iname[PATH_MAX];
CK_BBOOL priv;
unsigned int size;
int rc = 0, scount = 0, fcount = 0;
size_t read_size;
unsigned char *new_cipher = NULL;
unsigned long new_cipher_len;
snprintf(iname, sizeof(iname), "%s/TOK_OBJ/OBJ.IDX", data_store);
fp1 = fopen((char *) iname, "r");
if (!fp1)
return -1; // no token objects
while (fgets((char *) tmp, 50, fp1)) {
tmp[strlen((char *) tmp) - 1] = 0;
snprintf((char *) fname, sizeof(fname), "%s/TOK_OBJ/", data_store);
strcat((char *) fname, (char *) tmp);
fp2 = fopen((char *) fname, "r");
if (!fp2)
continue;
read_size = fread(&size, sizeof(CK_ULONG_32), 1, fp2);
if (read_size != 1) {
fprintf(stderr, "Cannot read size\n");
goto cleanup;
}
read_size = fread(&priv, sizeof(CK_BBOOL), 1, fp2);
if (read_size != 1) {
fprintf(stderr, "Cannot read boolean\n");
goto cleanup;
}
size = size - sizeof(CK_ULONG_32) - sizeof(CK_BBOOL);
buf = (unsigned char *) malloc(size);
if (!buf) {
fprintf(stderr, "Cannot malloc for object %s "
"(ignoring it).\n", tmp);
goto cleanup;
}
read_size = fread((char *) buf, 1, size, fp2);
if (read_size != size) {
fprintf(stderr, "Cannot read object %s " "(ignoring it).\n", tmp);
goto cleanup;
}
fclose(fp2);
fp2 = NULL;
if (priv != FALSE) {
/* private token object */
new_cipher_len = size * 2; /* obj may grow during processing ! */
new_cipher = malloc(new_cipher_len);
if (!new_cipher) {
fprintf(stderr, "Cannot malloc space for new "
"cipher (ignoring object %s).\n", tmp);
goto cleanup;
}
/* After reading the private token object,
* decrypt it using CCA des3 and then re-encrypt it
* using software des3.
*/
memset(new_cipher, 0, new_cipher_len);
rc = reencrypt_private_token_object(buf, size,
new_cipher, &new_cipher_len,
masterkey);
if (rc)
goto cleanup;
} else {
/* public token object */
rc = adjust_key_object_attributes(buf, size, &new_cipher,
&new_cipher_len);
if (rc)
goto cleanup;
/* Only save if the object has been changed */
if (new_cipher == NULL)
goto cleanup;
}
/* now save the newly re-encrypted object back to
* disk in its original file.
*/
fp2 = fopen((char *) fname, "w");
if (fp2 == NULL) {
printf("Failed to open file %s: %s", fname, strerror(errno));
rc = CKR_FUNCTION_FAILED;
goto cleanup;
}
size = sizeof(CK_ULONG_32) + sizeof(CK_BBOOL) + new_cipher_len;
(void) fwrite(&size, sizeof(CK_ULONG_32), 1, fp2);
(void) fwrite(&priv, sizeof(CK_BBOOL), 1, fp2);
(void) fwrite(new_cipher, new_cipher_len, 1, fp2);
rc = 0;
cleanup:
if (fp2)
fclose(fp2);
if (buf) {
free(buf);
buf = NULL;
}
if (new_cipher) {
free(new_cipher);
new_cipher = NULL;
}
if (rc) {
if (v_flag)
printf("Failed to process %s\n", fname);
fcount++;
} else {
if (v_flag)
printf("Processed %s.\n", fname);
scount++;
}
}
fclose(fp1);
printf("Successfully migrated %d object(s).\n", scount);
if (v_flag && fcount)
printf("Failed to migrate %d object(s).\n", fcount);
return 0;
}
int load_masterkey(char *mkfile, char *pin, char *masterkey)
{
unsigned char des3_key[3 * DES_KEY_SIZE];
char hash_sha[SHA1_HASH_SIZE];
char pin_md5_hash[MD5_HASH_SIZE];
unsigned char *cipher = NULL;
char *clear = NULL;
unsigned long cipher_len, clear_len;
int ret;
CK_RV rc;
FILE *fp = NULL;
clear_len = cipher_len =
(MASTER_KEY_SIZE + SHA1_HASH_SIZE +
(DES_BLOCK_SIZE - 1)) & ~(DES_BLOCK_SIZE - 1);
fp = fopen((char *) mkfile, "r");
if (!fp) {
print_error("Could not open %s: %s\n", mkfile, strerror(errno));
return -1;
}
cipher = malloc(cipher_len);
clear = malloc(clear_len);
if (cipher == NULL || clear == NULL) {
ret = -1;
goto done;
}
ret = fread(cipher, cipher_len, 1, fp);
if (ret != 1) {
print_error("Could not read %s: %s\n", mkfile, strerror(errno));
ret = -1;
goto done;
}
/* decrypt the masterkey */
ret = compute_md5(pin, strlen(pin), pin_md5_hash);
if (ret) {
print_error("Error calculating MD5 of PIN!\n");
goto done;
}
memcpy(des3_key, pin_md5_hash, MD5_HASH_SIZE);
memcpy(des3_key + MD5_HASH_SIZE, pin_md5_hash, DES_KEY_SIZE);
rc = sw_des3_cbc_decrypt(cipher, cipher_len, (unsigned char *)clear,
&clear_len, (unsigned char *) "12345678",
des3_key);
if (rc != CKR_OK) {
print_error("Error decrypting master key file after read");
ret = -1;
goto done;
}
/*
* technically should strip PKCS padding here but since I already know
* what the length should be, I don't bother.
*
* compare the hashes to verify integrity
*/
ret = compute_sha1(clear, MASTER_KEY_SIZE, hash_sha);
if (ret) {
print_error("Failed to compute sha for masterkey.\n");
goto done;
}
if (memcmp(hash_sha, clear + MASTER_KEY_SIZE, SHA1_HASH_SIZE) != 0) {
print_error("%s appears to have been tampered!\n", mkfile);
print_error("Cannot migrate.\n");
ret = -1;
goto done;
}
memcpy(masterkey, clear, MASTER_KEY_SIZE);
ret = 0;
done:
if (fp)
fclose(fp);
if (clear)
free(clear);
if (cipher)
free(cipher);
return ret;
}
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;
}
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, 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) {
print_error("Could 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 {
print_error("%s: invalid size.\n", fname);
ret = -1;
goto done;
}
ret = fread(&td, tdlen, 1, fp);
if (ret != 1) {
print_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) {
print_error("Failed to compute sha for SO.\n");
goto done;
}
if (memcmp(td.so_pin_sha, pin_sha, SHA1_HASH_SIZE) != 0) {
print_error("SO PIN is incorrect.\n");
ret = -1;
goto done;
}
}
if (userpin != NULL) {
ret = compute_sha1(userpin, userpinlen, pin_sha);
if (ret) {
print_error("Failed to compute sha for USER.\n");
goto done;
}
if (memcmp(td.user_pin_sha, pin_sha, SHA1_HASH_SIZE) != 0) {
print_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) {
print_error("PBKDF2 failed.\n");
goto done;
}
if (CRYPTO_memcmp(td.dat.so_login_key, so_login_key, 32) != 0) {
print_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) {
print_error("PBKDF2 failed.\n");
goto done;
}
if (CRYPTO_memcmp(td.dat.user_login_key, user_login_key, 32) != 0) {
print_error("USER PIN is incorrect.\n");
ret = -1;
goto done;
}
}
} else {
print_error("Unknown 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;
}
CK_FUNCTION_LIST *p11_init(void)
{
CK_RV rv;
CK_RV (*pfoo) ();
char *loc1_lib = "/usr/lib/pkcs11/PKCS11_API.so64";
char *loc2_lib = "libopencryptoki.so";
CK_FUNCTION_LIST *funcs = NULL;
p11_lib = dlopen(loc1_lib, RTLD_NOW);
if (p11_lib != NULL)
goto get_list;
p11_lib = dlopen(loc2_lib, RTLD_NOW);
if (p11_lib == NULL) {
print_error("Couldn't get a handle to the PKCS#11 library.");
return NULL;
}
get_list:
*(void **)(&pfoo) = dlsym(p11_lib, "C_GetFunctionList");
if (pfoo == NULL) {
print_error("Couldn't get the address of the C_GetFunctionList "
"routine.");
dlclose(p11_lib);
return NULL;
}
rv = pfoo(&funcs);
if (rv != CKR_OK) {
p11_error("C_GetFunctionList", rv);
dlclose(p11_lib);
return NULL;
}
rv = funcs->C_Initialize(NULL_PTR);
if (rv != CKR_OK) {
p11_error("C_Initialize", rv);
dlclose(p11_lib);
return NULL;
}
if (v_flag)
printf("PKCS#11 library initialized\n");
return funcs;
}
void p11_fini(CK_FUNCTION_LIST *funcs)
{
funcs->C_Finalize(NULL_PTR);
if (p11_lib)
dlclose(p11_lib);
}
/* Expect attribute array to have 3 entries,
* 0 CKA_IBM_OPAQUE
* 1 CKA_KEY_TYPE
* 2 CKA_LABEL
*/
int add_key(CK_OBJECT_HANDLE handle, CK_ATTRIBUTE *attrs, struct key **keys)
{
struct key *new_key;
CK_ULONG key_type = *(CK_ULONG *) attrs[1].pValue;
new_key = malloc(sizeof(struct key));
if (!new_key) {
print_error("Malloc of %zd bytes failed!", sizeof(struct key));
return 1;
}
switch (key_type) {
case CKK_AES:
case CKK_DES:
case CKK_DES2:
case CKK_DES3:
case CKK_EC:
case CKK_GENERIC_SECRET:
case CKK_RSA:
break;
default:
free(new_key);
return 0;
}
new_key->type = key_type;
new_key->opaque_attr = malloc(attrs[0].ulValueLen);
if (!new_key->opaque_attr) {
print_error("Malloc of %lu bytes failed!", attrs[0].ulValueLen);
free(new_key);
return 2;
}
new_key->handle = handle;
new_key->attr_len = attrs[0].ulValueLen;
memcpy(new_key->opaque_attr, attrs[0].pValue, attrs[0].ulValueLen);
new_key->label = malloc(attrs[2].ulValueLen + 1);
if (!new_key->label) {
print_error("Malloc of %lu bytes failed!", attrs[2].ulValueLen + 1);
free(new_key);
return 2;
}
memset(new_key->label, 0, attrs[2].ulValueLen + 1);
memcpy(new_key->label, attrs[2].pValue, attrs[2].ulValueLen);
new_key->next = *keys;
*keys = new_key;
if (v_flag) {
char *type_name;
switch (new_key->type) {
case CKK_AES:
type_name = AES_NAME;
break;
case CKK_DES:
type_name = DES_NAME;
break;
case CKK_DES2:
type_name = DES2_NAME;
break;
case CKK_DES3:
type_name = DES3_NAME;
break;
case CKK_EC:
type_name = ECC_NAME;
break;
case CKK_GENERIC_SECRET:
type_name = HMAC_NAME;
break;
case CKK_RSA:
type_name = RSA_NAME;
break;
default:
type_name = BAD_NAME;
}
printf("Migratable key found: type=%s, label=%s, handle=%lu\n",
type_name, new_key->label, handle);
}
return 0;
}
int find_wrapped_keys(CK_FUNCTION_LIST *funcs, CK_SESSION_HANDLE sess,
CK_KEY_TYPE *key_type, struct key **keys)
{
CK_RV rv;
void *ptr;
CK_OBJECT_HANDLE *handles = NULL, tmp;
CK_ULONG ulObjectCount = 0, ulTotalCount = 0;
CK_BBOOL true = TRUE;
CK_ATTRIBUTE key_tmpl[] = {
{CKA_KEY_TYPE, key_type, sizeof(*key_type)},
{CKA_TOKEN, &true, sizeof(true)},
{CKA_EXTRACTABLE, &true, sizeof(true)}
};
CK_ATTRIBUTE attrs[] = {
{CKA_IBM_OPAQUE, NULL, 0},
{CKA_KEY_TYPE, NULL, 0},
{CKA_LABEL, NULL, 0}
};
int i, rc, num_attrs = 3;
/* Find all objects in the store */
rv = funcs->C_FindObjectsInit(sess, key_tmpl, 3);
if (rv != CKR_OK) {
p11_error("C_FindObjectsInit", rv);
print_error("Error finding CCA key objects");
return 1;
}
while (1) {
rv = funcs->C_FindObjects(sess, &tmp, 1, &ulObjectCount);
if (rv != CKR_OK) {
p11_error("C_FindObjects", rv);
print_error("Error finding CCA key objects");
if (handles != NULL)
free(handles);
return 1;
}
if (ulObjectCount == 0)
break;
ptr = realloc(handles, sizeof(CK_OBJECT_HANDLE) * (++ulTotalCount));
if (!ptr) {
print_error("Malloc of %lu bytes failed!",
sizeof(CK_OBJECT_HANDLE) * ulTotalCount);
funcs->C_FindObjectsFinal(sess);
if (handles != NULL)
free(handles);
return 1;
}
handles = ptr;
handles[ulTotalCount - 1] = tmp;
}
if (v_flag)
printf("Found %lu keys to examine\n", ulTotalCount);
/* Don't care if this fails */
funcs->C_FindObjectsFinal(sess);
/* At this point we have an array with handles to every object in the
* store. We only care about those with a CKA_IBM_OPAQUE attribute,
* so whittle down the list accordingly */
for (tmp = 0; tmp < ulTotalCount; tmp++) {
rv = funcs->C_GetAttributeValue(sess, handles[tmp], attrs, num_attrs);
if (rv != CKR_OK) {
p11_error("C_GetAttributeValue", rv);
print_error("Error finding CCA key objects");
free(handles);
return 1;
}
/* If the opaque attr DNE, move to the next key */
if (attrs[0].ulValueLen == ((CK_ULONG) - 1)) {
continue;
}
/* Allocate space in the template for the actual data */
for (i = 0; i < num_attrs; i++) {
attrs[i].pValue = malloc(attrs[i].ulValueLen);
if (!attrs[i].pValue) {
print_error("Malloc of %lu bytes failed!", attrs[i].ulValueLen);
free(handles);
return 1;
}
}
/* Pull in the actual data */
rv = funcs->C_GetAttributeValue(sess, handles[tmp], attrs, num_attrs);
if (rv != CKR_OK) {
p11_error("C_GetAttributeValue", rv);
print_error("Error getting object attributes");
free(handles);
return 1;
}
rc = add_key(handles[tmp], attrs, keys);
if (rc) {
free(handles);
return 1;
}
for (i = 0; i < num_attrs; i++) {
free(attrs[i].pValue);
attrs[i].pValue = NULL_PTR;
attrs[i].ulValueLen = 0;
}
}
free(handles);
return 0;
}
int replace_keys(CK_FUNCTION_LIST *funcs, CK_SESSION_HANDLE sess,
struct key *keys)
{
CK_RV rv;
CK_ATTRIBUTE new_attr[] = { {CKA_IBM_OPAQUE, NULL, 0} };
struct key *key;
for (key = keys; key; key = key->next) {
new_attr->pValue = key->opaque_attr;
new_attr->ulValueLen = key->attr_len;
rv = funcs->C_SetAttributeValue(sess, key->handle, new_attr, 1);
if (rv != CKR_OK) {
p11_error("C_SetAttributeValue", rv);
print_error("Error replacing old key with " "migrated key.");
return 1;
}
}
return 0;
}
int cca_migrate_asymmetric(struct key *key, char **out, struct algo algo)
{
long return_code, reason_code, exit_data_length, key_identifier_length;
unsigned char *key_identifier;
exit_data_length = 0;
key_identifier_length = key->attr_len;
key_identifier = calloc(1, key->attr_len);
if (!key_identifier) {
print_error("Malloc of %lu bytes failed!", key->attr_len);
return 1;
}
memcpy(key_identifier, (char *) key->opaque_attr, key->attr_len);
CSNDKTC(&return_code,
&reason_code,
&exit_data_length,
NULL,
&(algo.rule_array_count),
algo.rule_array, &key_identifier_length, key_identifier);
if (return_code != CCA_SUCCESS) {
cca_error("CSNDKTC (Key Token Change)", return_code, reason_code);
print_error("Migrating %s key failed. label=%s, handle=%lu",
algo.name, key->label, key->handle);
return 1;
} else if (v_flag) {
printf("Successfully migrated %s key. label=%s, handle=%lu\n",
algo.name, key->label, key->handle);
}
*out = (char *) key_identifier;
if (!memcmp((CK_BYTE *) key->opaque_attr,
(CK_BYTE *) key_identifier, key_identifier_length)) {
printf("Skipping, %s token is wrapped with current master key. "
"label=%s, handle=%lu\n",
algo.name, key->label, key->handle);
}
return 0;
}
int cca_migrate_symmetric(struct key *key, char **out, struct algo algo)
{
long return_code, reason_code, exit_data_length;
unsigned char *key_identifier;
exit_data_length = 0;
key_identifier = calloc(1, key->attr_len);
if (!key_identifier) {
print_error("Malloc of %lu bytes failed!", key->attr_len);
return 1;
}
memcpy(key_identifier, (char *) key->opaque_attr, key->attr_len);
CSNBKTC(&return_code,
&reason_code,
&exit_data_length,
NULL, &(algo.rule_array_count), algo.rule_array, key_identifier);
if (return_code != CCA_SUCCESS) {
cca_error("CSNBKTC (Key Token Change)", return_code, reason_code);
print_error("Migrating %s key failed. label=%s, handle=%lu",
algo.name, key->label, key->handle);
return 1;
} else if (v_flag) {
printf("Successfully migrated %s key. label=%s, handle=%lu\n",
algo.name, key->label, key->handle);
}
*out = (char *) key_identifier;
if (!memcmp((CK_BYTE *) key->opaque_attr,
(CK_BYTE *) key_identifier, key->attr_len)) {
printf("Skipping, %s token is wrapped with current master key. "
"label=%s, handle=%lu\n",
algo.name, key->label, key->handle);
}
return 0;
}
int cca_migrate_hmac(struct key *key, char **out, struct algo algo)
{
long return_code, reason_code, exit_data_length, key_identifier_length;
unsigned char *key_identifier;
exit_data_length = 0;
key_identifier_length = key->attr_len;
key_identifier = calloc(1, key->attr_len);
if (!key_identifier) {
print_error("Malloc of %lu bytes failed!", key->attr_len);
return 1;
}
memcpy(key_identifier, (char *) key->opaque_attr, key->attr_len);
CSNBKTC2(&return_code,
&reason_code,
&exit_data_length,
NULL,
&(algo.rule_array_count),
algo.rule_array, &key_identifier_length, key_identifier);
if (return_code != CCA_SUCCESS) {
cca_error("CSNBKTC2 (Key Token Change)", return_code, reason_code);
print_error("Migrating %s key failed. label=%s, handle=%lu",
algo.name, key->label, key->handle);
return 1;
} else if (v_flag) {
printf("Successfully migrated %s key. label=%s, handle=%lu\n",
algo.name, key->label, key->handle);
}
*out = (char *) key_identifier;
if (!memcmp((CK_BYTE *) key->opaque_attr,
(CK_BYTE *) key_identifier, key_identifier_length)) {
printf("Skipping, %s token is wrapped with current master key. "
"label=%s, handle=%lu\n",
algo.name, key->label, key->handle);
}
return 0;
}
/* @keys: A linked list of data to migrate and the PKCS#11 handle for the
* object in the data store.
* @count: counter for number of keys migrated
* @count_failed: counter for number of keys that failed to migrate
*/
int cca_migrate(struct key *keys, struct key_count *count,
struct key_count *count_failed)
{
struct key *key;
char *migrated_data;
int rc;
for (key = keys; key; key = key->next) {
migrated_data = NULL;
switch (key->type) {
case CKK_AES:
rc = cca_migrate_symmetric(key, &migrated_data, aes);
if (rc)
count_failed->aes++;
else
count->aes++;
break;
case CKK_DES:
case CKK_DES2:
case CKK_DES3:
rc = cca_migrate_symmetric(key, &migrated_data, des);
if (rc)
count_failed->des++;
else
count->des++;
break;
case CKK_EC:
rc = cca_migrate_asymmetric(key, &migrated_data, ecc);
if (rc)
count_failed->ecc++;
else
count->ecc++;
break;
case CKK_GENERIC_SECRET:
rc = cca_migrate_hmac(key, &migrated_data, hmac);
if (rc)
count_failed->hmac++;
else
count->hmac++;
break;
case CKK_RSA:
rc = cca_migrate_asymmetric(key, &migrated_data, rsa);
if (rc)
count_failed->rsa++;
else
count->rsa++;
break;
default:
rc = 1;
break;
}
/* replace the original key with the migrated key */
if (!rc && migrated_data) {
free(key->opaque_attr);
key->opaque_attr = (CK_BYTE *) migrated_data;
}
}
return 0;
}
int migrate_keytype(CK_FUNCTION_LIST *funcs, CK_SESSION_HANDLE sess,
CK_KEY_TYPE *k_type, struct key_count *count,
struct key_count *count_failed)
{
struct key *keys = NULL, *tmp, *to_free;
int rc;
rc = find_wrapped_keys(funcs, sess, k_type, &keys);
if (rc) {
goto done;
}
rc = cca_migrate(keys, count, count_failed);
if (rc) {
goto done;
}
rc = replace_keys(funcs, sess, keys);
if (rc) {
goto done;
}
done:
for (to_free = keys; to_free; to_free = tmp) {
tmp = to_free->next;
free(to_free->opaque_attr);
free(to_free);
}
return rc;
}
void key_migration_results(struct key_count migrated, struct key_count failed)
{
if (migrated.aes || migrated.des || migrated.des2 || migrated.des3 ||
migrated.ecc || migrated.hmac || migrated.rsa)
printf("Successfully migrated: ");
if (migrated.aes)
printf("AES: %d. ", migrated.aes);
if (migrated.des)
printf("DES: %d. ", migrated.des);
if (migrated.des2)
printf("DES2: %d. ", migrated.des2);
if (migrated.des3)
printf("DES3: %d. ", migrated.des3);
if (migrated.ecc)
printf("ECC: %d. ", migrated.ecc);
if (migrated.hmac)
printf("HMAC: %d. ", migrated.hmac);
if (migrated.rsa)
printf("RSA: %d. ", migrated.rsa);
if (failed.aes || failed.des || failed.des2 || failed.des3 ||
failed.ecc || failed.hmac || failed.rsa)
printf("\nFailed to migrate: ");
if (failed.aes)
printf("AES: %d. ", failed.aes);
if (failed.des)
printf("DES: %d. ", failed.des);
if (failed.des2)
printf("DES2: %d. ", failed.des2);
if (failed.des3)
printf("DES3: %d. ", failed.des3);
if (failed.ecc)
printf("ECC: %d. ", failed.ecc);
if (failed.hmac)
printf("HMAC: %d. ", failed.hmac);
if (failed.rsa)
printf("RSA: %d. ", failed.rsa);
printf("\n");
}
int migrate_wrapped_keys(CK_SLOT_ID slot_id, char *userpin, int masterkey)
{
CK_FUNCTION_LIST *funcs;
CK_KEY_TYPE key_type = 0;
CK_ULONG slot_count;
CK_SESSION_HANDLE sess;
CK_RV rv;
struct key_count count = { 0, 0, 0, 0, 0, 0, 0 };
struct key_count count_failed = { 0, 0, 0, 0, 0, 0, 0 };
int exit_code = 0, rc;
funcs = p11_init();
if (!funcs) {
return 2;
}
rv = funcs->C_GetSlotList(TRUE, NULL_PTR, &slot_count);
if (rv != CKR_OK) {
p11_error("C_GetSlotList", rv);
exit_code = 3;
goto finalize;
}
if (slot_id >= slot_count) {
print_error("%lu is not a valid slot ID.", slot_id);
exit_code = 4;
goto finalize;
}
rv = funcs->C_OpenSession(slot_id, CKF_RW_SESSION |
CKF_SERIAL_SESSION, NULL_PTR, NULL_PTR, &sess);
if (rv != CKR_OK) {
p11_error("C_OpenSession", rv);
exit_code = 5;
goto finalize;
}
rv = funcs->C_Login(sess, CKU_USER, (CK_BYTE *) userpin, strlen(userpin));
if (rv != CKR_OK) {
p11_error("C_Login (USER)", rv);
exit_code = 8;
goto finalize;
}
switch (masterkey) {
case MK_AES:
if (v_flag)
printf("Search for AES keys\n");
key_type = CKK_AES;
rc = migrate_keytype(funcs, sess, &key_type, &count, &count_failed);
if (rc) {
goto done;
}
if (v_flag)
printf("Search for HMAC keys\n");
key_type = CKK_GENERIC_SECRET;
rc = migrate_keytype(funcs, sess, &key_type, &count, &count_failed);
if (rc) {
goto done;
}
break;
case MK_APKA:
if (v_flag)
printf("Search for ECC keys\n");
key_type = CKK_EC;
rc = migrate_keytype(funcs, sess, &key_type, &count, &count_failed);
if (rc) {
goto done;
}
break;
case MK_ASYM:
if (v_flag)
printf("Search for RSA keys\n");
key_type = CKK_RSA;
rc = migrate_keytype(funcs, sess, &key_type, &count, &count_failed);
if (rc) {
goto done;
}
break;
case MK_SYM:
if (v_flag)
printf("Search for DES keys\n");
key_type = CKK_DES;
rc = migrate_keytype(funcs, sess, &key_type, &count, &count_failed);
if (rc) {
goto done;
}
if (v_flag)
printf("Search for DES2 keys\n");
key_type = CKK_DES2;
rc = migrate_keytype(funcs, sess, &key_type, &count, &count_failed);
if (rc) {
goto done;
}
if (v_flag)
printf("Search for DES3 keys\n");
key_type = CKK_DES3;
rc = migrate_keytype(funcs, sess, &key_type, &count, &count_failed);
if (rc) {
goto done;
}
break;
default:
print_error("unknown key type (%lu)\n", key_type);
return -1;
}
key_migration_results(count, count_failed);
done:
funcs->C_CloseSession(sess);
finalize:
p11_fini(funcs);
return exit_code;
}
int migrate_version(char *sopin, char *userpin, unsigned char *data_store)
{
char masterkey[MASTER_KEY_SIZE];
char fname[PATH_MAX];
struct stat statbuf;
int ret = 0;
/* Verify that the data store is valid by looking for
* MK_SO, MK_USER, and TOK_OBJ/OBJ.IDX.
*/
memset(fname, 0, PATH_MAX);
snprintf(fname, PATH_MAX, "%s/MK_SO", data_store);
if (stat(fname, &statbuf) != 0) {
fprintf(stderr, "Cannot find %s.\n", fname);
ret = -1;
goto done;
}
memset(fname, 0, PATH_MAX);
snprintf(fname, PATH_MAX, "%s/MK_USER", data_store);
if (stat(fname, &statbuf) != 0) {
fprintf(stderr, "Cannot find %s.\n", fname);
ret = -1;
goto done;
}
memset(fname, 0, PATH_MAX);
snprintf(fname, PATH_MAX, "%s/TOK_OBJ/OBJ.IDX", data_store);
if (stat(fname, &statbuf) != 0) {
fprintf(stderr, "Cannot find %s.\n", fname);
ret = -1;
goto done;
}
/* If the OBJ.IDX is empty, then no objects to migrate. */
if (statbuf.st_size == 0) {
printf("OBJ.IDX file is empty. Thus no objects to migrate.\n");
goto done;
}
if (v_flag)
printf("%s has an MK_SO, MK_USER and TOK/OBJ.IDX\n", data_store);
/* Get the masterkey from MK_SO.
* This also helps verify that correct SO pin was entered.
*/
memset(masterkey, 0, MASTER_KEY_SIZE);
memset(fname, 0, PATH_MAX);
snprintf(fname, PATH_MAX, "%s/MK_SO", data_store);
ret = load_masterkey(fname, sopin, masterkey);
if (ret) {
fprintf(stderr, "Could not load masterkey from MK_SO.\n");
goto done;
}
if (v_flag)
printf("Successfully verified SO Pin.\n");
/* Get the masterkey from MK_USER.
* This also helps verift that correct USER pin was entered.
*/
memset(masterkey, 0, MASTER_KEY_SIZE);
memset(fname, 0, PATH_MAX);
snprintf(fname, PATH_MAX, "%s/MK_USER", data_store);
ret = load_masterkey(fname, userpin, masterkey);
if (ret) {
fprintf(stderr, "Could not load masterkey from MK_USER.\n");
goto done;
}
if (v_flag)
printf("Successfully verified USER Pin.\n");
/* Load all the private token objects and re-encrypt them
* using software des3, instead of CSNBENC.
* For private and public token objects, migrate the key object's
* attributes to IBM_OPAQUE.
*/
(void)load_token_objects(data_store, (CK_BYTE *)masterkey);
done:
return ret;
}
void usage(char *progname)
{
printf(" Help:\t\t\t\t%s -h\n", progname);
printf(" -h\t\t\t\tShow this help\n\n");
printf(" Migrate Object Version:\t%s -m v2objectsv3 [OPTIONS] \n",
progname);
printf(" -m v2objectsv3.\t\tMigrates CCA private token objects from");
printf(" CCA\n\t\t\t\tencryption (used in v2) to software encryption");
printf(" \n\t\t\t\t(used in v3). \n");
printf(" Migrate Wrapped Keys:\t\t%s -m keys -s SLOTID -k KEYTYPE "
"[OPTIONS] \n", progname);
printf(" -m keys.\t\t\tUnwraps private keys with the");
printf(" old CCA master\n\t\t\t\tkey and wraps them with the");
printf(" new CCA master key\n");
printf(" -s, --slotid SLOTID\t\tPKCS slot number\n");
printf(" -k aes|apka|asym|sym\t\tMigrate selected keytype\n\n");
printf(" Options:\n");
printf(" -d, --datastore DATASTORE\tCCA token datastore location\n");
printf(" -v, --verbose\t\t\tProvide more detailed output\n");
printf(" \n\t\t\t\tthe migrated data\n\n");
return;
}
int main(int argc, char **argv)
{
int ret = 0, opt = 0, c_flag = 0, masterkey = 0;
int data_store_len = 0;
CK_SLOT_ID slot_id = 0;
char *sopin = NULL, *userpin = NULL;
size_t sopinlen, userpinlen;
char *data_store = NULL;
char *m_type = NULL;
char *mk_type = NULL;
void *lib_csulcca;
int m_version = 0;
int m_keys = 0;
struct option long_opts[] = {
{"datastore", required_argument, NULL, 'd'},
{"slotid", required_argument, NULL, 's'},
{"verbose", no_argument, NULL, 'v'},
{0, 0, 0, 0}
};
while ((opt = getopt_long(argc, argv, "m:d:s:k:hv", long_opts, NULL))
!= -1) {
switch (opt) {
case 'd':
data_store = strdup(optarg);
break;
case 'h':
usage(argv[0]);
return 0;
case 'k':
mk_type = strdup(optarg);
if (!memcmp(mk_type, "aes", 3)) {
masterkey = MK_AES;
} else if (!memcmp(mk_type, "apka", 4)) {
masterkey = MK_APKA;
} else if (!memcmp(mk_type, "asym", 4)) {
masterkey = MK_ASYM;
} else if (!memcmp(mk_type, "sym", 3)) {
masterkey = MK_SYM;
} else {
print_error("unknown key type (%s)\n", mk_type);
usage(argv[0]);
return -1;
}
break;
case 'm':
m_type = strdup(optarg);
if (!memcmp(m_type, "v2objectsv3", 11)) {
m_version = 1;
} else if (!memcmp(m_type, "keys", 4)) {
m_keys = 1;
} else {
print_error("unknown migration type (%s)\n", m_type);
usage(argv[0]);
return -1;
}
break;
case 's':
c_flag++;
slot_id = atoi(optarg);
break;
case 'v':
v_flag++;
break;
default:
usage(argv[0]);
return -1;
}
}
/* check for missing parameters */
if (!m_version && !m_keys) {
print_error("missing migration type\n");
usage(argv[0]);
return -1;
}
/* use default data_store if one is not given */
if (data_store == NULL) {
data_store_len = strlen(TOK_DATASTORE);
data_store = malloc(data_store_len + 1);
if (data_store == NULL) {
fprintf(stderr, "malloc failed: %s\n", strerror(errno));
return -1;
}
memset(data_store, 0, data_store_len + 1);
memcpy(data_store, TOK_DATASTORE, data_store_len);
}
/* get the SO pin to authorize migration */
printf("Enter the SO PIN: ");
fflush(stdout);
ret = get_pin(&sopin, &sopinlen);
if (ret != 0) {
print_error("Could not get SO PIN.\n");
goto done;
}
/* get the USER pin to authorize migration */
printf("Enter the USER PIN: ");
fflush(stdout);
ret = get_pin(&userpin, &userpinlen);
if (ret != 0) {
print_error("Could not get USER PIN.\n");
goto done;
}
/* verify the SO and USER PINs entered. */
ret = verify_pins(data_store, sopin, sopinlen, userpin, userpinlen);
if (ret)
goto done;
lib_csulcca = dlopen(CCA_LIBRARY, (RTLD_GLOBAL | RTLD_NOW));
if (lib_csulcca == NULL) {
fprintf(stderr, "dlopen(%s) failed: %s\n", CCA_LIBRARY,
strerror(errno));
return -1;
}
if (m_version) {
*(void **)(&CSNBDEC) = dlsym(lib_csulcca, "CSNBDEC");
ret = migrate_version(sopin, userpin, (CK_BYTE *)data_store);
} else if (m_keys) {
if (!slot_id) {
print_error("missing slot number\n");
usage(argv[0]);
return -1;
}
if (!masterkey) {
print_error("missing key type\n");
usage(argv[0]);
return -1;
}
*(void **)(&CSNDKTC) = dlsym(lib_csulcca, "CSNDKTC");
*(void **)(&CSNBKTC) = dlsym(lib_csulcca, "CSNBKTC");
*(void **)(&CSNBKTC2) = dlsym(lib_csulcca, "CSNBKTC2");
ret = migrate_wrapped_keys(slot_id, userpin, masterkey);
}
done:
if (sopin)
free(sopin);
if (userpin)
free(userpin);
if (data_store)
free(data_store);
return ret;
}
char *p11strerror(CK_RV rc)
{
switch (rc) {
case CKR_OK:
return "CKR_OK";
case CKR_CANCEL:
return "CKR_CANCEL";
case CKR_HOST_MEMORY:
return "CKR_HOST_MEMORY";
case CKR_SLOT_ID_INVALID:
return "CKR_SLOT_ID_INVALID";
case CKR_GENERAL_ERROR:
return "CKR_GENERAL_ERROR";
case CKR_FUNCTION_FAILED:
return "CKR_FUNCTION_FAILED";
case CKR_ARGUMENTS_BAD:
return "CKR_ARGUMENTS_BAD";
case CKR_NO_EVENT:
return "CKR_NO_EVENT";
case CKR_NEED_TO_CREATE_THREADS:
return "CKR_NEED_TO_CREATE_THREADS";
case CKR_CANT_LOCK:
return "CKR_CANT_LOCK";
case CKR_ATTRIBUTE_READ_ONLY:
return "CKR_ATTRIBUTE_READ_ONLY";
case CKR_ATTRIBUTE_SENSITIVE:
return "CKR_ATTRIBUTE_SENSITIVE";
case CKR_ATTRIBUTE_TYPE_INVALID:
return "CKR_ATTRIBUTE_TYPE_INVALID";
case CKR_ATTRIBUTE_VALUE_INVALID:
return "CKR_ATTRIBUTE_VALUE_INVALID";
case CKR_DATA_INVALID:
return "CKR_DATA_INVALID";
case CKR_DATA_LEN_RANGE:
return "CKR_DATA_LEN_RANGE";
case CKR_DEVICE_ERROR:
return "CKR_DEVICE_ERROR";
case CKR_DEVICE_MEMORY:
return "CKR_DEVICE_MEMORY";
case CKR_DEVICE_REMOVED:
return "CKR_DEVICE_REMOVED";
case CKR_ENCRYPTED_DATA_INVALID:
return "CKR_ENCRYPTED_DATA_INVALID";
case CKR_ENCRYPTED_DATA_LEN_RANGE:
return "CKR_ENCRYPTED_DATA_LEN_RANGE";
case CKR_FUNCTION_CANCELED:
return "CKR_FUNCTION_CANCELED";
case CKR_FUNCTION_NOT_PARALLEL:
return "CKR_FUNCTION_NOT_PARALLEL";
case CKR_FUNCTION_NOT_SUPPORTED:
return "CKR_FUNCTION_NOT_SUPPORTED";
case CKR_KEY_HANDLE_INVALID:
return "CKR_KEY_HANDLE_INVALID";
case CKR_KEY_SIZE_RANGE:
return "CKR_KEY_SIZE_RANGE";
case CKR_KEY_TYPE_INCONSISTENT:
return "CKR_KEY_TYPE_INCONSISTENT";
case CKR_KEY_NOT_NEEDED:
return "CKR_KEY_NOT_NEEDED";
case CKR_KEY_CHANGED:
return "CKR_KEY_CHANGED";
case CKR_KEY_NEEDED:
return "CKR_KEY_NEEDED";
case CKR_KEY_INDIGESTIBLE:
return "CKR_KEY_INDIGESTIBLE";
case CKR_KEY_FUNCTION_NOT_PERMITTED:
return "CKR_KEY_FUNCTION_NOT_PERMITTED";
case CKR_KEY_NOT_WRAPPABLE:
return "CKR_KEY_NOT_WRAPPABLE";
case CKR_KEY_UNEXTRACTABLE:
return "CKR_KEY_UNEXTRACTABLE";
case CKR_MECHANISM_INVALID:
return "CKR_MECHANISM_INVALID";
case CKR_MECHANISM_PARAM_INVALID:
return "CKR_MECHANISM_PARAM_INVALID";
case CKR_OBJECT_HANDLE_INVALID:
return "CKR_OBJECT_HANDLE_INVALID";
case CKR_OPERATION_ACTIVE:
return "CKR_OPERATION_ACTIVE";
case CKR_OPERATION_NOT_INITIALIZED:
return "CKR_OPERATION_NOT_INITIALIZED";
case CKR_PIN_INCORRECT:
return "CKR_PIN_INCORRECT";
case CKR_PIN_INVALID:
return "CKR_PIN_INVALID";
case CKR_PIN_LEN_RANGE:
return "CKR_PIN_LEN_RANGE";
case CKR_PIN_EXPIRED:
return "CKR_PIN_EXPIRED";
case CKR_PIN_LOCKED:
return "CKR_PIN_LOCKED";
case CKR_SESSION_CLOSED:
return "CKR_SESSION_CLOSED";
case CKR_SESSION_COUNT:
return "CKR_SESSION_COUNT";
case CKR_SESSION_HANDLE_INVALID:
return "CKR_SESSION_HANDLE_INVALID";
case CKR_SESSION_PARALLEL_NOT_SUPPORTED:
return "CKR_SESSION_PARALLEL_NOT_SUPPORTED";
case CKR_SESSION_READ_ONLY:
return "CKR_SESSION_READ_ONLY";
case CKR_SESSION_EXISTS:
return "CKR_SESSION_EXISTS";
case CKR_SESSION_READ_ONLY_EXISTS:
return "CKR_SESSION_READ_ONLY_EXISTS";
case CKR_SESSION_READ_WRITE_SO_EXISTS:
return "CKR_SESSION_READ_WRITE_SO_EXISTS";
case CKR_SIGNATURE_INVALID:
return "CKR_SIGNATURE_INVALID";
case CKR_SIGNATURE_LEN_RANGE:
return "CKR_SIGNATURE_LEN_RANGE";
case CKR_TEMPLATE_INCOMPLETE:
return "CKR_TEMPLATE_INCOMPLETE";
case CKR_TEMPLATE_INCONSISTENT:
return "CKR_TEMPLATE_INCONSISTENT";
case CKR_TOKEN_NOT_PRESENT:
return "CKR_TOKEN_NOT_PRESENT";
case CKR_TOKEN_NOT_RECOGNIZED:
return "CKR_TOKEN_NOT_RECOGNIZED";
case CKR_TOKEN_WRITE_PROTECTED:
return "CKR_TOKEN_WRITE_PROTECTED";
case CKR_UNWRAPPING_KEY_HANDLE_INVALID:
return "CKR_UNWRAPPING_KEY_HANDLE_INVALID";
case CKR_UNWRAPPING_KEY_SIZE_RANGE:
return "CKR_UNWRAPPING_KEY_SIZE_RANGE";
case CKR_UNWRAPPING_KEY_TYPE_INCONSISTENT:
return "CKR_UNWRAPPING_KEY_TYPE_INCONSISTENT";
case CKR_USER_ALREADY_LOGGED_IN:
return "CKR_USER_ALREADY_LOGGED_IN";
case CKR_USER_NOT_LOGGED_IN:
return "CKR_USER_NOT_LOGGED_IN";
case CKR_USER_PIN_NOT_INITIALIZED:
return "CKR_USER_PIN_NOT_INITIALIZED";
case CKR_USER_TYPE_INVALID:
return "CKR_USER_TYPE_INVALID";
case CKR_USER_ANOTHER_ALREADY_LOGGED_IN:
return "CKR_USER_ANOTHER_ALREADY_LOGGED_IN";
case CKR_USER_TOO_MANY_TYPES:
return "CKR_USER_TOO_MANY_TYPES";
case CKR_WRAPPED_KEY_INVALID:
return "CKR_WRAPPED_KEY_INVALID";
case CKR_WRAPPED_KEY_LEN_RANGE:
return "CKR_WRAPPED_KEY_LEN_RANGE";
case CKR_WRAPPING_KEY_HANDLE_INVALID:
return "CKR_WRAPPING_KEY_HANDLE_INVALID";
case CKR_WRAPPING_KEY_SIZE_RANGE:
return "CKR_WRAPPING_KEY_SIZE_RANGE";
case CKR_WRAPPING_KEY_TYPE_INCONSISTENT:
return "CKR_WRAPPING_KEY_TYPE_INCONSISTENT";
case CKR_RANDOM_SEED_NOT_SUPPORTED:
return "CKR_RANDOM_SEED_NOT_SUPPORTED";
case CKR_RANDOM_NO_RNG:
return "CKR_RANDOM_NO_RNG";
case CKR_BUFFER_TOO_SMALL:
return "CKR_BUFFER_TOO_SMALL";
case CKR_SAVED_STATE_INVALID:
return "CKR_SAVED_STATE_INVALID";
case CKR_INFORMATION_SENSITIVE:
return "CKR_INFORMATION_SENSITIVE";
case CKR_STATE_UNSAVEABLE:
return "CKR_STATE_UNSAVEABLE";
case CKR_CRYPTOKI_NOT_INITIALIZED:
return "CKR_CRYPTOKI_NOT_INITIALIZED";
case CKR_CRYPTOKI_ALREADY_INITIALIZED:
return "CKR_CRYPTOKI_ALREADY_INITIALIZED";
case CKR_MUTEX_BAD:
return "CKR_MUTEX_BAD";
case CKR_MUTEX_NOT_LOCKED:
return "CKR_MUTEX_NOT_LOCKED";
default:
return "UNKNOWN";
}
return "UNKNOWN";
}