#include "test_common.h"

CK_RV test_crypto();
CK_RV test_gost28147_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, 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);
    if (rc != CKR_OK)
    {
        fprintf(stderr,
                "ERROR call to C_OpenSession failed, rc = 0x%lx\n", rc);
        goto out;
    }
    fprintf(stderr, "C_OpenSession success\n");
/*
        // log in as normal user
        rc = funcs->C_Login(hSession, CKU_USER, user_pin, strlen(user_pin));
        if (rc != CKR_OK) {
                fprintf(stderr, "ERROR call to C_Login failed, rc = 0x%lx\n", rc);
                goto out_close;
        }
        fprintf(stderr, "C_Login success\n");
 */
    rc = funcs->C_GetMechanismInfo(SlotId, CKM_GOST28147_ECB, &minfo);
    if (rc != CKR_OK)
    {
        fprintf(stderr,
                "\n======== Mechanism CKM_GOST28147_ECB not supported ===========\n");
    }
    else
    {
        fprintf(stderr,
                "\n==================== CKM_GOST28147_ECB "
                "+ MAC + WRAP test ==================\n");
        rc = test_gost28147_wrap(hSession);
        if (rc != CKR_OK)
        {
            fprintf(stderr,
                    "ERROR CKM_GOST28147_ECB + MAC + WRAP failed, rc = 0x%lx\n",
                    rc);
        }
        else
        {
            fprintf(stderr,
                    "CKM_GOST28147_ECB + MAC + WRAP test passed.\n");
        }
    }

    if ((ret = funcs->C_CloseSession(hSession)) != CKR_OK)
    {
        fprintf(stderr,
                "Error: C_CloseSession failed with 0x%lx\n", ret);
        rc = ret;
    }
    else
    {
        fprintf(stderr, "C_CloseSession success\n");
    }

 out:
    return rc;
}

CK_RV test_gost28147_wrap(CK_SESSION_HANDLE sess)
{
    CK_RV                  rc = CKR_OK;
    CK_BYTE                value[256];
    CK_ULONG               len;
    CK_OBJECT_HANDLE       keyh                = CK_INVALID_HANDLE;
    CK_OBJECT_HANDLE       keyh_to_wrap        = CK_INVALID_HANDLE;
    CK_OBJECT_HANDLE       unwrapped_keyh      = CK_INVALID_HANDLE;
    CK_MECHANISM           mechanism_desc_ecb  = { CKM_GOST28147_ECB, NULL, 0 };
    CK_MECHANISM_PTR       mechanism_ecb       = &mechanism_desc_ecb;
    CK_MECHANISM           mechanism_desc_wrap = { CKM_GOST28147_KEY_WRAP, NULL, 0 };
    CK_MECHANISM_PTR       mechanism_wrap      = &mechanism_desc_wrap;
    CK_MECHANISM           mechanism_desc_mac  = { CKM_GOST28147_MAC, NULL, 0 };
    CK_MECHANISM_PTR       mechanism_mac       = &mechanism_desc_mac;

    static CK_BBOOL        ltrue    = CK_TRUE;
//    static CK_BBOOL        lfalse   = CK_FALSE;
    static CK_OBJECT_CLASS oclass   = CKO_SECRET_KEY;
    static CK_KEY_TYPE     key_type = CKK_GOST28147;

    static CK_BYTE         wrapping_key[] = {
        0xc3, 0x73, 0x0c, 0x5c, 0xbc, 0xca, 0xcf, 0x91,
        0x5a, 0xc2, 0x92, 0x67, 0x6f, 0x21, 0xe8, 0xbd,
        0x4e, 0xf7, 0x53, 0x31, 0xd9, 0x40, 0x5e, 0x5f,
        0x1a, 0x61, 0xdc, 0x31, 0x30, 0xa6, 0x50, 0x11
    };
    static CK_BYTE         iv[] = {
        0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
    };
    // Шифруемый ключ
    static CK_BYTE         key_data[] = {
        0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
        0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10,
        0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18,
        0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20,
    };
    // CryptoPro gost28147 A Param Set (default)
    static CK_BYTE         oid2[] =
    { 0x06, 0x07, 0x2a, 0x85, 0x03, 0x02, 0x02, 0x1f, 0x01 };
    static CK_BYTE         et2_wrap[] = {
        0x66, 0xb6, 0xee, 0x72, 0xac, 0x33, 0x6e, 0x8f,
        0xa3, 0x87, 0xfe, 0x57, 0x5b, 0x93, 0x9c, 0x43,
        0x05, 0xd6, 0xcd, 0x07, 0x2e, 0x98, 0xfb, 0xce,
        0x8a, 0x6b, 0x31, 0xfc, 0x7f, 0xde, 0x5c, 0x8c,
        0x39, 0x57, 0x97, 0x2d,
    };
    static CK_BYTE         et2_ecb[] = {
        0x66, 0xb6, 0xee, 0x72, 0xac, 0x33, 0x6e, 0x8f,
        0xa3, 0x87, 0xfe, 0x57, 0x5b, 0x93, 0x9c, 0x43,
        0x05, 0xd6, 0xcd, 0x07, 0x2e, 0x98, 0xfb, 0xce,
        0x8a, 0x6b, 0x31, 0xfc, 0x7f, 0xde, 0x5c, 0x8c,
    };
    static CK_BYTE         et2_mac[] = {
        0x39, 0x57, 0x97, 0x2d,
    };

    CK_ATTRIBUTE           key_to_wrap_template[] = {
        { CKA_CLASS,           &oclass,   sizeof(oclass)   },
        { CKA_KEY_TYPE,        &key_type, sizeof(key_type) },
        { CKA_GOST28147PARAMS, oid2,      sizeof(oid2)     },
        { CKA_ENCRYPT,         &ltrue,    sizeof(CK_BBOOL) },
        { CKA_DECRYPT,         &ltrue,    sizeof(CK_BBOOL) },
        { CKA_VALUE,           key_data,  sizeof(key_data) }
    };
    CK_ATTRIBUTE           unwrapped_key_template[] = {
        { CKA_CLASS,           &oclass,   sizeof(oclass)   },
        { CKA_KEY_TYPE,        &key_type, sizeof(key_type) },
        { CKA_GOST28147PARAMS, oid2,      sizeof(oid2)     },
        { CKA_ENCRYPT,         &ltrue,    sizeof(CK_BBOOL) },
        { CKA_DECRYPT,         &ltrue,    sizeof(CK_BBOOL) },
    };

    CK_ATTRIBUTE           wrapping_key_template[] = {
        { CKA_VALUE,           wrapping_key, sizeof(wrapping_key) },
        { CKA_CLASS,           &oclass,      sizeof(oclass)       },
        { CKA_KEY_TYPE,        &key_type,    sizeof(key_type)     },
        { CKA_GOST28147PARAMS, oid2,         sizeof(oid2)         },
        { CKA_ENCRYPT,         &ltrue,       sizeof(CK_BBOOL)     },
        { CKA_DECRYPT,         &ltrue,       sizeof(CK_BBOOL)     },
        { CKA_SIGN,            &ltrue,       sizeof(CK_BBOOL)     },
        { CKA_VERIFY,          &ltrue,       sizeof(CK_BBOOL)     },
        { CKA_WRAP,            &ltrue,       sizeof(CK_BBOOL)     },
        { CKA_UNWRAP,          &ltrue,       sizeof(CK_BBOOL)     },
    };

    CK_ATTRIBUTE           value_template[] = {
        { CKA_VALUE, NULL, 0 },
    };
    CK_BYTE                dummy[8];
    CK_ULONG               dummy_len = sizeof(dummy);

	value_template[0].pValue = value;
	value_template[0].ulValueLen = sizeof(value);

    rc = funcs->C_CreateObject(sess,
                               key_to_wrap_template,
                               sizeof(key_to_wrap_template) / sizeof(CK_ATTRIBUTE),
                               &keyh_to_wrap);
    if (rc != CKR_OK)
    {
        fprintf(stderr, "C_CreateObject failed: 0x%lx\n", rc);
        return rc;
    }

    rc = funcs->C_CreateObject(sess,
                               wrapping_key_template,
                               sizeof(wrapping_key_template) / sizeof(CK_ATTRIBUTE),
                               &keyh);
    if (rc != CKR_OK)
    {
        fprintf(stderr, "C_CreateObject failed: 0x%lx\n", rc);
        return rc;
    }

    memset(value, 0, sizeof(value));
    rc = funcs->C_EncryptInit(sess, mechanism_ecb, keyh);
    if (rc != CKR_OK)
    {
        fprintf(stderr, "C_EncryptInit failed: 0x%lx\n", rc);
        return rc;
    }
    len = sizeof(value);
    rc  = funcs->C_EncryptUpdate(sess,
                                 key_data, sizeof(key_data), value, &len);
    if (rc != CKR_OK)
    {
        fprintf(stderr, "C_EncryptUpdate failed: 0x%lx\n", rc);
        return rc;
    }
    rc = funcs->C_EncryptFinal(sess, dummy, &dummy_len);
    if (rc != CKR_OK)
    {
        fprintf(stderr, "C_EncryptFinal failed: 0x%lx\n", rc);
        return rc;
    }

    CHECK(value, len, et2_ecb);

    memset(value, 0, sizeof(value));
    mechanism_mac[0].pParameter     = iv;
    mechanism_mac[0].ulParameterLen = sizeof(iv);
    rc                              = funcs->C_SignInit(sess, mechanism_mac, keyh);
    if (rc != CKR_OK)
    {
        fprintf(stderr, "C_SignInit failed: 0x%lx\n", rc);
        return rc;
    }
    rc = funcs->C_Sign(sess,
                       key_data, sizeof(key_data), value, &len);
    if (rc != CKR_OK)
    {
        fprintf(stderr, "C_Sign failed: 0x%lx\n", rc);
        return rc;
    }

    CHECK(value, len, et2_mac);

    memset(value, 0, sizeof(value));
    mechanism_wrap[0].pParameter     = iv;
    mechanism_wrap[0].ulParameterLen = sizeof(iv);
    rc                               = funcs->C_WrapKey(sess,
                                                        mechanism_wrap, keyh, keyh_to_wrap, value, &len);
    if (rc != CKR_OK)
    {
        fprintf(stderr, "C_WrapKey failed: 0x%lx\n", rc);
        return rc;
    }

    CHECK(value, len, et2_wrap);

    rc = funcs->C_UnwrapKey(sess, mechanism_wrap, keyh, value, len,
                            unwrapped_key_template,
                            sizeof(unwrapped_key_template) / sizeof(CK_ATTRIBUTE),
                            &unwrapped_keyh);
    if (rc != CKR_OK)
    {
        fprintf(stderr, "C_UnwrapKey failed: 0x%lx\n", rc);
        return rc;
    }

    memset(value, 0, sizeof(value));
    value_template[0].pValue     = value;
    value_template[0].ulValueLen = sizeof(value);
    rc                           = funcs->C_GetAttributeValue(sess, unwrapped_keyh,
                                                              value_template, sizeof(value_template) / sizeof(CK_ATTRIBUTE));
    if (rc != CKR_OK)
    {
        fprintf(stderr, "C_GetAttributeValue failed: 0x%lx\n", rc);
        return rc;
    }

    len = value_template[0].ulValueLen;
    CHECK(value, len, key_data);

    rc = funcs->C_DestroyObject(sess, keyh_to_wrap);
    if (rc != CKR_OK)
    {
        fprintf(stderr, "C_DestroyObject failed: 0x%lx\n", rc);
        return rc;
    }
    rc = funcs->C_DestroyObject(sess, unwrapped_keyh);
    if (rc != CKR_OK)
    {
        fprintf(stderr, "C_DestroyObject failed: 0x%lx\n", rc);
        return rc;
    }
    rc = funcs->C_DestroyObject(sess, keyh);
    if (rc != CKR_OK)
    {
        fprintf(stderr, "C_DestroyObject failed: 0x%lx\n", rc);
        return rc;
    }

    return rc;
}



