#include "hcu_cases.h"

/*
_a = 0xFFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFC
_b = 0x28E9FA9E9D9F5E344D5A9E4BCF6509A7F39789F515AB8F92DDBCBD414D940E93
_p = 0xFFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFF
_Gx = 0x32C4AE2C1F1981195F9904466A39C9948FE30BBFF2660BE1715A4589334C74C7
_Gy = 0xBC3736A2F4F6779C59BDCEE36B692153D0A9877CC62A474002DF32E52139F0A0
*/

/* SM2 parameter */
static const hcu_ecc_config_t eccConfig = {
    .opLength = ECC_P_256,
    .a = {0xfffffffc, 0xffffffff, 0x00000000, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xfffffffe,},
    .b = {0x4d940e93, 0xddbcbd41, 0x15ab8f92, 0xf39789f5, 0xcf6509a7, 0x4d5a9e4b, 0x9d9f5e34, 0x28e9fa9e},
    .p = {0xffffffff, 0xffffffff, 0x00000000, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xfffffffe,},
    .gx = {0x334c74c7, 0x715a4589, 0xf2660be1, 0x8fe30bbf, 0x6a39c994, 0x5f990446, 0x1f198119, 0x32c4ae2c},
    .gy = {0x2139f0a0, 0x2df32e5, 0xc62a4740, 0xd0a9877c, 0x6b692153, 0x59bdcee3, 0xf4f6779c, 0xbc3736a2},
};

/* Base point of SM2 */
static hcu_ecc_point_t pointG = {
    (uint32_t *)eccConfig.gx, 
    (uint32_t *)eccConfig.gy
};

static const uint32_t K[8] = {
    0x20BB0DA0, 0x08DDBC29, 0xB89463F2, 0x34AA7F7C, 0x3FBF3535, 0x5E2EFE28, 0xA00637BD, 0x1649AB77,
}; 
static const uint32_t K2[8] = {2, 0, 0, 0, 0, 0, 0, 0};
static uint32_t KN[8] = {0, 0, 0, 0, 0, 0, 0, 0};

/* Result point for K*G */
static uint32_t const PX_KG[8] = {
    0x5044DCB1, 0x7AD4663F, 0x655D8D59, 0x903B3CA3, 0x974DB9F4, 0x72D857CB, 0x48006EEA, 0x191BFF81
};
static uint32_t const PY_KG[8] = {
    0x855EF119, 0xEE1E914C, 0x7709E2C1, 0x5389F71A, 0xD2F37985, 0x53A8059C, 0xF1FCD8C6, 0xE2F7888A
};

/* Result point for 2*G */
static uint32_t const PX_2G[8] = {
    0xA3F2BD52, 0x495C2E1D, 0xC08A7331, 0x9C0DFA08, 0xFA73BA4D, 0x0D58EF57, 0xD7C87C00, 0x56CEFD60
};
static uint32_t const PY_2G[8] = {
    0x970A23C3, 0x6F780D3A, 0x2F6C8E71, 0x6DE84C18, 0xF8EAF1BD, 0x68535CE0, 0xCC8189F6, 0x31B7E7E6
};

static uint32_t pointX[8], pointY[8];
static hcu_ecc_point_t resultPoint = {pointX, pointY};

static uint32_t pointPx[8];
static uint32_t pointPy[8];
static uint32_t pointQx[8];
static uint32_t pointQy[8];
/* 2 * P */
static uint32_t pointRx[8];
static uint32_t pointRy[8];
/* P + Q */
static uint32_t pointR2x[8];
static uint32_t pointR2y[8];
static hcu_ecc_point_t pointP = {pointPx, pointPy};
static hcu_ecc_point_t pointQ = {pointQx, pointQy};
static hcu_ecc_point_t pointR = {pointRx, pointRy};
static hcu_ecc_point_t pointR2 = {pointR2x, pointR2y};

static pke_user_config_t pkeUserConfig;

static pke_state_t pkeState;

static void checkResult(const uint32_t *expected, const uint32_t *actual, uint32_t length)
{
    for (uint32_t i = 0; i < length; i++)
    {
        if (expected[i] != actual[i])
        {
            PRINTF("Expected[%zu]: 0x%08X, Actual[%zu]: 0x%08X\r\n", i, expected[i], i, actual[i]);
        }
    }
}

void hcu_ecc_demo(void)
{
    PRINTF("Show ECC-256 point add and point multiply.\r\n");
    pkeUserConfig.eccConfig = &eccConfig;
    /* PKE init */
    PKE_DRV_Init(&pkeUserConfig, &pkeState);

    PRINTF("Calculate K * G!\r\n");
    HCU_DRV_ECCPointMul(K, &pointG, &resultPoint);
    checkResult(PX_KG, pointX, 8);
    checkResult(PY_KG, pointY, 8);

    PRINTF("Calculate 2 * G!\r\n");
    HCU_DRV_ECCPointMul(K2, &pointG, &resultPoint);
    checkResult(PX_2G, pointX, 8);
    checkResult(PY_2G, pointY, 8);

    PRINTF("Calculate Point P, Q, R and R2.\r\n");
    /* Calculate 3 * G for P */
    KN[0] = 3;
    HCU_DRV_ECCPointMul(KN, &pointG, &pointP);
    /* Calculate 7 * G for Q */
    KN[0] = 7;
    HCU_DRV_ECCPointMul(KN, &pointG, &pointQ);
    /* Calculate 6 * G for R */
    KN[0] = 6;
    HCU_DRV_ECCPointMul(KN, &pointG, &pointR);
    /* Calculate 10 * G for R2 */
    KN[0] = 10;
    HCU_DRV_ECCPointMul(KN, &pointG, &pointR2);

    PRINTF("Calculate 2 * P.\r\n");
    HCU_DRV_ECCPointAdd(&pointP, &pointP, &resultPoint);
    checkResult(pointRx, pointX, 8);
    checkResult(pointRy, pointY, 8);

    PRINTF("Calculate P + Q.\r\n");
    HCU_DRV_ECCPointAdd(&pointP, &pointQ, &resultPoint);
    checkResult(pointR2x, pointX, 8);
    checkResult(pointR2y, pointY, 8);

    PKE_DRV_DeInit(&pkeState);
}

