/**
 * \file kem.c
 * \brief Implementation of kem_api.h
 */

#include "hash.h"
#include "kem_api.h"
#include "parameters.h"
#include "piglet1.h"
#include "parsing.h"
#include "ffi_field.h"
#include "ffi_vec.h"
#include "ffi_elt.h"


 /**
  * \fn int kem_keygen(unsigned char* pk, unsigned char* sk)
  * \brief Keygen of the piglet1_KEM IND-CCA2 scheme
  *
  * As a technicality, the public key is appended to the secret key.
  * \param[out] pk String containing the public key
  * \param[out] sk String containing the secret key
  * \return 0 if keygen is sucessfull
  */
int kem_keygen(unsigned char *pk, unsigned char *sk) {

	piglet1_pke_keygen(pk, sk);
	return 0;
}

/**
 * \fn int int kem_enc(const unsigned char *pk, unsigned char *ss, unsigned char *ct)
 * \brief Encapsulation of the piglet1_KEM IND-CCA2 scheme
 *
 * \param[in] pk String containing the public key
 * \param[in] sk String containing the secret key
 * \param[out] ss String containing the shared secret
 * \param[out] ct String containing the ciphertext
 * \return 0 if encapsulation is sucessfull
 */
int kem_enc(const unsigned char *pk, unsigned char *ss, unsigned char *ct) 
{
	// Computing plain text m , using random generator
	ffi_vec m;
	m.SetLength(PARAM_L);
	ffi_vec_set_random_using_rng(m, PARAM_L);

	// Generating G function
	unsigned char seed_G[VEC_L_BYTES];
	ffi_vec_to_string(seed_G, m, PARAM_L);
	AES_XOF_struct* G_seedexpander = (AES_XOF_struct*)malloc(sizeof(AES_XOF_struct));
	seedexpander_init(G_seedexpander, seed_G, seed_G + 28, SEEDEXPANDER_MAX_LENGTH);

	// Computing theta
	unsigned char theta[SK_SEED_LEN];
	seedexpander(G_seedexpander, theta, SK_SEED_LEN);

	// Encrypting m
	ffi_vec u[PARAM_K], v;
	for (int i = 0; i < PARAM_K; i++)
		u[i].SetLength(PARAM_R);
	v.SetLength(PARAM_R);
	piglet1_pke_encryption(m, pk, theta, u, v);

	// Computing d
	unsigned char tmp_m[VEC_L_BYTES];
	ffi_vec_to_string(tmp_m, m, PARAM_L);

	//printf("\nOrginal m");
	//for (int sss = 0; sss < VEC_L_BYTES; sss++)
	//{
	//	if (sss % 16 == 0) printf("\n");
	//	printf("%02X ", tmp_m[sss]);
	//}
	//printf("\n\n");

	unsigned char d[SHA512_BYTES];
	sha512(d, tmp_m, VEC_L_BYTES);

	// 序列化密文
	piglet1_kem_ciphertext_to_string(ct, u, v, d);

	// 计算共享密钥
	unsigned char mc[PARAM_K*VEC_R_BYTES + VEC_R_BYTES + VEC_L_BYTES];
	for (int i = 0; i < PARAM_K; i++)
	{
		ffi_vec_to_string(mc + i*VEC_R_BYTES, u[i], PARAM_R);
	}
	ffi_vec_to_string(mc + PARAM_K*VEC_R_BYTES, v, PARAM_R);
	ffi_vec_to_string(mc + PARAM_K*VEC_R_BYTES + VEC_R_BYTES, m, PARAM_L);
	sha512(ss, mc, PARAM_K*VEC_R_BYTES + VEC_R_BYTES + VEC_L_BYTES);

	return 0;
}

/**
 * \fn int kem_dec(const unsigned char *sk, const unsigned char *ct, unsigned char *ss)
 * \brief Decapsulation of the piglet1_KEM IND-CCA2 scheme
 *
 * \param[out] ss String containing the shared secret
 * \param[in] ct String containing the ciphertext
 * \param[in] sk String containing the secret key
 * \return 0 if decapsulation is successfull, -1 otherwise
 */
int kem_dec(const unsigned char *sk, const unsigned char *ct, unsigned char *ss) 
{
	// Read date to initilize C1 and C2 and d from the ciphertext
	unsigned char d1[SHA512_BYTES];
	ffi_vec u1[PARAM_K], v1;
	
	for (int i = 0; i < PARAM_K; i++)
	{
		u1[i].SetLength(PARAM_R);
	}
	
	v1.SetLength(PARAM_R);
	piglet1_kem_ciphertext_from_string(ct, u1, v1, d1);

	// Retrieving pk from sk
	unsigned char pk[PUBLIC_KEY_BYTES];
	memcpy(pk, sk + SK_SEED_LEN + GD_SEED_LEN, PUBLIC_KEY_BYTES);

	// Decrypting
	ffi_vec dec_m;
	dec_m.SetLength(PARAM_L);
	piglet1_pke_decryption(sk, dec_m, u1, v1);

	// Generating G function
	unsigned char seed_G[VEC_L_BYTES];
	ffi_vec_to_string(seed_G, dec_m, PARAM_L);
	AES_XOF_struct* G_seedexpander = (AES_XOF_struct*)malloc(sizeof(AES_XOF_struct));
	seedexpander_init(G_seedexpander, seed_G, seed_G + 28, SEEDEXPANDER_MAX_LENGTH);

	// Computing theta
	unsigned char theta[SK_SEED_LEN];
	seedexpander(G_seedexpander, theta, SK_SEED_LEN);

	// Encrypting m'
	ffi_vec u2[PARAM_K], v2;
	for (int i = 0; i < PARAM_K; i++)
	{
		u2[i].SetLength(PARAM_R);
	}
	v2.SetLength(PARAM_R);
	piglet1_pke_encryption(dec_m, pk, theta, u2, v2);//xiugai

	// Checking that c = c' and abort otherwise
	int abort = 0;
	for (int i = 0; i < PARAM_K; i++)
	{
		if (ffi_vec_is_equal_to(u1[i], u2[i], PARAM_R) == 0)
		{
			abort = 1;
			break;
		}
	}
	if (!abort && ffi_vec_is_equal_to(v1, v2, PARAM_R) == 0)
	{
		abort = 1;
	}

	// Computing d'
	unsigned char tmp_m[VEC_L_BYTES];
	ffi_vec_to_string(tmp_m, dec_m, PARAM_L);
	
	//printf("\nDecoded m");
	//for (int sss = 0; sss < VEC_L_BYTES; sss++)
	//{
	//	if (sss % 16 == 0) printf("\n");
	//	printf("%02X ", tmp_m[sss]);
	//}
	//printf( "\n\n");

	unsigned char d2[SHA512_BYTES];
	sha512(d2, tmp_m, VEC_L_BYTES);

	// Checking that d = d' and abort otherwise
	if (!abort && memcmp(d1, d2, SHA512_BYTES) != 0) {
		abort = 1;
	}

	if (abort == 1) {
		printf("\n\nCheck result : ABORT");
		printf("\n\n## Checking Ciphertext - End ##");
		memset(ss, 0, KEM_BYTES);
		return -1;
	}

	// Computing shared secret
	unsigned char mc[PARAM_K*VEC_R_BYTES + VEC_R_BYTES + VEC_L_BYTES];
	for (int i = 0; i < PARAM_K; i++)
	{
		ffi_vec_to_string(mc + i*VEC_R_BYTES, u2[i], PARAM_R);
	}
	ffi_vec_to_string(mc + PARAM_K*VEC_R_BYTES, v2, PARAM_R);
	ffi_vec_to_string(mc + PARAM_K*VEC_R_BYTES + VEC_R_BYTES, dec_m, PARAM_L);
	sha512(ss, mc, PARAM_K*VEC_R_BYTES + VEC_R_BYTES + VEC_L_BYTES);
	return 0;
}

