#include "test_common.h"

CK_RV test_crypto();
CK_RV test_magma_cbc(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_MAGMA_CBC, &minfo);
    if (rc != CKR_OK)
    {
        fprintf(stderr,
                "\n======== Mechanism CKM_MAGMA_CBC not supported ===========\n");
    }
    else
    {
        fprintf(stderr,
                "\n==================== CKM_MAGMA_CBC test ==================\n");
        rc = test_magma_cbc(hSession);
        if (rc != CKR_OK)
        {
            fprintf(stderr,
                    "ERROR CKM_MAGMA_CBC failed, rc = 0x%lx\n", rc);
        }
        else
        {
            fprintf(stderr, "CKM_MAGMA_CBC 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_magma_cbc(CK_SESSION_HANDLE sess)
{
    CK_RV rc = CKR_OK;
    CK_BYTE                value[256];
    CK_BYTE                value2[256];
    CK_ULONG               len;
    CK_OBJECT_HANDLE       keyh           = CK_INVALID_HANDLE;
    CK_MECHANISM           mechanism_desc = { CKM_MAGMA_CBC, NULL, 0 };
    CK_MECHANISM_PTR       mechanism      = &mechanism_desc;

    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;
    // id-tc26-gost-28147-param-Z (1.2.643.7.1.2.5.1.1)
//    static CK_BYTE         oid_Z[] = {
//        0x06, 0x09, 0x2a, 0x85, 0x03, 0x07, 0x01, 0x02, 0x05, 0x01, 0x01
//    };
    static unsigned char   plaintext_cbc_Z[] = {
        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, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
    };
    static unsigned char   iv_cbc_Z[] = {
        0x81, 0x16, 0x29, 0xec, 0x62, 0xb4, 0xd0, 0xb4,
    };
    static unsigned char   key_cbc_Z[] = {
        0x0d, 0xe3, 0xf0, 0x8b, 0x21, 0x18, 0x3c, 0x28,
        0x0c, 0x5c, 0xa8, 0x7f, 0x7b, 0xf2, 0x7a, 0x89,
        0x14, 0x3a, 0xa4, 0x47, 0x68, 0xd5, 0x51, 0x05,
        0x23, 0x0a, 0xbe, 0xdc, 0x86, 0x68, 0x4b, 0x7a,
    };
    static unsigned char   encr_cbc_tc26_Z[] = {
		0xba, 0x1a, 0x50, 0x73, 0xfe, 0xe0, 0x5c, 0x5f,
		0xef, 0x52, 0x44, 0xa4, 0x77, 0xa4, 0xcd, 0x87,
		0x83, 0x11, 0x0f, 0x64, 0x1a, 0x19, 0xa5, 0xab,
		0x4d, 0x5c, 0xdf, 0xcb, 0xcd, 0x5e, 0xda, 0xb9,
		0xc0, 0xc6, 0x4f, 0x70, 0xfe, 0xc7, 0xd6, 0x51,
		0x82, 0x93, 0xb1, 0xeb, 0x55, 0x24, 0xa6, 0x21,
		0x84, 0x04, 0xaa, 0x67, 0x3c, 0xc5, 0x69, 0x53,
		0xa6, 0x83, 0x16, 0xe6, 0x1a, 0x4a, 0x8b, 0x23,
    };

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

    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     = iv_cbc_Z;
    mechanism->ulParameterLen = sizeof(iv_cbc_Z);
    rc                        = funcs->C_EncryptInit(sess, mechanism, keyh);
    if (rc != CKR_OK)
    {
        fprintf(stderr, "C_EncryptInit failed: 0x%lx\n", rc);
        goto end;
    }

    fprintf(stderr, "Plain text to encrypt:\n");
    print_bytes(plaintext_cbc_Z, sizeof(plaintext_cbc_Z));
    // Данные для шифрования в блочных режимах должны
    // поступать на вход порциями, длина которых
    // кратна размеру блока, т.е. 8-ми.
    len = sizeof(plaintext_cbc_Z);
    rc  = funcs->C_Encrypt(sess, plaintext_cbc_Z, len, value, &len);
    if (rc != CKR_OK)
    {
        fprintf(stderr, "C_Encrypt failed: 0x%lx\n", rc);
        goto end;
    }
    if (len != sizeof(encr_cbc_tc26_Z))
    {
        fprintf(stderr, "Invalid ciphertext length: %ld\n", len);
        rc = CKR_FUNCTION_FAILED;
        goto end;
    }
    if (memcmp(value, encr_cbc_tc26_Z, len) != 0)
    {
        fprintf(stderr, "Invalid ciphertext\n");
        rc = CKR_FUNCTION_FAILED;
        goto end;
    }
    fprintf(stderr, "CBC encryption result OK\n");
    print_bytes(value, len);
    rc = funcs->C_DecryptInit(sess, mechanism, keyh);
    if (rc != CKR_OK)
    {
        fprintf(stderr, "C_DecryptInit failed: 0x%lx\n", rc);
        goto end;
    }

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

    if (len != sizeof(plaintext_cbc_Z))
    {
        fprintf(stderr, "Invalid decrypted text length: %ld\n", len);
        rc = CKR_FUNCTION_FAILED;
        goto end;
    }
    if (memcmp(value2, plaintext_cbc_Z, len) != 0)
    {
        fprintf(stderr, "Invalid decrypted text\n");
        rc = CKR_FUNCTION_FAILED;
        goto end;
    }
    fprintf(stderr, "CBC decryption result OK\n");

 end:
    if (keyh != CK_INVALID_HANDLE)
    {
        funcs->C_DestroyObject(sess, keyh);
    }

    return rc;
}
