/*
 * Copyright (C) 2003-2007  Justin Karneges <justin@affinix.com>
 * Copyright (C) 2004,2005  Brad Hards <bradh@frogmouth.net>
 *
 * 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
 *
 */

#include "qca_publickey.h"

#include "qcaprovider.h"

#include <QFile>
#include <QTextStream>

namespace QCA {

Provider::Context *getContext(const QString &type, const QString &provider);
Provider::Context *getContext(const QString &type, Provider *p);

bool stringToFile(const QString &fileName, const QString &content)
{
	QFile f(fileName);
	if(!f.open(QFile::WriteOnly))
		return false;
	QTextStream ts(&f);
	ts << content;
	return true;
}

bool stringFromFile(const QString &fileName, QString *s)
{
	QFile f(fileName);
	if(!f.open(QFile::ReadOnly))
		return false;
	QTextStream ts(&f);
	*s = ts.readAll();
	return true;
}

bool arrayToFile(const QString &fileName, const QByteArray &content)
{
	QFile f(fileName);
	if(!f.open(QFile::WriteOnly))
		return false;
	f.write(content.data(), content.size());
	return true;
}

bool arrayFromFile(const QString &fileName, QByteArray *a)
{
	QFile f(fileName);
	if(!f.open(QFile::ReadOnly))
		return false;
	*a = f.readAll();
	return true;
}

bool ask_passphrase(const QString &fname, void *ptr, SecureArray *answer)
{
	PasswordAsker asker;
	asker.ask(Event::StylePassphrase, fname, ptr);
	asker.waitForResponse();
	if(!asker.accepted())
		return false;
	*answer = asker.password();
	return true;
}

ProviderList allProviders()
{
	ProviderList pl = providers();
	pl += defaultProvider();
	return pl;
}

Provider *providerForName(const QString &name)
{
	ProviderList pl = allProviders();
	for(int n = 0; n < pl.count(); ++n)
	{
		if(pl[n]->name() == name)
			return pl[n];
	}
	return nullptr;
}

bool use_asker_fallback(ConvertResult r)
{
	// FIXME: we should only do this if we get ErrorPassphrase?
	//if(r == ErrorPassphrase)
	if(r != ConvertGood)
		return true;
	return false;
}

class Getter_GroupSet
{
public:
	static QList<DLGroupSet> getList(Provider *p)
	{
		QList<DLGroupSet> list;
		const DLGroupContext *c = static_cast<const DLGroupContext *>(getContext(QStringLiteral("dlgroup"), p));
		if(!c)
			return list;
		list = c->supportedGroupSets();
		delete c;
		return list;
	}
};

class Getter_PBE
{
public:
	static QList<PBEAlgorithm> getList(Provider *p)
	{
		QList<PBEAlgorithm> list;
		const PKeyContext *c = static_cast<const PKeyContext *>(getContext(QStringLiteral("pkey"), p));
		if(!c)
			return list;
		list = c->supportedPBEAlgorithms();
		delete c;
		return list;
	}
};

class Getter_Type
{
public:
	static QList<PKey::Type> getList(Provider *p)
	{
		QList<PKey::Type> list;
		const PKeyContext *c = static_cast<const PKeyContext *>(getContext(QStringLiteral("pkey"), p));
		if(!c)
			return list;
		list = c->supportedTypes();
		delete c;
		return list;
	}
};

class Getter_IOType
{
public:
	static QList<PKey::Type> getList(Provider *p)
	{
		QList<PKey::Type> list;
		const PKeyContext *c = static_cast<const PKeyContext *>(getContext(QStringLiteral("pkey"), p));
		if(!c)
			return list;
		list = c->supportedIOTypes();
		delete c;
		return list;
	}
};

template <typename I>
class Getter_PublicKey
{
public:
	// DER
	static ConvertResult fromData(PKeyContext *c, const QByteArray &in)
	{
		return c->publicFromDER(in);
	}

	// PEM
	static ConvertResult fromData(PKeyContext *c, const QString &in)
	{
		return c->publicFromPEM(in);
	}

	static PublicKey getKey(Provider *p, const I &in, const SecureArray &, ConvertResult *result)
	{
		PublicKey k;
		PKeyContext *c = static_cast<PKeyContext *>(getContext(QStringLiteral("pkey"), p));
		if(!c)
		{
			if(result)
				*result = ErrorDecode;
			return k;
		}
		ConvertResult r = fromData(c, in);
		if(result)
			*result = r;
		if(r == ConvertGood)
			k.change(c);
		else
			delete c;
		return k;
	}
};

template <typename I>
class Getter_PrivateKey
{
public:
	// DER
	static ConvertResult fromData(PKeyContext *c, const SecureArray &in, const SecureArray &passphrase)
	{
		return c->privateFromDER(in, passphrase);
	}

	// PEM
	static ConvertResult fromData(PKeyContext *c, const QString &in, const SecureArray &passphrase)
	{
		return c->privateFromPEM(in, passphrase);
	}

	static PrivateKey getKey(Provider *p, const I &in, const SecureArray &passphrase, ConvertResult *result)
	{
		PrivateKey k;
		PKeyContext *c = static_cast<PKeyContext *>(getContext(QStringLiteral("pkey"), p));
		if(!c)
		{
			if(result)
				*result = ErrorDecode;
			return k;
		}
		ConvertResult r = fromData(c, in, passphrase);
		if(result)
			*result = r;
		if(r == ConvertGood)
			k.change(c);
		else
			delete c;
		return k;
	}
};

Provider *providerForGroupSet(DLGroupSet set)
{
	ProviderList pl = allProviders();
	for(int n = 0; n < pl.count(); ++n)
	{
		if(Getter_GroupSet::getList(pl[n]).contains(set))
			return pl[n];
	}
	return nullptr;
}

Provider *providerForPBE(PBEAlgorithm alg, PKey::Type ioType, const PKeyContext *prefer = nullptr)
{
	Provider *preferProvider = nullptr;
	if(prefer)
	{
		preferProvider = prefer->provider();
		if(prefer->supportedPBEAlgorithms().contains(alg) && prefer->supportedIOTypes().contains(ioType))
			return preferProvider;
	}

	ProviderList pl = allProviders();
	for(int n = 0; n < pl.count(); ++n)
	{
		if(preferProvider && pl[n] == preferProvider)
			continue;

		if(Getter_PBE::getList(pl[n]).contains(alg) && Getter_IOType::getList(pl[n]).contains(ioType))
			return pl[n];
	}
	return nullptr;
}

Provider *providerForIOType(PKey::Type type, const PKeyContext *prefer = nullptr)
{
	Provider *preferProvider = nullptr;
	if(prefer)
	{
		preferProvider = prefer->provider();
		if(prefer && prefer->supportedIOTypes().contains(type))
			return preferProvider;
	}

	ProviderList pl = allProviders();
	for(int n = 0; n < pl.count(); ++n)
	{
		if(preferProvider && pl[n] == preferProvider)
			continue;

		if(Getter_IOType::getList(pl[n]).contains(type))
			return pl[n];
	}
	return nullptr;
}

template <typename T, typename G>
QList<T> getList(const QString &provider)
{
	QList<T> list;

	// single
	if(!provider.isEmpty())
	{
		Provider *p = providerForName(provider);
		if(p)
			list = G::getList(p);
	}
	// all
	else
	{
		ProviderList pl = allProviders();
		for(int n = 0; n < pl.count(); ++n)
		{
			const QList<T> other = G::getList(pl[n]);
			for(int k = 0; k < other.count(); ++k)
			{
				// only add what we don't have in the list
				if(!list.contains(other[k]))
					list += other[k];
			}
		}
	}

	return list;
}

template <typename T, typename G, typename I>
T getKey(const QString &provider, const I &in, const SecureArray &passphrase, ConvertResult *result)
{
	T k;

	// single
	if(!provider.isEmpty())
	{
		Provider *p = providerForName(provider);
		if(!p)
			return k;
		k = G::getKey(p, in, passphrase, result);
	}
	// all
	else
	{
		ProviderList pl = allProviders();
		for(int n = 0; n < pl.count(); ++n)
		{
			ConvertResult r;
			k = G::getKey(pl[n], in, passphrase, &r);
			if(result)
				*result = r;
			if(!k.isNull())
				break;
			if(r == ErrorPassphrase) // don't loop if we get this
				break;
		}
	}

	return k;
}

PBEAlgorithm get_pbe_default()
{
	return PBES2_TripleDES_SHA1;
}

static PrivateKey get_privatekey_der(const SecureArray &der, const QString &fileName, void *ptr, const SecureArray &passphrase, ConvertResult *result, const QString &provider)
{
	PrivateKey out;
	ConvertResult r;
	out = getKey<PrivateKey, Getter_PrivateKey<SecureArray>, SecureArray>(provider, der, passphrase, &r);

	// error converting without passphrase?  maybe a passphrase is needed
	if(use_asker_fallback(r) && passphrase.isEmpty())
	{
		SecureArray pass;
		if(ask_passphrase(fileName, ptr, &pass))
			out = getKey<PrivateKey, Getter_PrivateKey<SecureArray>, SecureArray>(provider, der, pass, &r);
	}
	if(result)
		*result = r;
	return out;
}

static PrivateKey get_privatekey_pem(const QString &pem, const QString &fileName, void *ptr, const SecureArray &passphrase, ConvertResult *result, const QString &provider)
{
	PrivateKey out;
	ConvertResult r;
	out = getKey<PrivateKey, Getter_PrivateKey<QString>, QString>(provider, pem, passphrase, &r);

	// error converting without passphrase?  maybe a passphrase is needed
	if(use_asker_fallback(r) && passphrase.isEmpty())
	{
		SecureArray pass;
		if(ask_passphrase(fileName, ptr, &pass))
			out = getKey<PrivateKey, Getter_PrivateKey<QString>, QString>(provider, pem, pass, &r);
	}
	if(result)
		*result = r;
	return out;
}

//----------------------------------------------------------------------------
// Global
//----------------------------------------------------------------------------

// adapted from Botan
static const unsigned char pkcs_sha1[] =
{
	0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2B, 0x0E, 0x03, 0x02,
	0x1A, 0x05, 0x00, 0x04, 0x14
};

static const unsigned char pkcs_md5[] =
{
	0x30, 0x20, 0x30, 0x0C, 0x06, 0x08, 0x2A, 0x86, 0x48, 0x86,
	0xF7, 0x0D, 0x02, 0x05, 0x05, 0x00, 0x04, 0x10
};

static const unsigned char pkcs_md2[] =
{
	0x30, 0x20, 0x30, 0x0C, 0x06, 0x08, 0x2A, 0x86, 0x48, 0x86,
	0xF7, 0x0D, 0x02, 0x02, 0x05, 0x00, 0x04, 0x10
};

static const unsigned char pkcs_ripemd160[] =
{
	0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2B, 0x24, 0x03, 0x02,
	0x01, 0x05, 0x00, 0x04, 0x14
};

QByteArray get_hash_id(const QString &name)
{
	if(name == QLatin1String("sha1"))
		return QByteArray::fromRawData((const char *)pkcs_sha1, sizeof(pkcs_sha1));
	else if(name == QLatin1String("md5"))
		return QByteArray::fromRawData((const char *)pkcs_md5, sizeof(pkcs_md5));
	else if(name == QLatin1String("md2"))
		return QByteArray::fromRawData((const char *)pkcs_md2, sizeof(pkcs_md2));
	else if(name == QLatin1String("ripemd160"))
		return QByteArray::fromRawData((const char *)pkcs_ripemd160, sizeof(pkcs_ripemd160));
	else
		return QByteArray();
}

QByteArray emsa3Encode(const QString &hashName, const QByteArray &digest, int size)
{
	const QByteArray hash_id = get_hash_id(hashName);
	if(hash_id.isEmpty())
		return QByteArray();

	// logic adapted from Botan
	const int basesize = hash_id.size() + digest.size() + 2;
	if(size == -1)
		size = basesize + 1; // default to 1-byte pad
	int padlen = size - basesize;
	if(padlen < 1)
		return QByteArray();

	QByteArray out(size, (char)0xff); // pad with 0xff
	out[0] = 0x01;
	out[padlen + 1] = 0x00;
	int at = padlen + 2;
	memcpy(out.data() + at, hash_id.data(), hash_id.size());
	at += hash_id.size();
	memcpy(out.data() + at, digest.data(), digest.size());
	return out;
}

//----------------------------------------------------------------------------
// DLGroup
//----------------------------------------------------------------------------
class DLGroup::Private
{
public:
	BigInteger p, q, g;

	Private(const BigInteger &p1, const BigInteger &q1, const BigInteger &g1)
	:p(p1), q(q1), g(g1)
	{
	}
};

DLGroup::DLGroup()
{
	d = nullptr;
}

DLGroup::DLGroup(const BigInteger &p, const BigInteger &q, const BigInteger &g)
{
	d = new Private(p, q, g);
}

DLGroup::DLGroup(const BigInteger &p, const BigInteger &g)
{
	d = new Private(p, 0, g);
}

DLGroup::DLGroup(const DLGroup &from)
{
	d = nullptr;
	*this = from;
}

DLGroup::~DLGroup()
{
	delete d;
}

DLGroup & DLGroup::operator=(const DLGroup &from)
{
	delete d;
	d = nullptr;

	if(from.d)
		d = new Private(*from.d);

	return *this;
}

QList<DLGroupSet> DLGroup::supportedGroupSets(const QString &provider)
{
	return getList<DLGroupSet, Getter_GroupSet>(provider);
}

bool DLGroup::isNull() const
{
	return (d ? false: true);
}

BigInteger DLGroup::p() const
{
	Q_ASSERT(d);
	return d->p;
}

BigInteger DLGroup::q() const
{
	Q_ASSERT(d);
	return d->q;
}

BigInteger DLGroup::g() const
{
	Q_ASSERT(d);
	return d->g;
}

//----------------------------------------------------------------------------
// PKey
//----------------------------------------------------------------------------
class PKey::Private
{
public:
};

PKey::PKey()
{
	d = new Private;
}

PKey::PKey(const QString &type, const QString &provider)
:Algorithm(type, provider)
{
	d = new Private;
}

PKey::PKey(const PKey &from)
:Algorithm(from)
{
	d = new Private;
	*this = from;
}

PKey::~PKey()
{
	delete d;
}

PKey & PKey::operator=(const PKey &from)
{
	Algorithm::operator=(from);
	*d = *from.d;
	return *this;
}

void PKey::set(const PKey &k)
{
	*this = k;
}

void PKey::assignToPublic(PKey *dest) const
{
	dest->set(*this);

	// converting private to public
	if(dest->isPrivate())
		static_cast<PKeyContext *>(dest->context())->key()->convertToPublic();
}

void PKey::assignToPrivate(PKey *dest) const
{
	dest->set(*this);
}

QList<PKey::Type> PKey::supportedTypes(const QString &provider)
{
	return getList<Type, Getter_Type>(provider);
}

QList<PKey::Type> PKey::supportedIOTypes(const QString &provider)
{
	return getList<Type, Getter_IOType>(provider);
}

bool PKey::isNull() const
{
	return (!context() ? true : false);
}

PKey::Type PKey::type() const
{
	if(isNull())
		return RSA; // some default so we don't explode
	return static_cast<const PKeyContext *>(context())->key()->type();
}

int PKey::bitSize() const
{
	return static_cast<const PKeyContext *>(context())->key()->bits();
}

bool PKey::isRSA() const
{
	return (type() == RSA);
}

bool PKey::isDSA() const
{
	return (type() == DSA);
}

bool PKey::isDH() const
{
	return (type() == DH);
}

bool PKey::isPublic() const
{
	if(isNull())
		return false;
	return !isPrivate();
}

bool PKey::isPrivate() const
{
	if(isNull())
		return false;
	return static_cast<const PKeyContext *>(context())->key()->isPrivate();
}

bool PKey::canExport() const
{
	return static_cast<const PKeyContext *>(context())->key()->canExport();
}

bool PKey::canKeyAgree() const
{
	return isDH();
}

PublicKey PKey::toPublicKey() const
{
	PublicKey k;
	if(!isNull())
		assignToPublic(&k);
	return k;
}

PrivateKey PKey::toPrivateKey() const
{
	PrivateKey k;
	if(!isNull() && isPrivate())
		assignToPrivate(&k);
	return k;
}

RSAPublicKey PKey::toRSAPublicKey() const
{
	RSAPublicKey k;
	if(!isNull() && isRSA())
		assignToPublic(&k);
	return k;
}

RSAPrivateKey PKey::toRSAPrivateKey() const
{
	RSAPrivateKey k;
	if(!isNull() && isRSA() && isPrivate())
		assignToPrivate(&k);
	return k;
}

DSAPublicKey PKey::toDSAPublicKey() const
{
	DSAPublicKey k;
	if(!isNull() && isDSA())
		assignToPublic(&k);
	return k;
}

DSAPrivateKey PKey::toDSAPrivateKey() const
{
	DSAPrivateKey k;
	if(!isNull() && isDSA() && isPrivate())
		assignToPrivate(&k);
	return k;
}

DHPublicKey PKey::toDHPublicKey() const
{
	DHPublicKey k;
	if(!isNull() && isDH())
		assignToPublic(&k);
	return k;
}

DHPrivateKey PKey::toDHPrivateKey() const
{
	DHPrivateKey k;
	if(!isNull() && isDH() && isPrivate())
		assignToPrivate(&k);
	return k;
}

bool PKey::operator==(const PKey &a) const
{
	if(isNull() || a.isNull() || type() != a.type())
		return false;

	if(a.isPrivate())
		return (toPrivateKey().toDER() == a.toPrivateKey().toDER());
	else
		return (toPublicKey().toDER() == a.toPublicKey().toDER());
}

bool PKey::operator!=(const PKey &a) const
{
	return !(*this == a);
}

//----------------------------------------------------------------------------
// PublicKey
//----------------------------------------------------------------------------
PublicKey::PublicKey()
{
}

PublicKey::PublicKey(const QString &type, const QString &provider)
:PKey(type, provider)
{
}

PublicKey::PublicKey(const PrivateKey &k)
{
	set(k.toPublicKey());
}

PublicKey::PublicKey(const QString &fileName)
{
	*this = fromPEMFile(fileName, nullptr, QString());
}

PublicKey::PublicKey(const PublicKey &from)
:PKey(from)
{
}

PublicKey::~PublicKey()
{
}

PublicKey & PublicKey::operator=(const PublicKey &from)
{
	PKey::operator=(from);
	return *this;
}

RSAPublicKey PublicKey::toRSA() const
{
	return toRSAPublicKey();
}

DSAPublicKey PublicKey::toDSA() const
{
	return toDSAPublicKey();
}

DHPublicKey PublicKey::toDH() const
{
	return toDHPublicKey();
}

bool PublicKey::canEncrypt() const
{
	return isRSA();
}

bool PublicKey::canDecrypt() const
{
	return isRSA();
}

bool PublicKey::canVerify() const
{
	return (isRSA() || isDSA());
}

int PublicKey::maximumEncryptSize(EncryptionAlgorithm alg) const
{
	const PKeyContext* ctx = qobject_cast<const PKeyContext *>(context());
	if (ctx)
		return ctx->key()->maximumEncryptSize(alg);
	else
		return -1;
}

SecureArray PublicKey::encrypt(const SecureArray &a, EncryptionAlgorithm alg)
{
	PKeyContext* ctx = qobject_cast<PKeyContext *>(context());
	if (ctx)
		return ctx->key()->encrypt(a, alg);
	else
		return SecureArray();
}

bool PublicKey::decrypt(const SecureArray &in, SecureArray *out, EncryptionAlgorithm alg)
{
        PKeyContext* ctx = qobject_cast<PKeyContext *>(context());
	if (ctx)
		return ctx->key()->decrypt(in, out, alg);
	else
		return false;
}

void PublicKey::startVerify(SignatureAlgorithm alg, SignatureFormat format)
{
	if(isDSA() && format == DefaultFormat)
		format = IEEE_1363;
	PKeyContext* ctx = qobject_cast<PKeyContext *>(context());
	if(ctx)
		ctx->key()->startVerify(alg, format);

}

void PublicKey::update(const MemoryRegion &a)
{
	PKeyContext* ctx = qobject_cast<PKeyContext *>(context());
	if(ctx) {
		ctx->key()->update(a);
	}
}

bool PublicKey::validSignature(const QByteArray &sig)
{
	PKeyContext* ctx = qobject_cast<PKeyContext *>(context());
	if(ctx)
		return ctx->key()->endVerify(sig);
	return false;
}

bool PublicKey::verifyMessage(const MemoryRegion &a, const QByteArray &sig, SignatureAlgorithm alg, SignatureFormat format)
{
	startVerify(alg, format);
	update(a);
	return validSignature(sig);
}

QByteArray PublicKey::toDER() const
{
	QByteArray out;
	const PKeyContext *cur = static_cast<const PKeyContext *>(context());
	Provider *p = providerForIOType(type(), cur);
	if(!p)
		return out;
	if(cur->provider() == p)
	{
		out = cur->publicToDER();
	}
	else
	{
		PKeyContext *pk = static_cast<PKeyContext *>(getContext(QStringLiteral("pkey"), p));
		if(pk && pk->importKey(cur->key()))
			out = pk->publicToDER();
		delete pk;
	}
	return out;
}

QString PublicKey::toPEM() const
{
	QString out;
	const PKeyContext *cur = static_cast<const PKeyContext *>(context());
	if(!cur)
		return out;
	Provider *p = providerForIOType(type(), cur);
	if(!p)
		return out;
	if(cur->provider() == p)
	{
		out = cur->publicToPEM();
	}
	else
	{
		PKeyContext *pk = static_cast<PKeyContext *>(getContext(QStringLiteral("pkey"), p));
		if(pk && pk->importKey(cur->key()))
			out = pk->publicToPEM();
		delete pk;
	}
	return out;
}

bool PublicKey::toPEMFile(const QString &fileName) const
{
	return stringToFile(fileName, toPEM());
}

PublicKey PublicKey::fromDER(const QByteArray &a, ConvertResult *result, const QString &provider)
{
	return getKey<PublicKey, Getter_PublicKey<QByteArray>, QByteArray>(provider, a, SecureArray(), result);
}

PublicKey PublicKey::fromPEM(const QString &s, ConvertResult *result, const QString &provider)
{
	return getKey<PublicKey, Getter_PublicKey<QString>, QString>(provider, s, SecureArray(), result);
}

PublicKey PublicKey::fromPEMFile(const QString &fileName, ConvertResult *result, const QString &provider)
{
	QString pem;
	if(!stringFromFile(fileName, &pem))
	{
		if(result)
			*result = ErrorFile;
		return PublicKey();
	}
	return fromPEM(pem, result, provider);
}

//----------------------------------------------------------------------------
// PrivateKey
//----------------------------------------------------------------------------
PrivateKey::PrivateKey()
{
}

PrivateKey::PrivateKey(const QString &type, const QString &provider)
:PKey(type, provider)
{
}

PrivateKey::PrivateKey(const QString &fileName, const SecureArray &passphrase)
{
	*this = fromPEMFile(fileName, passphrase, nullptr, QString());
}

PrivateKey::PrivateKey(const PrivateKey &from)
:PKey(from)
{
}

PrivateKey::~PrivateKey()
{
}

PrivateKey & PrivateKey::operator=(const PrivateKey &from)
{
	PKey::operator=(from);
	return *this;
}

RSAPrivateKey PrivateKey::toRSA() const
{
	return toRSAPrivateKey();
}

DSAPrivateKey PrivateKey::toDSA() const
{
	return toDSAPrivateKey();
}

DHPrivateKey PrivateKey::toDH() const
{
	return toDHPrivateKey();
}

bool PrivateKey::canDecrypt() const
{
	return isRSA();
}

bool PrivateKey::canEncrypt() const
{
	return isRSA();
}

bool PrivateKey::canSign() const
{
	return (isRSA() || isDSA());
}

int PrivateKey::maximumEncryptSize(EncryptionAlgorithm alg) const
{
    return static_cast<const PKeyContext *>(context())->key()->maximumEncryptSize(alg);
}

bool PrivateKey::decrypt(const SecureArray &in, SecureArray *out, EncryptionAlgorithm alg)
{
	return static_cast<PKeyContext *>(context())->key()->decrypt(in, out, alg);
}

SecureArray PrivateKey::encrypt(const SecureArray &a, EncryptionAlgorithm alg)
{
	return static_cast<PKeyContext *>(context())->key()->encrypt(a, alg);
}

void PrivateKey::startSign(SignatureAlgorithm alg, SignatureFormat format)
{
	if(isDSA() && format == DefaultFormat)
		format = IEEE_1363;
	static_cast<PKeyContext *>(context())->key()->startSign(alg, format);
}

void PrivateKey::update(const MemoryRegion &a)
{
	static_cast<PKeyContext *>(context())->key()->update(a);
}

QByteArray PrivateKey::signature()
{
	return static_cast<PKeyContext *>(context())->key()->endSign();
}

QByteArray PrivateKey::signMessage(const MemoryRegion &a, SignatureAlgorithm alg, SignatureFormat format)
{
	startSign(alg, format);
	update(a);
	return signature();
}

SymmetricKey PrivateKey::deriveKey(const PublicKey &theirs)
{
	const PKeyContext *theirContext = static_cast<const PKeyContext *>(theirs.context());
	return static_cast<PKeyContext *>(context())->key()->deriveKey(*(theirContext->key()));
}

QList<PBEAlgorithm> PrivateKey::supportedPBEAlgorithms(const QString &provider)
{
	return getList<PBEAlgorithm, Getter_PBE>(provider);
}

SecureArray PrivateKey::toDER(const SecureArray &passphrase, PBEAlgorithm pbe) const
{
	SecureArray out;
	if(pbe == PBEDefault)
		pbe = get_pbe_default();
	const PKeyContext *cur = static_cast<const PKeyContext *>(context());
	Provider *p = providerForPBE(pbe, type(), cur);
	if(!p)
		return out;
	if(cur->provider() == p)
	{
		out = cur->privateToDER(passphrase, pbe);
	}
	else
	{
		PKeyContext *pk = static_cast<PKeyContext *>(getContext(QStringLiteral("pkey"), p));
		if(pk->importKey(cur->key()))
			out = pk->privateToDER(passphrase, pbe);
		delete pk;
	}
	return out;
}

QString PrivateKey::toPEM(const SecureArray &passphrase, PBEAlgorithm pbe) const
{
	QString out;
	if(pbe == PBEDefault)
		pbe = get_pbe_default();
	const PKeyContext *cur = static_cast<const PKeyContext *>(context());
	Provider *p = providerForPBE(pbe, type(), cur);
	if(!p)
		return out;
	if(cur->provider() == p)
	{
		out = cur->privateToPEM(passphrase, pbe);
	}
	else
	{
		PKeyContext *pk = static_cast<PKeyContext *>(getContext(QStringLiteral("pkey"), p));
		if(pk->importKey(cur->key()))
			out = pk->privateToPEM(passphrase, pbe);
		delete pk;
	}
	return out;
}

bool PrivateKey::toPEMFile(const QString &fileName, const SecureArray &passphrase, PBEAlgorithm pbe) const
{
	return stringToFile(fileName, toPEM(passphrase, pbe));
}

PrivateKey PrivateKey::fromDER(const SecureArray &a, const SecureArray &passphrase, ConvertResult *result, const QString &provider)
{
	return get_privatekey_der(a, QString(), (void *)&a, passphrase, result, provider);
}

PrivateKey PrivateKey::fromPEM(const QString &s, const SecureArray &passphrase, ConvertResult *result, const QString &provider)
{
	return get_privatekey_pem(s, QString(), (void *)&s, passphrase, result, provider);
}

PrivateKey PrivateKey::fromPEMFile(const QString &fileName, const SecureArray &passphrase, ConvertResult *result, const QString &provider)
{
	QString pem;
	if(!stringFromFile(fileName, &pem))
	{
		if(result)
			*result = ErrorFile;
		return PrivateKey();
	}
	return get_privatekey_pem(pem, fileName, nullptr, passphrase, result, provider);
}

//----------------------------------------------------------------------------
// KeyGenerator
//----------------------------------------------------------------------------
class KeyGenerator::Private : public QObject
{
	Q_OBJECT
public:
	KeyGenerator *parent;
	bool blocking, wasBlocking;
	PrivateKey key;
	DLGroup group;

	PKeyBase *k;
	PKeyContext *dest;
	DLGroupContext *dc;

	Private(KeyGenerator *_parent) : QObject(_parent), parent(_parent)
	{
		k = nullptr;
		dest = nullptr;
		dc = nullptr;
	}

	~Private() override
	{
		delete k;
		delete dest;
		delete dc;
	}

public Q_SLOTS:
	void done()
	{
		if(!k->isNull())
		{
			if(!wasBlocking)
			{
				k->setParent(nullptr);
				k->moveToThread(nullptr);
			}
			dest->setKey(k);
			k = nullptr;

			key.change(dest);
			dest = nullptr;
		}
		else
		{
			delete k;
			k = nullptr;
			delete dest;
			dest = nullptr;
		}

		if(!wasBlocking)
			emit parent->finished();
	}

	void done_group()
	{
		if(!dc->isNull())
		{
			BigInteger p, q, g;
			dc->getResult(&p, &q, &g);
			group = DLGroup(p, q, g);
		}
		delete dc;
		dc = nullptr;

		if(!wasBlocking)
			emit parent->finished();
	}
};

KeyGenerator::KeyGenerator(QObject *parent)
:QObject(parent)
{
	d = new Private(this);
	d->blocking = true;
}

KeyGenerator::~KeyGenerator()
{
	delete d;
}

bool KeyGenerator::blockingEnabled() const
{
	return d->blocking;
}

void KeyGenerator::setBlockingEnabled(bool b)
{
	d->blocking = b;
}

bool KeyGenerator::isBusy() const
{
	return (d->k ? true: false);
}

PrivateKey KeyGenerator::createRSA(int bits, int exp, const QString &provider)
{
	if(isBusy())
		return PrivateKey();

	d->key = PrivateKey();
	d->wasBlocking = d->blocking;
	d->k = static_cast<RSAContext *>(getContext(QStringLiteral("rsa"), provider));
	if (!d->k)
		return PrivateKey();
	d->dest = static_cast<PKeyContext *>(getContext(QStringLiteral("pkey"), d->k->provider()));

	if(!d->blocking)
	{
		d->k->moveToThread(thread());
		d->k->setParent(d);
		connect(d->k, &RSAContext::finished, d, &Private::done);
		static_cast<RSAContext *>(d->k)->createPrivate(bits, exp, false);
	}
	else
	{
		static_cast<RSAContext *>(d->k)->createPrivate(bits, exp, true);
		d->done();
	}

	return d->key;
}

PrivateKey KeyGenerator::createDSA(const DLGroup &domain, const QString &provider)
{
	if(isBusy())
		return PrivateKey();

	d->key = PrivateKey();
	d->wasBlocking = d->blocking;
	d->k = static_cast<DSAContext *>(getContext(QStringLiteral("dsa"), provider));
	d->dest = static_cast<PKeyContext *>(getContext(QStringLiteral("pkey"), d->k->provider()));

	if(!d->blocking)
	{
		d->k->moveToThread(thread());
		d->k->setParent(d);
		connect(d->k, &DSAContext::finished, d, &Private::done);
		static_cast<DSAContext *>(d->k)->createPrivate(domain, false);
	}
	else
	{
		static_cast<DSAContext *>(d->k)->createPrivate(domain, true);
		d->done();
	}

	return d->key;
}

PrivateKey KeyGenerator::createDH(const DLGroup &domain, const QString &provider)
{
	if(isBusy())
		return PrivateKey();

	d->key = PrivateKey();
	d->wasBlocking = d->blocking;
	d->k = static_cast<DHContext *>(getContext(QStringLiteral("dh"), provider));
	d->dest = static_cast<PKeyContext *>(getContext(QStringLiteral("pkey"), d->k->provider()));

	if(!d->blocking)
	{
		d->k->moveToThread(thread());
		d->k->setParent(d);
		connect(d->k, &DHContext::finished, d, &Private::done);
		static_cast<DHContext *>(d->k)->createPrivate(domain, false);
	}
	else
	{
		static_cast<DHContext *>(d->k)->createPrivate(domain, true);
		d->done();
	}

	return d->key;
}

PrivateKey KeyGenerator::key() const
{
	return d->key;
}

DLGroup KeyGenerator::createDLGroup(QCA::DLGroupSet set, const QString &provider)
{
	if(isBusy())
		return DLGroup();

	Provider *p;
	if(!provider.isEmpty())
		p = providerForName(provider);
	else
		p = providerForGroupSet(set);

	d->dc = static_cast<DLGroupContext *>(getContext(QStringLiteral("dlgroup"), p));
	d->group = DLGroup();

	if (d->dc)
	{
		d->wasBlocking = d->blocking;
		if(!d->blocking)
		{
			connect(d->dc, &DLGroupContext ::finished, d, &Private::done_group);
			d->dc->fetchGroup(set, false);
		}
		else
		{
			d->dc->fetchGroup(set, true);
			d->done_group();
		}
	}

	return d->group;
}

DLGroup KeyGenerator::dlGroup() const
{
	return d->group;
}

//----------------------------------------------------------------------------
// RSAPublicKey
//----------------------------------------------------------------------------
RSAPublicKey::RSAPublicKey()
{
}

RSAPublicKey::RSAPublicKey(const BigInteger &n, const BigInteger &e, const QString &provider)
{
	RSAContext *k = static_cast<RSAContext *>(getContext(QStringLiteral("rsa"), provider));
	k->createPublic(n, e);
	PKeyContext *c = static_cast<PKeyContext *>(getContext(QStringLiteral("pkey"), k->provider()));
	c->setKey(k);
	change(c);
}

RSAPublicKey::RSAPublicKey(const RSAPrivateKey &k)
:PublicKey(k)
{
}

BigInteger RSAPublicKey::n() const
{
	return static_cast<const RSAContext *>(static_cast<const PKeyContext *>(context())->key())->n();
}

BigInteger RSAPublicKey::e() const
{
	return static_cast<const RSAContext *>(static_cast<const PKeyContext *>(context())->key())->e();
}

//----------------------------------------------------------------------------
// RSAPrivateKey
//----------------------------------------------------------------------------
RSAPrivateKey::RSAPrivateKey()
{
}

RSAPrivateKey::RSAPrivateKey(const BigInteger &n, const BigInteger &e, const BigInteger &p, const BigInteger &q, const BigInteger &d, const QString &provider)
{
	RSAContext *k = static_cast<RSAContext *>(getContext(QStringLiteral("rsa"), provider));
	k->createPrivate(n, e, p, q, d);
	PKeyContext *c = static_cast<PKeyContext *>(getContext(QStringLiteral("pkey"), k->provider()));
	c->setKey(k);
	change(c);
}

BigInteger RSAPrivateKey::n() const
{
	return static_cast<const RSAContext *>(static_cast<const PKeyContext *>(context())->key())->n();
}

BigInteger RSAPrivateKey::e() const
{
	return static_cast<const RSAContext *>(static_cast<const PKeyContext *>(context())->key())->e();
}

BigInteger RSAPrivateKey::p() const
{
	return static_cast<const RSAContext *>(static_cast<const PKeyContext *>(context())->key())->p();
}

BigInteger RSAPrivateKey::q() const
{
	return static_cast<const RSAContext *>(static_cast<const PKeyContext *>(context())->key())->q();
}

BigInteger RSAPrivateKey::d() const
{
	return static_cast<const RSAContext *>(static_cast<const PKeyContext *>(context())->key())->d();
}

//----------------------------------------------------------------------------
// DSAPublicKey
//----------------------------------------------------------------------------
DSAPublicKey::DSAPublicKey()
{
}

DSAPublicKey::DSAPublicKey(const DLGroup &domain, const BigInteger &y, const QString &provider)
{
	DSAContext *k = static_cast<DSAContext *>(getContext(QStringLiteral("dsa"), provider));
	k->createPublic(domain, y);
	PKeyContext *c = static_cast<PKeyContext *>(getContext(QStringLiteral("pkey"), k->provider()));
	c->setKey(k);
	change(c);
}

DSAPublicKey::DSAPublicKey(const DSAPrivateKey &k)
:PublicKey(k)
{
}

DLGroup DSAPublicKey::domain() const
{
	return static_cast<const DSAContext *>(static_cast<const PKeyContext *>(context())->key())->domain();
}

BigInteger DSAPublicKey::y() const
{
	return static_cast<const DSAContext *>(static_cast<const PKeyContext *>(context())->key())->y();
}

//----------------------------------------------------------------------------
// DSAPrivateKey
//----------------------------------------------------------------------------
DSAPrivateKey::DSAPrivateKey()
{
}

DSAPrivateKey::DSAPrivateKey(const DLGroup &domain, const BigInteger &y, const BigInteger &x, const QString &provider)
{
	DSAContext *k = static_cast<DSAContext *>(getContext(QStringLiteral("dsa"), provider));
	k->createPrivate(domain, y, x);
	PKeyContext *c = static_cast<PKeyContext *>(getContext(QStringLiteral("pkey"), k->provider()));
	c->setKey(k);
	change(c);
}

DLGroup DSAPrivateKey::domain() const
{
	return static_cast<const DSAContext *>(static_cast<const PKeyContext *>(context())->key())->domain();
}

BigInteger DSAPrivateKey::y() const
{
	return static_cast<const DSAContext *>(static_cast<const PKeyContext *>(context())->key())->y();
}

BigInteger DSAPrivateKey::x() const
{
	return static_cast<const DSAContext *>(static_cast<const PKeyContext *>(context())->key())->x();
}

//----------------------------------------------------------------------------
// DHPublicKey
//----------------------------------------------------------------------------
DHPublicKey::DHPublicKey()
{
}

DHPublicKey::DHPublicKey(const DLGroup &domain, const BigInteger &y, const QString &provider)
{
	DHContext *k = static_cast<DHContext *>(getContext(QStringLiteral("dh"), provider));
	k->createPublic(domain, y);
	PKeyContext *c = static_cast<PKeyContext *>(getContext(QStringLiteral("pkey"), k->provider()));
	c->setKey(k);
	change(c);
}

DHPublicKey::DHPublicKey(const DHPrivateKey &k)
:PublicKey(k)
{
}

DLGroup DHPublicKey::domain() const
{
	return static_cast<const DHContext *>(static_cast<const PKeyContext *>(context())->key())->domain();
}

BigInteger DHPublicKey::y() const
{
	return static_cast<const DHContext *>(static_cast<const PKeyContext *>(context())->key())->y();
}

//----------------------------------------------------------------------------
// DHPrivateKey
//----------------------------------------------------------------------------
DHPrivateKey::DHPrivateKey()
{
}

DHPrivateKey::DHPrivateKey(const DLGroup &domain, const BigInteger &y, const BigInteger &x, const QString &provider)
{
	DHContext *k = static_cast<DHContext *>(getContext(QStringLiteral("dh"), provider));
	k->createPrivate(domain, y, x);
	PKeyContext *c = static_cast<PKeyContext *>(getContext(QStringLiteral("pkey"), k->provider()));
	c->setKey(k);
	change(c);
}

DLGroup DHPrivateKey::domain() const
{
	return static_cast<const DHContext *>(static_cast<const PKeyContext *>(context())->key())->domain();
}

BigInteger DHPrivateKey::y() const
{
	return static_cast<const DHContext *>(static_cast<const PKeyContext *>(context())->key())->y();
}

BigInteger DHPrivateKey::x() const
{
	return static_cast<const DHContext *>(static_cast<const PKeyContext *>(context())->key())->x();
}

}

#include "qca_publickey.moc"
