/*
 encrypt.c - EPOC-3 encryption 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"
#include "random.h"

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

/*
EPOC-3 encryption
Return: TRUE if succeed; otherwise FALSE
*/
BYTE EPOC3_Encryption (
   BYTE             *message,
   EPOC3_PUB_KEY    *publicKey,  /* EPOC3 public key */
   CIPHER_INFO      *cipherInfo, /* cipher and key information */
   EPOC3_CIPHERTEXT *ciphertext  /* returned ciphertext */
)
{
mpz_t m, r, R, comp1, comp2;
BYTE *m_raw, *r_raw, *R_raw, *K_raw, *H_in, *c1_raw, *tmp_ptr;
WORD H_in_len;
RANDOM_STRUCT randomStruct;
unsigned int bytesNeeded;
static unsigned char seedByte = 0;
long i;

	mpz_init(m);
	mpz_init(r);
	mpz_init(R);
	mpz_init(comp1);
	mpz_init(comp2);

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

	memset(m_raw, 0, cipherInfo->mLen/8);

   /* prepare (mpz_t) m and (BYTE *) m_raw */
   mpz_set_str(m, message, 0);
	/*
   printf("EPOC3_Encryption: message = %s\n\n", message);
   printf("EPOC3_Encryption: m = %s\n\n", mpz_get_str(NULL, 16, m));
   printf("EPOC3_Encryption: ABS(m->_mp_size) = %lu\n\n", ABS(m->_mp_size));
   printf("EPOC3_Encryption: m->_mp_d =\n");
   for(i=0; i< ABS(m->_mp_size); i++)
      printf("%04lx ", m->_mp_d[i]);
   printf("\n\n");
	*/

   WORD2BYTE(m_raw+(cipherInfo->mLen/8-ABS(m->_mp_size)*4), m->_mp_d,
	          ABS(m->_mp_size));
	/*
   printf("EPOC3_Encryption: m_raw =\n");
   for(i=0; i< cipherInfo->mLen/8; i++)
      printf("%02x ", m_raw[i]);
   printf("\n\n");
	*/

   /* initialize random number generator */
   RandomInit(&randomStruct);
   /* initialize with all zero seed bytes for testing purpose only */
   while (1) {
      GetRandomBytesNeeded (&bytesNeeded, &randomStruct);
      if (bytesNeeded == 0) break;
      RandomUpdate (&randomStruct, &seedByte, 1);
   }

   /* generate r */
   GenerateBytes(r_raw, publicKey->rLen/8, &randomStruct);
	/*
   printf("EPOC2_Encryption: r_raw =\n");
   for(i=0; i< publicKey->rLen/8; i++)
      printf("%02x ", r_raw[i]);
   printf("\n\n");
	*/
   BYTE2WORD(r, r_raw, publicKey->rLen/8);
   printf("r = 0x%s\n", mpz_get_str(NULL, 16, r));

   /* generate R */
   GenerateBytes(R_raw, publicKey->RLen/8, &randomStruct);
	/*
   printf("EPOC2_Encryption: R_raw =\n");
   for(i=0; i< publicKey->RLen/8; i++)
      printf("%02x ", R_raw[i]);
   printf("\n\n");
	*/
   BYTE2WORD(R, R_raw, publicKey->RLen/8);
   printf("R = 0x%s\n", mpz_get_str(NULL, 16, R));

	/* compute c1 */
	mpz_powm(comp1, publicKey->g, R, publicKey->n);
	/* printf("EPOC3_Encryption: comp1 =\n%s\n\n",mpz_get_str(NULL,16,comp1)); */

	mpz_powm(comp2, publicKey->h, r, publicKey->n);
	/* printf("EPOC3_Encryption: comp2 =\n%s\n\n",mpz_get_str(NULL,16,comp2)); */

	mpz_mul(ciphertext->c1, comp1, comp2);
	mpz_mod(ciphertext->c1, ciphertext->c1, publicKey->n);
	/*
	printf("EPOC3_Encryption: ciphertext->c1 =\n");
	printf("%s\n\n", mpz_get_str(NULL, 16, ciphertext->c1));
	*/
	WORD2BYTE(c1_raw, (ciphertext->c1)->_mp_d, ABS((ciphertext->c1)->_mp_size));

	/* compute K <- G(R) */
	indexedSHA(K_raw, publicKey->gLen/8, R_raw, publicKey->RLen/8);
	/*
   printf("EPOC3_Encryption: K_raw =\n");
   for(i=0; i<publicKey->gLen/8; i++)
      printf("%02x ", K_raw[i]);
	printf("\n\n");
	*/

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

	/*
   printf("EPOC3_Encryption: ciphertext->c2 =\n");
   for(i=0; i<ciphertext->cipherLen; i++)
      printf("%02x ", ciphertext->c2[i]);
	printf("\n\n");
	*/

	/* compute c3 */
	/* c3 = H(c1, c2, R, m) */
	H_in_len= (3*publicKey->pLen + cipherInfo->mLen +
	           ciphertext->cipherLen*8 + 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(ciphertext->c3, publicKey->hLen/8, H_in, H_in_len);

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

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

	mpz_clear(m);
	mpz_clear(r);
	mpz_clear(R);
	mpz_clear(comp1);
	mpz_clear(comp2);

	H_in_len = 0;

	return TRUE;
}


/*
Symmetric Key Encryption

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