#include <crypto/rem.h>

NTL_CLIENT

void REM1 :: PrettyPrint(ostream& tofile)
{
	tofile << "Rem1(Hash=";
	h.PrettyPrint(tofile);
	tofile << ", Kdf=";
	k.PrettyPrint(tofile);
	tofile << ")";
}


ostring REM1 :: encode(const ostring& M, const ostring& L, int Elen)
{
	ostring E, check, pad, DataBlock, DataBlockMask;
	ostring seed, MaskedDataBlock, SeedMask, MaskedSeed;

	if(M.size() > Elen - Bound)
		throw General_error();

	pad = ostring(Elen - M.size() - Bound, octet(0));

	seed = i2osp(RandomBits_ZZ(8*Hlen), Hlen);

	check = h.eval(L);

	DataBlock = check + pad + octet(1) + M;

	DataBlockMask = k.eval(seed, Elen - Hlen - 1);

	MaskedDataBlock = Xor(DataBlockMask, DataBlock);

	SeedMask = k.eval(MaskedDataBlock, Hlen);

	MaskedSeed = Xor(SeedMask, seed);

	E = octet(0) + MaskedSeed + MaskedDataBlock;

	if (logfile)
	{
		*logfile << "\n\nMessage in ASCII = \"";
		printostring(*logfile, M);
		*logfile << "\"\n\nMessage as octet string = ";
		prettyprint(*logfile, M);
		*logfile << "\n\nLabel in ASCII = \"";
		printostring(*logfile, L);
		*logfile << "\"\n\nLabel as octet string = ";
		prettyprint(*logfile, L);
		*logfile << "\n\nseed = ";
		prettyprint(*logfile, seed);
		*logfile << "\n\nDataBlock = ";
		prettyprint(*logfile, DataBlock);
		*logfile << "\n\nDataBlockMask = ";
		prettyprint(*logfile, DataBlockMask);
		*logfile << "\n\nMaskedDataBlock = ";
		prettyprint(*logfile, MaskedDataBlock);
		*logfile << "\n\nSeedMask = ";
		prettyprint(*logfile, SeedMask);
		*logfile << "\n\nMaskedSeed = ";
		prettyprint(*logfile, MaskedSeed);
	}

	return E;
}


ostring REM1 :: decode(const ostring& E, const ostring& L)
{
	int m, l, Elen;
	octet X, T;
	ostring M, check, DataBlock, DataBlockMask;
        ostring seed, MaskedDataBlock, SeedMask, MaskedSeed;
	ostring checkp, Mp;

	Elen = E.size();

	if(Elen < Bound)
		throw Decrypt_error();

	check = h.eval(L);

	X = E[0];
	MaskedSeed = E.substr(1, Hlen);
	MaskedDataBlock = E.substr(Hlen + 1, Elen - Hlen - 1);

	SeedMask = k.eval(MaskedDataBlock, Hlen);

	seed =  Xor(MaskedSeed, SeedMask);

	DataBlockMask = k.eval(seed, Elen - Hlen - 1);

	DataBlock = Xor(MaskedDataBlock, DataBlockMask);

	checkp = DataBlock.substr(0, Hlen);
	Mp = DataBlock.substr(Hlen, Elen - 2 * Hlen - 1);

	l = Elen - 2 * Hlen - 1;

	for( m = 0; m <= l; m++)
		if (Mp[m] != octet(0))
			break;

	T = Mp[m];
	if(m == l)
		M = "";
	else
		M = Mp.substr(m+1, l-m);

	if(check != checkp || X != octet(0) || T != octet(1))
		throw Decrypt_error();

	return M;
}

