/*
 sign.c - ESIGN Signature Generation

 Copyright NTT MCL, 2000.

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

#include <stdio.h>
#include <gmp.h>
#include "esign.h"
#include "hash.h"
#include "utils.h"
#include "random.h"

/*
 ESIGN Signature Generation

 Return: TRUE if succeed; otherwise FALSE
*/
BYTE ESIGN_Sign (
   BYTE             *message,
	WORD             mLen,
   ESIGN_PUB_KEY    *publicKey,
	ESIGN_PRIV_KEY   *privateKey,
	BYTE             *signature
)
{
RANDOM_STRUCT randomStruct;
unsigned int bytesNeeded;
static unsigned char seedByte = 0;
mpz_t r, gcd_val, m, z, alpha, w0, w1, pq, t, S;
size_t w1_size;     /* the size of w1 is in # bits */
BYTE *r_raw, *m_raw, *z_raw, *H_out;
long i;

	mpz_init(r);
	mpz_init(gcd_val);
	mpz_init(m);
	mpz_init(z);
	mpz_init(alpha);
	mpz_init(w0);
	mpz_init(w1);
	mpz_init(pq);
	mpz_init(t);
	mpz_init(S);
	r_raw = (BYTE *) malloc (publicKey->pLen*2/8);
	m_raw = (BYTE *) malloc (mLen/8);
	z_raw = (BYTE *) malloc (publicKey->pLen*3/8);
	H_out = (BYTE *) malloc (publicKey->pLen/8);

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

	/* prepare (mpz_t) m and (BYTE *) m_raw */
	mpz_set_str(m, message, 0);

	WORD2BYTE(m_raw, m->_mp_d, ABS(m->_mp_size));

	/* H'(M) and set it to 0||H(M) */
	indexedSHA(H_out, publicKey->pLen/8, m_raw, mLen/8);
	H_out[0] = H_out[0] & 0x7F;

	/* construct z := (0||H(M)||0^{2*pLen}) */
	memset(z_raw, 0, publicKey->pLen*3/8);
	assignBYTE(z_raw, H_out, publicKey->pLen/8);
	BYTE2WORD(z, z_raw, publicKey->pLen*3/8);

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

	mpz_mul(pq, privateKey->p, privateKey->q);

	do {
		/* randomly choose r \in Z_pq\pZ */
		do {
			GenerateBytes(r_raw, publicKey->pLen*2/8, &randomStruct);

			BYTE2WORD(r, r_raw, publicKey->pLen*2/8);
			mpz_mod(r, r, pq);
			mpz_gcd(gcd_val, r, privateKey->p);
		} while (mpz_cmp_ui(gcd_val, 1) != 0);

		/* compute alpha := (z - r^e) mod n */
		mpz_powm(alpha, r, publicKey->e, publicKey->n);
		mpz_sub(alpha, z, alpha);
		mpz_mod(alpha, alpha, publicKey->n);

		/* compute w_0 := \ceiling{alpha/(pq)} and w1 := w0*pq - alpha */
		mpz_tdiv_qr(w0, w1, alpha, pq);
		if(mpz_cmp_ui(w1, 0) != 0) {
			mpz_add_ui(w0, w0, 1);
			mpz_sub(w1, pq, w1);
		}
		w1_size = mpz_sizeinbase(w1, 2);
	} while (w1_size >= 2*publicKey->pLen-1);
	printf("r = 0x%s\n", mpz_get_str(NULL, 16, r));

	/*
	printf("ESIGN_Sign: alpha =\n%s\n\n", mpz_get_str(NULL, 16, alpha));
	printf("ESIGN_Sign: w0=\n%s\n\n", mpz_get_str(NULL, 16, w0));
	printf("ESIGN_Sign: w1=\n%s\n\n", mpz_get_str(NULL, 16, w1));
	*/
	
	/* compute t := w0 * (e * r^(e-1))^{-1} mod p */
	mpz_sub_ui(t, publicKey->e, 1);
	mpz_powm(t, r, t, privateKey->p);
	mpz_mul(t, publicKey->e, t);
	mpz_invert(t, t, privateKey->p);
	mpz_mul(t, w0, t);
	mpz_mod(t, t, privateKey->p);

	/* compute S := (r + tpq) mod n */
	mpz_mul(S, t, pq);
	mpz_add(S, r, S);
	mpz_mod(S, S, publicKey->n);
	/* printf("ESIGN_Sign: S=\n%s\n\n", mpz_get_str(NULL, 16, S)); */

	sprintf(signature, "0x%s", mpz_get_str(NULL, 16, S));

	/* clean up */
	memset(r_raw, 0, publicKey->pLen*2/8);
	memset(m_raw, 0, mLen/8);
	memset(z_raw, 0, publicKey->pLen*3/8);
	memset(H_out, 0, publicKey->pLen/8);
	free(r_raw);
	free(m_raw);
	free(z_raw);
	free(H_out);

	mpz_clear(r);
	mpz_clear(gcd_val);
	mpz_clear(m);
	mpz_clear(z);
	mpz_clear(alpha);
	mpz_clear(w0);
	mpz_clear(w1);
	mpz_clear(pq);
	mpz_clear(t);
	mpz_clear(S);

	w1_size = 0;

	return TRUE;
}
