
template<class group> ECIESKEM<group> ::
ECIESKEM(KDF& K, group& Grp, int Keylen, unsigned int cofactorMode,
		unsigned int oldCofactorMode, unsigned int checkMode,
		unsigned int singleHashMode)
	: KEM(Keylen), k(K), grp(Grp), CofactorMode(cofactorMode),
		OldCofactorMode(oldCofactorMode), CheckMode(checkMode),
			SingleHashMode(singleHashMode)
{
	if (CofactorMode + OldCofactorMode + CheckMode > 1)
		throw Init_error();

	x = RandomBnd(grp.mu - 1) + 1;
	h = grp.mul(x, grp.g);
}

template<class group> ECIESKEM<group> ::
ECIESKEM(KDF& K, group& Grp, int Keylen,int unsigned cofactorMode,
		unsigned int oldCofactorMode, unsigned int checkMode,
		unsigned int singleHashMode, std::istream& ifile)
	: KEM(Keylen), k(K), grp(Grp), CofactorMode(cofactorMode),
		OldCofactorMode(oldCofactorMode), CheckMode(checkMode),
			SingleHashMode(singleHashMode)
{
	if (CofactorMode + OldCofactorMode + CheckMode > 1)
                throw Init_error();

	ifile >> x >> h;
}

template<class group> void ECIESKEM<group> ::
writetofile(std::ostream& ofile)
{
	ofile << x << "\n";
	grp.writetofile(ofile, h);
}

template<class group> void ECIESKEM<group> ::
PrettyPrint(std::ostream& tofile)
{
	tofile << "\n-------------";
        tofile << "\nECIES-KEM";
	tofile << "\n-------------";
	tofile << "\n\nKdf=";
        k.PrettyPrint(tofile);
        tofile <<"\nKeylen=" << keylen;
	tofile << "\nCofactorMode=" << CofactorMode;
	tofile << "\nOldCofactorMode=" << OldCofactorMode;
	tofile << "\nCheckMode=" << CheckMode;
	tofile << "\nSingleHashMode=" << SingleHashMode;
	tofile << "\n\n-----\n";
        tofile << "\nGroup=";
        grp.PrettyPrint(tofile);
	tofile << "\n\n-----\n";
        tofile << "\nPublic Key\n";
        prettyprint("\nh", h, tofile);
	tofile << "\n\n-----\n";
        tofile << "\nPrivate Key\n";
        tofile << "\nx = "; prettyprint(tofile, x);
	tofile << "\n\n";
}

template<class group> ostring ECIESKEM<group> ::
encrypt(ostring& C0)
{
	typedef typename group::GrpElt GrpElt;
	const GrpElt& g = grp.g;
        const NTL::ZZ& mu = grp.mu;
	const NTL::ZZ& nu = grp.nu;

	ostring PEH, K, Z = "";
	NTL::ZZ r, R;
	GrpElt G, H;

	r = RandomBnd(mu - 1) + 1;

	if (OldCofactorMode == 1) 
		R = (r * nu) % mu;
	else
		R = r;

	if(logfile)
	{
		*logfile << "\n-------------------------------";
		*logfile << "\nTrace for ECIES-KEM encrypt";
		*logfile << "\n-------------------------------";
		grp.print_fmt(*logfile);
		*logfile << "\n\nr = ";
		prettyprint(*logfile, r);
		*logfile << "\n\nr' = ";
		prettyprint(*logfile, R);
	}

	G = grp.mul(r, g);
	H = grp.mul(R, h);

	if (logfile)
	{
		prettyprint("\ng~", G, *logfile);
		prettyprint("\nh~", H, *logfile);
	}

	C0 = grp.encode(G);

	if (SingleHashMode != 1)
		Z = C0;

	PEH = grp.pencode(H);

	K = k.eval(Z + PEH, keylen);

	if (logfile)
	{
		*logfile << "\n\nPEH = "; prettyprint(*logfile, PEH);
		*logfile << "\n\nz = "; prettyprint(*logfile, Z);
		*logfile << "\n\nC0 = "; prettyprint(*logfile, C0);
		*logfile << "\n\nK = "; prettyprint(*logfile, K);
		*logfile << "\n\n";
	}

	return K;
}

template<class group> ostring ECIESKEM<group> ::
decrypt(const ostring& C0, int& nbytes)
{
	typedef typename group::GrpElt GrpElt;
	const GrpElt& g = grp.g;
	const NTL::ZZ& mu = grp.mu;
	const NTL::ZZ& nu = grp.nu;

        ostring u, EG, PEH, K, Z = "";
        NTL::ZZ r, X, nu_inv;
	GrpElt G, Gbar, H;

	nbytes = grp.decode(G, C0, 0);

	if(CheckMode == 1 && !(grp.subgroup(G)))
		throw Decrypt_error();

	if(CofactorMode == 1 || OldCofactorMode == 1)
		Gbar = grp.mul(nu, G);
	else
		Gbar = G;

	if(CofactorMode == 1)
	{
                nu_inv = InvMod(nu % mu, mu);

		X = (nu_inv * x) % mu; 
	}
	else
		X = x;

	H = grp.mul(X, Gbar);

	if(grp.is_ident(H))
		throw Decrypt_error();

	if(SingleHashMode != 1)
		Z = C0.substr(0, nbytes);

	PEH = grp.pencode(H);

	K = k.eval(Z + PEH, keylen);

	return K;
}

