#include "test_common.h"

CK_RV test_crypto();
CK_RV test_gost3410_with_gost3411_12_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\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_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\n", rc);
        goto out_close;
    }
    fprintf(stderr, "C_Login success\n");
    rc = funcs->C_GetMechanismInfo(SlotId,
                                   CKM_GOSTR3410_WITH_GOSTR3411_12_512, &minfo);
    if (rc != CKR_OK)
    {
        fprintf(stderr,
                "\n======== Mechanism CKM_GOSTR3410_WITH_GOSTR3411_12_512 "
                "not supported ===========\n");
    }
    else
    {
        fprintf(stderr,
                "\n==================== CKM_GOSTR3410_WITH_GOSTR3411_12_512 "
                "test ==================\n");
        rc = test_gost3410_with_gost3411_12_512(hSession);
        if (rc != CKR_OK)
        {
            fprintf(stderr,
                    "ERROR, CKM_GOSTR3410_WITH_GOSTR3411_12_512 failed, "
                    "rc = 0x%lx\n", rc);
        }
        else
        {
            fprintf(stderr,
                    "CKM_GOSTR3410_WITH_GOSTR3411_12_512 test SUCCESS\n");
        }
    }

 out_close:
    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_gost3410_with_gost3411_12_512(CK_SESSION_HANDLE sess)
{
    CK_RV               rc;
    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_WITH_GOSTR3411_12_512, NULL, 0 };
    CK_MECHANISM        hash_mechanism_desc = { CKM_GOSTR3411_12_512, NULL, 0 };
    CK_MECHANISM        mechanism_3410_desc = { CKM_GOSTR3410_512, NULL, 0 };
    CK_MECHANISM_PTR    mechanism           = &mechanism_desc;
    CK_MECHANISM_PTR    hash_mechanism      = &hash_mechanism_desc;
    CK_MECHANISM_PTR    mechanism_3410      = &mechanism_3410_desc;
    CK_MECHANISM        mechanism_gen_desc  =
    { CKM_GOSTR3410_512_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_BYTE      gostR3410params[] = {
        0x06, 0x09, 0x2a, 0x85, 0x03, 0x07, 0x01, 0x02, 0x01, 0x02, 0x01
    };
    static CK_BYTE      gostR3411params[] = {
        0x06, 0x08, 0x2a, 0x85, 0x03, 0x07, 0x01, 0x01, 0x02, 0x03
    };

    static CK_BYTE      data[] = {
        0x61, 0x62, 0x63, 0x64, 0x62, 0x63, 0x64, 0x65,
        0x63, 0x64, 0x65, 0x66, 0x64, 0x65, 0x66, 0x67,
        0x65, 0x66, 0x67, 0x68, 0x66, 0x67, 0x68, 0x69,
        0x67, 0x68, 0x69, 0x6A, 0x68, 0x69, 0x6A, 0x6B,
        0x69, 0x6A, 0x6B, 0x6C, 0x6A, 0x6B, 0x6C, 0x6D,
        0x6B, 0x6C, 0x6D, 0x6E, 0x6C, 0x6D, 0x6E, 0x6F,
        0x6D, 0x6E, 0x6F, 0x70, 0x6E, 0x6F, 0x70, 0x71,
        0x0A
    };

    static CK_BYTE      data_hash[] = {
        0x89, 0x8c, 0xd4, 0xf2, 0x23, 0x32, 0xf6, 0x2f,
        0x2b, 0xae, 0x32, 0xaf, 0x46, 0xc1, 0x1d, 0x4e,
        0x64, 0x13, 0xff, 0x89, 0xaa, 0xd4, 0xeb, 0x53,
        0xb4, 0xa2, 0xd8, 0x85, 0x4b, 0x0e, 0xf0, 0xe1,
        0xd2, 0xbe, 0x75, 0x01, 0xec, 0x40, 0x2e, 0x98,
        0x2a, 0xb2, 0x95, 0xb8, 0xec, 0x25, 0x72, 0xe8,
        0xa8, 0x0d, 0x1a, 0xe3, 0xbf, 0xff, 0x17, 0x85,
        0xad, 0xcc, 0x42, 0x8f, 0x4f, 0xbb, 0x76, 0x0b,
    };

    static CK_ATTRIBUTE pub_template[] = {
        { CKA_VERIFY,          &ltrue, sizeof(CK_BBOOL) },
        { CKA_GOSTR3410PARAMS,
          gostR3410params, sizeof(gostR3410params) },
        { CKA_GOSTR3411PARAMS,
          gostR3411params, sizeof(gostR3411params) },
    };

    static CK_ATTRIBUTE priv_template[] = {
        { CKA_SIGN, &ltrue, sizeof(CK_BBOOL) },
    };


//====================================================
    memset(value, 0, sizeof(value));

//	hash_mechanism->pParameter = gostR3411params;
//	hash_mechanism->ulParameterLen = sizeof(gostR3411params);
    rc = funcs->C_DigestInit(sess, hash_mechanism);
    if (rc != CKR_OK)
    {
        fprintf(stderr,
                "%4d: C_DigestInit failed, rc = 0x%lx\n", __LINE__, rc);
        return rc;
    }

    len = sizeof(value);
    rc  = funcs->C_Digest(sess, data, sizeof(data), value, &len);
    if (rc != CKR_OK)
    {
        fprintf(stderr,
                "%4d: C_Digest failed, rc = 0x%lx\n", __LINE__, rc);
        return rc;
    }

    if (len != sizeof(data_hash))
    {
        fprintf(stderr,
                "%4d: Invalid result length: %ld\n", __LINE__, len);
        return CKR_FUNCTION_FAILED;
    }
    print_bytes(value, len);
    if (memcmp(value, data_hash, len) != 0)
    {
        fprintf(stderr,
                "%4d: Invalid result value\n", __LINE__);
        return CKR_FUNCTION_FAILED;
    }

//====================================================
    memset(value, 0, sizeof(value));

    rc = funcs->C_DigestInit(sess, hash_mechanism);
    if (rc != CKR_OK)
    {
        fprintf(stderr,
                "%4d: C_DigestInit failed, rc = 0x%lx\n", __LINE__, rc);
        return rc;
    }

    rc = funcs->C_DigestUpdate(sess, data, 39);
    if (rc != CKR_OK)
    {
        fprintf(stderr,
                "%4d: C_DigestUpdate failed, rc = 0x%lx\n", __LINE__, rc);
        return rc;
    }

    rc = funcs->C_DigestUpdate(sess, data + 39, sizeof(data) - 39);
    if (rc != CKR_OK)
    {
        fprintf(stderr,
                "%4d: C_DigestUpdate failed, rc = 0x%lx\n", __LINE__, rc);
        return rc;
    }

    len = sizeof(value);
    rc  = funcs->C_DigestFinal(sess, value, &len);
    if (rc != CKR_OK)
    {
        fprintf(stderr,
                "%4d: C_DigestFinal failed, rc = 0x%lx\n", __LINE__, rc);
        return rc;
    }

    if (len != sizeof(data_hash))
    {
        fprintf(stderr,
                "%4d: Invalid result length: %ld\n", __LINE__, len);
        return CKR_FUNCTION_FAILED;
    }
    if (memcmp(value, data_hash, len) != 0)
    {
        fprintf(stderr, "%4d: Invalid result value\n", __LINE__);
        return CKR_FUNCTION_FAILED;
    }

    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);
    if (rc != CKR_OK)
    {
        fprintf(stderr,
                "%4d: C_GenerateKeyPair failed, rc = 0x%lx\n",
                __LINE__, rc);
        return rc;
    }
    fprintf(stderr, "C_GenerateKeyPair OK\n");

    rc = funcs->C_SignInit(sess, mechanism_3410, priv_key);
    if (rc != CKR_OK)
    {
        fprintf(stderr,
                "%4d: C_SignInit failed, rc = 0x%lx\n", __LINE__, rc);
        return rc;
    }
    fprintf(stderr, "C_SignInit CKM_GOSTR3410_512 OK\n");

    len = sizeof(value);
    rc  = funcs->C_Sign(sess,
                        data_hash, sizeof(data_hash), value, &len);
    if (rc != CKR_OK)
    {
        fprintf(stderr,
                "%4d: C_Sign failed, rc = 0x%lx\n", __LINE__, rc);
        return rc;
    }
    fprintf(stderr, "C_Sign OK\n");

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

    rc = funcs->C_VerifyInit(sess, mechanism_3410, pub_key);
    if (rc != CKR_OK)
    {
        fprintf(stderr,
                "%4d: C_VerifyInit failed, rc = 0x%lx\n",
                __LINE__, rc);
        return rc;
    }
    fprintf(stderr, "C_VerifyInit CKM_GOSTR3410_512 OK\n");
    rc = funcs->C_Verify(sess,
                         data_hash, sizeof(data_hash), value, len);
    if (rc != CKR_OK)
    {
        fprintf(stderr,
                "%4d: C_Verify failed, rc = 0x%lx\n", __LINE__, rc);
        return rc;
    }
    fprintf(stderr, "C_Verify OK\n");

    rc = funcs->C_VerifyInit(sess, mechanism, pub_key);
    if (rc != CKR_OK)
    {
        fprintf(stderr,
                "%4d: C_VerifyInit failed, rc = 0x%lx\n", __LINE__, rc);
        return rc;
    }
    fprintf(stderr,
            "C_VerifyInit CKM_GOSTR3410_WITH_GOSTR3411_12_512 OK\n");

    rc = funcs->C_VerifyUpdate(sess, data, 39);
    if (rc != CKR_OK)
    {
        fprintf(stderr,
                "%4d: C_VerifyUpdate failed, rc = 0x%lx\n", __LINE__, rc);
        return rc;
    }
    rc = funcs->C_VerifyUpdate(sess, data + 39, sizeof(data) - 39);
    if (rc != CKR_OK)
    {
        fprintf(stderr,
                "%4d: C_VerifyUpdate failed, rc = 0x%lx\n", __LINE__, rc);
        return rc;
    }
    rc = funcs->C_VerifyFinal(sess, value, len);
    if (rc != CKR_OK)
    {
        fprintf(stderr,
                "%4d: C_VerifyFinal failed, rc = 0x%lx\n", __LINE__, rc);
        return rc;
    }
    fprintf(stderr, "C_VerifyFinal OK\n");

    rc = funcs->C_VerifyInit(sess, mechanism, pub_key);
    if (rc != CKR_OK)
    {
        fprintf(stderr,
                "%4d: C_VerifyInit failed, rc = 0x%lx\n", __LINE__, rc);
        return rc;
    }
    fprintf(stderr,
            "C_VerifyInit CKM_GOSTR3410_WITH_GOSTR3411_12_512 OK\n");

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

    rc = funcs->C_SignInit(sess, mechanism, priv_key);
    if (rc != CKR_OK)
    {
        fprintf(stderr,
                "%4d: C_SignInit failed, rc = 0x%lx\n", __LINE__, rc);
        return rc;
    }
    fprintf(stderr,
            "C_SignInit CKM_GOSTR3410_WITH_GOSTR3411_12_512 OK\n");

    rc = funcs->C_SignUpdate(sess, data, 39);
    if (rc != CKR_OK)
    {
        fprintf(stderr,
                "%4d: C_SignUpdate failed, rc = 0x%lx\n", __LINE__, rc);
        return rc;
    }
    rc = funcs->C_SignUpdate(sess, data + 39, sizeof(data) - 39);
    if (rc != CKR_OK)
    {
        fprintf(stderr,
                "%4d: C_SignUpdate failed, rc = 0x%lx\n", __LINE__, rc);
        return rc;
    }
    len = sizeof(value);
    rc  = funcs->C_SignFinal(sess, value, &len);
    if (rc != CKR_OK)
    {
        fprintf(stderr,
                "%4d: C_SignFinal failed, rc = 0x%lx\n", __LINE__, rc);
        return rc;
    }
    fprintf(stderr, "C_SignFinal OK\n");

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

    rc = funcs->C_VerifyInit(sess, mechanism_3410, pub_key);
    if (rc != CKR_OK)
    {
        fprintf(stderr,
                "%4d: C_VerifyInit failed, rc = 0x%lx\n", __LINE__, rc);
        return rc;
    }
    fprintf(stderr, "C_VerifyInit CKM_GOSTR3410_512 OK\n");

    rc = funcs->C_Verify(sess,
                         data_hash, sizeof(data_hash), value, len);
    if (rc != CKR_OK)
    {
        fprintf(stderr,
                "%4d: C_Verify failed, rc = 0x%lx\n", __LINE__, rc);
        return rc;
    }
    fprintf(stderr, "C_Verify OK\n");

    rc = funcs->C_SignInit(sess, mechanism, priv_key);
    if (rc != CKR_OK)
    {
        fprintf(stderr,
                "%4d: C_SignInit failed, rc = 0x%lx\n", __LINE__, rc);
        return rc;
    }
    fprintf(stderr,
            "C_SignInit CKM_GOSTR3410_WITH_GOSTR3411_12_512 OK\n");

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

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

    rc = funcs->C_VerifyInit(sess, mechanism_3410, pub_key);
    if (rc != CKR_OK)
    {
        fprintf(stderr,
                "%4d: C_VerifyInit failed, rc = 0x%lx\n", __LINE__, rc);
        return rc;
    }
    fprintf(stderr, "C_VerifyInit CKM_GOSTR3410_512 OK\n");
    rc = funcs->C_Verify(sess,
                         data_hash, sizeof(data_hash), value, len);
    if (rc != CKR_OK)
    {
        fprintf(stderr,
                "%4d: C_Verify failed, rc = 0x%lx\n", __LINE__, rc);
        return rc;
    }
    fprintf(stderr, "C_Verify OK\n");

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

    return rc;
}
