#include "verifier.h"

#include <time.h>
int ve_get_verifier_identifier_M(verifier_identifier_t *identifier)
{
    u_int8_t data[16];
    WORD SW12;
    WORD La;

    if (identifier == NULL)
    {
        return -1;
    }


    SW12 = multosSendAPDU(CLA_APPLICATION, INS_HELLO_WORLD, 0x00, 0x00, 0, VERIFIER_MAX_ID_LENGTH, &La, 0x00, data, VERIFIER_MAX_ID_LENGTH, 1000);
    if (SW12 != 0x9000)
    {
        return -1;
    }

    memcpy(identifier->buffer, data, VERIFIER_MAX_ID_LENGTH);
    identifier->buffer_length = VERIFIER_MAX_ID_LENGTH;


    return 0;
}
int ve_set_verifier_private_key_M(verifier_private_key_t *ve_private_key){

    uint8_t ve_key[16] = {0};
    WORD SW12;
    WORD La;
    uint8_t lc = 0;

    /// VERIFIER key
    memcpy(&ve_key[lc], ve_private_key->sk, VERIFIER_MAX_ID_LENGTH);
    lc += VERIFIER_MAX_ID_LENGTH;


    SW12 = multosSendAPDU(CLA_APPLICATION, INS_KEY_VERIFIER, 0x00, 0x00, lc, 0, &La, 0x00, ve_key, VERIFIER_MAX_ID_LENGTH, 1000);
    if (SW12 != 0x9000)
    {
        return -1;
    }

    return 0;
}
int ve_get_verifier_nonce_M(verifier_nonce_t *ve_nonce){

    u_int8_t data[16];
    WORD SW12;
    WORD La;

    if (ve_nonce == NULL)
    {
        return -1;
    }

    fprintf(stdout, "[+] TC request :\n");
    fprintf(stdout, "%02X %02X %02X %02X %02X", CLA_APPLICATION, INS_VE_NONCE, 0x00,0x00, NONCE_LENGTH);
    fprintf(stdout, "\n");

    SW12 = multosSendAPDU(CLA_APPLICATION, INS_VE_NONCE, 0x00, 0x00, 0, NONCE_LENGTH, &La, 0x00, data, NONCE_LENGTH, 1000);
    if (SW12 != 0x9000)
    {
        return -1;
    }

    memcpy(ve_nonce->nonce, data, NONCE_LENGTH);
    ve_nonce->nonce_length = NONCE_LENGTH;

    fprintf(stdout, "[+] TC response :\n");
    for (int i = 0; i < ve_nonce->nonce_length; i++)
    {
        fprintf(stdout, "%02X ", ve_nonce->nonce[i]);
    }
    fprintf(stdout, "%02X\n", SW12);
    fprintf(stdout, "\n");


    return 0;
}

int ve_set_user_identifier_M(user_identifier_t *identifier){

    uint8_t ue_id[16] = {0};
    WORD SW12;
    WORD La;
    uint8_t lc = 0;
   // FILE *userID;

    /// user identifier
    memcpy(&ue_id[lc], identifier->buffer, USER_MAX_ID_LENGTH);
    lc += USER_MAX_ID_LENGTH;

    /*if( access( "/tmp/hello-world-pc/userID.txt", F_OK ) != 0 ){
        printf("Soubor s userID  chybí!");
        return 0;
    }
    userID = fopen("/tmp/hello-world-pc/userID.txt", "r");

    //read file into array
    uint8_t user_ID[AES_BLOCK_SIZE + 1] ={0};

    for (uint8_t i = 0; i < 16; i++){
        fscanf(userID, "%u ", &user_ID[i] );
        //printf("[%u]",user_ID[i]);
    }

    fclose(userID);*/

    //memcpy(&ue_id[lc], &user_ID[lc], USER_MAX_ID_LENGTH);
    lc += USER_MAX_ID_LENGTH;

    /*for (uint8_t i = 0; i < 16; i++){
        printf("%u ",ue_id[i]);
    }*/
    fprintf(stdout, "[+] TC request :\n");
    fprintf(stdout, "%02X %02X %02X %02X %02X ", CLA_APPLICATION, INS_ID_USER, 0x00,0x00, USER_MAX_ID_LENGTH);
    for (int i = 0; i < identifier->buffer_length; i++)
    {
        fprintf(stdout, "%02X ", identifier->buffer[i]);
    }
    fprintf(stdout, "\n");

    SW12 = multosSendAPDU(CLA_APPLICATION, INS_ID_USER, 0x00, 0x00, lc, 0, &La, 0x00, ue_id, USER_MAX_ID_LENGTH, 1000);
    if (SW12 != 0x9000)
    {
        return -1;
    }
    fprintf(stdout, "[+] TC response :\n");
    fprintf(stdout, "%02X\n", SW12);
    fprintf(stdout, "\n");

    return 0;
}
int ve_set_user_nonce_M(user_nonce_t *nonce){

    uint8_t ue_nonce[16] = {0};
    WORD SW12;
    WORD La;
    uint8_t lc = 0;

    /// user nonce
    memcpy(&ue_nonce[lc], nonce->nonce, NONCE_LENGTH);
    lc += NONCE_LENGTH;

    fprintf(stdout, "[+] TC request :\n");
    fprintf(stdout, "%02X %02X %02X %02X %02X ", CLA_APPLICATION, INS_NONCE_USER, 0x00,0x00, NONCE_LENGTH);
    for (int i = 0; i < nonce->nonce_length; i++)
    {
        fprintf(stdout, "%02X ", nonce->nonce[i]);
    }
    fprintf(stdout, "\n");

    SW12 = multosSendAPDU(CLA_APPLICATION, INS_NONCE_USER, 0x00, 0x00, lc, 0, &La, 0x00, ue_nonce, NONCE_LENGTH, 1000);
    if (SW12 != 0x9000)
    {
        return -1;
    }

    fprintf(stdout, "[+] TC response :\n");
    fprintf(stdout, "%02X\n", SW12);
    fprintf(stdout, "\n");

    return 0;
}
int ve_verify_M(reader_t *reader, verifier_session_key_t *sessionKey, verifier_cipher_output_t *ve_cipher_output, user_keys_t *ue_keys, verifier_identifier_t *ve_identifier, user_identifier_t *ue_identifier, verifier_nonce_t *ve_nonce, user_nonce_t *ue_nonce){

    u_int8_t verifier_vkey[AES_BLOCK_SIZE];
    u_int8_t vkey_M[5 * AES_BLOCK_SIZE];
    verifier_cipher_data_t verifier_decipher_data;
    WORD SW12;
    WORD La;
    FILE *verifierID;
    FILE *userID;
    FILE *userkey;

    int r;

    time_t start_M, end_M,start, end;
    double cpu_time_used_M,cpu_time_used;

    if (sessionKey == NULL)
    {
        return -1;
    }

    start_M = clock();

   struct timespec ts_start = {0, 0};
    struct timespec ts_end = {0, 0};
    double elapsed_time;

    clock_gettime(CLOCK_MONOTONIC, &ts_start);

    fprintf(stdout, "[+] TC request :\n");
    fprintf(stdout, "%02X %02X %02X %02X %02X", CLA_APPLICATION, INS_VERIFY, 0x00,0x00, 5 * AES_BLOCK_SIZE);
    fprintf(stdout, "\n");

    SW12 = multosSendAPDU(CLA_APPLICATION, INS_VERIFY, 0x00, 0x00, 0, 5 * AES_BLOCK_SIZE, &La, 0x00, vkey_M, 5 * AES_BLOCK_SIZE, 1000);
    if (SW12 != 0x9000)
    {
        return -1;
    }

   clock_gettime(CLOCK_MONOTONIC, &ts_end);
    elapsed_time = ((double) ts_end.tv_sec + 1.0e-9 * (double) ts_end.tv_nsec) - ((double) ts_start.tv_sec + 1.0e-9 * (double) ts_start.tv_nsec);

    memcpy(ve_cipher_output->cipher_output,vkey_M,5 * AES_BLOCK_SIZE);

    fprintf(stdout, "[+] TC response :\n");
    for (int i = 0; i < 5 * AES_BLOCK_SIZE; i++)
    {
        fprintf(stdout, "%02X ", ve_cipher_output->cipher_output[i]);
    }
    fprintf(stdout, "%02X\n", SW12);
    fprintf(stdout, "\n");


    uint8_t pbSendBuffer[MAX_APDU_LENGTH_T0] = {0};
    uint8_t pbRecvBuffer[MAX_APDU_LENGTH_T0] = {0};
    uint32_t dwSendLength;
    uint32_t dwRecvLength;

    uint8_t AES[256] = {0};
    //int r;

    //toto tu musí zůstat kvuli sestavení commandu, viz odeslání lc
    uint8_t lc = 0;

    struct timespec ts_start1 = {0, 0};
    struct timespec ts_end1 = {0, 0};
    double elapsed_time1;

    clock_gettime(CLOCK_MONOTONIC, &ts_start1);

    // user identifier
    memcpy(&AES[lc], ve_cipher_output->cipher_output, 5 * AES_BLOCK_SIZE);
    lc += 5 * AES_BLOCK_SIZE;


    dwSendLength = sizeof(pbSendBuffer);
    r = apdu_build_command(CASE3S, CLA_APPLICATION, INS_VERIFY, 0x00, 0x00, lc, AES, 0, pbSendBuffer, &dwSendLength);
    if (r < 0)
    {
        return -1;
    }

    dwRecvLength = sizeof(pbRecvBuffer);
    r = sc_transmit_data(*reader, pbSendBuffer, dwSendLength, pbRecvBuffer, &dwRecvLength, NULL);
    if (r < 0)
    {
        fprintf(stderr, "Error: %s\n", sc_get_error(r));
        return r;
    }

    clock_gettime(CLOCK_MONOTONIC, &ts_end1);
    elapsed_time1 = ((double) ts_end1.tv_sec + 1.0e-9 * (double) ts_end1.tv_nsec) - ((double) ts_start1.tv_sec + 1.0e-9 * (double) ts_start1.tv_nsec);
    printf("Elapsed time (Case verify Card MC4) = %f sec.\n", elapsed_time1);
    printf("Elapsed time (Case verify Trust Core) = %f sec.\n\n", elapsed_time);

    return 0;

}

int test(reader_t *reader, verifier_identifier_t *identifier){

    uint8_t pbSendBuffer[MAX_APDU_LENGTH_T0] = {0};
    uint8_t pbRecvBuffer[MAX_APDU_LENGTH_T0] = {0};
    uint32_t dwSendLength;
    uint32_t dwRecvLength;

    int r;

    if (identifier == NULL)
    {
        return -1;
    }

    dwSendLength = sizeof(pbSendBuffer);
    r = apdu_build_command(CASE2S, CLA_APPLICATION, INS_HELLO_WORLD, 0x00, 0x00, 0, NULL, VERIFIER_MAX_ID_LENGTH, pbSendBuffer, &dwSendLength);
    if (r < 0)
    {
        return -1;
    }

    dwRecvLength = sizeof(pbRecvBuffer);
    r = sc_transmit_data(*reader, pbSendBuffer, dwSendLength, pbRecvBuffer, &dwRecvLength, NULL);
    if (r < 0)
    {
        fprintf(stderr, "Error: %s\n", sc_get_error(r));
        return r;
    }

    memcpy(identifier->buffer, pbRecvBuffer, VERIFIER_MAX_ID_LENGTH);
    identifier->buffer_length = VERIFIER_MAX_ID_LENGTH;

    return 0;

}