/*
 decrypt.c - EPOC-2 decryption function

 Copyright NTT MCL, 2000.

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

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

void BulkDecrypt(BYTE *message, CIPHER_INFO *cipherInfo, BYTE *K, WORD KLen,
                 EPOC2_CIPHERTEXT *ciphertext);

/*
 EPOC-2 dercyption

 Return: TRUE if succeed; otherwise FALSE
*/
BYTE EPOC2_Decryption (
   EPOC2_CIPHERTEXT *ciphertext,
	CIPHER_INFO      *cipherInfo,
   EPOC2_PRIV_KEY   *privateKey,
   EPOC2_PUB_KEY    *publicKey,
   BYTE             *message
)
{
mpz_t verC, comp1, comp2, p2, p_1, c_p, L_c_p, g_p_1, L_g_p, R, r;
BYTE *H_in, *H_out, *G_R_raw, *R_raw, *m_raw, *tmp_ptr;
WORD H_in_len;
long i;

	mpz_init(verC);
	mpz_init(comp1);
	mpz_init(comp2);
	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);
	mpz_init(r);

	G_R_raw = (BYTE *) malloc (publicKey->gLen/8);
	R_raw = (BYTE *) malloc (publicKey->rLen/8);
	m_raw = (BYTE *) malloc (cipherInfo->mLen/8);

	/* 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);

	mpz_sub_ui(g_p_1, privateKey->g_p, 1);
	mpz_divexact(L_g_p, g_p_1, privateKey->p);
	/*
	printf("EPOC2_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);
	/*
	printf("EPOC2_Decryption: R =\n");
	printf("%s\n\n", mpz_get_str(NULL, 16, R));
	*/

	/* compute G_R_raw := G(R) */
	WORD2BYTE(R_raw, R->_mp_d, ABS(R->_mp_size));
	indexedSHA (G_R_raw, publicKey->gLen/8, R_raw, publicKey->rLen/8);


	/* find message */
	BulkDecrypt(m_raw, cipherInfo, G_R_raw, cipherInfo->mLen, ciphertext);

	/* find r := H(m_raw || R) */
	H_in_len = (cipherInfo->mLen + publicKey->rLen) / 8;
	H_in = (BYTE *) malloc (H_in_len);
	assignBYTE(H_in, m_raw, cipherInfo->mLen/8);
	assignBYTE(H_in+cipherInfo->mLen/8, R_raw, publicKey->rLen/8);

	/*
	printf("EPOC2_Decryption: H_in =\n");
	for(i=0; i<H_in_len; i++)
		printf("%02x ", H_in[i]);
	printf("\n\n");
	*/

	H_out = (BYTE *) malloc (publicKey->hLen/8);
	
	indexedSHA (H_out, publicKey->hLen/8, H_in, H_in_len);

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

	BYTE2WORD(r, H_out, publicKey->hLen/8);

	/* compute verC = g^R h^r mod n */
   mpz_powm(comp1, publicKey->g, R, publicKey->n);

   mpz_powm(comp2, publicKey->h, r, publicKey->n);

   mpz_mul(verC, comp1, comp2);
   mpz_mod(verC, verC, publicKey->n);

	/*
   printf("EPOC2_Decryption: ciphertext->c1 =\n");
   printf("%s\n\n", mpz_get_str(NULL, 16, ciphertext->c1));
   printf("EPOC2_Decryption: verC =\n");
   printf("%s\n\n", mpz_get_str(NULL, 16, verC));
	*/

	if(mpz_cmp(ciphertext->c1, verC) == 0) {
		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++;
      }  
	} else {
		printf("EPOC2_Decryption: checking failed\n");
		message[0] = '\0';
	}

	/* clean up */
	mpz_clear(verC);
	mpz_clear(comp1);
	mpz_clear(comp2);
	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);
	mpz_clear(r);

	memset(G_R_raw, 0, publicKey->gLen/8);
	memset(R_raw, 0, publicKey->rLen/8);
	memset(m_raw, 0, cipherInfo->mLen/8);
	memset(H_out, 0, publicKey->hLen/8);
	memset(H_in, 0, H_in_len);
	free(R_raw);
	free(G_R_raw);
	free(m_raw);
	free(H_out);
	free(H_in);

	H_in_len = 0;
	i = 0;

	return TRUE;
}


void BulkDecrypt(BYTE *message, CIPHER_INFO *cipherInfo, BYTE *K, WORD KLen,
                 EPOC2_CIPHERTEXT *ciphertext)
{     
   if(cipherInfo->algorithmID == ENCRYPT_ALG_XOR) {
      ciphertext->cipherLen = Vernam(K, KLen, ciphertext->c2,
		                               message);
   } 
}  
