#ifndef KEM_H_INCLUDED
#define KEM_H_INCLUDED

#include <crypto/crypto.h>
#include <crypto/kdf.h>
#include <crypto/hash.h>
#include <crypto/group.h>


class KEM
{
 public:
	int keylen;
	virtual ostring encrypt(ostring&) = 0;
	virtual ostring decrypt(const ostring&, int&) = 0;
	virtual void writetofile(std::ostream&) = 0;

	virtual void PrettyPrint(std::ostream&) = 0;

	std::ostream *logfile;
	KEM(int len) : keylen(len) { logfile = NULL; };
};


class RSAKEM : public KEM
{
        NTL::ZZ n, e, d;
        KDF& k;
        ostring rsatransform(const ostring& X, NTL::ZZ& a);
 public:

        ostring encrypt(ostring& cipher);
        ostring decrypt(const ostring& cipher, int& nbytes);
        void writetofile(std::ostream& ofile);

	void PrettyPrint(std::ostream&);

        RSAKEM(KDF& kdf, int Keylen, int Primelen = 256);
        RSAKEM(KDF& kdf, int Keylen, std::istream& ifile);
};

template <class group> class ACEKEM : public KEM
{
	NTL::ZZ w, x, y, z;
	typename group::GrpElt c, d, h, gp;
	KDF& k;
	HASH& hash;
	group& grp;
 public:

	int CofactorMode;
	ostring encrypt(ostring& C0);
	ostring decrypt(const ostring& C, int& nbytes);
	void writetofile(std::ostream& ofile);

	void PrettyPrint(std::ostream&);

	ACEKEM(int cofactormode, KDF& K, HASH& H, int Keylen, group& Grp);
	ACEKEM(int cofactormode, KDF& K, HASH& H, int Keylen, group& Grp, std::istream& ifile);
};

template <class group> class PSECKEM : public KEM
{
        NTL::ZZ x;
	typename group::GrpElt h;
        KDF &k;
        group& grp;
 public:

        int seedlen;
        ostring encrypt(ostring& C0);
        ostring decrypt(const ostring& C0, int& nbytes);
        void writetofile(std::ostream& ofile);

	void PrettyPrint(std::ostream&);

        PSECKEM(KDF& K, group& g, int Keylen, int Seedlen);
        PSECKEM(KDF& K, group& g, int Keylen, int Seedlen, std::istream& ifile);
};

template <class group> class ECIESKEM : public KEM
{
        NTL::ZZ x;
	int CofactorMode, OldCofactorMode, CheckMode, SingleHashMode;
        typename group::GrpElt h;
        KDF &k;
        group& grp;
 public:

        ostring encrypt(ostring& C0);
        ostring decrypt(const ostring& C0, int& nbytes);
        void writetofile(std::ostream& ofile);

	void PrettyPrint(std::ostream&);

        ECIESKEM(KDF& K, group& g, int Keylen, unsigned int CofactorMode,
			unsigned int OldCofactorMode, unsigned int CheckMode,
			unsigned int SingleHashMode);
        ECIESKEM(KDF& K, group& g, int Keylen, unsigned int CofactorMode,
			unsigned int OldCofactorMode, unsigned int CheckMode,
			unsigned int SingleHashMode, std::istream& ifile);
};

#include <crypto/ace_kem.h>
#include <crypto/psec_kem.h>
#include <crypto/ecies_kem.h>

#endif  /* KEM_H_INCLUDED */

