#include "test_common.h"

CK_RV test_crypto();
CK_RV test_gost_cipher_pkcs8_key_wrap(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\n", rc);
        return rc;
    }
    rc = test_crypto();
    if (rc != CKR_OK)
    {
        fprintf(stderr, "test_crypto failed: 0x%lx\n", rc);
        return rc;
    }
    return CKR_OK;
}

CK_RV test_crypto()
{
    CK_RV             rc;
    CK_SESSION_HANDLE hSession;
    CK_MECHANISM_INFO minfo;

    printf(
        "CKM_GOST_CIPHER_PKCS8_KEY_WRAP "
        "and CKM_GOSTR3410_PUBLIC_KEY_DERIVE test\n");

    rc = funcs->C_GetMechanismInfo(SlotId,
                                   CKM_GOSTR3410_KEY_PAIR_GEN, &minfo);
    if (rc != CKR_OK)
    {
        fprintf(stderr,
                "Mechanism CKM_GOSTR3410_KEY_PAIR_GEN not supported\n");
        return rc;
    }
    rc = funcs->C_GetMechanismInfo(SlotId,
                                   CKM_GOST_CIPHER_PKCS8_KEY_WRAP, &minfo);
    if (rc != CKR_OK)
    {
        fprintf(stderr,
                "Mechanism CKM_GOST_CIPHER_PKCS8_KEY_WRAP not supported\n");
        return rc;
    }
    rc = funcs->C_GetMechanismInfo(SlotId,
                                   CKM_GOSTR3410_PUBLIC_KEY_DERIVE, &minfo);
    if (rc != CKR_OK)
    {
        fprintf(stderr,
                "Mechanism CKM_GOSTR3410_PUBLIC_KEY_DERIVE not supported\n");
        return rc;
    }

    rc = funcs->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\n", rc);
        goto out;
    }

    rc = funcs->C_Login(hSession, CKU_USER, user_pin, (CK_ULONG)strlen(user_pin));
    if (rc != CKR_OK) {
        fprintf(stderr, "ERROR call to C_Login failed, rc = 0x%lx\n", rc);
        goto out;
    }
 
    rc = test_gost_cipher_pkcs8_key_wrap(hSession);

    if (rc != CKR_OK)
    {
        fprintf(stderr,
                "CKM_GOST_CIPHER_PKCS8_KEY_WRAP and CKM_GOSTR3410_PUBLIC_KEY_DERIVE "
                "test failed, rc = 0x%lx\n",
                rc);
    }
    else
    {
        printf(
            "CKM_GOST_CIPHER_PKCS8_KEY_WRAP and CKM_GOSTR3410_PUBLIC_KEY_DERIVE "
            "test SUCCESS\n");
    }

 out:
    funcs->C_CloseSession(hSession);
    return rc;
}

CK_RV test_gost_cipher_pkcs8_key_wrap(CK_SESSION_HANDLE sess)
{
    CK_RV                        rc = CKR_OK;
    char                         str[4096];
    CK_BYTE                      value[4096];
    CK_ULONG                     len;
    CK_OBJECT_HANDLE             pub_key        = CK_INVALID_HANDLE;
    CK_OBJECT_HANDLE             priv_key       = CK_INVALID_HANDLE;
    CK_OBJECT_HANDLE             unw_priv_key   = CK_INVALID_HANDLE;
    CK_OBJECT_HANDLE             der_pub_key    = CK_INVALID_HANDLE;
//    CK_OBJECT_HANDLE             cipher_key     = CK_INVALID_HANDLE;
    CK_MECHANISM                 mechanism_desc =
    { CKM_GOST_CIPHER_PKCS8_KEY_WRAP, NULL, 0 };
    CK_MECHANISM_PTR             mechanism          = &mechanism_desc;
    CK_MECHANISM                 mechanism_der_desc =
    { CKM_GOSTR3410_PUBLIC_KEY_DERIVE, NULL, 0 };
    CK_MECHANISM_PTR             mechanism_der          = &mechanism_der_desc;
    CK_MECHANISM                 mechanism_gen_256_desc =
    { CKM_GOSTR3410_KEY_PAIR_GEN, NULL, 0 };
    CK_MECHANISM_PTR             mechanism_gen_256      = &mechanism_gen_256_desc;
    CK_MECHANISM                 mechanism_gen_512_desc =
    { CKM_GOSTR3410_512_KEY_PAIR_GEN, NULL, 0 };
    CK_MECHANISM_PTR             mechanism_gen_512 = &mechanism_gen_512_desc;

    static CK_BBOOL              ltrue        = CK_TRUE;
    static CK_BBOOL              lfalse       = CK_FALSE;
    static const CK_OBJECT_CLASS oclass_pub   = CKO_PUBLIC_KEY;
    static const CK_OBJECT_CLASS oclass_priv  = CKO_PRIVATE_KEY;
    static CK_KEY_TYPE           key_type_256 = CKK_GOSTR3410;
    static CK_KEY_TYPE           key_type_512 = CKK_GOSTR3410_512;
    // PAR 256 A OID
    static CK_BYTE               gostR3410_256_params_A[] = {
        0x06, 0x07, 0x2a, 0x85, 0x03, 0x02, 0x02, 0x23, 0x01
    };
    // PAR 512 A OID
    static CK_BYTE                      gostR3410_512_params_A[] = {
        0x06, 0x09, 0x2a, 0x85, 0x03, 0x07, 0x01, 0x2, 0x01, 0x02, 0x01
    };
    // PAR HASH 1 OID
    static CK_BYTE               gostR3411_94_params[] = {
        0x06, 0x07, 0x2a, 0x85, 0x03, 0x02, 0x02, 0x1e, 0x01
    };
    // PAR 3411-2012-256 OID
    static CK_BYTE                      gostR3411_2012_256_params[] = {
        0x06, 0x08, 0x2a, 0x85, 0x03, 0x07, 0x01, 0x01, 0x02, 0x02
    };
    // PAR 3411-2012-512 OID
    static CK_BYTE                      gostR3411_2012_512_params[] = {
        0x06, 0x08, 0x2a, 0x85, 0x03, 0x07, 0x01, 0x01, 0x02, 0x03
    };
/*
   typedef struct CK_GOST_CIPHER_PKCS8_KEY_WRAP_PARAMS {
		CK_CHAR_PTR      pPassword;
		CK_ULONG         ulPasswordLen;
        CK_BYTE_PTR      pSalt;
        CK_ULONG         ulSaltLen;
		CK_ULONG         ulIterCount;
		CK_KEY_TYPE      cipherKeyType;
        CK_BYTE_PTR      pIV;
        CK_ULONG         ulIVLen;
   } CK_GOST_CIPHER_PKCS8_KEY_WRAP_PARAMS;
 */
    CK_GOST_CIPHER_PKCS8_KEY_WRAP_PARAMS params;

    static CK_BYTE                     salt[] = {
        0x12, 0x34, 0x56, 0x78, 0x78, 0x56, 0x34, 0x26
    };
    static CK_BYTE                     salt32[] = {
        0x12, 0x34, 0x56, 0x78, 0x78, 0x56, 0x34, 0x26,
        0x12, 0x34, 0x56, 0x78, 0x78, 0x56, 0x34, 0xab,
        0x12, 0x34, 0x56, 0x78, 0x78, 0x56, 0x34, 0xab,
        0x12, 0x34, 0x56, 0x78, 0x78, 0x56, 0x34, 0xab,
    };
    static CK_ULONG                    iter     = 2048;
    static CK_ULONG                    iter2000 = 2000;
    static CK_BYTE                     iv_8[]     = {
        0x12, 0x34, 0x56, 0x78, 0x78, 0x56, 0x34, 0xab
    };
    static CK_BYTE                     iv_16[]     = {
        0x12, 0x34, 0x56, 0x78, 0x78, 0x56, 0x34, 0xab,
        0x12, 0x34, 0x56, 0x78, 0x78, 0x56, 0x34, 0xab
    };
    // Use UTF-8 password
    static CK_UTF8CHAR_PTR             password                = (CK_UTF8CHAR_PTR)"4444";
//    static CK_ULONG                    password_len            = 4;
    CK_ATTRIBUTE                       pub_2001_256_template[] = {
        { CKA_GOSTR3410PARAMS,
          gostR3410_256_params_A, sizeof(gostR3410_256_params_A) },
        { CKA_GOSTR3411PARAMS,
          gostR3411_94_params, sizeof(gostR3411_94_params) },
		{ CKA_TOKEN, &ltrue, sizeof(ltrue) }, 
    };
    CK_ATTRIBUTE                       pub_2012_256_template[] = {
        { CKA_GOSTR3410PARAMS,
          gostR3410_256_params_A, sizeof(gostR3410_256_params_A) },
        { CKA_GOSTR3411PARAMS,
          gostR3411_2012_256_params, sizeof(gostR3411_2012_256_params) },
		{ CKA_TOKEN, &ltrue, sizeof(ltrue) }, 
    };
    CK_ATTRIBUTE                       pub_2012_512_template[] = {
        { CKA_GOSTR3410PARAMS,
          gostR3410_512_params_A, sizeof(gostR3410_512_params_A) },
        { CKA_GOSTR3411PARAMS,
          gostR3411_2012_512_params, sizeof(gostR3411_2012_512_params) },
		{ CKA_TOKEN, &ltrue, sizeof(ltrue) }, 
    };
    // Template for token private key generation.
    CK_ATTRIBUTE                       priv_template[] = {
        { CKA_EXTRACTABLE, &ltrue, sizeof(ltrue) },
        { CKA_SENSITIVE,   &ltrue, sizeof(ltrue) },
		{ CKA_TOKEN, &ltrue, sizeof(ltrue) }, 
		{ CKA_PRIVATE, &ltrue, sizeof(ltrue) }, 
    };
    // Additional attributes for token unwrapped private key generation.
    CK_ATTRIBUTE                       unw_256_template[] = {
        { CKA_CLASS,    (CK_VOID_PTR) &oclass_priv, sizeof(oclass_priv)  },
        { CKA_KEY_TYPE, &key_type_256,              sizeof(key_type_256) },
        { CKA_EXTRACTABLE, &ltrue, sizeof(ltrue) },
        { CKA_SENSITIVE,   &lfalse, sizeof(ltrue) },
		{ CKA_TOKEN, &ltrue, sizeof(ltrue) }, 
		{ CKA_PRIVATE, &ltrue, sizeof(ltrue) }, 
    };
    CK_ATTRIBUTE                       unw_512_template[] = {
        { CKA_CLASS,    (CK_VOID_PTR) &oclass_priv, sizeof(oclass_priv)  },
        { CKA_KEY_TYPE, &key_type_512,              sizeof(key_type_512) },
        { CKA_EXTRACTABLE, &ltrue, sizeof(ltrue) },
        { CKA_SENSITIVE,   &ltrue, sizeof(ltrue) },
		{ CKA_TOKEN, &ltrue, sizeof(ltrue) }, 
		{ CKA_PRIVATE, &ltrue, sizeof(ltrue) }, 
    };
	CK_BYTE pub_key_value[128];
	CK_BYTE der_pub_key_value[128];
	CK_ULONG pub_key_len = 128;
	CK_ATTRIBUTE cka_value_template[] = {
		{ CKA_VALUE, NULL, 0 },
	};
	// Поскольку параметры ключевой сохраняются в объекте закрытого ключа
	// при его генерации, то при получении открытого ключа по закрытому
	// их лучше вообще не задавать, чтобы не нарваться на конфликт атрибутов.
	// Задавать следует другие атрибуты, например CKA_TOKEN, CKA_LABEL и т.д.,
	// только если они необходимы для дальнейшей работы с объектом открытого ключа.

    // GOSTR3410-2012-256
	printf("GOSTR3410-2012-256\n");
    rc = funcs->C_GenerateKeyPair(sess, mechanism_gen_256,
                                  pub_2012_256_template,
                                  sizeof(pub_2012_256_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, rc = 0x%lx\n", rc);
        goto end;
    }
	cka_value_template[0].pValue = pub_key_value;
	cka_value_template[0].ulValueLen = 64;
	rc = funcs->C_GetAttributeValue(sess, pub_key, cka_value_template, sizeof(cka_value_template) / sizeof(CK_ATTRIBUTE));
    if (rc != CKR_OK)
    {
        fprintf(stderr, "C_GetAttributeValue failed, rc = 0x%lx\n", rc);
        goto end;
    }
    // Упаковка ключей 2012-256 должна производиться на gostR3411_2012_512_params!
    params.pPassword         = password;
    params.ulPasswordLen     = (CK_ULONG)strlen((char *)password);
    params.pSalt             = salt32;
    params.ulSaltLen         = sizeof(salt32);
    params.ulIterCount       = iter2000;
	params.cipherKeyType     = CKK_MAGMA;
    params.pIV               = iv_8;
    params.ulIVLen           = sizeof(iv_8)/2;	// Для режима CTR

    mechanism->pParameter     = &params;
    mechanism->ulParameterLen = sizeof(params);
	len = sizeof(value);
    rc                        = funcs->C_WrapKey(sess, mechanism,
                                                 CK_INVALID_HANDLE, priv_key, value, &len);
    if (rc != CKR_OK)
    {
        fprintf(stderr, "C_WrapKey failed, rc = 0x%lx\n", rc);
        goto end;
    }

    print_bytes_str(value, len, str);
    printf("PKCS#8:\n%s\n", str);


    mechanism->pParameter     = password;
    mechanism->ulParameterLen = (CK_ULONG)strlen((char *)password);
    rc                        = funcs->C_UnwrapKey(sess, mechanism, CK_INVALID_HANDLE,
                                                   value, len,
                                                   unw_256_template,
                                                   sizeof(unw_256_template) / sizeof(CK_ATTRIBUTE),
                                                   &unw_priv_key);
    if (rc != CKR_OK)
    {
        fprintf(stderr, "C_UnwrapKey failed, rc = 0x%lx\n", rc);
        goto end;
    }
    // Получаем открытый ключ по закрытому
    rc = funcs->C_DeriveKey(sess, mechanism_der, unw_priv_key,
		// Шаблон открытого ключа задавать не обязательно,
		// если генерируется сессионный ключ.
		NULL, 0,
        &der_pub_key);
    if (rc != CKR_OK)
    {
        fprintf(stderr, "C_DeriveKey failed, rc = 0x%lx\n", rc);
        goto end;
    }
	cka_value_template[0].pValue = der_pub_key_value;
	cka_value_template[0].ulValueLen = 64;
	rc = funcs->C_GetAttributeValue(sess, der_pub_key, cka_value_template, sizeof(cka_value_template) / sizeof(CK_ATTRIBUTE));
    if (rc != CKR_OK)
    {
        fprintf(stderr, "C_GetAttributeValue failed, rc = 0x%lx\n", rc);
        goto end;
    }
    print_bytes_str(der_pub_key_value, cka_value_template[0].ulValueLen, str);
    printf("Public key value:\n%s\n", str);
	if (memcmp(pub_key_value, der_pub_key_value, 64) != 0){
        fprintf(stderr, "Invalid derived public key\n");
		rc = CKR_FUNCTION_FAILED;
        goto end;
	}
	printf("GOST R 3410-2012-256: CKM_GOSTR3410_PUBLIC_KEY_DERIVE OK\n");
    // Для проверки распакованного ключа нужно что-нибудь им подписать
    // и проверить подпись открытым ключом.
    {
        CK_MECHANISM mechanism_sign = { CKM_GOSTR3410, NULL, 0 };
        // Подписываемый дайджест (32 байта)
        CK_BYTE      pData[32] = {
            0x4D, 0x89, 0x9E, 0x48, 0xC5, 0x39, 0x64, 0xD1,
            0x78, 0xB4, 0x6D, 0x58, 0x40, 0x5F, 0x62, 0x8F,
            0xA4, 0x46, 0x4C, 0xDC, 0x73, 0x75, 0xB0, 0xE5,
            0x2E, 0x91, 0xFB, 0x64, 0x9C, 0x06, 0xAB, 0x75
        };
        CK_ULONG     ulDataLen = 32;
        CK_BYTE      pSignatureData[64];
        CK_ULONG     signatureDataLen = 64;

        rc = funcs->C_SignInit(sess, &mechanism_sign, unw_priv_key);
        if (rc != CKR_OK)
        {
            fprintf(stderr, "C_SignInit failed, rc = 0x%lx\n", rc);
            goto end;
        }
        rc = funcs->C_Sign(sess,
                           pData, ulDataLen, pSignatureData, &signatureDataLen);
        if (rc != CKR_OK)
        {
            fprintf(stderr, "C_Sign failed, rc = 0x%lx\n", rc);
            goto end;
        }

        rc = funcs->C_VerifyInit(sess, &mechanism_sign, pub_key);
        if (rc != CKR_OK)
        {
            fprintf(stderr, "C_VerifyInit failed, rc = 0x%lx\n", rc);
            goto end;
        }
        rc = funcs->C_Verify(sess,
                             pData, ulDataLen, pSignatureData, signatureDataLen);
        if (rc != CKR_OK)
        {
            fprintf(stderr, "C_Verify failed, rc = 0x%lx\n", rc);
            goto end;
        }

        rc = funcs->C_VerifyInit(sess, &mechanism_sign, der_pub_key);
        if (rc != CKR_OK)
        {
            fprintf(stderr, "C_VerifyInit failed, rc = 0x%lx\n", rc);
            goto end;
        }
        rc = funcs->C_Verify(sess,
                             pData, ulDataLen, pSignatureData, signatureDataLen);
        if (rc != CKR_OK)
        {
            fprintf(stderr, "C_Verify failed, rc = 0x%lx\n", rc);
            goto end;
        }
    }
    funcs->C_DestroyObject(sess, der_pub_key);
    funcs->C_DestroyObject(sess, unw_priv_key);
    funcs->C_DestroyObject(sess, pub_key);
    funcs->C_DestroyObject(sess, priv_key);


    // GOSTR3410-2012-512
	printf("GOSTR3410-2012-512\n");
    rc = funcs->C_GenerateKeyPair(sess, mechanism_gen_512,
                                  pub_2012_512_template,
                                  sizeof(pub_2012_512_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, rc = 0x%lx\n", rc);
        goto end;
    }
	cka_value_template[0].pValue = pub_key_value;
	cka_value_template[0].ulValueLen = 128;
	rc = funcs->C_GetAttributeValue(sess, pub_key, cka_value_template, sizeof(cka_value_template) / sizeof(CK_ATTRIBUTE));
    if (rc != CKR_OK)
    {
        fprintf(stderr, "C_GetAttributeValue failed, rc = 0x%lx\n", rc);
        goto end;
    }

    params.pPassword         = password;
    params.ulPasswordLen     = (CK_ULONG)strlen((char *)password);
    params.pSalt             = salt32;
    params.ulSaltLen         = sizeof(salt32);
    params.ulIterCount       = iter2000;
    params.cipherKeyType     = CKK_KUZNYECHIK;
    params.pIV               = iv_16;
    params.ulIVLen           = sizeof(iv_16)/2;	// Для режима CTR

    mechanism->pParameter     = &params;
    mechanism->ulParameterLen = sizeof(params);
	len = sizeof(value);
    rc                        = funcs->C_WrapKey(sess, mechanism,
                                                 CK_INVALID_HANDLE, priv_key, value, &len);
    if (rc != CKR_OK)
    {
        fprintf(stderr, "C_WrapKey failed, rc = 0x%lx\n", rc);
        goto end;
    }

    print_bytes_str(value, len, str);
    printf("PKCS#8:\n%s\n", str);


    mechanism->pParameter     = password;
    mechanism->ulParameterLen = (CK_ULONG)strlen((char *)password);
    rc                        = funcs->C_UnwrapKey(sess, mechanism, CK_INVALID_HANDLE,
                                                   value, len,
                                                   unw_512_template, sizeof(unw_512_template) / sizeof(CK_ATTRIBUTE),
                                                   &unw_priv_key);
    if (rc != CKR_OK)
    {
        fprintf(stderr, "C_UnwrapKey failed, rc = 0x%lx\n", rc);
        goto end;
    }
    // Получаем открытый ключ по закрытому
    rc = funcs->C_DeriveKey(sess, mechanism_der, unw_priv_key,
		// Шаблон открытого ключа задавать не обязательно,
		// если генерируется сессионный ключ.
		NULL, 0,
        &der_pub_key);
    if (rc != CKR_OK)
    {
        fprintf(stderr, "C_DeriveKey failed, rc = 0x%lx\n", rc);
        goto end;
    }
	cka_value_template[0].pValue = der_pub_key_value;
	cka_value_template[0].ulValueLen = 128;
	rc = funcs->C_GetAttributeValue(sess, der_pub_key, cka_value_template, sizeof(cka_value_template) / sizeof(CK_ATTRIBUTE));
    if (rc != CKR_OK)
    {
        fprintf(stderr, "C_GetAttributeValue failed, rc = 0x%lx\n", rc);
        goto end;
    }
    print_bytes_str(der_pub_key_value, cka_value_template[0].ulValueLen, str);
    printf("Public key value:\n%s\n", str);
	if (memcmp(pub_key_value, der_pub_key_value, 128) != 0){
        fprintf(stderr, "Invalid derived public key\n");
		rc = CKR_FUNCTION_FAILED;
        goto end;
	}
	printf("GOST R 3410-2012-512: CKM_GOSTR3410_PUBLIC_KEY_DERIVE OK\n");
    // Для проверки распакованного ключа нужно что-нибудь им подписать
    // и проверить подпись открытым ключом.
    {
        CK_MECHANISM mechanism_sign = { CKM_GOSTR3410_512, NULL, 0 };
        // Подписываемый дайджест (64 байта)
        CK_BYTE      pData[] = {
            0x4D, 0x89, 0x9E, 0x48, 0xC5, 0x39, 0x64, 0xD1,
            0x78, 0xB4, 0x6D, 0x58, 0x40, 0x5F, 0x62, 0x8F,
            0xA4, 0x46, 0x4C, 0xDC, 0x73, 0x75, 0xB0, 0xE5,
            0x2E, 0x91, 0xFB, 0x64, 0x9C, 0x06, 0xAB, 0x75,
            0x4D, 0x89, 0x9E, 0x48, 0xC5, 0x39, 0x64, 0xD1,
            0x78, 0xB4, 0x6D, 0x58, 0x40, 0x5F, 0x62, 0x8F,
            0xA4, 0x46, 0x4C, 0xDC, 0x73, 0x75, 0xB0, 0xE5,
            0x2E, 0x91, 0xFB, 0x64, 0x9C, 0x06, 0xAB, 0x75,
        };
        CK_ULONG     ulDataLen = 64;
        CK_BYTE      pSignatureData[128];
        CK_ULONG     signatureDataLen = 128;

        rc = funcs->C_SignInit(sess, &mechanism_sign, unw_priv_key);
        if (rc != CKR_OK)
        {
            fprintf(stderr, "C_SignInit failed, rc = 0x%lx\n", rc);
            goto end;
        }
        rc = funcs->C_Sign(sess,
                           pData, ulDataLen, pSignatureData, &signatureDataLen);
        if (rc != CKR_OK)
        {
            fprintf(stderr, "C_Sign failed, rc = 0x%lx\n", rc);
            goto end;
        }

        rc = funcs->C_VerifyInit(sess, &mechanism_sign, pub_key);
        if (rc != CKR_OK)
        {
            fprintf(stderr, "C_VerifyInit failed, rc = 0x%lx\n", rc);
            goto end;
        }
        rc = funcs->C_Verify(sess,
                             pData, ulDataLen, pSignatureData, signatureDataLen);
        if (rc != CKR_OK)
        {
            fprintf(stderr, "C_Verify failed, rc = 0x%lx\n", rc);
            goto end;
        }

        rc = funcs->C_VerifyInit(sess, &mechanism_sign, der_pub_key);
        if (rc != CKR_OK)
        {
            fprintf(stderr, "C_VerifyInit failed, rc = 0x%lx\n", rc);
            goto end;
        }
        rc = funcs->C_Verify(sess,
                             pData, ulDataLen, pSignatureData, signatureDataLen);
        if (rc != CKR_OK)
        {
            fprintf(stderr, "C_Verify failed, rc = 0x%lx\n", rc);
            goto end;
        }
    }
    funcs->C_DestroyObject(sess, der_pub_key);
    funcs->C_DestroyObject(sess, unw_priv_key);
    funcs->C_DestroyObject(sess, pub_key);
    funcs->C_DestroyObject(sess, priv_key);


    printf("SUCCESS\n");
 end:

    return rc;
}
