/**
 *--------------------------------------------------------------------\n
 *          HSLU T&A Hochschule Luzern Technik+Architektur            \n
 *--------------------------------------------------------------------\n
 *
 * \brief         ASYD assignment crypto 03
 * \file
 * \author        Basil Estermann, basil.estermann@stud.hslu.ch
 *                Simon Frei, simon.frei@stud.hslu.ch
 *                Jonas Arnold, jonas.arnold@stud.hslu.ch
 * \date          14.03.2023
 *
 *--------------------------------------------------------------------
 */

#include <stdint.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "encryptionArithmetic.h"
#include "DHKE.h"


#define KEY_LENGTH 64

int createKeypair(void);
int calculateSessionKey(void);

void main(void) {

    int result = 0;

    printf("------ Calculate Keypair ------\n\n");
    result = createKeypair();
    if (result != 0) {
        return result;
    }

    printf("------ Calculate Sessionkey ------\n\n");
    result = calculateSessionKey();
    if (result != 0) {
        return result;
    }

    return 0;
}

int createKeypair(void) {
    /*** CREATE KEYPAIR ***/
    const char* P_string = "3203431780337000";
    const char* alpha_string = "AFFE12345678AFFE";
    const char* Priv_a_string = "9778729279583412";
    const char* Priv_b_string = "4825234752983495";

    // P has to be a prime number! Large prime number (64bit): 3203431780337000
    t_encryptionArithmetic P;
    encryptionArithmetic_Init(&P, KEY_LENGTH);
    if (encryptionArithmetic_stringToHex(P_string, P.number, KEY_LENGTH) == false) {
        printf("ERROR: Creating P.");
        return 1000000;
    }

    t_encryptionArithmetic alpha;
    encryptionArithmetic_Init(&alpha, KEY_LENGTH);
    if (encryptionArithmetic_stringToHex(alpha_string, alpha.number, KEY_LENGTH) == false) {
        printf("ERROR: Creating alpha.");
        return 1000001;
    }

    t_encryptionArithmetic priv_a;
    encryptionArithmetic_Init(&priv_a, KEY_LENGTH);
    if (encryptionArithmetic_stringToHex(Priv_a_string, priv_a.number, KEY_LENGTH) == false) {
        printf("ERROR: Creating priv a.");
        return 1000003;
    }

    t_encryptionArithmetic priv_b;
    encryptionArithmetic_Init(&priv_b, KEY_LENGTH);
    if (encryptionArithmetic_stringToHex(Priv_b_string, priv_b.number, KEY_LENGTH) == false) {
        printf("ERROR: Creating priv b.");
        return 1000004;
    }

    /* initialize and calculate public key a */
    t_encryptionArithmetic pub_a;
    encryptionArithmetic_Init(&pub_a, KEY_LENGTH);
    squareAndMultiply(&alpha, &priv_a, &P, &pub_a, KEY_LENGTH);
    printf("Calculated public key for person A: 0x");
    for (int num_bytes = KEY_LENGTH / 64; num_bytes >= 0; num_bytes--) {
        printf("%X", *((uint32_t*)(pub_a.number + num_bytes)));
    }
    printf("\n");

    /* initialize and calculate public key b */
    t_encryptionArithmetic pub_b;
    encryptionArithmetic_Init(&pub_b, KEY_LENGTH);
    squareAndMultiply(&alpha, &priv_b, &P, &pub_b, KEY_LENGTH);
    printf("Calculated public key for person B: 0x");
    for (int num_bytes = KEY_LENGTH / 64; num_bytes >= 0; num_bytes--) {
        printf("%X", *((uint32_t*)(pub_b.number + num_bytes)));
    }
    printf("\n");


    /* initialize and calculate session key for person a */
    t_encryptionArithmetic session_key_a;
    encryptionArithmetic_Init(&session_key_a, KEY_LENGTH);
    squareAndMultiply(&pub_b, &priv_a, &P, &session_key_a, KEY_LENGTH);
    printf("Calculated session key for person A: 0x");
    for (int num_bytes = KEY_LENGTH / 64; num_bytes >= 0; num_bytes--) {
        printf("%X", *((uint32_t*)(session_key_a.number + num_bytes)));
    }
    printf("\n");

    /* initialize and calculate session key for person b */
    t_encryptionArithmetic session_key_b;
    encryptionArithmetic_Init(&session_key_b, KEY_LENGTH);
    squareAndMultiply(&pub_a, &priv_b, &P, &session_key_b, KEY_LENGTH);
    printf("Calculated session key for person B: 0x");
    for (int num_bytes = KEY_LENGTH / 64; num_bytes >= 0; num_bytes--) {
        printf("%X", *((uint32_t*)(session_key_b.number + num_bytes)));
    }
    printf("\n");

    return 0;
}

int calculateSessionKey(void) {

    /*** Calculate Session Key ***/
    const char* P_string = "3203431780337000"; 
    const char* Priv_a_string = "9778729279583412";
    const char* Pub_b_string = "13BED5BE5045000";

    // P has to be a prime number! Large prime number (64bit): 3203431780337000
    t_encryptionArithmetic P;
    encryptionArithmetic_Init(&P, KEY_LENGTH);
    if (encryptionArithmetic_stringToHex(P_string, P.number, KEY_LENGTH) == false) {
        printf("ERROR: Creating P.");
        return 1100000;
    }

    t_encryptionArithmetic priv_a;
    encryptionArithmetic_Init(&priv_a, KEY_LENGTH);
    if (encryptionArithmetic_stringToHex(Priv_a_string, priv_a.number, KEY_LENGTH) == false) {
        printf("ERROR: Creating priv a.");
        return 1100003;
    }

    t_encryptionArithmetic pub_b;
    encryptionArithmetic_Init(&pub_b, KEY_LENGTH);
    if (encryptionArithmetic_stringToHex(Pub_b_string, pub_b.number, KEY_LENGTH) == false) {
        printf("ERROR: Creating pub b.");
        return 1100004;
    }

    /* initialize and calculate session key for person a */
    t_encryptionArithmetic session_key_a;
    encryptionArithmetic_Init(&session_key_a, KEY_LENGTH);
    squareAndMultiply(&pub_b, &priv_a, &P, &session_key_a, KEY_LENGTH);
    printf("Calculated session key for person A: 0x");
    for (int num_bytes = KEY_LENGTH / 64; num_bytes >= 0; num_bytes--) {
        printf("%X", *((uint32_t*)(session_key_a.number + num_bytes)));
    }
    printf("\n");

    return 0;
}