#include "test_common.h"

CK_RV test_crypto();
CK_RV public_key_derive_256(CK_SESSION_HANDLE sess);
CK_RV public_key_derive_512(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 test_crypto()
{
    CK_RV             rc;
    CK_SESSION_HANDLE hSession;
    CK_MECHANISM_INFO minfo;

    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(%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, (CK_ULONG)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);
    if (rc != CKR_OK)
    {
        fprintf(stderr,
            "\n======== Mechanism CKM_GOSTR3410 not supported ===========\n");
        goto out_close;
    }
    rc = funcs->C_GetMechanismInfo(SlotId, CKM_GOSTR3410_PUBLIC_KEY_DERIVE, &minfo);
    if (rc != CKR_OK)
    {
        fprintf(stderr,
            "\n======== Mechanism CKM_GOSTR3410_PUBLIC_KEY_DERIVE not supported ===========\n");
        goto out_close;
    }
    fprintf(stderr,
        "\n==================== CKM_GOSTR3410_PUBLIC_KEY_DERIVE test 256 ==================\n");
    rc = public_key_derive_256(hSession);
    if (rc != CKR_OK)
    {
        fprintf(stderr,
            "ERROR public_key_derive_256 failed, rc = 0x%lx(%s)\n",
            rc, get_ckr_name(rc));
        goto out_close;
    }
    else
    {
        fprintf(stderr, "CKM_GOSTR3410_PUBLIC_KEY_DERIVE test 256 SUCCESS.\n");
    }
    rc = funcs->C_GetMechanismInfo(SlotId, CKM_GOSTR3410_512, &minfo);
    if (rc != CKR_OK)
    {
        fprintf(stderr,
            "\n======== Mechanism CKM_GOSTR3410 not supported ===========\n");
        goto out_close;
    }
    fprintf(stderr,
        "\n==================== CKM_GOSTR3410_PUBLIC_KEY_DERIVE test 512 ==================\n");
    rc = public_key_derive_512(hSession);
    if (rc != CKR_OK)
    {
        fprintf(stderr,
            "ERROR public_key_derive_512 failed, rc = 0x%lx(%s)\n",
            rc, get_ckr_name(rc));
        goto out_close;
    }
    else
    {
        fprintf(stderr, "CKM_GOSTR3410_PUBLIC_KEY_DERIVE test 512 SUCCESS.\n");
    }
 out_close:
    funcs->C_CloseSession(hSession);
 out:
    return rc;
}

CK_RV public_key_derive_256(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_OBJECT_HANDLE       der_pub_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;
    CK_MECHANISM  mechanism_pub_der_desc	  = { CKM_GOSTR3410_PUBLIC_KEY_DERIVE, NULL, 0 };
    CK_MECHANISM_PTR       mechanism_pub_der  = &mechanism_pub_der_desc;

    static CK_BBOOL        ltrue       = CK_TRUE;
    static CK_BBOOL        lfalse      = CK_FALSE;
    static CK_OBJECT_CLASS oclass_pub  = CKO_PUBLIC_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
    };
    CK_UTF8CHAR     *label_priv         = (CK_UTF8CHAR *)"Priv Key 256";
    CK_UTF8CHAR     *label_pub         = (CK_UTF8CHAR *)"Derived Pub Key 512";
    CK_ATTRIBUTE           pub_gen_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_gen_template[] = {
		{ CKA_LABEL, NULL, 0 },
        { CKA_TOKEN,   &ltrue, sizeof(ltrue)    },
        { CKA_PRIVATE, &ltrue, sizeof(ltrue)    },
        { CKA_SIGN,    &ltrue, sizeof(CK_BBOOL) },
		{ CKA_DERIVE,  &ltrue, sizeof(CK_BBOOL) },
		{ CKA_EXTRACTABLE,  &lfalse, sizeof(CK_BBOOL) },
		{ CKA_SENSITIVE,  &ltrue, sizeof(CK_BBOOL) },
    };
	// В шаблоне выводимого публичного ключа нельзя указывать атрибуты,
	// которые определяются из атрибутов закрытого ключа - тип ключа и 
	// параметры алгоритма.
	// Шаблон выводимого публичного ключа можно вообще не задавать,
	// тогда остальные атрибуты будут назначены по умолчанию.
    CK_ATTRIBUTE           pub_der_template[] = {
		{ CKA_LABEL, NULL, 0 },
    };
	priv_gen_template[0].pValue = label_priv;
	priv_gen_template[0].ulValueLen = (CK_ULONG)strlen((char *)label_priv) + 1;
	pub_der_template[0].pValue = label_pub;
	pub_der_template[0].ulValueLen = (CK_ULONG)strlen((char *)label_pub) + 1;

    rc = funcs->C_GenerateKeyPair(sess, mechanism_gen,
        pub_gen_template, sizeof(pub_gen_template) / sizeof(CK_ATTRIBUTE),
        priv_gen_template, sizeof(priv_gen_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);
    if (rc != CKR_OK)
    {
        fprintf(stderr, "C_SignInit failed: 0x%lx(%s)\n",
            rc, get_ckr_name(rc));
        return rc;
    }
	fprintf(stderr, "C_SignInit OK\n");

    len = sizeof(value);
    rc  = funcs->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);
    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);
    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");

    // Получаем открытый ключ по закрытому
	printf("Try to derive public key from private key\n");
    rc = funcs->C_DeriveKey(sess, mechanism_pub_der, priv_key,
		pub_der_template, sizeof(pub_der_template)/sizeof(CK_ATTRIBUTE),
        &der_pub_key);
    if (rc != CKR_OK)
    {
        fprintf(stderr, "C_DeriveKey: 0x%lx(%s)\n",
            rc, get_ckr_name(rc));
        return rc;
	}
    fprintf(stderr, "C_DeriveKey OK\n");

    fprintf(stderr, "Verify with derived key\n");
    rc = funcs->C_VerifyInit(sess, mechanism, der_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);
    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);
    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, pub_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, der_pub_key);
    if (rc != CKR_OK)
    {
        fprintf(stderr, "C_DestroyObject failed: 0x%lx(%s)\n",
            rc, get_ckr_name(rc));
        return rc;
    }

    return rc;
}

CK_RV public_key_derive_512(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_OBJECT_HANDLE       der_pub_key           = CK_INVALID_HANDLE;
    CK_MECHANISM           mechanism_desc     = { CKM_GOSTR3410_512, NULL, 0 };
    CK_MECHANISM_PTR       mechanism          = &mechanism_desc;
    CK_MECHANISM           mechanism_gen_desc = { CKM_GOSTR3410_512_KEY_PAIR_GEN, NULL, 0 };
    CK_MECHANISM_PTR       mechanism_gen      = &mechanism_gen_desc;
    CK_MECHANISM  mechanism_pub_der_desc	  = { CKM_GOSTR3410_PUBLIC_KEY_DERIVE, NULL, 0 };
    CK_MECHANISM_PTR       mechanism_pub_der  = &mechanism_pub_der_desc;

    static CK_BBOOL        ltrue       = CK_TRUE;
    static CK_BBOOL        lfalse      = CK_FALSE;
    static CK_OBJECT_CLASS oclass_pub  = CKO_PUBLIC_KEY;
    static CK_KEY_TYPE     key_type    = CKK_GOSTR3410_512;

    static CK_BYTE         tc26_decc_A_der_oid[] =
    { 0x06, 0x09, 0x2a, 0x85, 0x03, 0x07, 0x01, 0x02, 0x01, 0x02, 0x01 };
    static CK_BYTE         tc26_gost3411_2012_512_der_oid[] =
    { 0x06, 0x08, 0x2a, 0x85, 0x03, 0x07, 0x01, 0x01, 0x02, 0x03 };

    static CK_BYTE         data[] = {
        0x1f, 0xd3, 0xf0, 0x6e, 0x70, 0x89, 0x0b, 0x08,
        0xa5, 0xc0, 0xe7, 0x86, 0x42, 0xf2, 0x45, 0xc2,
        0xe6, 0x5b, 0x29, 0x43, 0xfc, 0xa4, 0x34, 0x59,
        0xcb, 0x0f, 0xc8, 0xf1, 0x04, 0x78, 0x7f, 0x37,
        0x0B, 0x09, 0x0C, 0xEC, 0x2A, 0x26, 0x95, 0xE5,
        0xBF, 0x13, 0x01, 0xFA, 0x09, 0x51, 0xBE, 0x6F,
        0x81, 0x73, 0xAB, 0xF3, 0xCB, 0x11, 0x20, 0xB8,
        0x61, 0x5F, 0xA8, 0x5B, 0xF0, 0x75, 0xB3, 0x98,
    };
    CK_UTF8CHAR     *label_priv         = (CK_UTF8CHAR *)"Priv Key 512";
    CK_UTF8CHAR     *label_pub         = (CK_UTF8CHAR *)"Derived Pub Key 1024";
    CK_ATTRIBUTE           pub_gen_template[] = {
        { CKA_TOKEN,           &ltrue,          sizeof(ltrue)           },
        { CKA_GOSTR3410PARAMS, tc26_decc_A_der_oid, sizeof(tc26_decc_A_der_oid) },
        { CKA_GOSTR3411PARAMS, tc26_gost3411_2012_512_der_oid, 
			sizeof(tc26_gost3411_2012_512_der_oid) },
        { CKA_VERIFY,          &ltrue,          sizeof(CK_BBOOL)        },
    };
    CK_ATTRIBUTE           priv_gen_template[] = {
		{ CKA_LABEL, NULL, 0 },
        { CKA_TOKEN,   &ltrue, sizeof(ltrue)    },
        { CKA_PRIVATE, &ltrue, sizeof(ltrue)    },
        { CKA_SIGN,    &ltrue, sizeof(CK_BBOOL) },
		{ CKA_DERIVE,  &ltrue, sizeof(CK_BBOOL) },
		{ CKA_EXTRACTABLE,  &lfalse, sizeof(CK_BBOOL) },
		{ CKA_SENSITIVE,  &ltrue, sizeof(CK_BBOOL) },
    };
	// В шаблоне выводимого публичного ключа нельзя указывать атрибуты,
	// которые определяются из атрибутов закрытого ключа - тип ключа и 
	// параметры алгоритма.
	// Шаблон выводимого публичного ключа можно вообще не задавать,
	// тогда остальные атрибуты будут назначены по умолчанию.
    CK_ATTRIBUTE           pub_der_template[] = {
		{ CKA_LABEL, NULL, 0 },
    };
	priv_gen_template[0].pValue = label_priv;
	priv_gen_template[0].ulValueLen = (CK_ULONG)strlen((char *)label_priv) + 1;
	pub_der_template[0].pValue = label_pub;
	pub_der_template[0].ulValueLen = (CK_ULONG)strlen((char *)label_pub) + 1;

    rc = funcs->C_GenerateKeyPair(sess, mechanism_gen,
        pub_gen_template, sizeof(pub_gen_template) / sizeof(CK_ATTRIBUTE),
        priv_gen_template, sizeof(priv_gen_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);
    if (rc != CKR_OK)
    {
        fprintf(stderr, "C_SignInit failed: 0x%lx(%s)\n",
            rc, get_ckr_name(rc));
        return rc;
    }
	fprintf(stderr, "C_SignInit OK\n");

    len = sizeof(value);
    rc  = funcs->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);
    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);
    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");

    // Получаем открытый ключ по закрытому
	printf("Try to derive public key from private key\n");
    rc = funcs->C_DeriveKey(sess, mechanism_pub_der, priv_key,
		pub_der_template, sizeof(pub_der_template)/sizeof(CK_ATTRIBUTE),
        &der_pub_key);
    if (rc != CKR_OK)
    {
        fprintf(stderr, "C_DeriveKey: 0x%lx(%s)\n",
            rc, get_ckr_name(rc));
        return rc;
	}
    fprintf(stderr, "C_DeriveKey OK\n");

    fprintf(stderr, "Verify with derived key\n");
    rc = funcs->C_VerifyInit(sess, mechanism, der_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);
    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);
    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, pub_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, der_pub_key);
    if (rc != CKR_OK)
    {
        fprintf(stderr, "C_DestroyObject failed: 0x%lx(%s)\n",
            rc, get_ckr_name(rc));
        return rc;
    }

    return rc;
}



