#include "test_common.h"

CK_RV test_crypto();
CK_RV test_magma_acpkm_ctr(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_MAGMA_ACPKM_CTR, &minfo);
    if (rc != CKR_OK)
    {
        fprintf(stderr,
                "\n======== Mechanism CKM_MAGMA_ACPKM_CTR not supported ===========\n");
    }
    else
    {
        fprintf(stderr,
                "\n==================== CKM_MAGMA_ACPKM_CTR test ==================\n");
        rc = test_magma_acpkm_ctr(hSession);
        if (rc != CKR_OK)
        {
            fprintf(stderr,
                    "ERROR CKM_MAGMA_ACPKM_CTR failed, rc = 0x%lx\n", rc);
        }
        else
        {
            fprintf(stderr, "CKM_MAGMA_ACPKM_CTR 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;
}

// В документе ТК 26:
const unsigned int s = 8;
const unsigned int N = 16;

unsigned char K[] = {
	0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF, 
	0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 
	0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, 
	0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF
};
unsigned char IV[] = {
	0x12, 0x34, 0x56, 0x78
};
unsigned char M[] = {
	0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x00, 
	0xFF, 0xEE, 0xDD, 0xCC, 0xBB, 0xAA, 0x99, 0x88, 
	0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 
	0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xEE, 0xFF, 0x0A, 
	0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 
	0x99, 0xAA, 0xBB, 0xCC, 0xEE, 0xFF, 0x0A, 0x00, 
	0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99 
};
unsigned char encrM[] = {
/*
	0x2A, 0xB8, 0x1D, 0xEE, 0xEB, 0x1E, 0x4C, 0xAB,
	0x68, 0xE1, 0x04, 0xC4, 0xBD, 0x6B, 0x94, 0xEA,
	0x09, 0x34, 0x7C, 0x24, 0xC6, 0x3F, 0x36, 0xEC,
	0x2F, 0x2C, 0x06, 0x89, 0x69, 0x24, 0x61, 0x84,
	0x5D, 0xA8, 0x0A, 0x8C, 0xDF, 0xDF, 0x91, 0x20,
	0x62, 0xB1, 0x17, 0xDA, 0x3E, 0x17, 0x69, 0x92,
	0xCF, 0x00, 0xA1, 0xF6, 0x04, 0xDF, 0x41, 0x57,
*/
	// Изменения ТК 26 1.09.17
	0x2A, 0xB8, 0x1D, 0xEE, 0xEB, 0x1E, 0x4C, 0xAB, 
	0x68, 0xE1, 0x04, 0xC4, 0xBD, 0x6B, 0x94, 0xEA, 
	0xC7, 0x2C, 0x67, 0xAF, 0x6C, 0x2E, 0x5B, 0x6B, 
	0x0E, 0xAF, 0xB6, 0x17, 0x70, 0xF1, 0xB3, 0x2E, 
	0xA1, 0xAE, 0x71, 0x14, 0x9E, 0xED, 0x13, 0x82, 
	0xAB, 0xD4, 0x67, 0x18, 0x06, 0x72, 0xEC, 0x6F, 
	0x84, 0xA2, 0xF1, 0x5B, 0x3F, 0xCA, 0x72, 0xC1

};

CK_RV test_magma_acpkm_ctr(CK_SESSION_HANDLE sess)
{
    CK_RV                  rc = CKR_OK;
    CK_ULONG               len;
    CK_BYTE                value[1024], value2[1024];
    CK_OBJECT_HANDLE       keyh = CK_INVALID_HANDLE;
    CK_MECHANISM           mechanism_desc = { CKM_MAGMA_ACPKM_CTR, NULL, 0 };
    CK_MECHANISM_PTR       mechanism      = &mechanism_desc;
	CK_TC26_ACPKM_CTR_PARAMS params;

    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_KEY_TYPE     key_type = CKK_MAGMA;


    static CK_ATTRIBUTE  key_template[] = {
        { CKA_CLASS,           &oclass,		sizeof(oclass)		},
        { CKA_KEY_TYPE,        &key_type,	sizeof(key_type)	},
        { CKA_ENCRYPT,         &ltrue,		sizeof(CK_BBOOL)	},
        { CKA_DECRYPT,         &ltrue,		sizeof(CK_BBOOL)	},
        { CKA_VALUE,           K,			sizeof(K)			}
    };

    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;
    }

	params.pIV = IV;
	params.ulIVLen = sizeof(IV);
	params.ulGammaBlockLen = s;
	params.ulSectionLen = N;
    mechanism->pParameter     = &params;
    mechanism->ulParameterLen = sizeof(params);
    rc                        = funcs->C_EncryptInit(sess, mechanism, keyh);
    if (rc != CKR_OK)
    {
        fprintf(stderr, "C_EncryptInit failed: 0x%lx\n", rc);
        goto end;
    }

    len = sizeof(M);
    rc  = funcs->C_Encrypt(sess, M, len, value, &len);
    if (rc != CKR_OK)
    {
        fprintf(stderr, "C_Encrypt failed: 0x%lx\n", rc);
        goto end;
    }

    CHECK(value, len, encrM);

    rc = funcs->C_DecryptInit(sess, mechanism, keyh);
    if (rc != CKR_OK)
    {
        fprintf(stderr, "C_DecryptInit failed: 0x%lx\n", rc);
        goto end;
    }

    len = sizeof(encrM);
    rc = funcs->C_Decrypt(sess, value, len, value2, &len);
    if (rc != CKR_OK)
    {
        fprintf(stderr, "C_Decrypt failed: 0x%lx\n", rc);
        goto end;
    }

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

    return rc;
}

