/*
 * tqca.h - TQt Cryptographic Architecture
 * Copyright (C) 2003  Justin Karneges
 * Copyright (C) 2010-2020  TDE Team
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 *
 */

#ifndef TQCA_H
#define TQCA_H

#include <tqstring.h>
#include <tqcstring.h>
#include <tqdatetime.h>
#include <tqmap.h>
#include <tqptrlist.h>
#include <tqobject.h>

#if defined(__TDE_HAVE_GCC_VISIBILITY) || defined(G_HAVE_GCC_VISIBILITY)
#define TQCA_NO_EXPORT __attribute__ ((visibility("hidden")))
#define TQCA_EXPORT __attribute__ ((visibility("default")))
#elif defined(_WIN32)
#define TQCA_NO_EXPORT
#define TQCA_EXPORT __declspec(dllexport)
#else
#define TQCA_NO_EXPORT
#define TQCA_EXPORT
#endif

#ifdef Q_OS_WIN32
#  ifdef TQCA_PLUGIN_DLL
#    define TQCA_PLUGIN_EXPORT extern "C" __declspec(dllexport)
#  else
#    define TQCA_PLUGIN_EXPORT extern "C" __declspec(dllimport)
#  endif
#endif
#ifndef TQCA_PLUGIN_EXPORT
#define TQCA_PLUGIN_EXPORT extern "C" TQCA_EXPORT
#endif

class TQHostAddress;
class TQStringList;

class TQCAProvider;
class TQCA_HashContext;
class TQCA_CipherContext;
class TQCA_CertContext;

namespace TQCA
{
	enum {
		CAP_SHA1      = 0x0001,
		CAP_SHA256    = 0x0002,
		CAP_MD5       = 0x0004,
		CAP_BlowFish  = 0x0008,
		CAP_TripleDES = 0x0010,
		CAP_AES128    = 0x0020,
		CAP_AES256    = 0x0040,
		CAP_RSA       = 0x0080,
		CAP_X509      = 0x0100,
		CAP_TLS       = 0x0200,
		CAP_SASL      = 0x0400
	};

	enum {
		CBC = 0x0001,
		CFB = 0x0002
	};

	enum {
		Encrypt = 0x0001,
		Decrypt = 0x0002
	};

	TQCA_EXPORT void init();
	TQCA_EXPORT bool isSupported(int capabilities);
	TQCA_EXPORT void insertProvider(TQCAProvider *);
	TQCA_EXPORT void unloadAllPlugins();

	TQCA_EXPORT TQString arrayToHex(const TQByteArray &);
	TQCA_EXPORT TQByteArray hexToArray(const TQString &);

	class TQCA_EXPORT Hash
	{
	public:
		Hash(const Hash &);
		Hash & operator=(const Hash &);
		~Hash();

		void clear();
		void update(const TQByteArray &a);
		TQByteArray final();

	protected:
		Hash(TQCA_HashContext *);

	private:
		class Private;
		Private *d;
	};

	template <class T>
	class TQCA_EXPORT HashStatic
	{
	public:
		HashStatic<T>() {}

		static TQByteArray hash(const TQByteArray &a)
		{
			T obj;
			obj.update(a);
			return obj.final();
		}

		static TQByteArray hash(const TQCString &cs)
		{
			TQByteArray a(cs.length());
			memcpy(a.data(), cs.data(), a.size());
			return hash(a);
		}

		static TQString hashToString(const TQByteArray &a)
		{
			return arrayToHex(hash(a));
		}

		static TQString hashToString(const TQCString &cs)
		{
			return arrayToHex(hash(cs));
		}
	};

	class TQCA_EXPORT Cipher
	{
	public:
		Cipher(const Cipher &);
		Cipher & operator=(const Cipher &);
		~Cipher();

		TQByteArray dyn_generateKey(int size=-1) const;
		TQByteArray dyn_generateIV() const;
		void reset(int dir, int mode, const TQByteArray &key, const TQByteArray &iv, bool pad=true);
		bool update(const TQByteArray &a);
		TQByteArray final(bool *ok=0);

	protected:
		Cipher(TQCA_CipherContext *, int dir, int mode, const TQByteArray &key, const TQByteArray &iv, bool pad);

	private:
		class Private;
		Private *d;
	};

	template <class T>
	class TQCA_EXPORT CipherStatic
	{
	public:
		CipherStatic<T>() {}

		static TQByteArray generateKey(int size=-1)
		{
			T obj;
			return obj.dyn_generateKey(size);
		}

		static TQByteArray generateIV()
		{
			T obj;
			return obj.dyn_generateIV();
		}
	};

	class TQCA_EXPORT SHA1 : public Hash, public HashStatic<SHA1>
	{
	public:
		SHA1();
	};

	class TQCA_EXPORT SHA256 : public Hash, public HashStatic<SHA256>
	{
	public:
		SHA256();
	};

	class TQCA_EXPORT MD5 : public Hash, public HashStatic<MD5>
	{
	public:
		MD5();
	};

	class TQCA_EXPORT BlowFish : public Cipher, public CipherStatic<BlowFish>
	{
	public:
		BlowFish(int dir=Encrypt, int mode=CBC, const TQByteArray &key=TQByteArray(), const TQByteArray &iv=TQByteArray(), bool pad=true);
	};

	class TQCA_EXPORT TripleDES : public Cipher, public CipherStatic<TripleDES>
	{
	public:
		TripleDES(int dir=Encrypt, int mode=CBC, const TQByteArray &key=TQByteArray(), const TQByteArray &iv=TQByteArray(), bool pad=true);
	};

	class TQCA_EXPORT AES128 : public Cipher, public CipherStatic<AES128>
	{
	public:
		AES128(int dir=Encrypt, int mode=CBC, const TQByteArray &key=TQByteArray(), const TQByteArray &iv=TQByteArray(), bool pad=true);
	};

	class TQCA_EXPORT AES256 : public Cipher, public CipherStatic<AES256>
	{
	public:
		AES256(int dir=Encrypt, int mode=CBC, const TQByteArray &key=TQByteArray(), const TQByteArray &iv=TQByteArray(), bool pad=true);
	};

	class RSA;
	class TQCA_EXPORT RSAKey
	{
	public:
		RSAKey();
		RSAKey(const RSAKey &from);
		RSAKey & operator=(const RSAKey &from);
		~RSAKey();

		bool isNull() const;
		bool havePublic() const;
		bool havePrivate() const;

		TQByteArray toDER(bool publicOnly=false) const;
		bool fromDER(const TQByteArray &a);

		TQString toPEM(bool publicOnly=false) const;
		bool fromPEM(const TQString &);

		// only call if you know what you are doing
		bool fromNative(void *);

	private:
		class Private;
		Private *d;

		friend class RSA;
		friend class TLS;
		bool encrypt(const TQByteArray &a, TQByteArray *out, bool oaep) const;
		bool decrypt(const TQByteArray &a, TQByteArray *out, bool oaep) const;
		bool generate(unsigned int bits);
	};

	class TQCA_EXPORT RSA
	{
	public:
		RSA();
		~RSA();

		RSAKey key() const;
		void setKey(const RSAKey &);

		bool encrypt(const TQByteArray &a, TQByteArray *out, bool oaep=false) const;
		bool decrypt(const TQByteArray &a, TQByteArray *out, bool oaep=false) const;

		static RSAKey generateKey(unsigned int bits);

	private:
		RSAKey v_key;
	};

	typedef TQMap<TQString, TQString> CertProperties;
	class TQCA_EXPORT Cert
	{
	public:
		Cert();
		Cert(const Cert &);
		Cert & operator=(const Cert &);
		~Cert();

		bool isNull() const;

		TQString commonName() const;
		TQString serialNumber() const;
		TQString subjectString() const;
		TQString issuerString() const;
		CertProperties subject() const;
		CertProperties issuer() const;
		TQDateTime notBefore() const;
		TQDateTime notAfter() const;

		TQByteArray toDER() const;
		bool fromDER(const TQByteArray &a);

		TQString toPEM() const;
		bool fromPEM(const TQString &);

	private:
		class Private;
		Private *d;

		friend class TLS;
		void fromContext(TQCA_CertContext *);
	};

	class TQCA_EXPORT TLS : public TQObject
	{
		TQ_OBJECT
	public:
		enum Validity {
			NoCert,
			Valid,
			HostMismatch,
			Rejected,
			Untrusted,
			SignatureFailed,
			InvalidCA,
			InvalidPurpose,
			SelfSigned,
			Revoked,
			PathLengthExceeded,
			Expired,
			Unknown
		};
		enum Error { ErrHandshake, ErrCrypt };

		TLS(TQObject *parent=0);
		~TLS();

		void setCertificate(const Cert &cert, const RSAKey &key);
		void setCertificateStore(const TQPtrList<Cert> &store);  // note: store must persist

		void reset();
		bool startClient(const TQString &host="");
		bool startServer();
		void close();
		bool isHandshaken() const;

		// plain (application side)
		void write(const TQByteArray &a);
		TQByteArray read();

		// encoded (socket side)
		void writeIncoming(const TQByteArray &a);
		TQByteArray readOutgoing();
		TQByteArray readUnprocessed();

		// cert related
		const Cert & peerCertificate() const;
		int certificateValidityResult() const;

	signals:
		void handshaken();
		void readyRead();
		void readyReadOutgoing(int plainBytes);
		void closed();
		void error(int);

	private slots:
		void update();

	private:
		class Private;
		Private *d;
	};

	class TQCA_EXPORT SASL : public TQObject
	{
		TQ_OBJECT
	public:
		enum Error { ErrAuth, ErrCrypt };
		enum ErrorCond {
			NoMech,
			BadProto,
			BadServ,
			BadAuth,
			NoAuthzid,
			TooWeak,
			NeedEncrypt,
			Expired,
			Disabled,
			NoUser,
			RemoteUnavail
		};
		SASL(TQObject *parent=0);
		~SASL();

		static void setAppName(const TQString &name);

		void reset();
		int errorCondition() const;

		// options
		void setAllowPlain(bool);
		void setAllowAnonymous(bool);
		void setAllowActiveVulnerable(bool);
		void setAllowDictionaryVulnerable(bool);
		void setRequireForwardSecrecy(bool);
		void setRequirePassCredentials(bool);
		void setRequireMutualAuth(bool);

		void setMinimumSSF(int);
		void setMaximumSSF(int);
		void setExternalAuthID(const TQString &authid);
		void setExternalSSF(int);

		void setLocalAddr(const TQHostAddress &addr, TQ_UINT16 port);
		void setRemoteAddr(const TQHostAddress &addr, TQ_UINT16 port);

		// initialize
		bool startClient(const TQString &service, const TQString &host, const TQStringList &mechlist, bool allowClientSendFirst=true);
		bool startServer(const TQString &service, const TQString &host, const TQString &realm, TQStringList *mechlist);

		// authentication
		void putStep(const TQByteArray &stepData);
		void putServerFirstStep(const TQString &mech);
		void putServerFirstStep(const TQString &mech, const TQByteArray &clientInit);
		void setUsername(const TQString &user);
		void setAuthzid(const TQString &auth);
		void setPassword(const TQString &pass);
		void setRealm(const TQString &realm);
		void continueAfterParams();
		void continueAfterAuthCheck();

		// security layer
		int ssf() const;
		void write(const TQByteArray &a);
		TQByteArray read();
		void writeIncoming(const TQByteArray &a);
		TQByteArray readOutgoing();

	signals:
		// for authentication
		void clientFirstStep(const TQString &mech, const TQByteArray *clientInit);
		void nextStep(const TQByteArray &stepData);
		void needParams(bool user, bool authzid, bool pass, bool realm);
		void authCheck(const TQString &user, const TQString &authzid);
		void authenticated();

		// for security layer
		void readyRead();
		void readyReadOutgoing(int plainBytes);

		// error
		void error(int);

	private slots:
		void tryAgain();

	private:
		class Private;
		Private *d;

		void handleServerFirstStep(int r);
	};
};

#endif
