/*
 decrypt.c - EPOC-3 decryption function

 Copyright NTT MCL, 2000.

 Duncan S Wong   
 Security Group, NTT MCL
 July 2000
*/

#include <stdio.h>
#include <gmp.h>
#include "epoc3.h"
#include "hash.h"
#include "utils.h"

void BulkDecrypt(BYTE *message, CIPHER_INFO *cipherInfo, BYTE *K,
                 EPOC3_CIPHERTEXT *ciphertext);

/*
 EPOC-3 dercyption

 Return: TRUE if succeed; otherwise FALSE
*/
BYTE EPOC3_Decryption (
   EPOC3_CIPHERTEXT *ciphertext,
   EPOC3_PRIV_KEY   *privateKey,
   EPOC3_PUB_KEY    *publicKey,
   CIPHER_INFO      *cipherInfo,
	BYTE             *message
)
{
mpz_t p2, p_1, c_p, L_c_p, g_p_1, L_g_p, R;
BYTE *c1_raw, *K_raw, *R_raw, *m_raw, *H_in, *H_out, *tmp_ptr;
WORD H_in_len, RLen;
BYTE compare_result;
long i;

	mpz_init(p2);
	mpz_init(p_1);
	mpz_init(c_p);
	mpz_init(L_c_p);
	mpz_init(g_p_1);
	mpz_init(L_g_p);
	mpz_init(R);

	c1_raw = (BYTE *) malloc (3*publicKey->pLen/8);
	R_raw = (BYTE *) malloc (publicKey->RLen/8);
	K_raw = (BYTE *) malloc (publicKey->gLen/8);
	m_raw = (BYTE *) malloc (cipherInfo->mLen/8);
	H_out = (BYTE *) malloc (publicKey->hLen/8);

	/*
	printf("EPOC3_Decryption: ciphertext->c1 =\n");
	printf("%s\n\n", mpz_get_str(NULL, 16, ciphertext->c1));
	printf("EPOC3_Decryption: ciphertext->c2 =\n");
	for(i=0; i<cipherInfo->mLen/8; i++)
		printf("%02x ", ciphertext->c2[i]);
	printf("\n\n");
	printf("EPOC3_Decryption: ciphertext->c3 =\n");
	for(i=0; i<publicKey->hLen/8; i++)
		printf("%02x ", ciphertext->c3[i]);
	printf("\n\n");
	*/

	/* compute c_p */
	mpz_mul(p2, privateKey->p, privateKey->p);

	mpz_sub_ui(p_1, privateKey->p, 1);

	mpz_powm(c_p, ciphertext->c1, p_1, p2);

	/* compute L(c_p) and L(g_p) */
	mpz_sub_ui(c_p, c_p, 1);
	mpz_divexact(L_c_p, c_p, privateKey->p);

	/*
	printf("EPOC3_Decryption: L_c_p =\n");
	printf("%s\n\n", mpz_get_str(NULL, 16, L_c_p));
	*/

	mpz_sub_ui(g_p_1, privateKey->g_p, 1);
	mpz_divexact(L_g_p, g_p_1, privateKey->p);

	/*
	printf("EPOC3_Decryption: L_g_p =\n");
	printf("%s\n\n", mpz_get_str(NULL, 16, L_g_p));
	*/

	/* compute R' */
	mpz_invert(L_g_p, L_g_p, privateKey->p);
	mpz_mul(R, L_c_p, L_g_p);
	mpz_mod(R, R, privateKey->p);
	WORD2BYTE(R_raw, R->_mp_d, ABS(R->_mp_size));

	/* compute K_raw <- G(R_raw) */
	indexedSHA(K_raw, publicKey->gLen/8, R_raw, publicKey->RLen/8);

	/*
	printf("EPOC3_Decryption: K_raw =\n");
	for(i=0; i<publicKey->gLen/8; i++)
		printf("%02x ", K_raw[i]);
	printf("\n\n");
	*/

	/* compute c2 */
	BulkDecrypt(m_raw, cipherInfo, K_raw, ciphertext);

	/*
	printf("EPOC3_Decryption: m_raw =\n");
	for(i=0; i<cipherInfo->mLen/8; i++)
		printf("%02x ", m_raw[i]);
	printf("\n\n");
	*/

	/* check if c3 == H(c1_raw, c2, R_raw, m_raw) */
	WORD2BYTE(c1_raw, (ciphertext->c1)->_mp_d, ABS((ciphertext->c1)->_mp_size));
   H_in_len= (3*publicKey->pLen + 2*cipherInfo->mLen + publicKey->RLen)/8;

   H_in = (BYTE *) malloc (H_in_len);
   assignBYTE(H_in, c1_raw, 3*publicKey->pLen/8);
   tmp_ptr = H_in + 3*publicKey->pLen/8;
   assignBYTE(tmp_ptr, ciphertext->c2, ciphertext->cipherLen);
   tmp_ptr += ciphertext->cipherLen;
   assignBYTE(tmp_ptr, R_raw, publicKey->RLen/8);
   tmp_ptr += publicKey->RLen/8;
   assignBYTE(tmp_ptr, m_raw, cipherInfo->mLen/8);

   indexedSHA(H_out, publicKey->hLen/8, H_in, H_in_len);

	/*
   printf("EPOC3_Decryption: H_out =\n");
   for(i=0; i<publicKey->hLen/8; i++)
      printf("%02x ", H_out[i]);
   printf("\n\n");
	*/

	/* compare H_out with ciphertext->c3 */
	compare_result = TRUE;
	for(i=0; i<publicKey->hLen/8; i++)
		if(H_out[i] != ciphertext->c3[i]) compare_result = FALSE;

	RLen = mpz_size(R)*4;
	if(RLen == 1) RLen = 0;
	if(compare_result == FALSE || RLen != publicKey->RLen/8) {
		printf("EPOC3_Decryption:\nH_out and ciphertext->c3 are inconsistent");
		printf("\nor the length of session key ");
		printf(" is not equal to %lu bits\n",
		       publicKey->RLen);
		message[0] = '\0';
	} else {
		sprintf(message,"0x");
      tmp_ptr = message+2;
		i = 0;
		while(m_raw[i] == 0 && i != (cipherInfo->mLen/8-1)) i++;
		while (i<cipherInfo->mLen/8) {
			sprintf(tmp_ptr,"%02x",m_raw[i]);
			tmp_ptr += 2;
			i++;
      }
	}

	/* clean up */
	mpz_clear(p2);
	mpz_clear(p_1);
	mpz_clear(c_p);
	mpz_clear(L_c_p);
	mpz_clear(g_p_1);
	mpz_clear(L_g_p);
	mpz_clear(R);

	memset(c1_raw, 0, 3*publicKey->pLen/8);
	memset(R_raw, 0, publicKey->RLen/8);
	memset(K_raw, 0, publicKey->gLen/8);
	memset(m_raw, 0, cipherInfo->mLen/8);
	memset(H_out, 0, publicKey->hLen/8);
   memset(H_in, 0, H_in_len);
	free(c1_raw);
	free(R_raw);
	free(K_raw);
	free(m_raw);
	free(H_out);
	free(H_in);

   H_in_len = 0;
	RLen = 0;
   i = 0;

	return TRUE;
}


/*
 Symmetric Key Decryption

 in_fp  : plaintext
 out_fp : ciphertext

 Return : # bytes decrypted

 Comment : only one-time pad implemented at this moment
*/
void BulkDecrypt(BYTE *message, CIPHER_INFO *cipherInfo, BYTE *K,
                 EPOC3_CIPHERTEXT *ciphertext)
{
	if(cipherInfo->algorithmID == ENCRYPT_ALG_XOR) {
		Vernam(K, cipherInfo->mLen, ciphertext->c2, message);
	}
}
