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

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

/*
 EPOC-2 encryption

 Return: TRUE if succeed; otherwise FALSE
*/
BYTE EPOC2_Encryption (
   BYTE             *message,
   CIPHER_INFO      *cipherInfo,
   EPOC2_PUB_KEY    *publicKey,
   EPOC2_CIPHERTEXT *ciphertext     /* returned ciphertext */
)
{
mpz_t R, m, r, m_n_R, comp1, comp2;
RANDOM_STRUCT randomStruct;
unsigned int bytesNeeded;
static unsigned char seedByte = 0;
BYTE *H_in, *tmp_p, *m_raw, *R_raw, *r_raw, *G_R_raw;
WORD H_in_len;
long i;

	mpz_init(R);
	mpz_init(m);
	mpz_init(r);
	mpz_init(m_n_R);
	mpz_init(comp1);
	mpz_init(comp2);
	m_raw = (BYTE *) malloc (cipherInfo->mLen/8);
	r_raw = (BYTE *) malloc (publicKey->hLen/8);
	R_raw = (BYTE *) malloc (publicKey->rLen/8);
	G_R_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);

	WORD2BYTE(m_raw+(cipherInfo->mLen/8-ABS(m->_mp_size)*4), m->_mp_d,
	          ABS(m->_mp_size));

	/*
	printf("EPOC2_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));

	/* compute m_n_R := (m || R) and r := H(m || R) */
	H_in_len = (cipherInfo->mLen + publicKey->rLen)/8;
	H_in = (BYTE *) malloc (H_in_len);
	tmp_p = H_in;
	assignBYTE(tmp_p, m_raw, cipherInfo->mLen/8);
	tmp_p += cipherInfo->mLen/8;
	assignBYTE(tmp_p, R_raw, publicKey->rLen/8);
	BYTE2WORD(m_n_R, H_in, H_in_len);

	/*
	printf("EPOC2_Encryption: m_n_R=\n");
	printf("%s\n\n", mpz_get_str(NULL, 16, m_n_R));
	printf("EPOC2_Encryption: H_in =\n");
	for(i=0; i< H_in_len; i++)
		printf("%02x ", H_in[i]);
	printf("\n\n");
	*/

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

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

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


	/* compute c1 */
	mpz_powm(comp1, publicKey->g, R, publicKey->n);

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

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

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

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

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

	/* compute c2 */
	BulkEncrypt(m_raw, cipherInfo, G_R_raw, cipherInfo->mLen, ciphertext);

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

	/* clean up */
	mpz_clear(R);
	mpz_clear(m);
	mpz_clear(r);
	mpz_clear(m_n_R);
	mpz_clear(comp1);
	mpz_clear(comp2);

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

	H_in_len = 0;

	return TRUE;
}


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