#include "test_common.h"

CK_RV test_crypto();
CK_RV test_kdf_4357(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");

    rc = funcs->C_GetMechanismInfo(SlotId,
                                   CKM_KDF_4357, &minfo);
    if (rc != CKR_OK)
    {
        fprintf(stderr,
                "\n======== Mechanism CKM_KDF_4357 "
                "not supported ===========\n");
    }
    else
    {
        fprintf(stderr,
                "\n==================== CKM_KDF_4357 "
                "test ==================\n");
        rc = test_kdf_4357(hSession);
        if (rc != CKR_OK)
        {
            fprintf(stderr,
                    "ERROR CKM_KDF_4357 failed, "
                    "rc = 0x%lx\n", rc);
        }
        else
        {
            fprintf(stderr,
                    "CKM_KDF_4357 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_kdf_4357(CK_SESSION_HANDLE sess)
{
    CK_RV rc = CKR_OK;
    static CK_BYTE                value[256];
    CK_ULONG               len;
    CK_MECHANISM           mechanism_desc =
    { CKM_KDF_4357, NULL, 0 };
    CK_MECHANISM_PTR       mechanism = &mechanism_desc;

    CK_OBJECT_HANDLE       keyh     = CK_INVALID_HANDLE;
    CK_OBJECT_HANDLE       kdfkeyh  = CK_INVALID_HANDLE;
    static CK_OBJECT_CLASS oclass   = CKO_SECRET_KEY;
    static CK_KEY_TYPE     key_type = CKK_GOST28147;
    static CK_BBOOL        ltrue    = CK_TRUE;
    // CryptoPro gost28147 A Param Set
    static CK_BYTE         oid_28147_A[] = {
        0x06, 0x07, 0x2a, 0x85, 0x03, 0x02, 0x02, 0x1f, 0x01
    };
    static CK_BYTE         oid_28147_Z[] = {
        0x06, 0x09, 0x2a, 0x85, 0x03, 0x07, 0x01, 0x02, 0x05, 0x01, 0x01
    };
    CK_BYTE                ukm[8] = {
        0x9D, 0x23, 0x98, 0xC0, 0x12, 0x31, 0x2A, 0x8E
    };
    CK_BYTE                vko[32] = {
        0x97, 0xB9, 0xE9, 0xD0, 0xDB, 0x1B, 0xEC, 0x26,
        0x71, 0x17, 0x20, 0x50, 0x8D, 0xA4, 0x30, 0x2B,
        0x15, 0x19, 0x65, 0xF9, 0xBB, 0xAD, 0x5A, 0x91,
        0xF6, 0xB5, 0x0F, 0x5B, 0x84, 0xAC, 0xC5, 0x80
    };
    CK_BYTE                kdf_CP_A[32] = {
        0x44, 0x9e, 0x3f, 0x63, 0x6d, 0x9e, 0x79, 0x77,
        0xde, 0x03, 0x78, 0x8f, 0x51, 0x38, 0xa0, 0x50,
        0xb5, 0xff, 0x1a, 0xe7, 0x74, 0x4f, 0x5d, 0x47,
        0x1a, 0xef, 0x8f, 0x87, 0xe9, 0xd4, 0x22, 0x66,
    };
    CK_BYTE                kdf_TC26_Z[32] = {
        0xea, 0x83, 0x1d, 0x5b, 0x21, 0x0b, 0xf0, 0xb3,
        0x6f, 0x68, 0xc1, 0xac, 0x98, 0xd4, 0x8f, 0x32,
        0x8b, 0xf2, 0x07, 0x22, 0x87, 0xd6, 0xb8, 0xef,
        0x48, 0x14, 0xa2, 0x23, 0x66, 0xb8, 0x1c, 0x9b,
    };

    CK_ATTRIBUTE           key_template[] = {
        { CKA_VALUE,            NULL_PTR,                 0 },
        { CKA_CLASS,            &oclass,   sizeof(oclass)   },
        { CKA_KEY_TYPE,         &key_type, sizeof(key_type) },
        { CKA_GOST28147_PARAMS,
          oid_28147_A, sizeof(oid_28147_A) },
        { CKA_ENCRYPT,          &ltrue,    sizeof(ltrue)    },
        { CKA_DECRYPT,          &ltrue,    sizeof(ltrue)    }
    };

    CK_ATTRIBUTE           key_derive_template[] = {
        { CKA_CLASS,            &oclass,   sizeof(oclass)   },
        { CKA_KEY_TYPE,         &key_type, sizeof(key_type) },
        { CKA_GOST28147_PARAMS,
          oid_28147_A, sizeof(oid_28147_A) },
        { CKA_ENCRYPT,          &ltrue,    sizeof(ltrue)    },
        { CKA_DECRYPT,          &ltrue,    sizeof(ltrue)    }
    };
    CK_ATTRIBUTE           value_template[] = {
        { CKA_VALUE, value, sizeof(value) },
    };

    key_template[0].pValue     = vko;
    key_template[0].ulValueLen = sizeof(vko);
    rc                         = funcs->C_CreateObject(sess,
                                                       key_template, sizeof(key_template) / sizeof(CK_ATTRIBUTE),
                                                       &keyh);
    if (rc != CKR_OK)
    {
        fprintf(stderr, "C_CreateObject failed: 0x%lx\n", rc);
        goto end;
    }

    mechanism->pParameter     = ukm;
    mechanism->ulParameterLen = sizeof(ukm);
    rc                        = funcs->C_DeriveKey(sess, mechanism, keyh,
                                                   key_derive_template,
                                                   sizeof(key_derive_template) / sizeof(CK_ATTRIBUTE),
                                                   &kdfkeyh);
    if (rc != CKR_OK)
    {
        fprintf(stderr, "C_DeriveKey failed: 0x%lx\n", rc);
        goto end;
    }
    rc = funcs->C_GetAttributeValue(sess, kdfkeyh,
                                    value_template,
                                    sizeof(value_template) / sizeof(CK_ATTRIBUTE));
    if (rc != CKR_OK)
    {
        fprintf(stderr, "C_GetAttributeValue failed: 0x%lx\n", rc);
        goto end;
    }
    len = value_template[0].ulValueLen;
    if (len != sizeof(kdf_CP_A))
    {
        fprintf(stderr, "Invalid length: %ld\n", len);
        rc = CKR_FUNCTION_FAILED;
        goto end;
    }
    print_bytes(value, len);
    if (memcmp(value, kdf_CP_A, len) != 0)
    {
        fprintf(stderr, "Invalid value\n");
        rc = CKR_FUNCTION_FAILED;
        goto end;
    }
    printf("KDF CP A OK\n");

    funcs->C_DestroyObject(sess, keyh);
    keyh = CK_INVALID_HANDLE;
    funcs->C_DestroyObject(sess, kdfkeyh);
    kdfkeyh = CK_INVALID_HANDLE;

    key_template[0].pValue     = vko;
    key_template[0].ulValueLen = sizeof(vko);
    key_template[3].pValue     = oid_28147_Z;
    key_template[3].ulValueLen = sizeof(oid_28147_Z);
    rc                         = funcs->C_CreateObject(sess,
                                                       key_template,
                                                       sizeof(key_template) / sizeof(CK_ATTRIBUTE),
                                                       &keyh);
    if (rc != CKR_OK)
    {
        fprintf(stderr, "C_CreateObject failed: 0x%lx\n", rc);
        goto end;
    }

    mechanism->pParameter             = ukm;
    mechanism->ulParameterLen         = sizeof(ukm);
    key_derive_template[2].pValue     = oid_28147_Z;
    key_derive_template[2].ulValueLen = sizeof(oid_28147_Z);
    rc                                = funcs->C_DeriveKey(sess, mechanism, keyh,
                                                           key_derive_template,
                                                           sizeof(key_derive_template) / sizeof(CK_ATTRIBUTE),
                                                           &kdfkeyh);
    if (rc != CKR_OK)
    {
        fprintf(stderr, "C_DeriveKey failed: 0x%lx\n", rc);
        goto end;
    }
    rc = funcs->C_GetAttributeValue(sess, kdfkeyh,
                                    value_template,
                                    sizeof(value_template) / sizeof(CK_ATTRIBUTE));
    if (rc != CKR_OK)
    {
        fprintf(stderr, "C_GetAttributeValue failed: 0x%lx\n", rc);
        goto end;
    }
    len = value_template[0].ulValueLen;
    if (len != sizeof(kdf_TC26_Z))
    {
        fprintf(stderr, "Invalid length: %ld\n", len);
        rc = CKR_FUNCTION_FAILED;
        goto end;
    }
    print_bytes(value, len);
    if (memcmp(value, kdf_TC26_Z, len) != 0)
    {
        fprintf(stderr, "Invalid value\n");
        rc = CKR_FUNCTION_FAILED;
        goto end;
    }
    printf("KDF TC 26 Z OK\n");

    printf("SUCCESS\n");
    rc = CKR_OK;
 end:
    if (keyh != CK_INVALID_HANDLE)
    {
        funcs->C_DestroyObject(sess, keyh);
    }
    if (kdfkeyh != CK_INVALID_HANDLE)
    {
        funcs->C_DestroyObject(sess, kdfkeyh);
    }

    return rc;
}

