/*
 * COPYRIGHT (c) International Business Machines Corp. 2001-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
 */

/* Modified for S390 by Robert Burroughs                             */

#include <pthread.h>
#include <string.h>             // for memcmp() et al
#include <strings.h>
#include <stdlib.h>
#include <dlfcn.h>              // for dlopen()
#include <errno.h>

#ifndef NOAES
#include <openssl/aes.h>
#endif

#include "pkcs11types.h"
#include "p11util.h"
#include "defs.h"
#include "host_defs.h"
#include "h_extern.h"
#include "trace.h"

#include <ica_api.h>

#ifndef EC_DH
#define NO_EC
#warning "Your Libica does not provide ECC support, use Libica 3.3.0 or newer for ECC."
#endif

#include "tok_specific.h"
#include "tok_struct.h"

#ifndef NO_EC
#include "ec_defs.h"
#include "openssl/obj_mac.h"
#include <openssl/ec.h>
#endif

// declare the adapter open handle localy
static ica_adapter_handle_t adapter_handle;

// Linux really does not need these so we just dummy them up
// so the common code across platforms is usable...
#define KEYTYPE_MODEXPO   1
#define KEYTYPE_PKCSCRT   2

#define MAX_GENERIC_KEY_SIZE 256

const char manuf[] = "IBM Corp.";
const char model[] = "IBM ICA     ";
const char descr[] = "IBM PKCS#11 ICA token ";
const char label[] = "IBM ICA  PKCS #11";

static pthread_mutex_t rngmtx = PTHREAD_MUTEX_INITIALIZER;

#define LIBICA_SHARED_LIB "libica.so"
#define BIND(dso, sym)  (*(void **)(&p_##sym) = dlsym(dso, #sym))

#ifndef NO_EC
typedef ICA_EC_KEY *(*ica_ec_key_new_t) (unsigned int nid,
                                         unsigned int *privlen);
typedef int (*ica_ec_key_init_t) (const unsigned char *X,
                                  const unsigned char *Y,
                                  const unsigned char *D, ICA_EC_KEY *key);
typedef int (*ica_ec_key_generate_t) (ica_adapter_handle_t adapter_handle,
                                      ICA_EC_KEY *key);
typedef int (*ica_ecdh_derive_secret_t) (ica_adapter_handle_t adapter_handle,
                                         const ICA_EC_KEY *privkey_A,
                                         const ICA_EC_KEY *pubkey_B,
                                         unsigned char *z,
                                         unsigned int z_length);
typedef int (*ica_ecdsa_sign_t) (ica_adapter_handle_t adapter_handle,
                                 const ICA_EC_KEY *privkey,
                                 const unsigned char *hash,
                                 unsigned int hash_length,
                                 unsigned char *signature,
                                 unsigned int signature_length);
typedef int (*ica_ecdsa_verify_t) (ica_adapter_handle_t adapter_handle,
                                   const ICA_EC_KEY *pubkey,
                                   const unsigned char *hash,
                                   unsigned int hash_length,
                                   const unsigned char *signature,
                                   unsigned int signature_length);
typedef int (*ica_ec_key_get_public_key_t) (ICA_EC_KEY *key,
                                            unsigned char *q,
                                            unsigned int *q_len);
typedef int (*ica_ec_key_get_private_key_t) (ICA_EC_KEY *key,
                                             unsigned char *d,
                                             unsigned int *d_len);
typedef void (*ica_ec_key_free_t) (ICA_EC_KEY *key);

static ica_ec_key_new_t                p_ica_ec_key_new;
static ica_ec_key_init_t               p_ica_ec_key_init;
static ica_ec_key_generate_t           p_ica_ec_key_generate;
static ica_ecdh_derive_secret_t        p_ica_ecdh_derive_secret;
static ica_ecdsa_sign_t                p_ica_ecdsa_sign;
static ica_ecdsa_verify_t              p_ica_ecdsa_verify;
static ica_ec_key_get_public_key_t     p_ica_ec_key_get_public_key;
static ica_ec_key_get_private_key_t    p_ica_ec_key_get_private_key;
static ica_ec_key_free_t               p_ica_ec_key_free;

static CK_RV mech_list_ica_initialize(void);
static CK_RV ica_specific_get_mechanism_list(CK_MECHANISM_TYPE_PTR pMechanismList,
                                             CK_ULONG_PTR pulCount);
static CK_RV ica_specific_get_mechanism_info(CK_MECHANISM_TYPE type,
                                             CK_MECHANISM_INFO_PTR pInfo);

#define ICATOK_EC_MAX_D_LEN     66      /* secp521 */
#define ICATOK_EC_MAX_Q_LEN     (2*ICATOK_EC_MAX_D_LEN)
#define ICATOK_EC_MAX_SIG_LEN   ICATOK_EC_MAX_Q_LEN
#define ICATOK_EC_MAX_Z_LEN     ICATOK_EC_MAX_D_LEN

static int ica_ec_support_available = 0;

static CK_RV ecc_support_in_libica_available(void)
{
    if (p_ica_ec_key_new != NULL &&
        p_ica_ec_key_init  != NULL &&
        p_ica_ec_key_generate  != NULL &&
        p_ica_ecdh_derive_secret  != NULL &&
        p_ica_ecdsa_sign  != NULL &&
        p_ica_ecdsa_verify  != NULL &&
        p_ica_ec_key_get_public_key  != NULL &&
        p_ica_ec_key_get_private_key  != NULL &&
        p_ica_ec_key_free != NULL)
        return 1;

    return 0;
}
#endif

#ifdef SHA512_224
typedef unsigned int (*ica_sha512_224_t)(unsigned int message_part,
                                         unsigned int input_length,
                                         unsigned char *input_data,
                                         sha512_context_t *sha_context,
                                         unsigned char *output_data);

static ica_sha512_224_t                p_ica_sha512_224;
#endif

#ifdef SHA512_256
typedef unsigned int (*ica_sha512_256_t)(unsigned int message_part,
                                         unsigned int input_length,
                                         unsigned char *input_data,
                                         sha512_context_t *sha_context,
                                         unsigned char *output_data);

static ica_sha512_256_t                p_ica_sha512_256;
#endif

static CK_RV load_libica(void)
{
    void *ibmca_dso = NULL;

    /* Load libica */
    ibmca_dso = dlopen(LIBICA_SHARED_LIB, RTLD_NOW);
    if (ibmca_dso == NULL) {
        TRACE_ERROR("%s: dlopen(%s) failed\n", __func__, LIBICA_SHARED_LIB);
        return CKR_FUNCTION_FAILED;
    }

#ifndef NO_EC
    /* Try to resolve all needed functions for ecc support */
    BIND(ibmca_dso, ica_ec_key_new);
    BIND(ibmca_dso, ica_ec_key_init);
    BIND(ibmca_dso, ica_ec_key_generate);
    BIND(ibmca_dso, ica_ecdh_derive_secret);
    BIND(ibmca_dso, ica_ecdsa_sign);
    BIND(ibmca_dso, ica_ecdsa_verify);
    BIND(ibmca_dso, ica_ec_key_get_public_key);
    BIND(ibmca_dso, ica_ec_key_get_private_key);
    BIND(ibmca_dso, ica_ec_key_free);
#endif

#ifdef SHA512_224
    BIND(ibmca_dso, ica_sha512_224);
#endif
#ifdef SHA512_256
    BIND(ibmca_dso, ica_sha512_256);
#endif

    return CKR_OK;
}

CK_RV token_specific_rng(STDLL_TokData_t *tokdata, CK_BYTE *output,
                         CK_ULONG bytes)
{
    unsigned int rc;

    UNUSED(tokdata);

    pthread_mutex_lock(&rngmtx);

    rc = ica_random_number_generate((unsigned int) bytes, output);

    if (rc != 0) {
        pthread_mutex_unlock(&rngmtx);
        return CKR_GENERAL_ERROR;
        /* report error */
    }

    pthread_mutex_unlock(&rngmtx);

    return CKR_OK;
}

CK_RV token_specific_init(STDLL_TokData_t *tokdata, CK_SLOT_ID SlotNumber,
                          char *conf_name)
{
    CK_ULONG rc = CKR_OK;

    UNUSED(tokdata);
    UNUSED(conf_name);

    rc = load_libica();
    if (rc != CKR_OK)
        return rc;

#ifndef NO_EC
    ica_ec_support_available = ecc_support_in_libica_available();
#endif

    rc = mech_list_ica_initialize();
    if (rc != CKR_OK) {
        TRACE_ERROR("mech_list_ica_initialize failed\n");
        return rc;
    }

    TRACE_INFO("ica %s slot=%lu running\n", __func__, SlotNumber);

    return ica_open_adapter(&adapter_handle);
}

CK_RV token_specific_final()
{
    TRACE_INFO("ica %s running\n", __func__);
    ica_close_adapter(adapter_handle);

    return CKR_OK;
}

// count_ones_in_byte: for use in adjust_des_key_parity_bits below
static CK_BYTE count_ones_in_byte(CK_BYTE byte)
{
    CK_BYTE and_mask,           // bit selector
     number_of_ones = 0;

    for (and_mask = 1; and_mask != 0; and_mask <<= 1)   // for each bit,
        if (byte & and_mask)    // if it's a one,
            ++number_of_ones;   // count it

    return number_of_ones;
}

#define EVEN_PARITY TRUE
#define ODD_PARITY FALSE
 // adjust_des_key_parity_bits: to conform to NIST spec for DES and 3DES keys
static void adjust_des_key_parity_bits(CK_BYTE *des_key, CK_ULONG key_size,
                                       CK_BBOOL parity)
{
    CK_ULONG i;

    for (i = 0; i < key_size; i++) // look at each byte in the key
    {
        if ((count_ones_in_byte(des_key[i]) % 2) ^ (parity == ODD_PARITY)) {
            // if parity for this byte isn't what it should be,
            // flip the parity (least significant) bit
            des_key[i] ^= 1;
        }
    }
}

CK_RV token_specific_des_key_gen(STDLL_TokData_t *tokdata, CK_BYTE *des_key,
                                 CK_ULONG len, CK_ULONG keysize)
{
    UNUSED(keysize);

    // Nothing different to do for DES or TDES here as this is just
    // random data...  Validation handles the rest
    // Only check for weak keys when DES.
    if (len == (3 * DES_KEY_SIZE)) {
        rng_generate(tokdata, des_key, len);
        adjust_des_key_parity_bits(des_key, len, ODD_PARITY);
    } else {
        do {
            rng_generate(tokdata, des_key, len);
            adjust_des_key_parity_bits(des_key, len, ODD_PARITY);
        } while (des_check_weak_key(des_key) == TRUE);
    }


    // we really need to validate the key for parity etc...
    // we should do that here... The caller validates the single des keys
    // against the known and suspected poor keys..<<
    return CKR_OK;
}

CK_RV token_specific_des_ecb(STDLL_TokData_t *tokdata,
                             CK_BYTE *in_data,
                             CK_ULONG in_data_len,
                             CK_BYTE *out_data,
                             CK_ULONG *out_data_len,
                             OBJECT *key, CK_BYTE encrypt)
{
    CK_RV rc;
    CK_ATTRIBUTE *attr = NULL;

    UNUSED(tokdata);

    /*
     * checks for input and output data length and block sizes
     * are already being carried out in mech_des.c
     * so we skip those
     */

    if (template_attribute_find(key->template, CKA_VALUE, &attr) == FALSE) {
        TRACE_ERROR("Could not find CKA_VALUE for the key.\n");
        return CKR_FUNCTION_FAILED;
    }

    if (encrypt) {
        rc = ica_des_ecb(in_data, out_data, in_data_len, attr->pValue,
                         ICA_ENCRYPT);
    } else {
        rc = ica_des_ecb(in_data, out_data, in_data_len, attr->pValue,
                         ICA_DECRYPT);
    }

    if (rc != 0) {
        rc = CKR_FUNCTION_FAILED;
        TRACE_ERROR("%s\n", ock_err(ERR_FUNCTION_FAILED));
    } else {
        *out_data_len = in_data_len;
        rc = CKR_OK;
    }

    return rc;
}

CK_RV token_specific_des_cbc(STDLL_TokData_t *tokdata,
                             CK_BYTE *in_data,
                             CK_ULONG in_data_len,
                             CK_BYTE *out_data,
                             CK_ULONG *out_data_len,
                             OBJECT *key, CK_BYTE *init_v, CK_BYTE encrypt)
{
    CK_RV rc;
    CK_ATTRIBUTE *attr = NULL;

    UNUSED(tokdata);

    /*
     * checks for input and output data length and block sizes
     * are already being carried out in mech_des.c
     * so we skip those
     */

    // get the key value
    if (template_attribute_find(key->template, CKA_VALUE, &attr) == FALSE) {
        TRACE_ERROR("Could not find CKA_VALUE for the key.\n");
        return CKR_FUNCTION_FAILED;
    }

    if (encrypt) {
        rc = ica_des_cbc(in_data, out_data, in_data_len, attr->pValue, init_v,
                         ICA_ENCRYPT);
    } else {
        rc = ica_des_cbc(in_data, out_data, in_data_len, attr->pValue, init_v,
                         ICA_DECRYPT);
    }
    if (rc != 0) {
        TRACE_ERROR("%s\n", ock_err(ERR_FUNCTION_FAILED));
        rc = CKR_FUNCTION_FAILED;
    } else {
        *out_data_len = in_data_len;
        rc = CKR_OK;
    }

    return rc;
}

CK_RV token_specific_tdes_ecb(STDLL_TokData_t *tokdata,
                              CK_BYTE *in_data,
                              CK_ULONG in_data_len,
                              CK_BYTE *out_data,
                              CK_ULONG *out_data_len,
                              OBJECT *key, CK_BYTE encrypt)
{
    CK_RV rc;
    CK_ATTRIBUTE *attr = NULL;
    CK_KEY_TYPE keytype;
    CK_BYTE key_value[3 * DES_KEY_SIZE];

    UNUSED(tokdata);

    /*
     * checks for input and output data length and block sizes
     * are already being carried out in mech_des3.c
     * so we skip those
     */

    // get the key type
    rc = template_attribute_find(key->template, CKA_KEY_TYPE, &attr);
    if (rc == FALSE) {
        TRACE_ERROR("Could not find CKA_KEY_TYPE for the key.\n");
        return CKR_FUNCTION_FAILED;
    }
    keytype = *(CK_KEY_TYPE *) attr->pValue;

    // get the key value
    if (template_attribute_find(key->template, CKA_VALUE, &attr) == FALSE) {
        TRACE_ERROR("Could not find CKA_VALUE for the key.\n");
        return CKR_FUNCTION_FAILED;
    }
    if (keytype == CKK_DES2) {
        memcpy(key_value, attr->pValue, 2 * DES_KEY_SIZE);
        memcpy(key_value + (2 * DES_KEY_SIZE), attr->pValue, DES_KEY_SIZE);
    } else {
        memcpy(key_value, attr->pValue, 3 * DES_KEY_SIZE);
    }

    if (encrypt) {
        rc = ica_3des_ecb(in_data, out_data, in_data_len, key_value,
                          ICA_ENCRYPT);
    } else {
        rc = ica_3des_ecb(in_data, out_data, in_data_len, key_value,
                          ICA_DECRYPT);
    }

    if (rc != 0) {
        TRACE_ERROR("%s\n", ock_err(ERR_FUNCTION_FAILED));
        rc = CKR_FUNCTION_FAILED;
    } else {
        *out_data_len = in_data_len;
        rc = CKR_OK;
    }

    return rc;
}

CK_RV token_specific_tdes_cbc(STDLL_TokData_t *tokdata,
                              CK_BYTE *in_data,
                              CK_ULONG in_data_len,
                              CK_BYTE *out_data,
                              CK_ULONG *out_data_len,
                              OBJECT *key, CK_BYTE *init_v, CK_BYTE encrypt)
{
    CK_RV rc;
    CK_ATTRIBUTE *attr = NULL;
    CK_KEY_TYPE keytype;
    CK_BYTE key_value[3 * DES_KEY_SIZE];

    UNUSED(tokdata);

    /*
     * checks for input and output data length and block sizes
     * are already being carried out in mech_des3.c
     * so we skip those
     */

    // get the key type
    rc = template_attribute_find(key->template, CKA_KEY_TYPE, &attr);
    if (rc == FALSE) {
        TRACE_ERROR("Could not find CKA_KEY_TYPE for the key.\n");
        return CKR_FUNCTION_FAILED;
    }
    keytype = *(CK_KEY_TYPE *) attr->pValue;
    // get the key value
    if (template_attribute_find(key->template, CKA_VALUE, &attr) == FALSE) {
        TRACE_ERROR("Could not find CKA_VALUE for the key.\n");
        return CKR_FUNCTION_FAILED;
    }
    if (keytype == CKK_DES2) {
        memcpy(key_value, attr->pValue, 2 * DES_KEY_SIZE);
        memcpy(key_value + (2 * DES_KEY_SIZE), attr->pValue, DES_KEY_SIZE);
    } else {
        memcpy(key_value, attr->pValue, 3 * DES_KEY_SIZE);
    }

    if (encrypt) {
        rc = ica_3des_cbc(in_data, out_data, in_data_len, key_value, init_v,
                          ICA_ENCRYPT);
    } else {
        rc = ica_3des_cbc(in_data, out_data, in_data_len, key_value, init_v,
                          ICA_DECRYPT);
    }
    if (rc != 0) {
        TRACE_ERROR("%s\n", ock_err(ERR_FUNCTION_FAILED));
        rc = CKR_FUNCTION_FAILED;
    } else {
        *out_data_len = in_data_len;
        rc = CKR_OK;
    }

    return rc;
}

/*
 *
 * 0 Use the decrypt function.
 * 1 Use the encrypt function.
 */
CK_RV token_specific_tdes_ofb(STDLL_TokData_t *tokdata, CK_BYTE *in_data,
                              CK_BYTE *out_data, CK_ULONG data_len,
                              OBJECT *key, CK_BYTE *iv, uint_32 direction)
{
    CK_RV rc;
    CK_ATTRIBUTE *attr = NULL;

    UNUSED(tokdata);

    if (template_attribute_find(key->template, CKA_VALUE, &attr) == FALSE) {
        TRACE_ERROR("Could not find CKA_VALUE for the key.\n");
        return CKR_FUNCTION_FAILED;
    }

    rc = ica_3des_ofb(in_data, out_data, (unsigned int) data_len,
                      (unsigned char *) attr->pValue, (unsigned char *) iv,
                      direction);

    if (rc != 0) {
        TRACE_ERROR("%s\n", ock_err(ERR_FUNCTION_FAILED));
        rc = CKR_FUNCTION_FAILED;
    }

    return rc;
}

/*
 * 0 Use the decrypt function.
 * 1 Use the encrypt function.
 */
CK_RV token_specific_tdes_cfb(STDLL_TokData_t *tokdata, CK_BYTE *in_data,
                              CK_BYTE *out_data, CK_ULONG data_len,
                              OBJECT *key, CK_BYTE *iv, uint_32 cfb_len,
                              uint_32 direction)
{
    CK_RV rc;
    CK_ATTRIBUTE *attr = NULL;

    UNUSED(tokdata);

    if (template_attribute_find(key->template, CKA_VALUE, &attr) == FALSE) {
        TRACE_ERROR("Could not find CKA_VALUE for the key.\n");
        return CKR_FUNCTION_FAILED;
    }

    rc = ica_3des_cfb(in_data, out_data, (unsigned int) data_len,
                      (unsigned char *) attr->pValue, (unsigned char *) iv,
                      cfb_len, direction);

    if (rc != 0) {
        TRACE_ERROR("%s\n", ock_err(ERR_FUNCTION_FAILED));
        rc = CKR_FUNCTION_FAILED;
    }

    return rc;
}

CK_RV token_specific_tdes_mac(STDLL_TokData_t *tokdata, CK_BYTE *message,
                              CK_ULONG message_len, OBJECT *key, CK_BYTE *mac)
{
    CK_RV rc;
    CK_ATTRIBUTE *attr = NULL;
    CK_KEY_TYPE keytype;
    CK_BYTE key_value[3 * DES_KEY_SIZE];

    UNUSED(tokdata);

    // get the key type
    rc = template_attribute_find(key->template, CKA_KEY_TYPE, &attr);
    if (rc == FALSE) {
        TRACE_ERROR("Could not find CKA_KEY_TYPE for the key.\n");
        return CKR_FUNCTION_FAILED;
    }
    keytype = *(CK_KEY_TYPE *) attr->pValue;

    // get the key value
    if (template_attribute_find(key->template, CKA_VALUE, &attr) == FALSE) {
        TRACE_ERROR("Could not find CKA_VALUE for the key.\n");
        return CKR_FUNCTION_FAILED;
    }
    if (keytype == CKK_DES2) {
        memcpy(key_value, attr->pValue, 2 * DES_KEY_SIZE);
        memcpy(key_value + (2 * DES_KEY_SIZE), attr->pValue, DES_KEY_SIZE);
    } else {
        memcpy(key_value, attr->pValue, 3 * DES_KEY_SIZE);
    }

    rc = ica_3des_cmac_intermediate(message, (unsigned long) message_len,
                                    (unsigned char *) key_value, mac);

    if (rc != 0) {
        TRACE_ERROR("%s\n", ock_err(ERR_FUNCTION_FAILED));
        rc = CKR_FUNCTION_FAILED;
    }

    return rc;
}

/*
 * Init SHA data structures
 */
CK_RV token_specific_sha_init(STDLL_TokData_t *tokdata, DIGEST_CONTEXT *ctx,
                              CK_MECHANISM *mech)
{
    unsigned int ctxsize, devctxsize;
    struct oc_sha_ctx *sc;

    UNUSED(tokdata);

    ctxsize = (sizeof(struct oc_sha_ctx) + 0x000F) & ~0x000F;
    switch (mech->mechanism) {
    case CKM_SHA_1:
        devctxsize = sizeof(sha_context_t);
        break;
    case CKM_SHA224:
    case CKM_SHA256:
        devctxsize = sizeof(sha256_context_t);
        break;
    case CKM_SHA384:
    case CKM_SHA512:
#ifdef SHA512_224
    case CKM_SHA512_224:
#endif
#ifdef SHA512_256
    case CKM_SHA512_256:
#endif
        devctxsize = sizeof(sha512_context_t);
        break;
    default:
        return CKR_MECHANISM_INVALID;
    }

    /* (re)alloc ctx in one memory area */
    if (ctx->context)
        free(ctx->context);
    ctx->context_len = 0;
    ctx->context = malloc(ctxsize + devctxsize);
    if (ctx->context == NULL) {
        TRACE_ERROR("%s\n", ock_err(ERR_HOST_MEMORY));
        return CKR_HOST_MEMORY;
    }
    memset(ctx->context, 0, ctxsize + devctxsize);
    ctx->context_len = ctxsize + devctxsize;
    sc = (struct oc_sha_ctx *) ctx->context;
    sc->dev_ctx_offs = ctxsize;

    sc->message_part = SHA_MSG_PART_ONLY;
    switch (mech->mechanism) {
    case CKM_SHA_1:
        sc->hash_len = SHA1_HASH_SIZE;
        sc->hash_blksize = SHA1_BLOCK_SIZE;
        break;
    case CKM_SHA224:
        sc->hash_len = SHA224_HASH_SIZE;
        sc->hash_blksize = SHA224_BLOCK_SIZE;
        break;
    case CKM_SHA256:
        sc->hash_len = SHA256_HASH_SIZE;
        sc->hash_blksize = SHA256_BLOCK_SIZE;
        break;
    case CKM_SHA384:
        sc->hash_len = SHA384_HASH_SIZE;
        sc->hash_blksize = SHA384_BLOCK_SIZE;
        break;
    case CKM_SHA512:
        sc->hash_len = SHA512_HASH_SIZE;
        sc->hash_blksize = SHA512_BLOCK_SIZE;
        break;
#ifdef SHA512_224
    case CKM_SHA512_224:
        sc->hash_len = SHA224_HASH_SIZE;
        sc->hash_blksize = SHA512_BLOCK_SIZE;
        break;
#endif
#ifdef SHA512_256
    case CKM_SHA512_256:
        sc->hash_len = SHA256_HASH_SIZE;
        sc->hash_blksize = SHA512_BLOCK_SIZE;
        break;
#endif
    }

    return CKR_OK;
}

CK_RV token_specific_sha(STDLL_TokData_t *tokdata, DIGEST_CONTEXT *ctx,
                         CK_BYTE *in_data, CK_ULONG in_data_len,
                         CK_BYTE *out_data, CK_ULONG *out_data_len)
{
    int rc;
    CK_RV rv = CKR_OK;
    struct oc_sha_ctx *sc;
    void *dev_ctx;

    UNUSED(tokdata);

    if (!ctx || !ctx->context)
        return CKR_OPERATION_NOT_INITIALIZED;

    if (!in_data || !out_data)
        return CKR_ARGUMENTS_BAD;

    sc = (struct oc_sha_ctx *) ctx->context;
    dev_ctx = ((CK_BYTE *) sc) + sc->dev_ctx_offs;

    if (*out_data_len < sc->hash_len)
        return CKR_BUFFER_TOO_SMALL;

    sc->message_part = SHA_MSG_PART_ONLY;

    switch (ctx->mech.mechanism) {
    case CKM_SHA_1:
        {
            sha_context_t *ica_sha_ctx = (sha_context_t *) dev_ctx;
            rc = ica_sha1(sc->message_part, in_data_len,
                          in_data, ica_sha_ctx, sc->hash);
            break;
        }
    case CKM_SHA224:
        {
            sha256_context_t *ica_sha2_ctx = (sha256_context_t *) dev_ctx;
            rc = ica_sha224(sc->message_part, in_data_len,
                            in_data, ica_sha2_ctx, sc->hash);
            break;
        }
    case CKM_SHA256:
        {
            sha256_context_t *ica_sha2_ctx = (sha256_context_t *) dev_ctx;
            rc = ica_sha256(sc->message_part, in_data_len,
                            in_data, ica_sha2_ctx, sc->hash);
            break;
        }
    case CKM_SHA384:
        {
            sha512_context_t *ica_sha3_ctx = (sha512_context_t *) dev_ctx;
            rc = ica_sha384(sc->message_part, in_data_len,
                            in_data, ica_sha3_ctx, sc->hash);
            break;
        }
    case CKM_SHA512:
        {
            sha512_context_t *ica_sha5_ctx = (sha512_context_t *) dev_ctx;
            rc = ica_sha512(sc->message_part, in_data_len,
                            in_data, ica_sha5_ctx, sc->hash);
            break;
        }
#ifdef SHA512_224
    case CKM_SHA512_224:
        {
            sha512_context_t *ica_sha5_ctx = (sha512_context_t *) dev_ctx;

            if (p_ica_sha512_224 == NULL)
                return CKR_MECHANISM_INVALID;

            rc = p_ica_sha512_224(sc->message_part, in_data_len,
                                  in_data, ica_sha5_ctx, sc->hash);
            break;
        }
#endif
#ifdef SHA512_256
    case CKM_SHA512_256:
        {
            sha512_context_t *ica_sha5_ctx = (sha512_context_t *) dev_ctx;

            if (p_ica_sha512_256 == NULL)
                 return CKR_MECHANISM_INVALID;

            rc = p_ica_sha512_256(sc->message_part, in_data_len,
                                  in_data, ica_sha5_ctx, sc->hash);
            break;
        }
#endif
    default:
        return CKR_MECHANISM_INVALID;
    }

    if (rc == CKR_OK) {
        memcpy(out_data, sc->hash, sc->hash_len);
        *out_data_len = sc->hash_len;
    } else {
        rv = CKR_FUNCTION_FAILED;
    }

    return rv;
}

static CK_RV ica_sha_call(DIGEST_CONTEXT *ctx, CK_BYTE *data,
                          CK_ULONG data_len)
{
    struct oc_sha_ctx *sc = (struct oc_sha_ctx *) ctx->context;
    void *dev_ctx = ((CK_BYTE *) sc) + sc->dev_ctx_offs;
    CK_RV ret;

    switch (ctx->mech.mechanism) {
    case CKM_SHA_1:
        {
            sha_context_t *ica_sha_ctx = (sha_context_t *) dev_ctx;
            if (ica_sha_ctx->runningLength == 0)
                sc->message_part = SHA_MSG_PART_FIRST;
            else
                sc->message_part = SHA_MSG_PART_MIDDLE;
            ret = ica_sha1(sc->message_part, data_len, data,
                           ica_sha_ctx, sc->hash);
            break;
        }
    case CKM_SHA224:
        {
            sha256_context_t *ica_sha_ctx = (sha256_context_t *) dev_ctx;
            if (ica_sha_ctx->runningLength == 0)
                sc->message_part = SHA_MSG_PART_FIRST;
            else
                sc->message_part = SHA_MSG_PART_MIDDLE;
            ret = ica_sha224(sc->message_part, data_len, data,
                             ica_sha_ctx, sc->hash);
            break;
        }
    case CKM_SHA256:
        {
            sha256_context_t *ica_sha_ctx = (sha256_context_t *) dev_ctx;
            if (ica_sha_ctx->runningLength == 0)
                sc->message_part = SHA_MSG_PART_FIRST;
            else
                sc->message_part = SHA_MSG_PART_MIDDLE;
            ret = ica_sha256(sc->message_part, data_len, data,
                             ica_sha_ctx, sc->hash);
            break;
        }
    case CKM_SHA384:
        {
            sha512_context_t *ica_sha_ctx = (sha512_context_t *) dev_ctx;
            if (ica_sha_ctx->runningLengthLow == 0 &&
                ica_sha_ctx->runningLengthHigh == 0)
                sc->message_part = SHA_MSG_PART_FIRST;
            else
                sc->message_part = SHA_MSG_PART_MIDDLE;
            ret = ica_sha384(sc->message_part, data_len, data,
                             ica_sha_ctx, sc->hash);
            break;
        }
    case CKM_SHA512:
        {
            sha512_context_t *ica_sha_ctx = (sha512_context_t *) dev_ctx;
            if (ica_sha_ctx->runningLengthLow == 0 &&
                ica_sha_ctx->runningLengthHigh == 0)
                sc->message_part = SHA_MSG_PART_FIRST;
            else
                sc->message_part = SHA_MSG_PART_MIDDLE;
            ret = ica_sha512(sc->message_part, data_len, data,
                             ica_sha_ctx, sc->hash);
            break;
        }
#ifdef SHA512_224
    case CKM_SHA512_224:
        {
            sha512_context_t *ica_sha_ctx = (sha512_context_t *) dev_ctx;

            if (p_ica_sha512_224 == NULL)
                return CKR_MECHANISM_INVALID;

            if (ica_sha_ctx->runningLengthLow == 0 &&
                ica_sha_ctx->runningLengthHigh == 0)
                sc->message_part = SHA_MSG_PART_FIRST;
            else
                sc->message_part = SHA_MSG_PART_MIDDLE;
            ret = p_ica_sha512_224(sc->message_part, data_len, data,
                                   ica_sha_ctx, sc->hash);
            break;
        }
#endif
#ifdef SHA512_256
    case CKM_SHA512_256:
        {
            sha512_context_t *ica_sha_ctx = (sha512_context_t *) dev_ctx;

            if (p_ica_sha512_256 == NULL)
                return CKR_MECHANISM_INVALID;

            if (ica_sha_ctx->runningLengthLow == 0 &&
                ica_sha_ctx->runningLengthHigh == 0)
                sc->message_part = SHA_MSG_PART_FIRST;
            else
                sc->message_part = SHA_MSG_PART_MIDDLE;
            ret = p_ica_sha512_256(sc->message_part, data_len, data,
                                   ica_sha_ctx, sc->hash);
            break;
        }
#endif
    default:
        return CKR_MECHANISM_INVALID;
    }

    return (ret ? CKR_FUNCTION_FAILED : CKR_OK);
}

CK_RV token_specific_sha_update(STDLL_TokData_t *tokdata, DIGEST_CONTEXT *ctx,
                                CK_BYTE *in_data, CK_ULONG in_data_len)
{
    struct oc_sha_ctx *sc;
    int fill, len, rest, ret;

    UNUSED(tokdata);

    if (!ctx || !ctx->context)
        return CKR_OPERATION_NOT_INITIALIZED;

    if (!in_data_len)
        return CKR_OK;

    if (!in_data)
        return CKR_ARGUMENTS_BAD;

    sc = (struct oc_sha_ctx *) ctx->context;

    /* if less than blocksize, save to context buffer for next time */
    if (sc->tail_len + in_data_len < sc->hash_blksize) {
        memcpy(sc->tail + sc->tail_len, in_data, in_data_len);
        sc->tail_len += in_data_len;
        return CKR_OK;
    }

    /* we have at least one block */

    /* if some leftovers from the last update are available
       copy together one block into the tail buffer and hash it */
    if (sc->tail_len) {
        fill = sc->hash_blksize - sc->tail_len;
        memcpy(sc->tail + sc->tail_len, in_data, fill);

        /* hash blksize bytes from the tail buffer */
        ret = ica_sha_call(ctx, sc->tail, sc->hash_blksize);
        if (ret != CKR_OK)
            return ret;

        /* tail buffer is empty now */
        sc->tail_len = 0;

        /* adjust input data pointer and input data len */
        in_data += fill;
        in_data_len -= fill;

        /* if there is no more data to process, we are done */
        if (!in_data_len)
            return CKR_OK;
    }

    /* The tail buffer is empty now, and in_data_len is > 0.
     * Calculate amount of remaining bytes...
     */
    rest = in_data_len % sc->hash_blksize;

    /* and amount of bytes fitting into hash blocks */
    len = in_data_len - rest;

    /* process the full hash blocks */
    if (len > 0) {
        /* hash len bytes from input starting at the beginning */
        ret = ica_sha_call(ctx, in_data, len);
        if (ret != CKR_OK)
            return ret;

        /* adjust input data pointer */
        in_data += len;
    }

    /* Store remaining bytes into the empty tail buffer */
    if (rest > 0) {
        memcpy(sc->tail, in_data, rest);
        sc->tail_len = rest;
    }

    return CKR_OK;
}

CK_RV token_specific_sha_final(STDLL_TokData_t *tokdata, DIGEST_CONTEXT *ctx,
                               CK_BYTE *out_data, CK_ULONG *out_data_len)
{
    int rc;
    CK_RV rv = CKR_OK;
    struct oc_sha_ctx *sc;
    void *dev_ctx;

    UNUSED(tokdata);

    if (!ctx || !ctx->context)
        return CKR_OPERATION_NOT_INITIALIZED;

    if (!out_data || !out_data_len)
        return CKR_ARGUMENTS_BAD;

    sc = (struct oc_sha_ctx *) ctx->context;
    dev_ctx = ((CK_BYTE *) sc) + sc->dev_ctx_offs;
    sc->message_part = SHA_MSG_PART_FINAL;

    if (*out_data_len < sc->hash_len)
        return CKR_BUFFER_TOO_SMALL;

    switch (ctx->mech.mechanism) {
    case CKM_SHA_1:
        {
            sha_context_t *ica_sha1_ctx = (sha_context_t *) dev_ctx;
            /* accommodate multi-part when input was so small
             * that we never got to call into libica until final
             */
            if (ica_sha1_ctx->runningLength == 0)
                sc->message_part = SHA_MSG_PART_ONLY;
            rc = ica_sha1(sc->message_part, sc->tail_len,
                          (unsigned char *) sc->tail, ica_sha1_ctx, sc->hash);
            break;
        }
    case CKM_SHA224:
        {
            sha256_context_t *ica_sha2_ctx = (sha256_context_t *) dev_ctx;
            /* accommodate multi-part when input was so small
             * that we never got to call into libica until final
             */
            if (ica_sha2_ctx->runningLength == 0)
                sc->message_part = SHA_MSG_PART_ONLY;
            rc = ica_sha224(sc->message_part, sc->tail_len,
                            sc->tail, ica_sha2_ctx, sc->hash);
            break;
        }
    case CKM_SHA256:
        {
            sha256_context_t *ica_sha2_ctx = (sha256_context_t *) dev_ctx;
            /* accommodate multi-part when input was so small
             * that we never got to call into libica until final
             */
            if (ica_sha2_ctx->runningLength == 0)
                sc->message_part = SHA_MSG_PART_ONLY;
            rc = ica_sha256(sc->message_part, sc->tail_len,
                            sc->tail, ica_sha2_ctx, sc->hash);
            break;
        }
    case CKM_SHA384:
        {
            sha512_context_t *ica_sha3_ctx = (sha512_context_t *) dev_ctx;
            /* accommodate multi-part when input was so small
             * that we never got to call into libica until final
             */
            if (ica_sha3_ctx->runningLengthLow == 0
                && ica_sha3_ctx->runningLengthHigh == 0)
                sc->message_part = SHA_MSG_PART_ONLY;
            rc = ica_sha384(sc->message_part, sc->tail_len,
                            sc->tail, ica_sha3_ctx, sc->hash);
            break;
        }
    case CKM_SHA512:
        {
            sha512_context_t *ica_sha5_ctx = (sha512_context_t *) dev_ctx;
            /* accommodate multi-part when input was so small
             * that we never got to call into libica until final
             */
            if (ica_sha5_ctx->runningLengthLow == 0
                && ica_sha5_ctx->runningLengthHigh == 0)
                sc->message_part = SHA_MSG_PART_ONLY;
            rc = ica_sha512(sc->message_part, sc->tail_len,
                            sc->tail, ica_sha5_ctx, sc->hash);
            break;
        }
#ifdef SHA512_224
    case CKM_SHA512_224:
        {
            sha512_context_t *ica_sha5_ctx = (sha512_context_t *) dev_ctx;

            if (p_ica_sha512_224 == NULL)
                return CKR_MECHANISM_INVALID;

            /* accommodate multi-part when input was so small
             * that we never got to call into libica until final
             */
            if (ica_sha5_ctx->runningLengthLow == 0
                && ica_sha5_ctx->runningLengthHigh == 0)
                sc->message_part = SHA_MSG_PART_ONLY;
            rc = p_ica_sha512_224(sc->message_part, sc->tail_len,
                                  sc->tail, ica_sha5_ctx, sc->hash);
            break;
        }
#endif
#ifdef SHA512_256
    case CKM_SHA512_256:
        {
            sha512_context_t *ica_sha5_ctx = (sha512_context_t *) dev_ctx;

            if (p_ica_sha512_256 == NULL)
                return CKR_MECHANISM_INVALID;

            /* accommodate multi-part when input was so small
             * that we never got to call into libica until final
             */
            if (ica_sha5_ctx->runningLengthLow == 0
                && ica_sha5_ctx->runningLengthHigh == 0)
                sc->message_part = SHA_MSG_PART_ONLY;
            rc = p_ica_sha512_256(sc->message_part, sc->tail_len,
                                  sc->tail, ica_sha5_ctx, sc->hash);
            break;
        }
#endif
    default:
        return CKR_MECHANISM_INVALID;
    }

    if (rc != CKR_OK) {
        rv = CKR_FUNCTION_FAILED;
        goto out;
    }

    memcpy(out_data, sc->hash, sc->hash_len);
    *out_data_len = sc->hash_len;

out:
    return rv;
}

#ifndef LITE
#define LITE
#endif

/* Creates a libICA modulus+exponent key representation using
 * PKCS#11 attributes
 */
static ica_rsa_key_mod_expo_t *rsa_convert_mod_expo_key(CK_ATTRIBUTE *modulus,
                                                        CK_ATTRIBUTE *mod_bits,
                                                        CK_ATTRIBUTE *exponent)
{
    CK_BYTE *ptr = NULL;
    ica_rsa_key_mod_expo_t *modexpokey = NULL;

    /* We need at least the modulus and a (public|private) exponent */
    if (!modulus || !exponent) {
        TRACE_ERROR("%s\n", ock_err(ERR_FUNCTION_FAILED));
        return NULL;
    }

    modexpokey =
        (ica_rsa_key_mod_expo_t *) calloc(1, sizeof(ica_rsa_key_mod_expo_t));
    if (modexpokey == NULL) {
        TRACE_ERROR("%s\n", ock_err(ERR_HOST_MEMORY));
        goto err;
    }

    /* We can't rely solely on CKA_MODULUS_BITS here since Private Keys
     * using the modulus + private exponent representation may also go
     * through this path. Use modulus length in bytes as key_length if
     * no mod_bits is present */
    if (mod_bits != NULL && mod_bits->ulValueLen
        && (*(CK_ULONG *) mod_bits->pValue)) {
        modexpokey->key_length = ((*(CK_ULONG *) mod_bits->pValue) + 7) / 8;
    } else {
        modexpokey->key_length = modulus->ulValueLen;
    }

    /* maybe I'm over-cautious here */
    if ((modulus->ulValueLen > modexpokey->key_length) ||
        (exponent->ulValueLen > modexpokey->key_length)) {
        TRACE_ERROR("%s\n", ock_err(ERR_FUNCTION_FAILED));
        goto err;
    }

    modexpokey->modulus = (unsigned char *) calloc(1, modexpokey->key_length);

    if (modexpokey->modulus == NULL) {
        TRACE_ERROR("%s\n", ock_err(ERR_HOST_MEMORY));
        goto err;
    }

    /* right-justified fields */
    ptr = modexpokey->modulus + modexpokey->key_length - modulus->ulValueLen;
    memcpy(ptr, modulus->pValue, modexpokey->key_length);

    modexpokey->exponent = (unsigned char *) calloc(1, modexpokey->key_length);
    if (modexpokey->exponent == NULL) {
        TRACE_ERROR("%s\n", ock_err(ERR_HOST_MEMORY));
        goto err;
    }

    ptr = modexpokey->exponent + modexpokey->key_length - exponent->ulValueLen;
    memcpy(ptr, exponent->pValue, exponent->ulValueLen);
    return modexpokey;

err:
    free(modexpokey->modulus);
    free(modexpokey->exponent);
    free(modexpokey);

    return NULL;
}

/* Creates a libICA CRT key representation using
 * PKCS#11 attributes
 */
static ica_rsa_key_crt_t *rsa_convert_crt_key(CK_ATTRIBUTE *modulus,
                                              CK_ATTRIBUTE *prime1,
                                              CK_ATTRIBUTE *prime2,
                                              CK_ATTRIBUTE *exp1,
                                              CK_ATTRIBUTE *exp2,
                                              CK_ATTRIBUTE *coeff)
{
    CK_BYTE *ptr = NULL;
    ica_rsa_key_crt_t *crtkey = NULL;

    /* All the above params are required to build a CRT key
     * that can be used by libICA. Private Keys with modulus
     * and private exponent should use rsa_convert_mod_expo_key() */
    if (!modulus || !prime1 || !prime2 || !exp1 || !exp2 || !coeff) {
        TRACE_ERROR("%s\n", ock_err(ERR_FUNCTION_FAILED));
        return NULL;
    } else {
        crtkey = (ica_rsa_key_crt_t *) calloc(1, sizeof(ica_rsa_key_crt_t));
        if (crtkey == NULL) {
            TRACE_ERROR("%s\n", ock_err(ERR_HOST_MEMORY));
            return NULL;
        }
        /* use modulus length in bytes as key_length */
        crtkey->key_length = modulus->ulValueLen;

        /* buffers pointed by p, q, dp, dq and qInverse in struct
         * ica_rsa_key_crt_t must be of size key_legth/2 or larger.
         * p, dp and qInverse have an additional 8-byte padding. */

        /* need to allocate the buffers. Also, all fields are
         * right-aligned, thus the use for ptr */

        /* FIXME: if individual components lengths are bigger then
         * what we support in libICA then we're in trouble,
         * but maybe explicitly checking them is being over-zealous? */
        if ((prime1->ulValueLen > (crtkey->key_length / 2)) ||
            (prime2->ulValueLen > (crtkey->key_length / 2)) ||
            (exp1->ulValueLen > (crtkey->key_length / 2)) ||
            (exp2->ulValueLen > (crtkey->key_length / 2)) ||
            (coeff->ulValueLen > (crtkey->key_length / 2))) {
            TRACE_ERROR("%s\n", ock_err(ERR_FUNCTION_FAILED));
            goto err_crtkey;
        }
        crtkey->p = (unsigned char *) calloc(1, (crtkey->key_length / 2) + 8);
        if (crtkey->p == NULL) {
            TRACE_ERROR("%s\n", ock_err(ERR_HOST_MEMORY));
            goto err_crtkey;
        }
        ptr = crtkey->p + (crtkey->key_length / 2) + 8 - prime1->ulValueLen;
        memcpy(ptr, prime1->pValue, prime1->ulValueLen);

        crtkey->q = (unsigned char *) calloc(1, crtkey->key_length / 2);

        if (crtkey->q == NULL) {
            TRACE_ERROR("%s\n", ock_err(ERR_HOST_MEMORY));
            goto err_crtkey;
        }
        ptr = crtkey->q + (crtkey->key_length / 2) - prime2->ulValueLen;
        memcpy(ptr, prime2->pValue, prime2->ulValueLen);

        crtkey->dp = (unsigned char *) calloc(1, (crtkey->key_length / 2) + 8);
        if (crtkey->dp == NULL) {
            TRACE_ERROR("%s\n", ock_err(ERR_HOST_MEMORY));
            goto err_crtkey;
        }
        ptr = crtkey->dp + (crtkey->key_length / 2) + 8 - exp1->ulValueLen;
        memcpy(ptr, exp1->pValue, exp1->ulValueLen);

        crtkey->dq = (unsigned char *) calloc(1, crtkey->key_length / 2);
        if (crtkey->dq == NULL) {
            TRACE_ERROR("%s\n", ock_err(ERR_HOST_MEMORY));
            goto err_crtkey;
        }
        ptr = crtkey->dq + (crtkey->key_length / 2) - exp2->ulValueLen;
        memcpy(ptr, exp2->pValue, exp2->ulValueLen);

        crtkey->qInverse =
            (unsigned char *) calloc(1, (crtkey->key_length / 2) + 8);
        if (crtkey->qInverse == NULL) {
            TRACE_ERROR("%s\n", ock_err(ERR_HOST_MEMORY));
            goto err_crtkey;
        }
        ptr =
            crtkey->qInverse + (crtkey->key_length / 2) + 8 - coeff->ulValueLen;
        memcpy(ptr, coeff->pValue, coeff->ulValueLen);

        return crtkey;
    }

err_crtkey:
    free(crtkey->p);
    free(crtkey->q);
    free(crtkey->dp);
    free(crtkey->dq);
    free(crtkey->qInverse);
    free(crtkey);

    return NULL;
}


//
static CK_RV os_specific_rsa_keygen(TEMPLATE *publ_tmpl, TEMPLATE *priv_tmpl)
{
    CK_ATTRIBUTE *publ_exp = NULL;
    CK_ATTRIBUTE *attr = NULL;
    CK_BYTE *ptr = NULL;
    CK_ULONG mod_bits;
    CK_BBOOL flag;
    unsigned long tmpsize;
    CK_RV rc;
    ica_rsa_key_mod_expo_t *publKey = NULL;
    ica_rsa_key_crt_t *privKey = NULL;

    flag = template_attribute_find(publ_tmpl, CKA_MODULUS_BITS, &attr);
    if (!flag) {
        TRACE_ERROR("%s\n", ock_err(ERR_TEMPLATE_INCOMPLETE));
        return CKR_TEMPLATE_INCOMPLETE; // should never happen
    }
    mod_bits = *(CK_ULONG *) attr->pValue;

    flag = template_attribute_find(publ_tmpl, CKA_PUBLIC_EXPONENT, &publ_exp);
    if (!flag) {
        TRACE_ERROR("%s\n", ock_err(ERR_TEMPLATE_INCOMPLETE));
        return CKR_TEMPLATE_INCOMPLETE;
    }
    // FIXME: is this check really necessary?
    if (mod_bits < 512 || mod_bits > 4096) {
        TRACE_ERROR("%s\n", ock_err(ERR_KEY_SIZE_RANGE));
        return CKR_KEY_SIZE_RANGE;
    }

    /* libICA replicates the openSSL requirement that the public exponent
     * can't be larger than the size of an unsigned long
     */
    if (publ_exp->ulValueLen > sizeof(unsigned long)) {
        TRACE_ERROR("%s\n", ock_err(ERR_KEY_SIZE_RANGE));
        return CKR_KEY_SIZE_RANGE;
    }

    /* Build publKey:
     * The buffers in ica_rsa_key_mod_expo_t must be
     * allocated by the caller, with key_length size
     * use calloc() so that memory is zeroed (right alignment) */
    publKey =
        (ica_rsa_key_mod_expo_t *) calloc(1, sizeof(ica_rsa_key_mod_expo_t));
    if (publKey == NULL) {
        TRACE_ERROR("%s\n", ock_err(ERR_HOST_MEMORY));
        return CKR_HOST_MEMORY;
    }

    /* key_length is in terms of bytes */
    publKey->key_length = ((mod_bits + 7) / 8);

    publKey->modulus = (unsigned char *) calloc(1, publKey->key_length);
    if (publKey->modulus == NULL) {
        TRACE_ERROR("%s\n", ock_err(ERR_HOST_MEMORY));
        rc = CKR_HOST_MEMORY;
        goto pubkey_cleanup;
    }

    publKey->exponent = (unsigned char *) calloc(1, publKey->key_length);
    if (publKey->exponent == NULL) {
        TRACE_ERROR("%s\n", ock_err(ERR_HOST_MEMORY));
        rc = CKR_HOST_MEMORY;
        goto pubkey_cleanup;
    }

    /* Use the provided public exponent:
     * all fields must be right-aligned, so make
     * sure we only use the rightmost part */
    /* We know the pub_exp attribute has it's value in BIG ENDIAN        *
     * byte order, and we're assuming we're on s390(x) which is also     *
     * BIG ENDIAN, so no byte swapping required.                         *
     * FIXME: Will need to fix that if porting for little endian         */
    ptr = publKey->exponent + publKey->key_length - publ_exp->ulValueLen;
    memcpy(ptr, publ_exp->pValue, publ_exp->ulValueLen);

    /* If the public exponent is zero, libica will generate a random one *
     * If it is an even number, then we have a problem. Use ptr to cast  *
     * to unsigned int and check                                         */
    ptr = publKey->exponent + publKey->key_length - sizeof(unsigned long);
    if (*((unsigned long *) ptr) != 0 && *((unsigned long *) ptr) % 2 == 0) {
        TRACE_ERROR("%s\n", ock_err(ERR_TEMPLATE_INCONSISTENT));
        return CKR_TEMPLATE_INCONSISTENT;
    }


    /* Build privKey:
     * buffers pointed by p, q, dp, dq and qInverse in struct
     * ica_rsa_key_crt_t must be of size key_legth/2 or larger.
     * p, dp and qInverse have an additional 8-byte padding */
    privKey = (ica_rsa_key_crt_t *) calloc(1, sizeof(ica_rsa_key_crt_t));
    if (privKey == NULL) {
        TRACE_ERROR("%s\n", ock_err(ERR_HOST_MEMORY));
        rc = CKR_HOST_MEMORY;
        goto pubkey_cleanup;
    }

    /* modexpo and crt key lengths are always the same */
    privKey->key_length = publKey->key_length;

    privKey->p = (unsigned char *) calloc(1, (privKey->key_length / 2) + 8);
    if (privKey->p == NULL) {
        TRACE_ERROR("%s\n", ock_err(ERR_HOST_MEMORY));
        rc = CKR_HOST_MEMORY;
        goto privkey_cleanup;
    }

    privKey->q = (unsigned char *) calloc(1, privKey->key_length / 2);
    if (privKey->q == NULL) {
        TRACE_ERROR("%s\n", ock_err(ERR_HOST_MEMORY));
        rc = CKR_HOST_MEMORY;
        goto privkey_cleanup;
    }

    privKey->dp = (unsigned char *) calloc(1, (privKey->key_length / 2) + 8);
    if (privKey->dp == NULL) {
        TRACE_ERROR("%s\n", ock_err(ERR_HOST_MEMORY));
        rc = CKR_HOST_MEMORY;
        goto privkey_cleanup;
    }

    privKey->dq = (unsigned char *) calloc(1, privKey->key_length / 2);
    if (privKey->dq == NULL) {
        TRACE_ERROR("%s\n", ock_err(ERR_HOST_MEMORY));
        rc = CKR_HOST_MEMORY;
        goto privkey_cleanup;
    }

    privKey->qInverse =
        (unsigned char *) calloc(1, (privKey->key_length / 2) + 8);
    if (privKey->qInverse == NULL) {
        TRACE_ERROR("%s\n", ock_err(ERR_HOST_MEMORY));
        rc = CKR_HOST_MEMORY;
        goto privkey_cleanup;
    }

    rc = ica_rsa_key_generate_crt(adapter_handle,
                                  (unsigned int) mod_bits, publKey, privKey);


    if (rc) {
        TRACE_ERROR("%s\n", ock_err(ERR_FUNCTION_FAILED));
        rc = CKR_FUNCTION_FAILED;
        goto privkey_cleanup;
    }


    /* Build the PKCS#11 public key */
    // modulus: n
    //
    tmpsize = publKey->key_length;
    ptr = p11_bigint_trim(publKey->modulus, &tmpsize);
    if (tmpsize != publKey->key_length) {
        /* This is bad */
        TRACE_ERROR("%s\n", ock_err(ERR_FUNCTION_FAILED));
        rc = CKR_FUNCTION_FAILED;
        goto privkey_cleanup;
    }
    rc = build_attribute(CKA_MODULUS, ptr, tmpsize, &attr);
    if (rc != CKR_OK) {
        TRACE_DEVEL("build_attribute failed\n");
        goto privkey_cleanup;
    }
    template_update_attribute(publ_tmpl, attr);

    // public exponent
    //
    tmpsize = publKey->key_length;
    ptr = p11_bigint_trim(publKey->exponent, &tmpsize);
    rc = build_attribute(CKA_PUBLIC_EXPONENT, ptr, tmpsize, &attr);
    if (rc != CKR_OK) {
        TRACE_DEVEL("build attribute failed\n");
        goto privkey_cleanup;
    }
    template_update_attribute(publ_tmpl, attr);


    // local = TRUE
    //
    flag = TRUE;
    rc = build_attribute(CKA_LOCAL, &flag, sizeof(CK_BBOOL), &attr);
    if (rc != CKR_OK) {
        TRACE_DEVEL("build_attribute failed\n");
        goto privkey_cleanup;
    }
    template_update_attribute(publ_tmpl, attr);

    //
    // now, do the private key
    //

    // public exponent: e
    //
    tmpsize = publKey->key_length;
    ptr = p11_bigint_trim(publKey->exponent, &tmpsize);
    rc = build_attribute(CKA_PUBLIC_EXPONENT, ptr, tmpsize, &attr);
    if (rc != CKR_OK) {
        TRACE_DEVEL("build_attribute failed\n");
        goto privkey_cleanup;
    }
    template_update_attribute(priv_tmpl, attr);

    // modulus: n
    //
    tmpsize = publKey->key_length;
    ptr = p11_bigint_trim(publKey->modulus, &tmpsize);
    if (tmpsize != publKey->key_length) {
        /* This is bad */
        TRACE_ERROR("%s\n", ock_err(ERR_FUNCTION_FAILED));
        rc = CKR_FUNCTION_FAILED;
        goto privkey_cleanup;
    }
    rc = build_attribute(CKA_MODULUS, ptr, tmpsize, &attr);
    if (rc != CKR_OK) {
        TRACE_DEVEL("build_attribute failed\n");
        goto privkey_cleanup;
    }
    template_update_attribute(priv_tmpl, attr);

    // exponent 1: d mod(p-1)
    //
    tmpsize = privKey->key_length / 2;
    ptr = p11_bigint_trim(privKey->dp + 8, &tmpsize);
    rc = build_attribute(CKA_EXPONENT_1, ptr, tmpsize, &attr);
    if (rc != CKR_OK) {
        TRACE_DEVEL("build_attribute failed\n");
        goto privkey_cleanup;
    }
    template_update_attribute(priv_tmpl, attr);

    // exponent 2: d mod(q-1)
    //
    tmpsize = privKey->key_length / 2;
    ptr = p11_bigint_trim(privKey->dq, &tmpsize);
    rc = build_attribute(CKA_EXPONENT_2, ptr, tmpsize, &attr);
    if (rc != CKR_OK) {
        TRACE_DEVEL("build_attribute failed\n");
        goto privkey_cleanup;
    }
    template_update_attribute(priv_tmpl, attr);

    // prime #1: p
    //
    tmpsize = privKey->key_length / 2;
    ptr = p11_bigint_trim(privKey->p + 8, &tmpsize);
    rc = build_attribute(CKA_PRIME_1, ptr, tmpsize, &attr);
    if (rc != CKR_OK) {
        TRACE_DEVEL("build_attribute failed\n");
        goto privkey_cleanup;
    }
    template_update_attribute(priv_tmpl, attr);


    // prime #2: q
    //
    tmpsize = privKey->key_length / 2;
    ptr = p11_bigint_trim(privKey->q, &tmpsize);
    rc = build_attribute(CKA_PRIME_2, privKey->q,
                         privKey->key_length / 2, &attr);
    if (rc != CKR_OK) {
        TRACE_DEVEL("build_attribute failed\n");
        goto privkey_cleanup;
    }
    template_update_attribute(priv_tmpl, attr);


    // CRT coefficient:  q_inverse mod(p)
    //
    tmpsize = privKey->key_length / 2;
    ptr = p11_bigint_trim(privKey->qInverse + 8, &tmpsize);
    rc = build_attribute(CKA_COEFFICIENT, ptr, tmpsize, &attr);
    if (rc != CKR_OK) {
        TRACE_DEVEL("build_attribute failed\n");
        goto privkey_cleanup;
    }
    template_update_attribute(priv_tmpl, attr);

privkey_cleanup:
    free(privKey->p);
    free(privKey->q);
    free(privKey->dp);
    free(privKey->dq);
    free(privKey->qInverse);
    free(privKey);
pubkey_cleanup:
    free(publKey->modulus);
    free(publKey->exponent);
    free(publKey);

    return rc;
}

CK_RV token_specific_rsa_generate_keypair(STDLL_TokData_t *tokdata,
                                          TEMPLATE *publ_tmpl,
                                          TEMPLATE *priv_tmpl)
{
    CK_RV rc;

    UNUSED(tokdata);

    rc = os_specific_rsa_keygen(publ_tmpl, priv_tmpl);
    if (rc != CKR_OK)
        TRACE_DEVEL("os_specific_rsa_keygen failed\n");

    return rc;
}


//
//
static CK_RV os_specific_rsa_encrypt(CK_BYTE *in_data,
                                     CK_ULONG in_data_len,
                                     CK_BYTE *out_data, OBJECT *key_obj)
{
    CK_ATTRIBUTE *modulus = NULL;
    CK_ATTRIBUTE *pub_exp = NULL;
    CK_ATTRIBUTE *mod_bits = NULL;
    ica_rsa_key_mod_expo_t *publKey = NULL;
    CK_RV rc;

    /* mech_sra.c:ckm_rsa_encrypt accepts only CKO_PUBLIC_KEY */
    template_attribute_find(key_obj->template, CKA_MODULUS, &modulus);
    template_attribute_find(key_obj->template, CKA_MODULUS_BITS, &mod_bits);
    template_attribute_find(key_obj->template, CKA_PUBLIC_EXPONENT, &pub_exp);

    publKey = rsa_convert_mod_expo_key(modulus, mod_bits, pub_exp);
    if (publKey == NULL) {
        TRACE_ERROR("rsa_convert_mod_expo_key failed\n");
        rc = CKR_FUNCTION_FAILED;
        goto done;
    }

    /* in_data must be in big endian format. 'in_data' size in bits must not
     * exceed the bit length of the key, and size in bytes must
     * be of the same length of the key */
    // FIXME: we're not cheking the size in bits of in_data - but how could we?
    if (publKey->key_length != in_data_len) {
        TRACE_ERROR("%s\n", ock_err(ERR_DATA_LEN_RANGE));
        rc = CKR_DATA_LEN_RANGE;
        goto cleanup_pubkey;
    }
    rc = ica_rsa_mod_expo(adapter_handle, in_data, publKey, out_data);
    if (rc != 0) {
        if (rc == EINVAL) {
            TRACE_ERROR("%s\n", ock_err(ERR_ARGUMENTS_BAD));
            rc = CKR_ARGUMENTS_BAD;
        } else {
            TRACE_ERROR("%s\n", ock_err(ERR_FUNCTION_FAILED));
            rc = CKR_FUNCTION_FAILED;
        }
        goto cleanup_pubkey;
    }

    rc = CKR_OK;

cleanup_pubkey:
    free(publKey->modulus);
    free(publKey->exponent);
    free(publKey);

done:
    return rc;
}

//
//
static CK_RV os_specific_rsa_decrypt(CK_BYTE *in_data,
                                     CK_ULONG in_data_len,
                                     CK_BYTE *out_data, OBJECT *key_obj)
{
    CK_ATTRIBUTE *modulus = NULL;
    CK_ATTRIBUTE *prime1 = NULL;
    CK_ATTRIBUTE *prime2 = NULL;
    CK_ATTRIBUTE *exp1 = NULL;
    CK_ATTRIBUTE *exp2 = NULL;
    CK_ATTRIBUTE *coeff = NULL;
    CK_ATTRIBUTE *priv_exp = NULL;
    ica_rsa_key_crt_t *crtKey = NULL;
    ica_rsa_key_mod_expo_t *modexpoKey = NULL;
    CK_RV rc;

    /* mech_rsa.c:ckm_rsa_decrypt accepts only CKO_PRIVATE_KEY,
     * but Private Key can have 2 representations (see PKCS#1):
     *  - Modulus + private exponent
     *  - p, q, dp, dq and qInv (CRT format)
     * The former should use ica_rsa_key_mod_expo_t and the latter
     * ica_rsa_key_crt_t. Detect what representation this
     * key_obj has and use the proper convert function */

    template_attribute_find(key_obj->template, CKA_MODULUS, &modulus);
    template_attribute_find(key_obj->template, CKA_PRIVATE_EXPONENT, &priv_exp);
    template_attribute_find(key_obj->template, CKA_PRIME_1, &prime1);
    template_attribute_find(key_obj->template, CKA_PRIME_2, &prime2);
    template_attribute_find(key_obj->template, CKA_EXPONENT_1, &exp1);
    template_attribute_find(key_obj->template, CKA_EXPONENT_2, &exp2);
    template_attribute_find(key_obj->template, CKA_COEFFICIENT, &coeff);

    /* Need to check for CRT Key format *BEFORE* check for mod_expo key,
     * that's because opencryptoki *HAS* a CKA_PRIVATE_EXPONENT attribute
     * even in CRT keys (but with zero length) */
    // FIXME: Checking for non-zero lengths anyway (might be overkill)

    if (modulus && modulus->ulValueLen &&
        prime1 && prime1->ulValueLen &&
        prime2 && prime2->ulValueLen &&
        exp1 && exp1->ulValueLen &&
        exp2 && exp2->ulValueLen && coeff && coeff->ulValueLen) {
        /* ica_rsa_key_crt_t representation */
        crtKey =
            rsa_convert_crt_key(modulus, prime1, prime2, exp1, exp2, coeff);
        if (crtKey == NULL) {
            TRACE_ERROR("rsa_convert_crt_key failed\n");
            rc = CKR_FUNCTION_FAILED;
            goto done;
        }
        /* same check as above */
        if (crtKey->key_length != in_data_len) {
            TRACE_ERROR("%s\n", ock_err(ERR_DATA_LEN_RANGE));
            rc = CKR_ENCRYPTED_DATA_LEN_RANGE;
            goto crt_cleanup;
        }

        rc = ica_rsa_crt(adapter_handle, in_data, crtKey, out_data);

        if (rc != 0) {
            TRACE_ERROR("%s\n", ock_err(ERR_FUNCTION_FAILED));
            rc = CKR_FUNCTION_FAILED;
        } else {
            rc = CKR_OK;
        }
        goto crt_cleanup;
    } else if (modulus && modulus->ulValueLen &&
               priv_exp && priv_exp->ulValueLen) {
        /* ica_rsa_key_mod_expo_t representation */
        modexpoKey = rsa_convert_mod_expo_key(modulus, NULL, priv_exp);
        if (modexpoKey == NULL) {
            TRACE_ERROR("rsa_convert_mod_expo_key failed\n");
            rc = CKR_FUNCTION_FAILED;
            goto done;
        }
        /* in_data must be in big endian format. Size in bits must not
         * exceed the bit length of the key, and size in bytes must
         * be the same */
        // FIXME: we're not cheking the size in bits of in_data
        // - but how could we?
        if (modexpoKey->key_length != in_data_len) {
            TRACE_ERROR("%s\n", ock_err(ERR_DATA_LEN_RANGE));
            rc = CKR_ENCRYPTED_DATA_LEN_RANGE;
            goto modexpo_cleanup;
        }

        rc = ica_rsa_mod_expo(adapter_handle, in_data, modexpoKey, out_data);

        if (rc != 0) {
            TRACE_ERROR("%s\n", ock_err(ERR_FUNCTION_FAILED));
            rc = CKR_FUNCTION_FAILED;
        } else {
            rc = CKR_OK;
        }
        goto modexpo_cleanup;
    } else {
        /* should never happen */
        TRACE_ERROR("%s\n", ock_err(ERR_MECHANISM_PARAM_INVALID));
        rc = CKR_MECHANISM_PARAM_INVALID;
        goto done;
    }

crt_cleanup:
    free(crtKey->p);
    free(crtKey->q);
    free(crtKey->dp);
    free(crtKey->dq);
    free(crtKey->qInverse);
    free(crtKey);
    goto done;

modexpo_cleanup:
    free(modexpoKey->modulus);
    free(modexpoKey->exponent);
    free(modexpoKey);

done:
    return rc;
}


CK_RV token_specific_rsa_encrypt(STDLL_TokData_t *tokdata, CK_BYTE *in_data,
                                 CK_ULONG in_data_len, CK_BYTE *out_data,
                                 CK_ULONG *out_data_len, OBJECT *key_obj)
{
    CK_RV rc;
    CK_BYTE clear[MAX_RSA_KEYLEN], cipher[MAX_RSA_KEYLEN];
    CK_ULONG modulus_bytes;
    CK_BBOOL flag;
    CK_ATTRIBUTE *attr = NULL;

    flag = template_attribute_find(key_obj->template, CKA_MODULUS, &attr);
    if (flag == FALSE) {
        TRACE_ERROR("Could not find CKA_MODULUS for the key.\n");
        return CKR_FUNCTION_FAILED;
    } else {
        modulus_bytes = attr->ulValueLen;
    }

    /* format the data */
    rc = rsa_format_block(tokdata, in_data, in_data_len, clear,
                          modulus_bytes, PKCS_BT_2);
    if (rc != CKR_OK) {
        TRACE_DEVEL("rsa_format_block failed\n");
        return rc;
    }

    rc = os_specific_rsa_encrypt(clear, modulus_bytes, cipher, key_obj);
    if (rc == CKR_OK) {
        memcpy(out_data, cipher, modulus_bytes);
        *out_data_len = modulus_bytes;
    } else {
        TRACE_DEVEL("os_specific_rsa_encrypt failed\n");
    }

    return rc;
}


CK_RV token_specific_rsa_decrypt(STDLL_TokData_t *tokdata, CK_BYTE *in_data,
                                 CK_ULONG in_data_len, CK_BYTE *out_data,
                                 CK_ULONG *out_data_len, OBJECT *key_obj)
{
    CK_BYTE out[MAX_RSA_KEYLEN];
    CK_RV rc;

    UNUSED(tokdata);

    rc = os_specific_rsa_decrypt(in_data, in_data_len, out, key_obj);

    if (rc != CKR_OK) {
        TRACE_DEVEL("os_specific_rsa_decrypt failed\n");
        return rc;
    }

    rc = rsa_parse_block(out, in_data_len, out_data, out_data_len, PKCS_BT_2);
    if (rc != CKR_OK) {
        TRACE_DEVEL("rsa_parse_block failed\n");
        return rc;
    }

    /*
     * For PKCS #1 v1.5 padding, out_data_len must be less
     * than in_data_len (which is modulus_bytes) - 11.
     */
    if (*out_data_len > (in_data_len - 11)) {
        TRACE_ERROR("%s\n", ock_err(ERR_ENCRYPTED_DATA_LEN_RANGE));
        rc = CKR_ENCRYPTED_DATA_LEN_RANGE;
    }

    return rc;
}

CK_RV token_specific_rsa_sign(STDLL_TokData_t *tokdata, SESSION *sess,
                              CK_BYTE *in_data, CK_ULONG in_data_len,
                              CK_BYTE *out_data, CK_ULONG *out_data_len,
                              OBJECT *key_obj)
{
    CK_ATTRIBUTE *attr = NULL;
    CK_BBOOL flag;
    CK_RV rc;
    CK_BYTE data[MAX_RSA_KEYLEN], sig[MAX_RSA_KEYLEN];
    CK_ULONG modulus_bytes;

    UNUSED(tokdata);
    UNUSED(sess);

    flag = template_attribute_find(key_obj->template, CKA_MODULUS, &attr);
    if (flag == FALSE) {
        TRACE_ERROR("Could not find CKA_MODULUS for the key.\n");
        return CKR_FUNCTION_FAILED;
    } else {
        modulus_bytes = attr->ulValueLen;
    }

    rc = rsa_format_block(tokdata, in_data, in_data_len, data,
                          modulus_bytes, PKCS_BT_1);
    if (rc != CKR_OK) {
        TRACE_DEVEL("rsa_format_block failed\n");
        return rc;
    }

    /* signing is a private key operation --> decrypt  */
    rc = os_specific_rsa_decrypt(data, modulus_bytes, sig, key_obj);
    if (rc == CKR_OK) {
        memcpy(out_data, sig, modulus_bytes);
        *out_data_len = modulus_bytes;
    } else {
        TRACE_DEVEL("os_specific_rsa_decrypt failed\n");
    }

    return rc;
}

CK_RV token_specific_rsa_verify(STDLL_TokData_t *tokdata, SESSION *sess,
                                CK_BYTE *in_data, CK_ULONG in_data_len,
                                CK_BYTE *signature, CK_ULONG sig_len,
                                OBJECT *key_obj)
{
    CK_RV rc;
    CK_BYTE out[MAX_RSA_KEYLEN], out_data[MAX_RSA_KEYLEN];
    CK_BBOOL flag;
    CK_ATTRIBUTE *attr = NULL;
    CK_ULONG modulus_bytes, out_data_len;

    UNUSED(tokdata);
    UNUSED(sess);
    UNUSED(sig_len);

    out_data_len = MAX_RSA_KEYLEN;
    flag = template_attribute_find(key_obj->template, CKA_MODULUS, &attr);
    if (flag == FALSE) {
        TRACE_ERROR("Could not find CKA_MODULUS for the key.\n");
        return CKR_FUNCTION_FAILED;
    } else {
        modulus_bytes = attr->ulValueLen;
    }

    rc = os_specific_rsa_encrypt(signature, modulus_bytes, out, key_obj);
    if (rc != CKR_OK) {
        /*
         * Return CKR_SIGNATURE_INVALID in case of CKR_ARGUMENTS_BAD
         * because we dont know why the RSA op failed and it may have
         * failed due to a tampered signature being greater or equal
         * to the modulus.
         */
        TRACE_DEVEL("os_specific_rsa_encrypt failed\n");
        return rc == CKR_ARGUMENTS_BAD ? CKR_SIGNATURE_INVALID : rc;
    }

    rc = rsa_parse_block(out, modulus_bytes, out_data, &out_data_len,
                         PKCS_BT_1);
    if (rc == CKR_ENCRYPTED_DATA_INVALID) {
        TRACE_ERROR("%s\n", ock_err(ERR_SIGNATURE_INVALID));
        return CKR_SIGNATURE_INVALID;
    } else if (rc != CKR_OK) {
        TRACE_ERROR("%s\n", ock_err(ERR_FUNCTION_FAILED));
        return CKR_FUNCTION_FAILED;
    }

    if (in_data_len != out_data_len) {
        TRACE_ERROR("%s\n", ock_err(ERR_SIGNATURE_INVALID));
        return CKR_SIGNATURE_INVALID;
    }

    if (memcmp(in_data, out_data, out_data_len) != 0) {
        TRACE_ERROR("%s\n", ock_err(ERR_SIGNATURE_INVALID));
        return CKR_SIGNATURE_INVALID;
    }

    return CKR_OK;
}

CK_RV token_specific_rsa_verify_recover(STDLL_TokData_t *tokdata,
                                        CK_BYTE *signature, CK_ULONG sig_len,
                                        CK_BYTE *out_data,
                                        CK_ULONG *out_data_len,
                                        OBJECT *key_obj)
{
    CK_RV rc;
    CK_BYTE out[MAX_RSA_KEYLEN];
    CK_BBOOL flag;
    CK_ATTRIBUTE *attr = NULL;
    CK_ULONG modulus_bytes;

    UNUSED(tokdata);
    UNUSED(sig_len);

    flag = template_attribute_find(key_obj->template, CKA_MODULUS, &attr);
    if (flag == FALSE) {
        TRACE_ERROR("Could not find CKA_MODULUS for the key.\n");
        return CKR_FUNCTION_FAILED;
    } else {
        modulus_bytes = attr->ulValueLen;
    }

    rc = os_specific_rsa_encrypt(signature, modulus_bytes, out, key_obj);
    if (rc != CKR_OK) {
        TRACE_DEVEL("os_specific_rsa_encrypt failed\n");
        return rc;
    }

    rc = rsa_parse_block(out, modulus_bytes, out_data, out_data_len, PKCS_BT_1);
    if (rc == CKR_ENCRYPTED_DATA_INVALID) {
        TRACE_ERROR("%s\n", ock_err(ERR_SIGNATURE_INVALID));
        return CKR_SIGNATURE_INVALID;
    } else if (rc != CKR_OK) {
        TRACE_DEVEL("rsa_parse_block failed\n");
    }

    return rc;
}

CK_RV token_specific_rsa_x509_encrypt(STDLL_TokData_t *tokdata,
                                      CK_BYTE *in_data, CK_ULONG in_data_len,
                                      CK_BYTE *out_data,
                                      CK_ULONG *out_data_len, OBJECT *key_obj)
{
    CK_RV rc;
    CK_BYTE clear[MAX_RSA_KEYLEN], cipher[MAX_RSA_KEYLEN];
    CK_BBOOL flag;
    CK_ATTRIBUTE *attr = NULL;
    CK_ULONG modulus_bytes;

    UNUSED(tokdata);

    flag = template_attribute_find(key_obj->template, CKA_MODULUS, &attr);
    if (flag == FALSE) {
        TRACE_ERROR("Could not find CKA_MODULUS for the key.\n");
        return CKR_FUNCTION_FAILED;
    } else {
        modulus_bytes = attr->ulValueLen;
    }

    // prepad with zeros
    //
    memset(clear, 0x0, modulus_bytes - in_data_len);
    memcpy(&clear[modulus_bytes - in_data_len], in_data, in_data_len);

    rc = os_specific_rsa_encrypt(clear, modulus_bytes, cipher, key_obj);
    if (rc == CKR_OK) {
        memcpy(out_data, cipher, modulus_bytes);
        *out_data_len = modulus_bytes;
    } else {
        TRACE_DEVEL("os_specific_rsa_encrypt failed\n");
    }

    return rc;
}

CK_RV token_specific_rsa_x509_decrypt(STDLL_TokData_t *tokdata,
                                      CK_BYTE *in_data, CK_ULONG in_data_len,
                                      CK_BYTE *out_data,
                                      CK_ULONG *out_data_len, OBJECT *key_obj)
{
    CK_RV rc;
    CK_BYTE out[MAX_RSA_KEYLEN];
    CK_BBOOL flag;
    CK_ATTRIBUTE *attr = NULL;
    CK_ULONG modulus_bytes;

    UNUSED(tokdata);
    UNUSED(in_data_len);

    flag = template_attribute_find(key_obj->template, CKA_MODULUS, &attr);
    if (flag == FALSE) {
        TRACE_ERROR("Could not find CKA_MODULUS for the key.\n");
        return CKR_FUNCTION_FAILED;
    } else {
        modulus_bytes = attr->ulValueLen;
    }

    rc = os_specific_rsa_decrypt(in_data, modulus_bytes, out, key_obj);
    if (rc == CKR_OK) {
        memcpy(out_data, out, modulus_bytes);
        *out_data_len = modulus_bytes;
    } else {
        TRACE_DEVEL("os_specific_rsa_decrypt failed\n");
    }

    return rc;
}

CK_RV token_specific_rsa_x509_sign(STDLL_TokData_t *tokdata, CK_BYTE *in_data,
                                   CK_ULONG in_data_len, CK_BYTE *out_data,
                                   CK_ULONG *out_data_len, OBJECT *key_obj)
{
    CK_RV rc;
    CK_BYTE data[MAX_RSA_KEYLEN], sig[MAX_RSA_KEYLEN];
    CK_BBOOL flag;
    CK_ATTRIBUTE *attr = NULL;
    CK_ULONG modulus_bytes;

    UNUSED(tokdata);

    flag = template_attribute_find(key_obj->template, CKA_MODULUS, &attr);
    if (flag == FALSE) {
        TRACE_ERROR("Could not find CKA_MODULUS for the key.\n");
        return CKR_FUNCTION_FAILED;
    } else {
        modulus_bytes = attr->ulValueLen;
    }

    // prepad with zeros
    //
    memset(data, 0x0, modulus_bytes - in_data_len);
    memcpy(&data[modulus_bytes - in_data_len], in_data, in_data_len);

    rc = os_specific_rsa_decrypt(data, modulus_bytes, sig, key_obj);
    if (rc == CKR_OK) {
        memcpy(out_data, sig, modulus_bytes);
        *out_data_len = modulus_bytes;
    } else {
        TRACE_DEVEL("os_specific_rsa_decrypt failed\n");
    }

    return rc;
}


CK_RV token_specific_rsa_x509_verify(STDLL_TokData_t *tokdata,
                                     CK_BYTE *in_data, CK_ULONG in_data_len,
                                     CK_BYTE *signature, CK_ULONG sig_len,
                                     OBJECT *key_obj)
{
    CK_RV rc;
    CK_BYTE out[MAX_RSA_KEYLEN];
    CK_BBOOL flag;
    CK_ATTRIBUTE *attr = NULL;
    CK_ULONG modulus_bytes;

    UNUSED(tokdata);
    UNUSED(sig_len);

    flag = template_attribute_find(key_obj->template, CKA_MODULUS, &attr);
    if (flag == FALSE) {
        TRACE_ERROR("Could not find CKA_MODULUS for the key.\n");
        return CKR_FUNCTION_FAILED;
    } else {
        modulus_bytes = attr->ulValueLen;
    }

    rc = os_specific_rsa_encrypt(signature, modulus_bytes, out, key_obj);
    if (rc == CKR_OK) {
        CK_ULONG pos1, pos2, len;

        // it should be noted that in_data_len is not necessarily
        // the same as the modulus length
        //
        for (pos1 = 0; pos1 < in_data_len; pos1++)
            if (in_data[pos1] != 0)
                break;

        for (pos2 = 0; pos2 < modulus_bytes; pos2++)
            if (out[pos2] != 0)
                break;

        // at this point, pos1 and pos2 point to the first non-zero
        // bytes in the input data and the decrypted signature
        // (the recovered data), respectively.
        //
        if ((in_data_len - pos1) != (modulus_bytes - pos2)) {
            TRACE_ERROR("%s\n", ock_err(ERR_SIGNATURE_INVALID));
            return CKR_SIGNATURE_INVALID;
        }
        len = in_data_len - pos1;

        if (memcmp(&in_data[pos1], &out[pos2], len) != 0) {
            TRACE_ERROR("%s\n", ock_err(ERR_SIGNATURE_INVALID));
            return CKR_SIGNATURE_INVALID;
        }
        return CKR_OK;
    } else {
        TRACE_DEVEL("os_specific_rsa_encrypt failed\n");
    }

    return rc;
}


CK_RV token_specific_rsa_x509_verify_recover(STDLL_TokData_t *tokdata,
                                             CK_BYTE *signature,
                                             CK_ULONG sig_len,
                                             CK_BYTE *out_data,
                                             CK_ULONG *out_data_len,
                                             OBJECT *key_obj)
{
    CK_RV rc;
    CK_BYTE out[MAX_RSA_KEYLEN];
    CK_BBOOL flag;
    CK_ATTRIBUTE *attr = NULL;
    CK_ULONG modulus_bytes;

    UNUSED(tokdata);
    UNUSED(sig_len);

    flag = template_attribute_find(key_obj->template, CKA_MODULUS, &attr);
    if (flag == FALSE) {
        TRACE_ERROR("Could not find CKA_MODULUS for the key.\n");
        return CKR_FUNCTION_FAILED;
    } else {
        modulus_bytes = attr->ulValueLen;
    }

    rc = os_specific_rsa_encrypt(signature, modulus_bytes, out, key_obj);
    if (rc == CKR_OK) {
        memcpy(out_data, out, modulus_bytes);
        *out_data_len = modulus_bytes;
    } else {
        TRACE_DEVEL("os_specific_rsa_encrypt failed\n");
    }

    return rc;
}

CK_RV token_specific_rsa_oaep_encrypt(STDLL_TokData_t *tokdata,
                                      ENCR_DECR_CONTEXT *ctx,
                                      CK_BYTE *in_data, CK_ULONG in_data_len,
                                      CK_BYTE *out_data,
                                      CK_ULONG *out_data_len, CK_BYTE *hash,
                                      CK_ULONG hlen)
{
    CK_RV rc;
    CK_BYTE cipher[MAX_RSA_KEYLEN];
    CK_ULONG modulus_bytes;
    CK_BBOOL flag;
    CK_ATTRIBUTE *attr = NULL;
    CK_BYTE *em_data = NULL;
    OBJECT *key_obj = NULL;
    CK_RSA_PKCS_OAEP_PARAMS_PTR oaepParms = NULL;

    if (!in_data || !out_data || !hash) {
        TRACE_ERROR("%s\n", ock_err(ERR_ARGUMENTS_BAD));
        return CKR_ARGUMENTS_BAD;
    }

    oaepParms = (CK_RSA_PKCS_OAEP_PARAMS_PTR) ctx->mech.pParameter;

    rc = object_mgr_find_in_map1(tokdata, ctx->key, &key_obj);
    if (rc != CKR_OK) {
        TRACE_DEVEL("object_mgr_find_in_map1 failed\n");
        return rc;
    }

    flag = template_attribute_find(key_obj->template, CKA_MODULUS, &attr);
    if (flag == FALSE) {
        TRACE_ERROR("Could not find CKA_MODULUS for the key.\n");
        return CKR_FUNCTION_FAILED;
    } else {
        modulus_bytes = attr->ulValueLen;
    }

    /* pkcs1v2.2, section 7.1.1 Step 2:
     * EME-OAEP encoding.
     */
    em_data = (CK_BYTE *) malloc(modulus_bytes);
    if (em_data == NULL) {
        TRACE_ERROR("%s\n", ock_err(ERR_HOST_MEMORY));
        return CKR_HOST_MEMORY;
    }

    rc = encode_eme_oaep(tokdata, in_data, in_data_len, em_data,
                         modulus_bytes, oaepParms->mgf, hash, hlen);
    if (rc != CKR_OK)
        goto done;

    rc = os_specific_rsa_encrypt(em_data, modulus_bytes, cipher, key_obj);
    if (rc == CKR_OK) {
        memcpy(out_data, cipher, modulus_bytes);
        *out_data_len = modulus_bytes;
    } else {
        TRACE_DEVEL("os_specific_rsa_encrypt failed\n");
    }

done:
    if (em_data)
        free(em_data);

    return rc;
}

CK_RV token_specific_rsa_oaep_decrypt(STDLL_TokData_t *tokdata,
                                      ENCR_DECR_CONTEXT *ctx,
                                      CK_BYTE *in_data, CK_ULONG in_data_len,
                                      CK_BYTE *out_data,
                                      CK_ULONG *out_data_len, CK_BYTE *hash,
                                      CK_ULONG hlen)
{
    CK_RV rc;
    CK_BYTE *decr_data = NULL;
    OBJECT *key_obj = NULL;
    CK_BBOOL flag;
    CK_ATTRIBUTE *attr = NULL;
    CK_RSA_PKCS_OAEP_PARAMS_PTR oaepParms = NULL;

    if (!in_data || !out_data || !hash) {
        TRACE_ERROR("Invalid function arguments.\n");
        return CKR_FUNCTION_FAILED;
    }

    oaepParms = (CK_RSA_PKCS_OAEP_PARAMS_PTR) ctx->mech.pParameter;

    rc = object_mgr_find_in_map1(tokdata, ctx->key, &key_obj);
    if (rc != CKR_OK) {
        TRACE_DEVEL("object_mgr_find_in_map1 failed\n");
        return rc;
    }

    flag = template_attribute_find(key_obj->template, CKA_MODULUS, &attr);
    if (flag == FALSE) {
        TRACE_ERROR("Could not find CKA_MODULUS for the key.\n");
        return CKR_FUNCTION_FAILED;
    } else {
        *out_data_len = attr->ulValueLen;
    }

    decr_data = (CK_BYTE *) malloc(in_data_len);
    if (decr_data == NULL) {
        TRACE_ERROR("%s\n", ock_err(ERR_HOST_MEMORY));
        return CKR_HOST_MEMORY;
    }

    rc = os_specific_rsa_decrypt(in_data, in_data_len, decr_data, key_obj);
    if (rc != CKR_OK)
        return rc;

    /* pkcs1v2.2, section 7.1.2 Step 2:
     * EME-OAEP decoding.
     */
    rc = decode_eme_oaep(tokdata, decr_data, in_data_len, out_data,
                         out_data_len, oaepParms->mgf, hash, hlen);

    if (decr_data)
        free(decr_data);

    return rc;
}

CK_RV token_specific_rsa_pss_sign(STDLL_TokData_t *tokdata, SESSION *sess,
                                  SIGN_VERIFY_CONTEXT *ctx,
                                  CK_BYTE *in_data, CK_ULONG in_data_len,
                                  CK_BYTE *sig, CK_ULONG *sig_len)
{
    CK_RV rc;
    CK_ULONG modbytes;
    CK_BBOOL flag;
    CK_ATTRIBUTE *attr = NULL;
    OBJECT *key_obj = NULL;
    CK_BYTE *emdata = NULL;
    CK_RSA_PKCS_PSS_PARAMS *pssParms = NULL;

    UNUSED(sess);

    /* check the arguments */
    if (!in_data || !sig) {
        TRACE_ERROR("%s\n", ock_err(ERR_ARGUMENTS_BAD));
        return CKR_ARGUMENTS_BAD;
    }

    if (!ctx) {
        TRACE_ERROR("%s\n", ock_err(ERR_FUNCTION_FAILED));
        return CKR_FUNCTION_FAILED;
    }

    pssParms = (CK_RSA_PKCS_PSS_PARAMS *) ctx->mech.pParameter;

    /* get the key */
    rc = object_mgr_find_in_map1(tokdata, ctx->key, &key_obj);
    if (rc != CKR_OK) {
        TRACE_DEVEL("object_mgr_find_in_map1 failed\n");
        return rc;
    }

    flag = template_attribute_find(key_obj->template, CKA_MODULUS, &attr);
    if (flag == FALSE) {
        TRACE_ERROR("Could not find CKA_MODULUS for the key.\n");
        return CKR_FUNCTION_FAILED;
    } else {
        modbytes = attr->ulValueLen;
    }


    emdata = (CK_BYTE *) malloc(modbytes);
    if (emdata == NULL) {
        TRACE_ERROR("%s\n", ock_err(ERR_HOST_MEMORY));
        return CKR_HOST_MEMORY;
    }

    rc = emsa_pss_encode(tokdata, pssParms, in_data, in_data_len, emdata,
                         &modbytes);
    if (rc != CKR_OK)
        goto done;

    /* signing is a private key operation --> decrypt  */
    rc = os_specific_rsa_decrypt(emdata, modbytes, sig, key_obj);
    if (rc == CKR_OK)
        *sig_len = modbytes;
    else
        TRACE_DEVEL("os_specific_rsa_decrypt failed\n");

done:
    if (emdata)
        free(emdata);

    return rc;
}


CK_RV token_specific_rsa_pss_verify(STDLL_TokData_t *tokdata, SESSION *sess,
                                    SIGN_VERIFY_CONTEXT *ctx,
                                    CK_BYTE *in_data, CK_ULONG in_data_len,
                                    CK_BYTE *signature, CK_ULONG sig_len)
{
    CK_RV rc;
    CK_ULONG modbytes;
    OBJECT *key_obj = NULL;
    CK_BBOOL flag;
    CK_ATTRIBUTE *attr = NULL;
    CK_BYTE out[MAX_RSA_KEYLEN];
    CK_RSA_PKCS_PSS_PARAMS *pssParms = NULL;

    UNUSED(sess);

    /* check the arguments */
    if (!in_data || !signature) {
        TRACE_ERROR("%s\n", ock_err(ERR_ARGUMENTS_BAD));
        return CKR_ARGUMENTS_BAD;
    }

    if (!ctx) {
        TRACE_ERROR("%s\n", ock_err(ERR_FUNCTION_FAILED));
        return CKR_FUNCTION_FAILED;
    }

    pssParms = (CK_RSA_PKCS_PSS_PARAMS *) ctx->mech.pParameter;

    /* get the key */
    rc = object_mgr_find_in_map1(tokdata, ctx->key, &key_obj);
    if (rc != CKR_OK) {
        TRACE_DEVEL("object_mgr_find_in_map1 failed\n");
        return rc;
    }

    /* verify is a public key operation ... encrypt */
    rc = os_specific_rsa_encrypt(signature, sig_len, out, key_obj);
    if (rc != CKR_OK) {
        TRACE_DEVEL("os_specific_rsa_encrypt failed\n");
        return rc;
    }

    flag = template_attribute_find(key_obj->template, CKA_MODULUS, &attr);
    if (flag == FALSE) {
        TRACE_ERROR("Could not find CKA_MODULUS for the key.\n");
        return CKR_FUNCTION_FAILED;
    } else {
        modbytes = attr->ulValueLen;
    }

    /* call the pss verify scheme */
    rc = emsa_pss_verify(tokdata, pssParms, in_data, in_data_len, out,
                         modbytes);

    return rc;
}

#ifndef NOAES

CK_RV token_specific_aes_key_gen(STDLL_TokData_t *tokdata, CK_BYTE *key,
                                 CK_ULONG len, CK_ULONG keysize)
{
    UNUSED(keysize);

    return rng_generate(tokdata, key, len);
}

CK_RV token_specific_aes_ecb(STDLL_TokData_t *tokdata, CK_BYTE *in_data,
                             CK_ULONG in_data_len,
                             CK_BYTE *out_data, CK_ULONG *out_data_len,
                             OBJECT *key, CK_BYTE encrypt)
{
    int rc = CKR_OK;
    CK_ATTRIBUTE *attr = NULL;

    UNUSED(tokdata);

    /*
     * checks for input and output data length and block sizes
     * are already being carried out in mech_aes.c
     * so we skip those
     */
    // get the key value
    rc = template_attribute_find(key->template, CKA_VALUE, &attr);
    if (rc == FALSE) {
        TRACE_ERROR("Could not find CKA_VALUE for the key.\n");
        return CKR_FUNCTION_FAILED;
    }

    if (encrypt) {
        rc = ica_aes_ecb(in_data, out_data, in_data_len, attr->pValue,
                         attr->ulValueLen, ICA_ENCRYPT);
    } else {
        rc = ica_aes_ecb(in_data, out_data, in_data_len, attr->pValue,
                         attr->ulValueLen, ICA_DECRYPT);
    }
    if (rc != 0) {
        (*out_data_len) = 0;
        rc = CKR_FUNCTION_FAILED;
    } else {
        (*out_data_len) = in_data_len;
        rc = CKR_OK;
    }

    return rc;
}

CK_RV token_specific_aes_cbc(STDLL_TokData_t *tokdata,
                             CK_BYTE *in_data,
                             CK_ULONG in_data_len,
                             CK_BYTE *out_data,
                             CK_ULONG *out_data_len,
                             OBJECT *key, CK_BYTE *init_v, CK_BYTE encrypt)
{
    CK_RV rc;
    CK_ATTRIBUTE *attr = NULL;

    UNUSED(tokdata);

    /*
     * checks for input and output data length and block sizes
     * are already being carried out in mech_aes.c
     * so we skip those
     */

    // get the key value
    if (template_attribute_find(key->template, CKA_VALUE, &attr) == FALSE) {
        TRACE_ERROR("Could not find CKA_VALUE for the key.\n");
        return CKR_FUNCTION_FAILED;
    }

    if (encrypt) {
        rc = ica_aes_cbc(in_data, out_data, in_data_len, attr->pValue,
                         attr->ulValueLen, init_v, ICA_ENCRYPT);
    } else {
        rc = ica_aes_cbc(in_data, out_data, in_data_len, attr->pValue,
                         attr->ulValueLen, init_v, ICA_DECRYPT);
    }
    if (rc != 0) {
        (*out_data_len) = 0;
        rc = CKR_FUNCTION_FAILED;
    } else {
        (*out_data_len) = in_data_len;
        rc = CKR_OK;
    }

    return rc;
}

CK_RV token_specific_aes_ctr(STDLL_TokData_t *tokdata,
                             CK_BYTE *in_data,
                             CK_ULONG in_data_len,
                             CK_BYTE *out_data,
                             CK_ULONG *out_data_len,
                             OBJECT *key,
                             CK_BYTE *counterblock,
                             CK_ULONG counter_width, CK_BYTE encrypt)
{
    CK_RV rc;
    CK_ATTRIBUTE *attr = NULL;

    UNUSED(tokdata);

    /*
     * checks for input and output data length and block sizes
     * are already being carried out in mech_aes.c
     * so we skip those
     */
    /* in libica for AES-Counter Mode if uses one function for both encrypt and
     * decrypt, so they use variable direction to know if the data is to be
     * encrypted or decrypted
     * 0 -- Decrypt
     * 1 -- Encrypt
     */

    // get the key value
    if (template_attribute_find(key->template, CKA_VALUE, &attr) == FALSE) {
        TRACE_ERROR("Could not find CKA_VALUE for the key.\n");
        return CKR_FUNCTION_FAILED;
    }

    if (encrypt) {
        rc = ica_aes_ctr(in_data, out_data, (unsigned int) in_data_len,
                         attr->pValue, (unsigned int) attr->ulValueLen,
                         counterblock, (unsigned int) counter_width, 1);
    } else {
        rc = ica_aes_ctr(in_data, out_data, (unsigned int) in_data_len,
                         attr->pValue, (unsigned int) attr->ulValueLen,
                         counterblock, (unsigned int) counter_width, 0);
    }
    if (rc != 0) {
        (*out_data_len) = 0;
        rc = CKR_FUNCTION_FAILED;
    } else {
        (*out_data_len) = in_data_len;
        rc = CKR_OK;
    }

    return rc;
}

CK_RV token_specific_aes_gcm_init(STDLL_TokData_t *tokdata, SESSION *sess,
                                  ENCR_DECR_CONTEXT *ctx, CK_MECHANISM *mech,
                                  CK_OBJECT_HANDLE key, CK_BYTE encrypt)
{
    CK_RV rc = CKR_OK;
    OBJECT *key_obj = NULL;
    CK_ATTRIBUTE *attr = NULL;
    CK_GCM_PARAMS *aes_gcm_param = NULL;
    AES_GCM_CONTEXT *context = NULL;
    CK_BYTE *icv, *icb, *ucb, *subkey;
    CK_ULONG icv_length;

    UNUSED(sess);

    /* find key object */
    rc = object_mgr_find_in_map1(tokdata, key, &key_obj);
    if (rc != CKR_OK) {
        TRACE_ERROR("Failed to find specified object.\n");
        return rc;
    }

    /* get the key value */
    rc = template_attribute_find(key_obj->template, CKA_VALUE, &attr);
    if (rc == FALSE) {
        TRACE_ERROR("Could not find CKA_KEY_VALUE for the key.\n");
        return CKR_FUNCTION_FAILED;
    }

    /* prepare initial counterblock */
    aes_gcm_param = (CK_GCM_PARAMS *) mech->pParameter;
    context = (AES_GCM_CONTEXT *) ctx->context;

    context->ulAlen = aes_gcm_param->ulAADLen;
    icb = (CK_BYTE *) context->icb;
    ucb = (CK_BYTE *) context->ucb;
    subkey = (CK_BYTE *) context->subkey;

    icv = (CK_BYTE *) aes_gcm_param->pIv;
    icv_length = aes_gcm_param->ulIvLen;

    if (encrypt) {
        rc = ica_aes_gcm_initialize(icv, icv_length,
                                    attr->pValue, attr->ulValueLen,
                                    icb, ucb, subkey, 1);
    } else {
        rc = ica_aes_gcm_initialize(icv, icv_length,
                                    attr->pValue, attr->ulValueLen,
                                    icb, ucb, subkey, 0);
    }
    if (rc != 0) {
        TRACE_ERROR("ica_aes_gcm_initialize() failed.\n");
        return CKR_FUNCTION_FAILED;
    }

    return CKR_OK;
}

CK_RV token_specific_aes_gcm(STDLL_TokData_t *tokdata, SESSION *sess,
                             ENCR_DECR_CONTEXT *ctx, CK_BYTE *in_data,
                             CK_ULONG in_data_len, CK_BYTE *out_data,
                             CK_ULONG *out_data_len, CK_BYTE encrypt)
{
    CK_RV rc;
    OBJECT *key = NULL;
    CK_ATTRIBUTE *attr = NULL;
    CK_GCM_PARAMS *aes_gcm_param = NULL;
    CK_BYTE *counterblock;
    CK_ULONG counter_width;
    CK_BYTE *tag_data, *auth_data;
    CK_ULONG auth_data_len;
    CK_ULONG tag_data_len;

    UNUSED(sess);

    /*
     * Checks for input and output data length and block sizes are already
     * being carried out in mech_aes.c, so we skip those
     *
     * libica for AES-GCM Mode uses one function for both encrypt
     * and decrypt, so they use the variable 'direction' to know if
     * the data is to be encrypted or decrypted.
     * 0 -- Decrypt
     * 1 -- Encrypt
     */

    aes_gcm_param = (CK_GCM_PARAMS *) ctx->mech.pParameter;
    counterblock = (CK_BYTE *) aes_gcm_param->pIv;
    counter_width = aes_gcm_param->ulIvLen;
    auth_data = (CK_BYTE *) aes_gcm_param->pAAD;
    auth_data_len = aes_gcm_param->ulAADLen;
    tag_data_len = (aes_gcm_param->ulTagBits + 7) / 8;

    /* find key object */
    rc = object_mgr_find_in_map1(tokdata, ctx->key, &key);
    if (rc != CKR_OK) {
        TRACE_ERROR("Failed to find specified object.\n");
        return rc;
    }

    /* get key value */
    if (template_attribute_find(key->template, CKA_VALUE, &attr) == FALSE) {
        TRACE_ERROR("Could not find CKA_VALUE for the key.\n");
        return CKR_FUNCTION_FAILED;
    }

    if (encrypt) {
        tag_data = out_data + in_data_len;
        rc = ica_aes_gcm(in_data, (unsigned int) in_data_len, out_data,
                         counterblock, (unsigned int) counter_width,
                         auth_data, (unsigned int) auth_data_len,
                         tag_data, AES_BLOCK_SIZE, attr->pValue,
                         (unsigned int) attr->ulValueLen, 1);
        if (rc == 0) {
            (*out_data_len) = in_data_len + tag_data_len;
            rc = CKR_OK;
        }
    } else {
        unsigned int len;

        tag_data = in_data + in_data_len - tag_data_len;
        len = in_data_len - tag_data_len;
        rc = ica_aes_gcm(out_data,
                         (unsigned int) len, in_data, counterblock,
                         (unsigned int) counter_width, auth_data,
                         (unsigned int) auth_data_len, tag_data,
                         (unsigned int) tag_data_len, attr->pValue,
                         (unsigned int) attr->ulValueLen, 0);
        if (rc == 0) {
            (*out_data_len) = len;
            rc = CKR_OK;
        }
    }

    if (rc != 0) {
        TRACE_ERROR("ica_aes_gcm failed with rc = 0x%lx.\n", rc);
        (*out_data_len) = 0;
        rc = CKR_FUNCTION_FAILED;
    }

    return rc;
}

CK_RV token_specific_aes_gcm_update(STDLL_TokData_t *tokdata, SESSION *sess,
                                    ENCR_DECR_CONTEXT *ctx, CK_BYTE *in_data,
                                    CK_ULONG in_data_len, CK_BYTE *out_data,
                                    CK_ULONG *out_data_len, CK_BYTE encrypt)
{
    CK_RV rc;
    CK_ATTRIBUTE *attr = NULL;
    OBJECT *key = NULL;
    AES_GCM_CONTEXT *context = NULL;
    CK_GCM_PARAMS *aes_gcm_param = NULL;
    CK_ULONG total, tag_data_len, remain, auth_data_len;
    CK_ULONG out_len;
    CK_BYTE *auth_data, *tag_data;
    CK_BYTE *ucb, *subkey;
    CK_BYTE *buffer = NULL;

    UNUSED(tokdata);
    UNUSED(sess);

    context = (AES_GCM_CONTEXT *) ctx->context;
    total = (context->len + in_data_len);
    ucb = (CK_BYTE *) context->ucb;
    tag_data = context->hash;
    auth_data_len = context->ulAlen;
    subkey = (CK_BYTE *) context->subkey;

    aes_gcm_param = (CK_GCM_PARAMS *) ctx->mech.pParameter;
    tag_data_len = (aes_gcm_param->ulTagBits + 7) / 8;
    auth_data = (CK_BYTE *) aes_gcm_param->pAAD;

    /* if there isn't enough data to make a block, just save it */
    if (encrypt) {
        remain = (total % AES_BLOCK_SIZE);
        if (total < AES_BLOCK_SIZE) {
            memcpy(context->data + context->len, in_data, in_data_len);
            context->len += in_data_len;
            *out_data_len = 0;
            return CKR_OK;
        }
    } else {
        /* decrypt */
        remain = ((total - tag_data_len) % AES_BLOCK_SIZE)
            + tag_data_len;
        if (total < AES_BLOCK_SIZE + tag_data_len) {
            memcpy(context->data + context->len, in_data, in_data_len);
            context->len += in_data_len;
            *out_data_len = 0;
            return CKR_OK;
        }
    }

    /* At least we have 1 block */
    /* find key object */
    rc = object_mgr_find_in_map_nocache(ctx->key, &key);
    if (rc != CKR_OK) {
        TRACE_ERROR("Failed to find specified object.\n");
        return rc;
    }

    /* get key value */
    if (template_attribute_find(key->template, CKA_VALUE, &attr) == FALSE) {
        TRACE_ERROR("Could not find CKA_VALUE for the key.\n");
        return CKR_FUNCTION_FAILED;
    }

    out_len = total - remain;

    buffer = (CK_BYTE *) malloc(out_len);
    if (!buffer) {
        TRACE_ERROR("%s\n", ock_err(ERR_HOST_MEMORY));
        rc = CKR_HOST_MEMORY;
        goto done;
    }

    if (encrypt) {
        /* copy all the leftover data from previous encryption first */
        memcpy(buffer, context->data, context->len);
        memcpy(buffer + context->len, in_data, out_len - context->len);

        TRACE_DEVEL("Ciphertext length (%ld bytes).\n", in_data_len);

        rc = ica_aes_gcm_intermediate(buffer, (unsigned int) out_len,
                                      out_data, ucb, auth_data,
                                      (unsigned int) auth_data_len,
                                      tag_data, AES_BLOCK_SIZE,
                                      attr->pValue,
                                      (unsigned int) attr->ulValueLen,
                                      subkey, 1);

        /* save any remaining data */
        if (remain != 0)
            memcpy(context->data, in_data + (in_data_len - remain), remain);
        context->len = remain;

    } else {
        /* decrypt */
        /* copy all the leftover data from previous encryption first */
        if (in_data_len >= tag_data_len) {      /* case 1  */
            /* copy complete context to buffer first */
            memcpy(buffer, context->data, context->len);
            /* Append in_data to buffer */
            memcpy(buffer + context->len, in_data, out_len - context->len);
            /* copy remaining data to context */
            memcpy(context->data, in_data + out_len - context->len, remain);
            context->len = remain;
        } else {                /* case 2 - partial data */
            memcpy(buffer, context->data, AES_BLOCK_SIZE);
            memcpy(context->data, context->data + AES_BLOCK_SIZE,
                   context->len - AES_BLOCK_SIZE);
            memcpy(context->data + context->len - AES_BLOCK_SIZE,
                   in_data, in_data_len);
            context->len = context->len - AES_BLOCK_SIZE + in_data_len;
        }

        rc = ica_aes_gcm_intermediate(out_data, (unsigned int) out_len,
                                      buffer, ucb, auth_data,
                                      (unsigned int) auth_data_len,
                                      tag_data,
                                      (unsigned int) tag_data_len,
                                      attr->pValue,
                                      (unsigned int) attr->ulValueLen,
                                      subkey, 0);

    }

    if (rc != 0) {
        TRACE_ERROR("ica_aes_gcm_update failed with rc = 0x%lx.\n", rc);
        rc = CKR_FUNCTION_FAILED;
        goto done;
    }

    (*out_data_len) = out_len;

    context->ulClen += out_len;

    /* AAD only processed in first update seuence,
     * mark it empty for all subsequent calls
     */
    context->ulAlen = 0;

done:
    if (buffer)
        free(buffer);

    return rc;
}

CK_RV token_specific_aes_gcm_final(STDLL_TokData_t *tokdata, SESSION *sess,
                                   ENCR_DECR_CONTEXT *ctx, CK_BYTE *out_data,
                                   CK_ULONG *out_data_len, CK_BYTE encrypt)
{
    CK_RV rc = CKR_OK;
    CK_ATTRIBUTE *attr = NULL;
    OBJECT *key = NULL;
    AES_GCM_CONTEXT *context = NULL;
    CK_GCM_PARAMS *aes_gcm_param = NULL;
    CK_BYTE *icb, *ucb;
    CK_BYTE *tag_data, *subkey, *auth_data, *final_tag_data;
    CK_ULONG auth_data_len, tag_data_len;
    CK_BYTE *buffer = NULL;

    UNUSED(tokdata);
    UNUSED(sess);

    /* find key object */
    rc = object_mgr_find_in_map_nocache(ctx->key, &key);
    if (rc != CKR_OK) {
        TRACE_ERROR("Failed to find specified object.\n");
        return rc;
    }
    if (template_attribute_find(key->template, CKA_VALUE, &attr) == FALSE) {
        TRACE_ERROR("Could not find CKA_VALUE for the key.\n");
        return CKR_FUNCTION_FAILED;
    }

    context = (AES_GCM_CONTEXT *) ctx->context;
    ucb = (CK_BYTE *) context->ucb;
    icb = (CK_BYTE *) context->icb;
    tag_data = context->hash;
    subkey = (CK_BYTE *) context->subkey;

    aes_gcm_param = (CK_GCM_PARAMS *) ctx->mech.pParameter;
    auth_data = (CK_BYTE *) aes_gcm_param->pAAD;
    auth_data_len = aes_gcm_param->ulAADLen;
    tag_data_len = (aes_gcm_param->ulTagBits + 7) / 8;

    if (encrypt) {
        if (context->len != 0) {
            buffer = (CK_BYTE *) malloc(AES_BLOCK_SIZE);
            if (!buffer) {
                TRACE_ERROR("%s\n", ock_err(ERR_HOST_MEMORY));
                return CKR_HOST_MEMORY;
            }
            memcpy(buffer, context->data, context->len);

            rc = ica_aes_gcm_intermediate(buffer, context->len,
                                          out_data, ucb, auth_data,
                                          context->ulAlen, tag_data,
                                          AES_BLOCK_SIZE, attr->pValue,
                                          (unsigned int) attr->ulValueLen,
                                          subkey, 1);

            if (rc != 0) {
                TRACE_ERROR("ica_aes_gcm_intermediate() "
                            "failed to encrypt\n");
                rc = CKR_FUNCTION_FAILED;
                goto done;
            }

            context->ulClen += context->len;
            *out_data_len = context->len + tag_data_len;
        } else {
            *out_data_len = tag_data_len;
        }

        TRACE_DEVEL("GCM Final: context->len=%ld, tag_data_len=%ld, "
                    "out_data_len=%ld\n",
                    context->len, tag_data_len, *out_data_len);

        rc = ica_aes_gcm_last(icb, (unsigned int) auth_data_len,
                              (unsigned int) context->ulClen, tag_data,
                              NULL, 0, attr->pValue,
                              (unsigned int) attr->ulValueLen, subkey, 1);

        if (rc != 0) {
            TRACE_ERROR("ica_aes_gcm_final failed with rc = 0x%lx.\n", rc);
            rc = CKR_FUNCTION_FAILED;
            goto done;
        }

        memcpy(out_data + context->len, tag_data, tag_data_len);
    } else {
        /* decrypt */

        if (context->len > tag_data_len) {
            buffer = (CK_BYTE *) malloc(AES_BLOCK_SIZE);
            if (!buffer) {
                TRACE_ERROR("%s\n", ock_err(ERR_HOST_MEMORY));
                rc = CKR_HOST_MEMORY;
                goto done;
            }
            memcpy(buffer, context->data, context->len - tag_data_len);

            rc = ica_aes_gcm_intermediate(out_data,
                                          (unsigned int) context->len -
                                          tag_data_len, buffer, ucb, auth_data,
                                          (unsigned int) context->ulAlen,
                                          tag_data, AES_BLOCK_SIZE,
                                          attr->pValue,
                                          (unsigned int) attr->ulValueLen,
                                          subkey, 0);

            if (rc != 0) {
                TRACE_ERROR("ica_aes_gcm_intermediate() "
                            "failed to decrypt.\n");
                rc = CKR_FUNCTION_FAILED;
                goto done;
            }
            (*out_data_len) = context->len - tag_data_len;
            context->ulClen += context->len - tag_data_len;

        } else if (context->len == tag_data_len) {
            /* remaining data are tag data */
            *out_data_len = 0;
        } else {                /* (context->len < tag_data_len) */
            TRACE_ERROR("Incoming data are not consistent.\n");
            rc = CKR_DATA_INVALID;
            goto done;
        }

        final_tag_data = context->data + context->len - tag_data_len;

        rc = ica_aes_gcm_last(icb, aes_gcm_param->ulAADLen,
                              context->ulClen, tag_data, final_tag_data,
                              tag_data_len, attr->pValue,
                              (unsigned int) attr->ulValueLen, subkey, 0);
        if (rc != 0) {
            TRACE_ERROR("ica_aes_gcm_final failed with rc = 0x%lx.\n", rc);
            rc = CKR_FUNCTION_FAILED;
        }
    }

done:
    if (buffer)
        free(buffer);

    return rc;
}

/**
 * In libica for AES-OFB Mode it uses one function for both encrypt and decrypt
 * The variable direction is used as an indicator either for encrypt or decrypt
 * 0 -- Decrypt
 * 1 -- Encrypt
 */
CK_RV token_specific_aes_ofb(STDLL_TokData_t *tokdata, CK_BYTE *in_data,
                             CK_ULONG in_data_len, CK_BYTE *out_data,
                             OBJECT *key, CK_BYTE *init_v, uint_32 direction)
{
    CK_RV rc;
    CK_ATTRIBUTE *attr = NULL;

    UNUSED(tokdata);

    if (template_attribute_find(key->template, CKA_VALUE, &attr) == FALSE) {
        TRACE_ERROR("Could not find CKA_VALUE for the key.\n");
        return CKR_FUNCTION_FAILED;
    }

    rc = ica_aes_ofb(in_data, out_data, (unsigned long) in_data_len,
                     attr->pValue, (unsigned int) attr->ulValueLen,
                     init_v, direction);

    if (rc != 0) {
        TRACE_ERROR("%s\n", ock_err(ERR_FUNCTION_FAILED));
        rc = CKR_FUNCTION_FAILED;
    }

    return rc;
}

/**
 * In libica for AES-CFB Mode it uses one function for both encrypt and decrypt
 * The variable direction is used as an indicator either for encrypt or decrypt
 *  0 -- Decrypt
 *  1 -- Encrypt
 */
CK_RV token_specific_aes_cfb(STDLL_TokData_t *tokdata, CK_BYTE *in_data,
                             CK_ULONG in_data_len, CK_BYTE *out_data,
                             OBJECT *key, CK_BYTE *init_v, uint_32 lcfb,
                             uint_32 direction)
{
    CK_RV rc;
    CK_ATTRIBUTE *attr = NULL;

    UNUSED(tokdata);

    if (template_attribute_find(key->template, CKA_VALUE, &attr) == FALSE) {
        TRACE_ERROR("Could not find CKA_VALUE for the key.\n");
        return CKR_FUNCTION_FAILED;
    }

    rc = ica_aes_cfb(in_data, out_data, (unsigned long) in_data_len,
                     attr->pValue, (unsigned int) attr->ulValueLen, init_v,
                     lcfb, direction);

    if (rc != 0) {
        TRACE_ERROR("%s\n", ock_err(ERR_FUNCTION_FAILED));
        rc = CKR_FUNCTION_FAILED;
    }

    return rc;
}

CK_RV token_specific_aes_mac(STDLL_TokData_t *tokdata, CK_BYTE *message,
                             CK_ULONG message_len, OBJECT *key, CK_BYTE *mac)
{
    CK_RV rc;
    CK_ATTRIBUTE *attr = NULL;

    UNUSED(tokdata);

    if (template_attribute_find(key->template, CKA_VALUE, &attr) == FALSE) {
        TRACE_ERROR("Could not find CKA_VALUE for the key.\n");
        return CKR_FUNCTION_FAILED;
    }

    rc = ica_aes_cmac_intermediate(message, (unsigned long) message_len,
                                   attr->pValue,
                                   (unsigned int) attr->ulValueLen, mac);

    if (rc != 0) {
        TRACE_ERROR("%s\n", ock_err(ERR_FUNCTION_FAILED));
        rc = CKR_FUNCTION_FAILED;
    }

    return rc;
}

#endif

typedef struct _REF_MECH_LIST_ELEMENT {
    CK_ULONG lica_idx;
    CK_MECHANISM_TYPE mech_type;
    CK_MECHANISM_INFO mech_info;
} REF_MECH_LIST_ELEMENT;

static REF_MECH_LIST_ELEMENT ref_mech_list[] = {
    {92, CKM_RSA_PKCS_KEY_PAIR_GEN,
     {512, 4096, CKF_HW | CKF_GENERATE_KEY_PAIR}
    },
#if !(NODSA)
//      {1, CKM_DSA_KEY_PAIR_GEN, {512, 1024, CKF_HW|CKF_GENERATE_KEY_PAIR}},
#endif
#if !(NOCDMF)
//      {4, CKM_CDMF_KEY_GEN, {0, 0, CKF_HW|CKF_GENERATE}},
#endif
    {80, CKM_DES_KEY_GEN, {8, 8, CKF_HW | CKF_GENERATE}},
    {80, CKM_DES3_KEY_GEN, {24, 24, CKF_HW | CKF_GENERATE}},
    {90, CKM_RSA_PKCS,
     {512, 4096, CKF_HW | CKF_ENCRYPT | CKF_DECRYPT | CKF_WRAP | CKF_UNWRAP |
      CKF_SIGN | CKF_VERIFY | CKF_SIGN_RECOVER | CKF_VERIFY_RECOVER}
    },
#if !(NOX509)
    {90, CKM_RSA_X_509,
     {512, 4096, CKF_HW | CKF_ENCRYPT | CKF_DECRYPT | CKF_WRAP | CKF_UNWRAP |
      CKF_SIGN | CKF_VERIFY | CKF_SIGN_RECOVER | CKF_VERIFY_RECOVER}
    },
#endif
    {90, CKM_RSA_PKCS_OAEP,
     {512, 4096, CKF_HW | CKF_ENCRYPT | CKF_DECRYPT | CKF_WRAP | CKF_UNWRAP}
    },
    {90, CKM_RSA_PKCS_PSS, {512, 4096, CKF_HW | CKF_SIGN | CKF_VERIFY}},
    {107, CKM_MD2_RSA_PKCS, {512, 4096, CKF_HW | CKF_SIGN | CKF_VERIFY}},
    {108, CKM_MD5_RSA_PKCS, {512, 4096, CKF_HW | CKF_SIGN | CKF_VERIFY}},
    {190, CKM_SHA1_RSA_PKCS, {512, 4096, CKF_HW | CKF_SIGN | CKF_VERIFY}},
    {190, CKM_SHA224_RSA_PKCS, {512, 4096, CKF_HW | CKF_SIGN | CKF_VERIFY}},
    {190, CKM_SHA256_RSA_PKCS, {512, 4096, CKF_HW | CKF_SIGN | CKF_VERIFY}},
    {190, CKM_SHA384_RSA_PKCS, {512, 4096, CKF_HW | CKF_SIGN | CKF_VERIFY}},
    {190, CKM_SHA512_RSA_PKCS, {512, 4096, CKF_HW | CKF_SIGN | CKF_VERIFY}},
    {20, CKM_DES_ECB,
     {8, 8, CKF_HW | CKF_ENCRYPT | CKF_DECRYPT | CKF_WRAP | CKF_UNWRAP}
    },
    {21, CKM_DES_CBC,
     {8, 8, CKF_HW | CKF_ENCRYPT | CKF_DECRYPT | CKF_WRAP | CKF_UNWRAP}
    },
    {21, CKM_DES_CBC_PAD,
     {8, 8, CKF_HW | CKF_ENCRYPT | CKF_DECRYPT | CKF_WRAP | CKF_UNWRAP}
    },
    {41, CKM_DES3_ECB,
     {24, 24, CKF_HW | CKF_ENCRYPT | CKF_DECRYPT | CKF_WRAP | CKF_UNWRAP}
    },
    {42, CKM_DES3_CBC,
     {24, 24, CKF_HW | CKF_ENCRYPT | CKF_DECRYPT | CKF_WRAP | CKF_UNWRAP}
    },
    {42, CKM_DES3_CBC_PAD,
     {24, 24, CKF_HW | CKF_ENCRYPT | CKF_DECRYPT | CKF_WRAP | CKF_UNWRAP}
    },
    {49, CKM_DES3_MAC, {24, 24, CKF_HW | CKF_SIGN | CKF_VERIFY}},
    {49, CKM_DES3_MAC_GENERAL, {24, 24, CKF_HW | CKF_SIGN | CKF_VERIFY}},
    {24, CKM_DES_CFB8, {8, 8, CKF_HW | CKF_ENCRYPT | CKF_DECRYPT}},
    {44, CKM_DES_OFB64, {8, 8, CKF_HW | CKF_ENCRYPT | CKF_DECRYPT}},
    {45, CKM_DES_CFB64, {8, 8, CKF_HW | CKF_ENCRYPT | CKF_DECRYPT}},
    {01, CKM_SHA_1, {0, 0, CKF_HW | CKF_DIGEST}},
    {01, CKM_SHA_1_HMAC, {0, 0, CKF_HW | CKF_SIGN | CKF_VERIFY}},
    {01, CKM_SHA_1_HMAC_GENERAL, {0, 0, CKF_HW | CKF_SIGN | CKF_VERIFY}},
    {03, CKM_SHA224, {0, 0, CKF_HW | CKF_DIGEST}},
    {03, CKM_SHA256, {0, 0, CKF_HW | CKF_DIGEST}},
    {03, CKM_SHA256_HMAC, {0, 0, CKF_HW | CKF_SIGN | CKF_VERIFY}},
    {03, CKM_SHA256_HMAC_GENERAL, {0, 0, CKF_HW | CKF_SIGN | CKF_VERIFY}},
    {04, CKM_SHA384, {0, 0, CKF_HW | CKF_DIGEST}},
    {04, CKM_SHA384_HMAC, {0, 0, CKF_HW | CKF_SIGN | CKF_VERIFY}},
    {04, CKM_SHA384_HMAC_GENERAL, {0, 0, CKF_HW | CKF_SIGN | CKF_VERIFY}},
    {05, CKM_SHA512, {0, 0, CKF_HW | CKF_DIGEST}},
    {05, CKM_SHA512_HMAC, {0, 0, CKF_HW | CKF_SIGN | CKF_VERIFY}},
    {05, CKM_SHA512_HMAC_GENERAL, {0, 0, CKF_HW | CKF_SIGN | CKF_VERIFY}},
#ifdef SHA512_224
    {95, CKM_SHA512_224, {0, 0, CKF_HW|CKF_DIGEST}},
#endif
#ifdef SHA512_256
    {96, CKM_SHA512_256, {0, 0, CKF_HW|CKF_DIGEST}},
#endif
#if !(NOMD5)
    {53, CKM_MD5, {0, 0, CKF_HW | CKF_DIGEST}},
    {54, CKM_MD5_HMAC, {0, 0, CKF_HW | CKF_SIGN | CKF_VERIFY}},
    {55, CKM_MD5_HMAC_GENERAL, {0, 0, CKF_HW | CKF_SIGN | CKF_VERIFY}},
#endif
#if !(NOAES)
    {80, CKM_AES_KEY_GEN, {16, 32, CKF_HW | CKF_GENERATE}},
    {60, CKM_AES_ECB,
     {16, 32, CKF_HW | CKF_ENCRYPT | CKF_DECRYPT | CKF_WRAP | CKF_UNWRAP}
    },
    {61, CKM_AES_CBC,
     {16, 32, CKF_HW | CKF_ENCRYPT | CKF_DECRYPT | CKF_WRAP | CKF_UNWRAP}
    },
    {61, CKM_AES_CBC_PAD,
     {16, 32, CKF_HW | CKF_ENCRYPT | CKF_DECRYPT | CKF_WRAP | CKF_UNWRAP}
    },
    {63, CKM_AES_OFB,
     {16, 32, CKF_HW | CKF_ENCRYPT | CKF_DECRYPT | CKF_WRAP | CKF_UNWRAP}
    },
    {64, CKM_AES_CFB8,
     {16, 32, CKF_HW | CKF_ENCRYPT | CKF_DECRYPT | CKF_WRAP | CKF_UNWRAP}
    },
    {64, CKM_AES_CFB64,
     {16, 32, CKF_HW | CKF_ENCRYPT | CKF_DECRYPT | CKF_WRAP | CKF_UNWRAP}
    },
    {64, CKM_AES_CFB128,
     {16, 32, CKF_HW | CKF_ENCRYPT | CKF_DECRYPT | CKF_WRAP | CKF_UNWRAP}
    },
    {65, CKM_AES_CTR,
     {16, 32, CKF_HW | CKF_ENCRYPT | CKF_DECRYPT | CKF_WRAP | CKF_UNWRAP}
    },
    {70, CKM_AES_GCM, {16, 32, CKF_HW | CKF_ENCRYPT | CKF_DECRYPT}},
    {68, CKM_AES_MAC, {16, 32, CKF_HW | CKF_SIGN | CKF_VERIFY}},
    {68, CKM_AES_MAC_GENERAL, {16, 32, CKF_HW | CKF_SIGN | CKF_VERIFY}},
#endif
    {80, CKM_GENERIC_SECRET_KEY_GEN, {80, 2048, CKF_HW | CKF_GENERATE}},
#ifndef NO_EC
    {85, CKM_ECDH1_DERIVE,
     {160, 521, CKF_HW | CKF_DERIVE | CKF_EC_NAMEDCURVE | CKF_EC_F_P}
    },
    {86, CKM_ECDSA,
     {160, 521, CKF_HW | CKF_SIGN | CKF_VERIFY | CKF_EC_NAMEDCURVE | CKF_EC_F_P}
    },
    {86, CKM_ECDSA_SHA1,
     {160, 521, CKF_HW | CKF_SIGN | CKF_VERIFY | CKF_EC_NAMEDCURVE | CKF_EC_F_P}
    },
    {86, CKM_ECDSA_SHA224,
     {160, 521, CKF_HW | CKF_SIGN | CKF_VERIFY | CKF_EC_NAMEDCURVE | CKF_EC_F_P}
    },
    {86, CKM_ECDSA_SHA256,
     {160, 521, CKF_HW | CKF_SIGN | CKF_VERIFY | CKF_EC_NAMEDCURVE | CKF_EC_F_P}
    },
    {86, CKM_ECDSA_SHA384,
     {160, 521, CKF_HW | CKF_SIGN | CKF_VERIFY | CKF_EC_NAMEDCURVE | CKF_EC_F_P}
    },
    {86, CKM_ECDSA_SHA512,
     {160, 521, CKF_HW | CKF_SIGN | CKF_VERIFY | CKF_EC_NAMEDCURVE | CKF_EC_F_P}
    },
    {88, CKM_EC_KEY_PAIR_GEN,
     {160, 521, CKF_HW | CKF_GENERATE_KEY_PAIR | CKF_EC_NAMEDCURVE | CKF_EC_F_P}
    },
#endif

};

static CK_ULONG ref_mech_list_len =
    (sizeof(ref_mech_list) / sizeof(REF_MECH_LIST_ELEMENT));

/**
 * new ica-token mechanism table
 * this list will be initialized the first time
 * when ica_get_functionlist (from libica) is called.
 * (preinitialized with software only supported mechanisms)
 */
MECH_LIST_ELEMENT mech_list[] = {
    {CKM_MD5, {0, 0, CKF_DIGEST}},
    {CKM_MD5_HMAC, {0, 0, CKF_SIGN | CKF_VERIFY}},
    {CKM_MD5_HMAC_GENERAL, {0, 0, CKF_SIGN | CKF_VERIFY}},
    {0, {0, 0, 0}},
    {0, {0, 0, 0}},
    {0, {0, 0, 0}},
    {0, {0, 0, 0}},
    {0, {0, 0, 0}},
    {0, {0, 0, 0}},
    {0, {0, 0, 0}},
    {0, {0, 0, 0}},
    {0, {0, 0, 0}},
    {0, {0, 0, 0}},
    {0, {0, 0, 0}},
    {0, {0, 0, 0}},
    {0, {0, 0, 0}},
    {0, {0, 0, 0}},
    {0, {0, 0, 0}},
    {0, {0, 0, 0}},
    {0, {0, 0, 0}},
    {0, {0, 0, 0}},
    {0, {0, 0, 0}},
    {0, {0, 0, 0}},
    {0, {0, 0, 0}},
    {0, {0, 0, 0}},
    {0, {0, 0, 0}},
    {0, {0, 0, 0}},
    {0, {0, 0, 0}},
    {0, {0, 0, 0}},
    {0, {0, 0, 0}},
    {0, {0, 0, 0}},
    {0, {0, 0, 0}},
    {0, {0, 0, 0}},
    {0, {0, 0, 0}},
    {0, {0, 0, 0}},
    {0, {0, 0, 0}},
    {0, {0, 0, 0}},
    {0, {0, 0, 0}},
    {0, {0, 0, 0}},
    {0, {0, 0, 0}},
    {0, {0, 0, 0}},
    {0, {0, 0, 0}},
    {0, {0, 0, 0}},
    {0, {0, 0, 0}},
    {0, {0, 0, 0}},
    {0, {0, 0, 0}},
    {0, {0, 0, 0}},
    {0, {0, 0, 0}},
    {0, {0, 0, 0}},
    {0, {0, 0, 0}},
    {0, {0, 0, 0}},
    {0, {0, 0, 0}},
    {0, {0, 0, 0}},
    {0, {0, 0, 0}},
    {0, {0, 0, 0}},
    {0, {0, 0, 0}},
    {0, {0, 0, 0}},
    {0, {0, 0, 0}},
    {0, {0, 0, 0}},
    {0, {0, 0, 0}},
    {0, {0, 0, 0}},
    {0, {0, 0, 0}},
    {0, {0, 0, 0}},
    {0, {0, 0, 0}},
    {0, {0, 0, 0}},
    {0, {0, 0, 0}},
    {0, {0, 0, 0}},
    {0, {0, 0, 0}},
    {0, {0, 0, 0}},
    {0, {0, 0, 0}},
    {0, {0, 0, 0}},
    {0, {0, 0, 0}},
    {0, {0, 0, 0}},
    {0, {0 ,0, 0}}, 
    {0, {0, 0, 0}}
};

CK_ULONG mech_list_len = 3;

CK_RV token_specific_get_mechanism_list(STDLL_TokData_t *tokdata,
                                        CK_MECHANISM_TYPE_PTR pMechanismList,
                                        CK_ULONG_PTR pulCount)
{
    UNUSED(tokdata);

    return ica_specific_get_mechanism_list(pMechanismList, pulCount);
}

static CK_RV ica_specific_get_mechanism_list(CK_MECHANISM_TYPE_PTR pMechanismList,
                                             CK_ULONG_PTR pulCount)
{
    unsigned int i;

    if (pulCount == NULL) {
        return CKR_ARGUMENTS_BAD;
    }

    if (pMechanismList == NULL) {
        *pulCount = mech_list_len;
        return CKR_OK;
    }

    if ((*pulCount) < mech_list_len) {
        (*pulCount) = mech_list_len;
        TRACE_ERROR("%s\n", ock_err(ERR_BUFFER_TOO_SMALL));
        return CKR_BUFFER_TOO_SMALL;
    }

    /* copy mechanisms from the internal mech_ica_list */
    for (i = 0; i < mech_list_len; i++) {
        pMechanismList[i] = mech_list[i].mech_type;
    }
    (*pulCount) = mech_list_len;

    return CKR_OK;
}

CK_RV token_specific_get_mechanism_info(STDLL_TokData_t *tokdata,
                                        CK_MECHANISM_TYPE type,
                                        CK_MECHANISM_INFO_PTR pInfo)
{
    CK_ULONG rc = CKR_OK;

    UNUSED(tokdata);

    rc = ica_specific_get_mechanism_info(type, pInfo);

    return rc;
}

static CK_RV ica_specific_get_mechanism_info(CK_MECHANISM_TYPE type,
                                             CK_MECHANISM_INFO_PTR pInfo)
{
    unsigned int i;

    /*
     * find the requested mechanism and grab additional
     * mechanism specific information (mech_info) from mech_list_ica
     */
    for (i = 0; i < mech_list_len; i++) {
        if (mech_list[i].mech_type == type) {
            pInfo->flags = mech_list[i].mech_info.flags;
            pInfo->ulMinKeySize = mech_list[i].mech_info.ulMinKeySize;
            pInfo->ulMaxKeySize = mech_list[i].mech_info.ulMaxKeySize;
            return CKR_OK;
        }
    }

    TRACE_DEBUG("%s\n", ock_err(ERR_MECHANISM_INVALID));

    return CKR_MECHANISM_INVALID;
}

static CK_RV getRefListIdxfromId(CK_ULONG ica_idx, CK_ULONG_PTR pRefIdx)
{
    unsigned int n;

    for (n = *pRefIdx; n < ref_mech_list_len; n++) {
        if (ica_idx == ref_mech_list[n].lica_idx) {
            *pRefIdx = n;
            return CKR_OK;
        }
    }

    return CKR_MECHANISM_INVALID;
}

static CK_RV getRefListIdxfromMech(CK_ULONG mech, CK_ULONG_PTR pRefIdx)
{
    unsigned int n;

    for (n = *pRefIdx; n < ref_mech_list_len; n++) {
        if (mech == ref_mech_list[n].mech_type) {
            *pRefIdx = n;
            return CKR_OK;
        }
    }
    return CKR_MECHANISM_INVALID;
}

static CK_BBOOL isMechanismAvailable(CK_ULONG mechanism)
{
    unsigned int i;

    for (i = 0; i < mech_list_len; i++) {
        if (mech_list[i].mech_type == mechanism)
            return TRUE;
    }

    return FALSE;
}

static CK_RV addMechanismToList(CK_ULONG mechanism)
{
    CK_ULONG ret;
    CK_ULONG refIdx = 0;

    ret = getRefListIdxfromMech(mechanism, &refIdx);
    if (ret != CKR_OK) {
        return CKR_FUNCTION_FAILED;
    }
    mech_list[mech_list_len].mech_type = ref_mech_list[refIdx].mech_type;
    mech_list[mech_list_len].mech_info.flags =
        (ref_mech_list[refIdx].mech_info.flags & 0xfffffffe);
    mech_list[mech_list_len].mech_info.ulMinKeySize =
        ref_mech_list[refIdx].mech_info.ulMinKeySize;
    mech_list[mech_list_len].mech_info.ulMaxKeySize =
        ref_mech_list[refIdx].mech_info.ulMaxKeySize;
    mech_list_len++;

    return CKR_OK;
}

/*
 * call libica to receive list of supported mechanisms
 * This method is called once per opencryptoki instance (application context)
 */
static CK_RV mech_list_ica_initialize(void)
{
    CK_ULONG ret, rc = CKR_OK;
    unsigned int i, n;
    unsigned int ica_specific_mech_list_len;
    CK_ULONG tmp, ulActMechCtr, ulPreDefMechCtr, refIdx;

    rc = ica_get_functionlist(NULL, &ica_specific_mech_list_len);
    if (rc != CKR_OK) {
        TRACE_ERROR("ica_get_functionlist failed\n");
        return CKR_FUNCTION_FAILED;
    }
    libica_func_list_element libica_func_list[ica_specific_mech_list_len];
    rc = ica_get_functionlist(libica_func_list, &ica_specific_mech_list_len);
    if (rc != CKR_OK) {
        TRACE_ERROR("ica_get_functionlist failed\n");
        return CKR_FUNCTION_FAILED;
    }

    /*
     * grab the mechanism of the corresponding ID returned by libICA
     * from the internel reference list put the mechanism ID and the
     * HW support indication into an internel ica_mech_list and get
     * additional flag information from the reference list
     */
    ulPreDefMechCtr = mech_list_len;
    for (i = 0; i < ica_specific_mech_list_len; i++) {

        if (libica_func_list[i].flags == 0)
            continue;

        // loop over libica supported list
        ulActMechCtr = (CK_ULONG)(-1);

        /* --- walk through the whole reflist and fetch all
         * matching mechanism's (if present) ---
         */
        refIdx = 0;
        while ((ret = getRefListIdxfromId(libica_func_list[i].mech_mode_id,
                                          &refIdx)) == CKR_OK) {
            /*
	     * Loop over the predefined mechanism list and check
             * if we have to overrule a software implemented
             * mechanism from token by libica HW supported
             * mechanism.
             */
            for (n = 0; n < ulPreDefMechCtr; n++) {
                if (mech_list[n].mech_type == ref_mech_list[refIdx].mech_type) {
                    ulActMechCtr = n;
                    break;
                }
            }
            if (ulActMechCtr == (CK_ULONG)(-1)) {
                /* add a new entry */
                mech_list[mech_list_len].mech_type =
                    ref_mech_list[refIdx].mech_type;
                mech_list[mech_list_len].mech_info.flags =
                    (libica_func_list[i].flags & 0x01) |
                    (ref_mech_list[refIdx].mech_info.flags & 0xfffffffe);
                mech_list[mech_list_len].mech_info.ulMinKeySize =
                    ref_mech_list[refIdx].mech_info.ulMinKeySize;
                mech_list[mech_list_len].mech_info.ulMaxKeySize =
                    ref_mech_list[refIdx].mech_info.ulMaxKeySize;
                mech_list_len++;
            } else {
                /* replace existing entry */
                mech_list[ulActMechCtr].mech_info.flags =
                    (libica_func_list[i].flags & 0x01) |
                    mech_list[ulActMechCtr].mech_info.flags;
            }
            refIdx++;
        }
    }

    /*
     * check if special combined mechanisms are supported
     * if SHA1 and RSA is available   -> insert CKM_SHA1_RSA_PKCS
     * if SHA256 and RSA is available -> insert CKM_SHA256_RSA_PKCS
     * if MD2 and RSA is available    -> insert CKM_MD2_RSA_PKCS
     * if MD5 and RSA is available    -> insert CKM_MD5_RSA_PKCS
     */
    if (isMechanismAvailable(CKM_SHA_1) && isMechanismAvailable(CKM_RSA_PKCS))
        addMechanismToList(CKM_SHA1_RSA_PKCS);
    if (isMechanismAvailable(CKM_SHA224) && isMechanismAvailable(CKM_RSA_PKCS))
        addMechanismToList(CKM_SHA224_RSA_PKCS);
    if (isMechanismAvailable(CKM_SHA256) && isMechanismAvailable(CKM_RSA_PKCS))
        addMechanismToList(CKM_SHA256_RSA_PKCS);
    if (isMechanismAvailable(CKM_SHA384) && isMechanismAvailable(CKM_RSA_PKCS))
        addMechanismToList(CKM_SHA384_RSA_PKCS);
    if (isMechanismAvailable(CKM_SHA512) && isMechanismAvailable(CKM_RSA_PKCS))
        addMechanismToList(CKM_SHA512_RSA_PKCS);
    if (isMechanismAvailable(CKM_MD2) && isMechanismAvailable(CKM_RSA_PKCS))
        addMechanismToList(CKM_MD2_RSA_PKCS);
    if (isMechanismAvailable(CKM_MD5) && isMechanismAvailable(CKM_RSA_PKCS))
        addMechanismToList(CKM_MD5_RSA_PKCS);

    /* sort the mech_list_ica by mechanism ID's (bubble sort)  */
    for (i = 0; i < mech_list_len; i++) {
        for (n = i; n < mech_list_len; n++) {
            if (mech_list[i].mech_type > mech_list[n].mech_type) {
                tmp = mech_list[i].mech_type;
                mech_list[i].mech_type = mech_list[n].mech_type;
                mech_list[n].mech_type = tmp;

                tmp = mech_list[i].mech_info.ulMinKeySize;
                mech_list[i].mech_info.ulMinKeySize =
                    mech_list[n].mech_info.ulMinKeySize;
                mech_list[n].mech_info.ulMinKeySize = tmp;

                tmp = mech_list[i].mech_info.ulMaxKeySize;
                mech_list[i].mech_info.ulMaxKeySize =
                    mech_list[n].mech_info.ulMaxKeySize;
                mech_list[n].mech_info.ulMaxKeySize = tmp;

                tmp = mech_list[i].mech_info.flags;
                mech_list[i].mech_info.flags = mech_list[n].mech_info.flags;
                mech_list[n].mech_info.flags = tmp;
            }
        }
    }

    return rc;
}

CK_RV token_specific_generic_secret_key_gen(STDLL_TokData_t *tokdata,
                                            TEMPLATE *tmpl)
{
    CK_ATTRIBUTE *attr = NULL;
    CK_RV rc = CKR_OK;
    CK_BYTE secret_key[MAX_GENERIC_KEY_SIZE];
    CK_ULONG key_length = 0;
    CK_ULONG key_length_in_bits = 0;
    CK_ATTRIBUTE *value_attr = NULL;

    rc = template_attribute_find(tmpl, CKA_VALUE_LEN, &attr);
    if (rc == FALSE) {
        TRACE_ERROR("CKA_VALUE_LEM missing in key template\n");
        return CKR_TEMPLATE_INCOMPLETE;
    }

    key_length = *(CK_ULONG *) attr->pValue;    //app specified key length in bytes
    key_length_in_bits = key_length * 8;

    /* After looking at fips cavs test vectors for HMAC ops,
     * it was decided that the key length should fall between
     * 80 and 2048 bits inclusive.
     */
    if ((key_length_in_bits < 80) || (key_length_in_bits > 2048)) {
        TRACE_ERROR("Generic secret key size of %lu bits not within"
                    " required range of 80-2048 bits\n", key_length_in_bits);
        return CKR_KEY_SIZE_RANGE;
    }

    /* libica does not have generic secret key generation,
     * so call token rng here.
     */
    rc = rng_generate(tokdata, secret_key, key_length);
    if (rc != CKR_OK) {
        TRACE_DEVEL("Generic secret key generation failed.\n");
        return rc;
    }

    rc = build_attribute(CKA_VALUE, secret_key, key_length, &value_attr);
    if (rc != CKR_OK) {
        TRACE_DEVEL("build_attribute(CKA_VALUE) failed\n");
        return rc;
    }
    rc = template_update_attribute(tmpl, value_attr);
    if (rc != CKR_OK)
        TRACE_DEVEL("template_update_attribute(CKA_VALUE) failed\n");

    return rc;
}

#ifndef NO_EC
static CK_RV build_update_attribute(TEMPLATE *tmpl,
                                    CK_ATTRIBUTE_TYPE type,
                                    CK_BYTE *data, CK_ULONG data_len)
{
    CK_ATTRIBUTE *attr;
    CK_RV rv;

    if ((rv = build_attribute(type, data, data_len, &attr)))
        return rv;

    template_update_attribute(tmpl, attr);

    return CKR_OK;
}

static CK_RV is_equal(unsigned char *a,
                      unsigned int a_length,
                      unsigned char *b, unsigned int b_length)
{
    if (a_length != b_length)
        return 0;

    if (memcmp(a, b, a_length) == 0)
        return 1;

    return 0;
}

static int nid_from_oid(CK_BYTE *oid, CK_ULONG oid_length)
{
    /* Supported Elliptic Curves */
    static const CK_BYTE brainpoolP160r1[] =
        { 0x06, 0x09, 0x2B, 0x24, 0x03, 0x03, 0x02, 0x08, 0x01, 0x01, 0x01 };
    static const CK_BYTE brainpoolP192r1[] =
        { 0x06, 0x09, 0x2B, 0x24, 0x03, 0x03, 0x02, 0x08, 0x01, 0x01, 0x03 };
    static const CK_BYTE brainpoolP224r1[] =
        { 0x06, 0x09, 0x2B, 0x24, 0x03, 0x03, 0x02, 0x08, 0x01, 0x01, 0x05 };
    static const CK_BYTE brainpoolP256r1[] =
        { 0x06, 0x09, 0x2B, 0x24, 0x03, 0x03, 0x02, 0x08, 0x01, 0x01, 0x07 };
    static const CK_BYTE brainpoolP320r1[] =
        { 0x06, 0x09, 0x2B, 0x24, 0x03, 0x03, 0x02, 0x08, 0x01, 0x01, 0x09 };
    static const CK_BYTE brainpoolP384r1[] =
        { 0x06, 0x09, 0x2B, 0x24, 0x03, 0x03, 0x02, 0x08, 0x01, 0x01, 0x0B };
    static const CK_BYTE brainpoolP512r1[] =
        { 0x06, 0x09, 0x2B, 0x24, 0x03, 0x03, 0x02, 0x08, 0x01, 0x01, 0x0D };
    static const CK_BYTE prime192[] =
        { 0x06, 0x08, 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x03, 0x01, 0x01 };
    static const CK_BYTE secp224[] = { 0x06, 0x05, 0x2B, 0x81, 0x04, 0x00, 0x21 };
    static const CK_BYTE prime256[] =
        { 0x06, 0x08, 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x03, 0x01, 0x07 };
    static const CK_BYTE secp384[] = { 0x06, 0x05, 0x2B, 0x81, 0x04, 0x00, 0x22 };
    static const CK_BYTE secp521[] = { 0x06, 0x05, 0x2B, 0x81, 0x04, 0x00, 0x23 };

    if (is_equal
        (oid, oid_length, (unsigned char *) &prime192, sizeof(prime192)))
        return NID_X9_62_prime192v1;
    else if (is_equal(oid, oid_length,
                      (unsigned char *) &secp224, sizeof(secp224)))
        return NID_secp224r1;
    else if (is_equal(oid, oid_length,
                      (unsigned char *) &prime256, sizeof(prime256)))
        return NID_X9_62_prime256v1;
    else if (is_equal(oid, oid_length,
                      (unsigned char *) &secp384, sizeof(secp384)))
        return NID_secp384r1;
    else if (is_equal(oid, oid_length,
                      (unsigned char *) &secp521, sizeof(secp521)))
        return NID_secp521r1;
    else if (is_equal(oid, oid_length,
                      (unsigned char *) &brainpoolP160r1,
                      sizeof(brainpoolP160r1)))
        return NID_brainpoolP160r1;
    else if (is_equal(oid, oid_length,
                      (unsigned char *) &brainpoolP192r1,
                      sizeof(brainpoolP192r1)))
        return NID_brainpoolP192r1;
    else if (is_equal(oid, oid_length,
                      (unsigned char *) &brainpoolP224r1,
                      sizeof(brainpoolP224r1)))
        return NID_brainpoolP224r1;
    else if (is_equal(oid, oid_length,
                      (unsigned char *) &brainpoolP256r1,
                      sizeof(brainpoolP256r1)))
        return NID_brainpoolP256r1;
    else if (is_equal(oid, oid_length,
                      (unsigned char *) &brainpoolP320r1,
                      sizeof(brainpoolP320r1)))
        return NID_brainpoolP320r1;
    else if (is_equal(oid, oid_length,
                      (unsigned char *) &brainpoolP384r1,
                      sizeof(brainpoolP384r1)))
        return NID_brainpoolP384r1;
    else if (is_equal(oid, oid_length,
                      (unsigned char *) &brainpoolP512r1,
                      sizeof(brainpoolP512r1)))
        return NID_brainpoolP512r1;

    return -1;
}

CK_RV token_specific_ec_generate_keypair(STDLL_TokData_t *tokdata,
                                         TEMPLATE *publ_tmpl,
                                         TEMPLATE *priv_tmpl)
{
    CK_RV ret = CKR_OK;
    CK_ATTRIBUTE *attr = NULL;
    ICA_EC_KEY *eckey;
    CK_BYTE q_array[ICATOK_EC_MAX_Q_LEN];
    CK_BYTE d_array[ICATOK_EC_MAX_D_LEN];
    unsigned int privlen, q_len, d_len;
    int rc, nid;

    UNUSED(tokdata);

    if (!ica_ec_support_available) {
        TRACE_ERROR("ECC support is not available in Libica\n");
        return CKR_FUNCTION_NOT_SUPPORTED;
    }

    if (!template_attribute_find(publ_tmpl, CKA_ECDSA_PARAMS, &attr)) {
        TRACE_ERROR("%s\n", ock_err(ERR_TEMPLATE_INCOMPLETE));
        return CKR_TEMPLATE_INCOMPLETE;
    }

    /* Determine curve nid */
    nid = nid_from_oid(attr->pValue, attr->ulValueLen);
    if (nid < 0) {
        TRACE_ERROR("curve not supported by icatoken.\n");
        return CKR_CURVE_NOT_SUPPORTED;
    }

    /* Create ICA_EC_KEY object */
    eckey = p_ica_ec_key_new(nid, &privlen);
    if (!eckey) {
        TRACE_ERROR("ica_ec_key_new() failed.\n");
        return CKR_FUNCTION_FAILED;
    }

    /* Generate key data for this key object */
    rc = p_ica_ec_key_generate(adapter_handle, eckey);
    if (rc != 0) {
        TRACE_ERROR("ica_ec_key_generate() failed with rc=%d.\n", rc);
        ret = CKR_FUNCTION_FAILED;
        goto end;
    }

    /* Return public key (X,Y) via CKA_EC_POINT */
    rc = p_ica_ec_key_get_public_key(eckey, (unsigned char *) &q_array, &q_len);
    if (rc != 0) {
        TRACE_ERROR("ica_ec_key_get_public_key() failed with rc=%d.\n", rc);
        ret = CKR_FUNCTION_FAILED;
        goto end;
    }

    ret = build_update_attribute(publ_tmpl, CKA_EC_POINT, q_array, q_len);
    if (ret != 0) {
        TRACE_ERROR("build_update_attribute for (X,Y) failed rc=0x%lx\n", ret);
        goto end;
    }

    /* Return private key (D) via CKA_VALUE */
    rc = p_ica_ec_key_get_private_key(eckey, (unsigned char *) &d_array,
                                      &d_len);
    if (rc != 0) {
        TRACE_ERROR("ica_ec_key_get_private_key() failed with rc=%d.\n", rc);
        ret = CKR_FUNCTION_FAILED;
        goto end;
    }

    ret = build_update_attribute(priv_tmpl, CKA_VALUE, d_array, d_len);
    if (ret != 0) {
        TRACE_ERROR("build_update_attribute for (D) failed, rc=0x%lx\n", ret);
        goto end;
    }

    /* Add CKA_ECDSA_PARAMS to private template also */
    ret = build_update_attribute(priv_tmpl, CKA_ECDSA_PARAMS, attr->pValue,
                                 attr->ulValueLen);
    if (ret != 0) {
        TRACE_ERROR("build_update_attribute for CKA_ECDSA_PARAMS failed, "
                    "rc=0x%lx\n", ret);
        goto end;
    }

    ret = CKR_OK;

end:
    p_ica_ec_key_free(eckey);

    return ret;
}

CK_RV token_specific_ec_sign(STDLL_TokData_t *tokdata,  SESSION *sess,
                             CK_BYTE *in_data, CK_ULONG in_data_len,
                             CK_BYTE *out_data, CK_ULONG *out_data_len,
                             OBJECT *key_obj)
{
    CK_RV ret = CKR_OK;
    CK_ATTRIBUTE *attr, *attr2;
    ICA_EC_KEY *eckey;
    unsigned int privlen;
    int rc, nid;

    UNUSED(tokdata);
    UNUSED(sess);

    *out_data_len = 0;

    if (!ica_ec_support_available) {
        TRACE_ERROR("ECC support is not available in Libica\n");
        return CKR_FUNCTION_NOT_SUPPORTED;
    }

    /* Get CKA_ECDSA_PARAMS from template */
    if (!template_attribute_find(key_obj->template, CKA_ECDSA_PARAMS, &attr)) {
        TRACE_ERROR("%s\n", ock_err(ERR_TEMPLATE_INCOMPLETE));
        return CKR_TEMPLATE_INCOMPLETE;
    }

    /* Determine curve nid */
    nid = nid_from_oid(attr->pValue, attr->ulValueLen);
    if (nid < 0) {
        TRACE_ERROR("Cannot determine curve nid. \n");
        return CKR_CURVE_NOT_SUPPORTED;
    }

    /* Create ICA_EC_KEY object */
    eckey = p_ica_ec_key_new(nid, &privlen);
    if (!eckey) {
        TRACE_ERROR("ica_ec_key_new() failed for curve %i.\n", nid);
        return CKR_FUNCTION_FAILED;
    }

    /* Get private key from template via CKA_VALUE */
    if (!template_attribute_find(key_obj->template, CKA_VALUE, &attr2)) {
        TRACE_ERROR("%s\n", ock_err(ERR_TEMPLATE_INCOMPLETE));
        ret = CKR_TEMPLATE_INCOMPLETE;
        goto end;
    }

    /* Plausibility check */
    if (privlen != attr2->ulValueLen) {
        TRACE_ERROR("Private key length for curve %i mismatch: length from "
                    "ica_ec_key_new() = %i, length from attribute = %ld",
                    nid, privlen, attr2->ulValueLen);
        ret = CKR_ATTRIBUTE_VALUE_INVALID;
        goto end;
    }

    /* Initialize ICA_EC_KEY with private key (D) */
    rc = p_ica_ec_key_init(NULL, NULL, attr2->pValue, eckey);
    if (rc != 0) {
        TRACE_ERROR("ica_ec_key_init() failed with rc = %d \n", rc);
        ret = CKR_FUNCTION_FAILED;
        goto end;
    }

    /* Create signature */
    rc = p_ica_ecdsa_sign(adapter_handle, eckey,
                          (unsigned char *) in_data,
                          (unsigned int) in_data_len,
                          (unsigned char *) out_data, 2 * privlen);
    if (rc != 0) {
        TRACE_ERROR("ica_ecdsa_sign() failed with rc = %d. \n", rc);
        ret = CKR_FUNCTION_FAILED;
        goto end;
    }

    *out_data_len = 2 * privlen;
    ret = CKR_OK;

end:
    p_ica_ec_key_free(eckey);

    return ret;
}

/**
 * decompress the given compressed public key. Sets x from given pub_key,
 * re-calculates y from format byte, x and nid.
 *
 * @return 0 on success
 */
static CK_RV decompress_pubkey(unsigned int nid,
                               const unsigned char *pub_key,
                               CK_ULONG pub_len,
                               unsigned int priv_len,
                               unsigned char *x, unsigned char *y)
{
    BN_CTX *ctx = BN_CTX_new();
    BIGNUM *bn_x = BN_bin2bn((unsigned char *) &(pub_key[1]), priv_len, NULL);
    BIGNUM *bn_y = BN_new();
    EC_GROUP *group = NULL;
    EC_POINT *point = NULL;
    int y_bit = (pub_key[0] == POINT_CONVERSION_COMPRESSED ? 0 : 1);
    CK_RV ret = CKR_OK;

    UNUSED(pub_len);

    group = EC_GROUP_new_by_curve_name(nid);
    if (!group) {
        TRACE_ERROR("Curve %d is not supported by openssl. Cannot decompress "
                    "public key\n", nid);
        ret = CKR_CURVE_NOT_SUPPORTED;
        goto end;
    }

    point = EC_POINT_new(group);
    if (!point) {
        ret = CKR_FUNCTION_FAILED;
        goto end;
    }

    if (!EC_POINT_set_compressed_coordinates_GFp(group,
                                                 point, bn_x, y_bit, ctx)) {
        ret = CKR_FUNCTION_FAILED;
        goto end;
    }

    if (!EC_POINT_is_on_curve(group, point, ctx)) {
        ret = CKR_FUNCTION_FAILED;
        goto end;
    }

    if (!EC_POINT_get_affine_coordinates_GFp(group, point, bn_x, bn_y, ctx)) {
        ret = CKR_FUNCTION_FAILED;
        goto end;
    }

    memcpy(x, &(pub_key[1]), priv_len);
    BN_bn2bin(bn_y, y);

end:
    if (ctx)
        BN_CTX_free(ctx);
    if (point)
        EC_POINT_free(point);
    if (group)
        EC_GROUP_free(group);
    if (bn_x)
        BN_free(bn_x);
    if (bn_y)
        BN_free(bn_y);

    return ret;
}

/**
 * returns the (X,Y) coordinates of the given EC public key.
 * For a compressed key, Y is calculated from X and an indication
 * if Y is even or odd.
 *
 * Refer to X9.62, section 4.3.6, "point to octet-string conversion".
 *
 * @return 0 on success
 */
static CK_RV set_pubkey_coordinates(unsigned int nid,
                                    const unsigned char *pub_key,
                                    CK_ULONG pub_len,
                                    unsigned int priv_len,
                                    unsigned char *x, unsigned char *y)
{
    int i, n;

    /* Check if key has no format byte: [X || Y] */
    if (pub_len == 2 * priv_len) {
        memcpy(x, pub_key, priv_len);
        memcpy(y, pub_key + priv_len, priv_len);
        return CKR_OK;
    }

    /* Check if key is compressed: [0x0n || X]
     *   0x0n: 0x02: Y is even
     *         0x03: Y is odd
     */
    if (pub_len == priv_len + 1 &&
        (pub_key[0] == POINT_CONVERSION_COMPRESSED ||
         pub_key[0] == POINT_CONVERSION_COMPRESSED + 1)) {
        return decompress_pubkey(nid, pub_key, pub_len, priv_len, x, y);
    }

    /* Check if key is uncompressed or hybrid: [0x0n || X || Y]
     *   0x0n: 0x04 : uncompressed
     *         0c06 : hybrid, Y is even
     *         0x07 : hybrid, Y is odd
     */
    if (pub_len == 2 * priv_len + 1 &&
        (pub_key[0] == POINT_CONVERSION_UNCOMPRESSED ||
         pub_key[0] == POINT_CONVERSION_HYBRID ||
         pub_key[0] == POINT_CONVERSION_HYBRID + 1)) {
        memcpy(x, pub_key + 1, priv_len);
        memcpy(y, pub_key + 1 + priv_len, priv_len);
        return CKR_OK;
    }

    /* Add leading null bytes to pub_key X, if necessary. In this
     * case there is no format byte */
    if (pub_len < 2 * priv_len) {
        n = 2 * priv_len - pub_len;
        for (i = 0; i < n; i++)
            x[i] = 0x00;
        memcpy(x + i, pub_key, priv_len - n);
        memcpy(y, pub_key + priv_len - n, priv_len);
        return CKR_OK;
    }

    memset(x, 0, priv_len);
    memset(y, 0, priv_len);

    return CKR_FUNCTION_FAILED;
}


CK_RV token_specific_ec_verify(STDLL_TokData_t *tokdata,
                               SESSION *sess,
                               CK_BYTE *in_data,
                               CK_ULONG in_data_len,
                               CK_BYTE *signature,
                               CK_ULONG signature_len, OBJECT *key_obj)
{
    CK_RV ret = CKR_OK;
    CK_ATTRIBUTE *attr, *attr2;
    ICA_EC_KEY *eckey;
    unsigned int privlen;
    unsigned char x_array[ICATOK_EC_MAX_D_LEN];
    unsigned char y_array[ICATOK_EC_MAX_D_LEN];
    int rc, nid;

    UNUSED(tokdata);
    UNUSED(sess);

    if (!ica_ec_support_available) {
        TRACE_ERROR("ECC support is not available in Libica\n");
        return CKR_FUNCTION_NOT_SUPPORTED;
    }

    /* Get CKA_ECDSA_PARAMS from template */
    if (!template_attribute_find(key_obj->template, CKA_ECDSA_PARAMS, &attr)) {
        TRACE_ERROR("%s\n", ock_err(ERR_TEMPLATE_INCOMPLETE));
        return CKR_TEMPLATE_INCOMPLETE;
    }

    /* Create ICA_EC_KEY object */
    nid = nid_from_oid(attr->pValue, attr->ulValueLen);
    if (nid < 0) {
        TRACE_ERROR("Cannot determine curve nid. \n");
        return CKR_CURVE_NOT_SUPPORTED;
    }

    eckey = p_ica_ec_key_new(nid, &privlen);
    if (!eckey) {
        TRACE_ERROR("ica_ec_key_new() failed for curve %i. \n", nid);
        return CKR_FUNCTION_FAILED;
    }

    /* Get public key (X,Y) from template via CKA_EC_POINT */
    if (!template_attribute_find(key_obj->template, CKA_EC_POINT, &attr2)) {
        TRACE_ERROR("%s\n", ock_err(ERR_TEMPLATE_INCOMPLETE));
        ret = CKR_TEMPLATE_INCOMPLETE;
        goto end;
    }

    /* Signature length ok? */
    if (signature_len != 2 * privlen) {
        TRACE_ERROR("Supplied signature length mismatch: "
                    "supplied length = %ld, length from libica = %i\n",
                    signature_len, 2 * privlen);
        ret = CKR_SIGNATURE_LEN_RANGE;
        goto end;
    }

    /* Provide (X,Y), decompress key if necessary */
    ret = set_pubkey_coordinates(nid,
                                 (unsigned char *) attr2->pValue,
                                 attr2->ulValueLen, privlen, x_array, y_array);
    if (ret != 0) {
        TRACE_ERROR("Cannot determine public key coordinates from "
                    "given public key\n");
        goto end;
    }

    /* Initialize ICA_EC_KEY with public key (Q) */
    rc = p_ica_ec_key_init(x_array, y_array, NULL, eckey);
    if (rc != 0) {
        TRACE_ERROR("ica_ec_key_init() for public key failed "
                    "with rc = %d \n", rc);
        ret = CKR_FUNCTION_FAILED;
        goto end;
    }

    /* Verify signature */
    rc = p_ica_ecdsa_verify(adapter_handle,
                            eckey,
                            (unsigned char *) in_data,
                            (unsigned int) in_data_len,
                            (unsigned char *) signature, signature_len);
    switch (rc) {
    case 0:
        ret = CKR_OK;
        break;
    case EFAULT:
        TRACE_ERROR("ica_ecdsa_verify() returned invalid signature, "
                    "rc = %d. \n", rc);
        ret = CKR_SIGNATURE_INVALID;
        break;
    default:
        TRACE_ERROR("ica_ecdsa_verify() returned internal error, "
                    "rc = %d. \n", rc);
        ret = CKR_FUNCTION_FAILED;
        break;
    }

end:
    p_ica_ec_key_free(eckey);

    return ret;
}

CK_RV token_specific_ecdh_pkcs_derive(STDLL_TokData_t *tokdata,
                                      CK_BYTE *priv_bytes,
                                      CK_ULONG priv_length,
                                      CK_BYTE *pub_bytes,
                                      CK_ULONG pub_length,
                                      CK_BYTE *secret_value,
                                      CK_ULONG *secret_value_len,
                                      CK_BYTE *oid, CK_ULONG oid_length)
{
    CK_RV ret = CKR_OK;
    ICA_EC_KEY *pubkey = NULL, *privkey = NULL;
    unsigned int n, privlen, i;
    unsigned char d_array[ICATOK_EC_MAX_D_LEN];
    unsigned char x_array[ICATOK_EC_MAX_D_LEN];
    unsigned char y_array[ICATOK_EC_MAX_D_LEN];
    int rc, nid;

    UNUSED(tokdata);

    *secret_value_len = 0;

    if (!ica_ec_support_available) {
        TRACE_ERROR("ECC support is not available in Libica\n");
        return CKR_FUNCTION_NOT_SUPPORTED;
    }

    /* Get nid from oid */
    nid = nid_from_oid(oid, oid_length);
    if (nid < 0) {
        TRACE_ERROR("curve not supported by icatoken.\n");
        return CKR_CURVE_NOT_SUPPORTED;
    }

    /* Create ICA_EC_KEY object with public key */
    pubkey = p_ica_ec_key_new(nid, &privlen);
    if (!pubkey) {
        TRACE_ERROR("ica_ec_key_new() for curve %i failed.\n", nid);
        return CKR_FUNCTION_FAILED;
    }

    /* Provide (X,Y), decompress key if necessary */
    ret = set_pubkey_coordinates(nid, pub_bytes, pub_length,
                                 privlen, x_array, y_array);
    if (ret != 0) {
        TRACE_ERROR("Cannot determine public key coordinates\n");
        goto end;
    }

    /* Format (D) as char array with leading nulls if necessary */
    n = privlen - priv_length;
    for (i = 0; i < n; i++)
        d_array[i] = 0x00;
    memcpy(&(d_array[n]), priv_bytes, priv_length);

    /* Initialize ICA_EC_KEY with public key (X,Y) */
    rc = p_ica_ec_key_init(x_array, y_array, NULL, pubkey);
    if (rc != 0) {
        TRACE_ERROR("ica_ec_key_init() for public key failed with "
                    "rc = %d \n", rc);
        ret = CKR_FUNCTION_FAILED;
        goto end;
    }

    /* Create ICA_EC_KEY object with private key */
    privkey = p_ica_ec_key_new(nid, &privlen);
    if (!privkey) {
        TRACE_ERROR("ica_ec_key_new() for curve %i failed. \n", nid);
        ret = CKR_FUNCTION_FAILED;
        goto end;
    }

    /* Initialize ICA_EC_KEY with private key (D) */
    rc = p_ica_ec_key_init(NULL, NULL, d_array, privkey);
    if (rc != 0) {
        TRACE_ERROR("ica_ec_key_init() for private key failed with "
                    "rc = %d \n", rc);
        ret = CKR_FUNCTION_FAILED;
        goto end;
    }

    /* Calculate shared secret z */
    rc = p_ica_ecdh_derive_secret(adapter_handle, privkey,
                                  pubkey, secret_value, privlen);
    if (rc != 0) {
        TRACE_ERROR("ica_ecdh_derive_secret() failed with rc = %d. \n", rc);
        ret = CKR_FUNCTION_FAILED;
        goto end;
    }

    *secret_value_len = privlen;
    ret = CKR_OK;

end:
    p_ica_ec_key_free(privkey);
    p_ica_ec_key_free(pubkey);

    return ret;
}
#endif
