
template<class group> PSECKEM<group> ::
PSECKEM(KDF& K, group& Grp, int Keylen, int Seedlen)
	: KEM(Keylen), k(K), grp(Grp), seedlen(Seedlen)
{
	x = RandomBnd(grp.mu - 1) + 1;

	h = grp.mul(x, grp.g);
}

template<class group> PSECKEM<group> ::
PSECKEM(KDF& K, group& Grp, int Keylen, int Seedlen, std::istream& ifile)
	: KEM(Keylen), k(K), grp(Grp), seedlen(Seedlen)
{
	ifile >> x; 
	grp.readfromfile(ifile, h);
}

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

template<class group> void PSECKEM<group> ::
PrettyPrint(std::ostream& tofile)
{
	tofile << "\n-------------";
        tofile << "\nPSEC-KEM";
	tofile << "\n-------------";
	tofile << "\n\nKdf=";
        k.PrettyPrint(tofile);
        tofile << "\nKeylen=" << keylen;
	tofile << "\nSeedlen=" << seedlen;
	tofile << "\n\n-----\n";
        tofile << "\nGroup=";
	grp.PrettyPrint(tofile);
	tofile << "\n\n-----\n";
        tofile << "\nPublic Key";
        prettyprint("\nh", h, tofile);
	tofile << "\n\n-----\n";
        tofile << "\nPrivate Key\n";
	tofile << "\nx = "; prettyprint(tofile, x);
	tofile << "\n\n";
}

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

	long lmu = NumBytes(mu - 1);

	ostring u, I0, I1, t, seed, EG, PEH, SeedMask, MaskedSeed, K;
	NTL::ZZ r;
	GrpElt G, H;

	I0 = i2osp(to_ZZ(0), 4);
	I1 = i2osp(to_ZZ(1), 4);

	// Generate random octet string of length seedlen
	seed = i2osp(RandomBits_ZZ(8*seedlen), seedlen);

	t = k.eval(I0 + seed, lmu + 16 + keylen);

	if (t.size() != lmu + 16 + keylen)
		throw General_error();

	u = t.substr(0, NumBytes(mu - 1) + 16);
	K = t.substr(NumBytes(mu - 1) + 16, keylen);

	r = (os2ip(u)) % mu;

	if(logfile)
	{
		*logfile << "\n-------------------------------";
		*logfile << "\nTrace for PSEC-KEM encrypt";
		*logfile << "\n-------------------------------";
		grp.print_fmt(*logfile);
		*logfile << "\n\nseed = "; prettyprint(*logfile, seed);
		*logfile << "\n\nt = "; prettyprint(*logfile, t);
		*logfile << "\n\nu = "; prettyprint(*logfile, u);
		*logfile << "\n\nr = "; prettyprint(*logfile, r);
		*logfile << "\n";
	}

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

	EG = grp.encode(G);
	PEH = grp.pencode(H);

	if (logfile)
	{
		prettyprint("\ng~", G, *logfile);
		prettyprint("\nh~", H, *logfile);
		*logfile << "\n\nEG = "; prettyprint(*logfile, EG);
		*logfile << "\n\nPEH = "; prettyprint(*logfile, PEH);
	}

	SeedMask = k.eval(I1 + EG + PEH, seedlen);

	MaskedSeed = Xor(seed, SeedMask);

	C0 = EG + MaskedSeed;

	if (logfile)
	{
		*logfile << "\n\nSeedMask = "; prettyprint(*logfile, SeedMask);
		*logfile << "\n\nMaskedSeed = "; prettyprint(*logfile, MaskedSeed);
		*logfile << "\n\nC0 = "; prettyprint(*logfile, C0);
		*logfile << "\n\nK = "; prettyprint(*logfile, K);
		*logfile << "\n\n";
	}

	return K;
}

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

	long lmu = NumBytes(mu - 1);

        ostring u, I0, I1, t, seed, EG, PEH, SeedMask, MaskedSeed, K;
        NTL::ZZ r;
	GrpElt G, Gbar, H;

        I0 = i2osp(to_ZZ(0), 4);
        I1 = i2osp(to_ZZ(1), 4);

	nbytes = grp.decode(G, C0, 0);
	if (!nbytes || nbytes + seedlen > C0.size())
		throw Decrypt_error();

	EG = C0.substr(0, nbytes);
	MaskedSeed = C0.substr(nbytes, seedlen);
	nbytes += seedlen;

	H = grp.mul(x, G);

	PEH = grp.pencode(H);

	SeedMask = k.eval(I1 + EG + PEH, seedlen);

	seed = Xor(MaskedSeed, SeedMask);

	t = k.eval(I0 + seed, lmu + 16 + keylen);

        if (t.size() != lmu + 16 + keylen)
                throw General_error();

        u = t.substr(0, NumBytes(mu - 1) + 16);
        K = t.substr(NumBytes(mu - 1) + 16, keylen);

        r = (os2ip(u)) % mu;

        Gbar = grp.mul(r, g);

	if(!grp.equal(G, Gbar))
	{
		throw Decrypt_error();
	}

	return K;
}

