#include "test_common.h"
#include "pkcs32.h"

// Данная программа - это эксперимент по реализации удаленной
// работы через интерфейс PKCS#11.
// В дальнейшем предполагается подключить сюда протокол SESPAKE
// для взаимной аутентификации клиента и сервера по паролю.
// Это позволит не таскать с собой токен, а подключаться к
// защищенному хранилищу через сеть с помощью клиентской
// библиотеки PKCS#11.
// При этом, трафик может быть зашифрован на общем ключе,
// сгенерированном сторонами при аутентификации.

CK_RV test_crypto();
CK_RV test_gost3410(CK_SESSION_HANDLE sess);

int main(int argc, char *argv[])
{
    CK_RV rc = CKR_OK;

    rc = test_main(argc, argv);
    if (rc != CKR_OK)
    {
        fprintf(stderr, "test_main failed: 0x%lx(%s)\n",
                rc, get_ckr_name(rc));
        return rc;
    }
    rc = test_crypto();
    if (rc != CKR_OK)
    {
        fprintf(stderr, "test_crypto failed: 0x%lx(%s)\n",
                rc, get_ckr_name(rc));
        return rc;
    }
    return CKR_OK;
}

CK_RV Client_C_OpenSession( 
	CK_SLOT_ID				slotID,        /* the slot's ID */
	CK_FLAGS				flags,         /* from CK_SESSION_INFO */
	CK_VOID_PTR				pApplication,  /* passed to callback */
	CK_NOTIFY				Notify,        /* callback function */
	CK_SESSION_HANDLE_PTR	phSession      /* gets session handle */
)
{
	CK_RV rc = CKR_OK;
	CK_SESSION_HANDLE hSession = CK_INVALID_HANDLE;

	// TODO: Все целые числа CK_ULONG нужно выгружать и загружать

	// Имитация работы сервера
	// Аргументы pApplication и Notify игнорируются
    rc = funcs->C_OpenSession(slotID,
                              flags,
                              NULL_PTR, NULL_PTR, &hSession);
	*phSession = hSession;
	return rc;
}

CK_RV Client_C_CloseSession(
	CK_SESSION_HANDLE hSession  /* the session's handle */
)
{
	CK_RV rc = CKR_OK;

	// TODO: Все целые числа CK_ULONG нужно выгружать и загружать

	rc = funcs->C_CloseSession(hSession);
	return rc;
}

CK_RV Client_C_Login(
	CK_SESSION_HANDLE hSession,  /* the session's handle */
	CK_USER_TYPE      userType,  /* the user type */
	CK_UTF8CHAR_PTR   pPin,      /* the user's PIN */
	CK_ULONG          ulPinLen   /* the length of the PIN */
)
{
	CK_RV rc = CKR_OK;

	// TODO: Все целые числа CK_ULONG нужно выгружать и загружать

    rc = funcs->C_Login(hSession, userType, pPin, ulPinLen);
	return rc;
}

CK_RV Client_C_Logout(
	CK_SESSION_HANDLE hSession  /* the session's handle */
)
{
	CK_RV rc = CKR_OK;

	// TODO: Все целые числа CK_ULONG нужно выгружать и загружать

    rc = funcs->C_Logout(hSession);
	return rc;
}

CK_RV Client_C_GetMechanismInfo(
	CK_SLOT_ID            slotID,  /* ID of the token's slot */
	CK_MECHANISM_TYPE     type,    /* type of mechanism */
	CK_MECHANISM_INFO_PTR pInfo    /* receives mechanism info */
)
{
	CK_RV rc = CKR_OK;
	CK_MECHANISM_INFO minfo;

	// TODO: Все целые числа CK_ULONG нужно выгружать и загружать

    rc = funcs->C_GetMechanismInfo(slotID, type, &minfo);
	if (rc == CKR_OK) {
		memcpy(pInfo, &minfo, sizeof(minfo));
	}
	return rc;
}

CK_BBOOL attribute_value_is_ulong(CK_ATTRIBUTE_TYPE type)
{
    return(type == CKA_CLASS ||
           type == CKA_KEY_TYPE ||
           type == CKA_CERTIFICATE_TYPE ||
           type == CKA_CERTIFICATE_CATEGORY ||
           type == CKA_NAME_HASH_ALGORITHM ||
           type == CKA_KEY_GEN_MECHANISM ||
           type == CKA_AUTH_PIN_FLAGS ||
           type == CKA_HW_FEATURE_TYPE ||
           type == CKA_MECHANISM_TYPE ||
           type == CKA_MODULUS_BITS ||
           type == CKA_PRIME_BITS ||
           type == CKA_SUBPRIME_BITS ||
           type == CKA_VALUE_BITS ||
           type == CKA_VALUE_LEN);
}

// TODO: Хорошо бы сразу производить расчеты и выгрузку единообразно для разных платформ.
// Для этого следует ввести структуру CK_ATTRIBUTE_32 и выгружать значение атрибута следом
// за его структурой либо в виде массива байтов, либо в виде четырехбайтового числа 
// с порядком байтов little-endian.
// А сам шаблон следует преобразовать в список структур типа CK_ATTRIBUTE_32.
// Заметим, что хэндлы тоже нужно выгружать и загружать единообразно.
// В случае ls11sw хэндлы - это порядковые номера, поэтому их можно
// выгружать и загружать как числа CK_ULONG_32.
CK_ULONG get_template_size(CK_ATTRIBUTE_PTR pTemplate, CK_ULONG ulCount)
{
	CK_ULONG len = 0;
	CK_ULONG i = 0;

	for (i=0; i<ulCount; i++) { 
		if (attribute_value_is_ulong(pTemplate[i].type)) {
			len += sizeof(CK_ATTRIBUTE_32) + sizeof(CK_ULONG_32);
		} else {
			len += sizeof(CK_ATTRIBUTE_32) + pTemplate[i].ulValueLen;
		}
	}
	return len;
}

CK_RV Client_C_GenerateKeyPair(
  CK_SESSION_HANDLE    hSession,                    /* session handle */
  CK_MECHANISM_PTR     pMechanism,                  /* key-gen mech. */
  CK_ATTRIBUTE_PTR     pPublicKeyTemplate,          /* template for pub. key */
  CK_ULONG             ulPublicKeyAttributeCount,   /* # pub. attrs. */
  CK_ATTRIBUTE_PTR     pPrivateKeyTemplate,         /* template for priv. key */
  CK_ULONG             ulPrivateKeyAttributeCount,  /* # priv.  attrs. */
  CK_OBJECT_HANDLE_PTR phPublicKey,                 /* gets pub. key handle */
  CK_OBJECT_HANDLE_PTR phPrivateKey                 /* gets priv. key handle */
)
{
	CK_RV rc = CKR_OK;
	CK_SESSION_HANDLE sess = CK_INVALID_HANDLE;
	CK_SESSION_HANDLE_32 sess_32 = CK_INVALID_HANDLE;
	CK_OBJECT_HANDLE_32 priv_key_32 = CK_INVALID_HANDLE;
	CK_OBJECT_HANDLE_32 pub_key_32 = CK_INVALID_HANDLE;
	CK_MECHANISM mech;
	CK_BYTE *pPar = NULL;
	CK_ULONG ulParLen = 0;
	CK_ATTRIBUTE_PTR pPubKeyTemplate = NULL;
	CK_ULONG ulPubKeyAttributeCount = ulPublicKeyAttributeCount;
	CK_ATTRIBUTE_PTR pPrivKeyTemplate = NULL; 
	CK_ULONG ulPrivKeyAttributeCount = ulPrivateKeyAttributeCount;
	CK_OBJECT_HANDLE hPubKey = CK_INVALID_HANDLE;
	CK_OBJECT_HANDLE hPrivKey = CK_INVALID_HANDLE;
	CK_BYTE *pub_buf = NULL;
	CK_ULONG pub_buf_size = 0;
	CK_BYTE *priv_buf = NULL;
	CK_ULONG priv_buf_size = 0;
	TEMPLATE *pub_tmpl = (TEMPLATE *) malloc(sizeof(TEMPLATE));
	TEMPLATE *priv_tmpl = (TEMPLATE *) malloc(sizeof(TEMPLATE));
	TEMPLATE *pub_tmpl_new = NULL;
	TEMPLATE *priv_tmpl_new = NULL;

	memset(pub_tmpl, 0x0, sizeof(TEMPLATE));
	memset(priv_tmpl, 0x0, sizeof(TEMPLATE));

	// Выгрузка и загрузка хэндла сессии
	sess_32 = hSession; 
	Uint32ToUint8(sess_32, (unsigned char *)&sess_32);
	sess_32 = Uint8ToUint32((unsigned char *)&sess_32);
	sess = sess_32;

	// Выгрузка механизма и параметра
	memcpy(&mech, pMechanism, sizeof(CK_MECHANISM));
	if (pMechanism->pParameter) {
		ulParLen = pMechanism->ulParameterLen;
		pPar = malloc(ulParLen);
		memcpy(pPar, pMechanism->pParameter, ulParLen); 
	}

	// Выгрузка шаблонов объектов ключевой пары.
	// CK_ATTRIBUTES * -> TEMPLATE
	rc = template_add_attributes(pub_tmpl, 
		pPublicKeyTemplate, ulPublicKeyAttributeCount);
//	template_dump_attributes(pub_tmpl);
	rc = template_add_attributes(priv_tmpl, 
		pPrivateKeyTemplate, ulPrivateKeyAttributeCount);  
//	template_dump_attributes(priv_tmpl);
	// TEMPLATE -> flatten buffer
	pub_buf_size = template_get_compressed_size(pub_tmpl);
	pub_buf = malloc(pub_buf_size);
	rc = template_flatten(pub_tmpl, pub_buf);
	priv_buf_size = template_get_compressed_size(priv_tmpl);
	priv_buf = malloc(priv_buf_size);
	rc = template_flatten(priv_tmpl, priv_buf);

	// Имитация передачи
	template_free(pub_tmpl);
	template_free(priv_tmpl);
	// flatten buffer -> unflatten TEMPLATE
	rc = template_unflatten(&pub_tmpl_new, pub_buf, ulPublicKeyAttributeCount);
//	template_dump_attributes(pub_tmpl_new);
	rc = template_unflatten(&priv_tmpl_new, priv_buf, ulPrivateKeyAttributeCount);
//	template_dump_attributes(priv_tmpl_new);
	// TEMPLATE ->CK_ATTRIBUTES *
	rc = template_to_array(pub_tmpl_new, &pPubKeyTemplate, &ulPubKeyAttributeCount);
	template_free(pub_tmpl_new);
	rc = template_to_array(priv_tmpl_new, &pPrivKeyTemplate, &ulPrivKeyAttributeCount);
	template_free(priv_tmpl_new);

	rc = funcs->C_GenerateKeyPair(sess, &mech, 
		pPubKeyTemplate, ulPubKeyAttributeCount, 
		pPrivKeyTemplate, ulPrivKeyAttributeCount,      
		&hPubKey, &hPrivKey); 

	if (pPar) {
		free(pPar);
	}
	template_array_free(pPubKeyTemplate, ulPubKeyAttributeCount);
	template_array_free(pPrivKeyTemplate, ulPrivKeyAttributeCount);

	// Хэндлы объектов тоже нужно выгружать и загружать
	pub_key_32 = hPubKey; 
	Uint32ToUint8(pub_key_32, (unsigned char *)&pub_key_32);
	pub_key_32 = Uint8ToUint32((unsigned char *)&pub_key_32);
	*phPublicKey = pub_key_32;
	priv_key_32 = hPrivKey; 
	Uint32ToUint8(priv_key_32, (unsigned char *)&priv_key_32);
	priv_key_32 = Uint8ToUint32((unsigned char *)&priv_key_32);
	*phPrivateKey = priv_key_32;
	return rc;
}

CK_RV Client_C_SignInit(
	CK_SESSION_HANDLE hSession,    /* the session's handle */
	CK_MECHANISM_PTR  pMechanism,  /* the signature mechanism */
	CK_OBJECT_HANDLE  hKey         /* handle of signature key */
)
{
	CK_RV rc = CKR_OK;
	CK_SESSION_HANDLE sess = CK_INVALID_HANDLE;
	CK_SESSION_HANDLE_32 sess_32 = CK_INVALID_HANDLE;
	CK_MECHANISM mech;
	CK_BYTE *pPar = NULL;
	CK_ULONG ulParLen = 0;

	// Выгрузка и загрузка хэндла сессии
	sess_32 = hSession; 
	Uint32ToUint8(sess_32, (unsigned char *)&sess_32);
	sess_32 = Uint8ToUint32((unsigned char *)&sess_32);
	sess = sess_32;

	// Выгрузка механизма и параметра
	memcpy(&mech, pMechanism, sizeof(CK_MECHANISM));
	if (pMechanism->pParameter) {
		ulParLen = pMechanism->ulParameterLen;
		pPar = malloc(ulParLen);
		memcpy(pPar, pMechanism->pParameter, ulParLen); 
	}

	// TODO: Хэндлы тоже нужно выгружать и загружать
    rc = funcs->C_SignInit(sess, &mech, hKey);
	if (pPar) {
		free(pPar);
	}
	return rc;
}

CK_RV Client_C_Sign(
	CK_SESSION_HANDLE hSession,        /* the session's handle */
	CK_BYTE_PTR       pData,           /* the data to sign */
	CK_ULONG          ulDataLen,       /* count of bytes to sign */
	CK_BYTE_PTR       pSignature,      /* gets the signature */
	CK_ULONG_PTR      pulSignatureLen  /* gets signature length */
)
{
	CK_RV rc = CKR_OK;
	CK_SESSION_HANDLE sess = CK_INVALID_HANDLE;
	CK_SESSION_HANDLE_32 sess_32 = CK_INVALID_HANDLE;
	CK_BYTE *data = NULL;
	CK_ULONG data_len = ulDataLen;
	CK_BYTE *sig = NULL;
	CK_ULONG sig_len = 0;

	// Выгрузка и загрузка хэндла сессии
	sess_32 = hSession; 
	Uint32ToUint8(sess_32, (unsigned char *)&sess_32);
	sess_32 = Uint8ToUint32((unsigned char *)&sess_32);
	sess = sess_32;

	if (data_len > 0) {
		data = malloc(data_len);
		memcpy(data, pData, data_len);
	}
	sig_len = *pulSignatureLen;
	if (pSignature) {
		sig = malloc(sig_len);
	}
    rc  = funcs->C_Sign(sess, data, data_len, sig, &sig_len);
	*pulSignatureLen = sig_len;
	if (rc == CKR_OK) {
		if (pSignature) {
			memcpy(pSignature, sig, sig_len);
		}
	}
	if (data) {
		free(data);
	}
	if (sig) {
		free(sig);
	}
	return rc;
}

CK_RV Client_C_VerifyInit(
	CK_SESSION_HANDLE hSession,    /* the session's handle */
	CK_MECHANISM_PTR  pMechanism,  /* the verification mechanism */
	CK_OBJECT_HANDLE  hKey         /* verification key */
)
{
	CK_RV rc = CKR_OK;
	CK_SESSION_HANDLE sess = CK_INVALID_HANDLE;
	CK_SESSION_HANDLE_32 sess_32 = CK_INVALID_HANDLE;
	CK_OBJECT_HANDLE key = CK_INVALID_HANDLE;
	CK_OBJECT_HANDLE_32 key_32 = CK_INVALID_HANDLE;
	CK_MECHANISM mech;
	CK_BYTE *pPar = NULL;
	CK_ULONG ulParLen = 0;

	// Выгрузка и загрузка хэндла сессии
	sess_32 = hSession; 
	Uint32ToUint8(sess_32, (unsigned char *)&sess_32);
	sess_32 = Uint8ToUint32((unsigned char *)&sess_32);
	sess = sess_32;

	// Выгрузка механизма и параметра
	memcpy(&mech, pMechanism, sizeof(CK_MECHANISM));
	if (pMechanism->pParameter) {
		ulParLen = pMechanism->ulParameterLen;
		pPar = malloc(ulParLen);
		memcpy(pPar, pMechanism->pParameter, ulParLen); 
	}

	// Хэндлы объектов тоже нужно выгружать и загружать
	key_32 = hKey; 
	Uint32ToUint8(key_32, (unsigned char *)&key_32);
	key_32 = Uint8ToUint32((unsigned char *)&key_32);
	key = key_32;

    rc = funcs->C_VerifyInit(sess, &mech, key);
	if (pPar) {
		free(pPar);
	}

	return rc;
}

CK_RV Client_C_Verify(
	CK_SESSION_HANDLE hSession,       /* the session's handle */
	CK_BYTE_PTR       pData,          /* signed data */
	CK_ULONG          ulDataLen,      /* length of signed data */
	CK_BYTE_PTR       pSignature,     /* signature */
	CK_ULONG          ulSignatureLen  /* signature length*/
)
{
	CK_RV rc = CKR_OK;
	CK_SESSION_HANDLE sess = CK_INVALID_HANDLE;
	CK_SESSION_HANDLE_32 sess_32 = CK_INVALID_HANDLE;
	CK_BYTE *data = NULL;
	CK_ULONG data_len = ulDataLen;
	CK_BYTE *sig = NULL;
	CK_ULONG sig_len = ulSignatureLen;

	// Выгрузка и загрузка хэндла сессии
	sess_32 = hSession; 
	Uint32ToUint8(sess_32, (unsigned char *)&sess_32);
	sess_32 = Uint8ToUint32((unsigned char *)&sess_32);
	sess = sess_32;

	if (data_len > 0) {
		data = malloc(data_len);
		memcpy(data, pData, data_len);
	}
	if (sig_len > 0) {
		sig = malloc(sig_len);
		memcpy(sig, pSignature, sig_len);
	}

    rc = funcs->C_Verify(sess, data, data_len, sig, sig_len);
	if (data) {
		free(data);
	}
	if (sig) {
		free(sig);
	}

	return rc;
}

CK_RV Client_C_DestroyObject(
  CK_SESSION_HANDLE hSession,  /* the session's handle */
  CK_OBJECT_HANDLE  hObject    /* the object's handle */
)
{
	CK_RV rc = CKR_OK;
	CK_SESSION_HANDLE sess = CK_INVALID_HANDLE;
	CK_SESSION_HANDLE_32 sess_32 = CK_INVALID_HANDLE;
	CK_OBJECT_HANDLE obj = CK_INVALID_HANDLE;
	CK_OBJECT_HANDLE_32 obj_32 = CK_INVALID_HANDLE;

	// Выгрузка и загрузка хэндла сессии
	sess_32 = hSession; 
	Uint32ToUint8(sess_32, (unsigned char *)&sess_32);
	sess_32 = Uint8ToUint32((unsigned char *)&sess_32);
	sess = sess_32;
	// Хэндлы объектов тоже нужно выгружать и загружать
	obj_32 = hObject; 
	Uint32ToUint8(obj_32, (unsigned char *)&obj_32);
	obj_32 = Uint8ToUint32((unsigned char *)&obj_32);
	obj = obj_32;

    rc = funcs->C_DestroyObject(sess, obj);

	return rc;
}

CK_RV test_crypto()
{
    CK_RV             rc, ret;
    CK_SESSION_HANDLE hSession;
    CK_MECHANISM_INFO minfo;
/*
    rc = funcs->C_OpenSession(SlotId,
                              CKF_RW_SESSION | CKF_SERIAL_SESSION,
                              NULL_PTR, NULL_PTR, &hSession);
*/
    rc = Client_C_OpenSession(SlotId,
                              CKF_RW_SESSION | CKF_SERIAL_SESSION,
                              NULL_PTR, NULL_PTR, &hSession);
    if (rc != CKR_OK)
    {
        fprintf(stderr,
                "ERROR call to C_OpenSession failed, rc = 0x%lx(%s)\n",
                rc, get_ckr_name(rc));
        goto out;
    }
    fprintf(stderr, "C_OpenSession success\n");

    // log in as normal user
//    rc = funcs->C_Login(hSession, CKU_USER, user_pin, strlen((char *)user_pin));
    rc = Client_C_Login(hSession, CKU_USER, user_pin, strlen((char *)user_pin));
    if (rc != CKR_OK)
    {
        fprintf(stderr,
                "ERROR call to C_Login failed, rc = 0x%lx(%s)\n",
                rc, get_ckr_name(rc));
        goto out_close;
    }
    fprintf(stderr, "C_Login success\n");
//    rc = funcs->C_GetMechanismInfo(SlotId, CKM_GOSTR3410, &minfo);
    rc = Client_C_GetMechanismInfo(SlotId, CKM_GOSTR3410, &minfo);
    if (rc != CKR_OK)
    {
        fprintf(stderr,
                "\n======== Mechanism CKM_GOSTR3410 not supported ===========\n");
    }
    else
    {
        fprintf(stderr,
                "\n==================== CKM_GOSTR3410 test ==================\n");
        rc = test_gost3410(hSession);
        if (rc != CKR_OK)
        {
            fprintf(stderr,
                    "ERROR CKM_GOSTR3410 failed, rc = 0x%lx(%s)\n",
                    rc, get_ckr_name(rc));
        }
        else
        {
            fprintf(stderr, "CKM_GOSTR3410 test SUCCESS.\n");
        }
    }
 out_close:
    if ((ret = Client_C_CloseSession(hSession)) != CKR_OK)
    {
        rc = ret;
        fprintf(stderr,
                "Error: C_CloseSession failed with 0x%lx(%s)\n", rc, get_ckr_name(rc));
    }
    else
    {
        fprintf(stderr, "C_CloseSession success\n");
    }

 out:
    return rc;
}

CK_RV test_gost3410(CK_SESSION_HANDLE sess)
{
    CK_RV                  rc = CKR_OK;
    CK_BYTE                value[256];
    CK_ULONG               len;
    CK_OBJECT_HANDLE       pub_key            = CK_INVALID_HANDLE;
    CK_OBJECT_HANDLE       priv_key           = CK_INVALID_HANDLE;
    CK_MECHANISM           mechanism_desc     = { CKM_GOSTR3410, NULL, 0 };
    CK_MECHANISM_PTR       mechanism          = &mechanism_desc;
    CK_MECHANISM           mechanism_gen_desc = { CKM_GOSTR3410_KEY_PAIR_GEN, NULL, 0 };
    CK_MECHANISM_PTR       mechanism_gen      = &mechanism_gen_desc;

    static CK_BBOOL        ltrue       = CK_TRUE;
//    static CK_BBOOL        lfalse      = CK_FALSE;
//    static CK_OBJECT_CLASS oclass      = CKO_SECRET_KEY;
//    static CK_OBJECT_CLASS oclass_pub  = CKO_PUBLIC_KEY;
//    static CK_OBJECT_CLASS oclass_priv = CKO_PRIVATE_KEY;
//    static CK_KEY_TYPE     key_type    = CKK_GOSTR3410;

    static CK_BYTE         gostR3410params[] = {
        0x06, 0x07, 0x2a, 0x85, 0x03, 0x02, 0x02, 0x23, 0x01
    };
    static CK_BYTE         gostR3411params[] = {
        0x06, 0x07, 0x2a, 0x85, 0x03, 0x02, 0x02, 0x1e, 0x01
    };
    static CK_BYTE         data[] = {
        0xee, 0x8c, 0xd9, 0x40, 0x23, 0xdd, 0x9a, 0xf8,
        0x17, 0xdd, 0xe8, 0x1f, 0x02, 0x25, 0x5b, 0xb3,
        0xaa, 0x6b, 0xd3, 0x21, 0x09, 0x41, 0x95, 0x7e,
        0xea, 0x7e, 0x0e, 0x3c, 0x35, 0x9f, 0x04, 0x5a
    };
//    static CK_BYTE         pub_key_value[64];
//    static CK_UTF8CHAR     *label         = (CK_UTF8CHAR *)"Yet Another Keypair";
    CK_ATTRIBUTE           pub_template[] = {
        { CKA_TOKEN,           &ltrue,          sizeof(ltrue)           },
        { CKA_GOSTR3410PARAMS, gostR3410params, sizeof(gostR3410params) },
        { CKA_GOSTR3411PARAMS, gostR3411params, sizeof(gostR3411params) },
        { CKA_VERIFY,          &ltrue,          sizeof(CK_BBOOL)        },
    };
    CK_ATTRIBUTE           priv_template[] = {
        { CKA_TOKEN,   &ltrue, sizeof(ltrue)    },
        { CKA_PRIVATE, &ltrue, sizeof(ltrue)    },
        { CKA_SIGN,    &ltrue, sizeof(CK_BBOOL) },
    };
/*
    rc = funcs->C_GenerateKeyPair(sess, mechanism_gen,
                                  pub_template, sizeof(pub_template) / sizeof(CK_ATTRIBUTE),
                                  priv_template, sizeof(priv_template) / sizeof(CK_ATTRIBUTE),
                                  &pub_key, &priv_key);
*/
    rc = Client_C_GenerateKeyPair(sess, mechanism_gen,
                                  pub_template, sizeof(pub_template) / sizeof(CK_ATTRIBUTE),
                                  priv_template, sizeof(priv_template) / sizeof(CK_ATTRIBUTE),
                                  &pub_key, &priv_key);
    if (rc != CKR_OK)
    {
        fprintf(stderr, "C_GenerateKeyPair failed: 0x%lx(%s)\n",
                rc, get_ckr_name(rc));
        return rc;
    }
    fprintf(stderr, "C_GenerateKeyPair OK\n");

//    rc = funcs->C_SignInit(sess, mechanism, priv_key);
    rc = Client_C_SignInit(sess, mechanism, priv_key);
    if (rc != CKR_OK)
    {
        fprintf(stderr, "C_SignInit failed: 0x%lx(%s)\n",
                rc, get_ckr_name(rc));
        return rc;
    }

    len = sizeof(value);
//    rc  = funcs->C_Sign(sess, data, sizeof(data), value, &len);
    rc  = Client_C_Sign(sess, data, sizeof(data), value, &len);
    if (rc != CKR_OK)
    {
        fprintf(stderr, "C_Sign failed: 0x%lx(%s)\n",
                rc, get_ckr_name(rc));
        return rc;
    }
    fprintf(stderr, "C_Sign OK\n");

    printf("SIGNATURE:\n");
    print_bytes(value, len);

//    rc = funcs->C_VerifyInit(sess, mechanism, pub_key);
    rc = Client_C_VerifyInit(sess, mechanism, pub_key);
    if (rc != CKR_OK)
    {
        fprintf(stderr, "C_VerifyInit failed: 0x%lx(%s)\n",
                rc, get_ckr_name(rc));
        return rc;
    }

//    rc = funcs->C_Verify(sess, data, sizeof(data), value, len);
    rc = Client_C_Verify(sess, data, sizeof(data), value, len);
    if (rc != CKR_OK)
    {
        fprintf(stderr, "C_Verify failed: 0x%lx(%s)\n",
                rc, get_ckr_name(rc));
        return rc;
    }
    fprintf(stderr, "C_Verify OK\n");


//    rc = funcs->C_DestroyObject(sess, priv_key);
    rc = Client_C_DestroyObject(sess, priv_key);
    if (rc != CKR_OK)
    {
        fprintf(stderr, "C_DestroyObject failed: 0x%lx(%s)\n",
                rc, get_ckr_name(rc));
        return rc;
    }
//    rc = funcs->C_DestroyObject(sess, priv_key);
    rc = Client_C_DestroyObject(sess, pub_key);
    if (rc != CKR_OK)
    {
        fprintf(stderr, "C_DestroyObject failed: 0x%lx(%s)\n",
                rc, get_ckr_name(rc));
        return rc;
    }

    return rc;
}



