// Common, group Crypto
// Copyright Alexander Liss

#include "cryptobase.h"
#include "cryptolayer.h"

	// CryptoHash

CryptoHash::~CryptoHash(){delete imp;}


int CryptoHash::next(const CommBuffer& s, ReadControl& c)const
{
	if(!imp) return 1;

	return imp->next(s,c);
}

int CryptoHash::get(CommBuffer& z)const
{
	if(!imp) return 1;

	return imp->get(z);
}

int CryptoHash::hash(CommBuffer& z,const CommBuffer& s, ReadControl& c)const
{
	int g=0;
	
			g=next(s,c);
	if(!g)	g=get(z);

	return g;
}

	// CryptoSymmetric

CryptoSymmetric::~CryptoSymmetric(){delete imp;}


int CryptoSymmetric::block_size()const
{
	if(!imp) return 1;

	return imp->block_size();
}


int CryptoSymmetric::encrypt_block(CommBuffer& d,const CommBuffer& s, ReadControl& c)const
{
	if(!imp) return 1;

	return imp->encrypt_block(d,s,c);
}

int CryptoSymmetric::decrypt_block(CommBuffer& d,const CommBuffer& s, ReadControl& c)const
{
	if(!imp) return 1;

	return imp->decrypt_block(d,s,c);
}


int CryptoSymmetric::start_encrypt(const CommBuffer& iv,ReadControl& c)const
{
	if(!imp) return 1;

	return imp->start_encrypt(iv,c);
}

int CryptoSymmetric::next_encrypt(CommBuffer& d,const CommBuffer& s, ReadControl& c,bool last)const
{
	if(!imp) return 1;

	return imp->next_encrypt(d,s,c,last);
}


int CryptoSymmetric::start_decrypt(CommBuffer& iv,const CommBuffer& s, ReadControl& c)const
{
	if(!imp) return 1;

	return imp->start_decrypt(iv,s,c);
}

int CryptoSymmetric::next_decrypt(CommBuffer& d,const CommBuffer& s, ReadControl& c,bool last)const
{
	if(!imp) return 1;

	return imp->next_decrypt(d,s,c,last);
}


	// CryptoPKS

CryptoPKS::~CryptoPKS(){delete imp;}

int CryptoPKS::encrypt_block_bits()const
{ 
	if(!imp) return 0;
	ReadControl c; 
	return imp->encrypt_block_bits(pub_key,c);
}

int CryptoPKS::decrypt_block_bits()const
{ 
	if(!imp) return 0;
	ReadControl c; 
	return imp->decrypt_block_bits(pub_key,c);
}


int CryptoPKS::encrypt_public(CommBuffer& d,const CommBuffer& s,ReadControl& c)const
{ 
	if(!imp) return 1;

	ReadControl ck;
	return imp->encrypt_public(d,s,c,pub_key,ck); 
}

int CryptoPKS::decrypt_public(CommBuffer& d,const CommBuffer& s,ReadControl& c)const
{ 
	if(!imp) return 1;

	ReadControl ck;
	return imp->decrypt_public(d,s,c,pub_key,ck); 
}


int CryptoPKS::encrypt_private(CommBuffer& d,const CommBuffer& s,ReadControl& c)const
{
	if(!imp || !storage) return 1;

	CommBuffer key;
	int g=0,t=0;

	ReadControl cck,ck;
	g=storage->get(t,key,pub_key,ck,1);
	if(!g) g=imp->encrypt_private(d,s,c,key,cck);

	return g;
}

int CryptoPKS::decrypt_private(CommBuffer& d,const CommBuffer& s,ReadControl& c)const
{
	if(!imp || !storage) return 1;

	CommBuffer key;
	int g=0,t=0;

	ReadControl cck,ck;
	g=storage->get(t,key,pub_key,ck,1);
	if(!g) g=imp->decrypt_private(d,s,c,key,cck);

	return g;
}


	// CryptoSign

CryptoSign::~CryptoSign(){delete imp;}


int CryptoSign::sign(CommBuffer& d,const CommBuffer& s,ReadControl& c)const
{
	if(!imp || !storage || !pub_key.data_size) return 1;

	CommBuffer key;
	int g=0,t=0;

	ReadControl cck,ck;
	g=storage->get(t,key,pub_key,ck,1);
	if(!g) g=imp->sign(d,s,c,key,cck);

	return g;
}

int CryptoSign::verify(CommBuffer& d,const CommBuffer& s,ReadControl& c)const
{
	if(!imp) return 1;

	ReadControl ck;
	return imp->verify(d,s,c,pub_key,ck);
}


	// CryptoLayer

CryptoLayer::CryptoLayer(const CryptoLayerConfig& z):
storage(0),
crypto_random(0),
config(z)
{
	initialize();	
}

CryptoLayer::~CryptoLayer()
{
	delete storage;
	delete crypto_random;
}

int CryptoLayer::seed(const CommBuffer& s,ReadControl& c)
{
	if(!crypto_random) return 1;

	return crypto_random->seed(s,c);
}


int CryptoLayer::initialize()
{

	storage = SecretsStorage::new_object(config.storage);
	if(!storage) return 1;

	crypto_random = CryptoRandom::new_object(config.random);
	if(!crypto_random) return 2;
	
	return 0;
}


int CryptoLayer::generate_symmetric_key(unsigned int& h,const CommBuffer& param, ReadControl& c,int type)const
{
	if(!crypto_random || !storage) return 1;

	SymmetricKey* symmetric_key = SymmetricKey::new_object(type,config.symmetric_key);
	if(!symmetric_key) return 2;

	symmetric_key->set(*crypto_random);

	CommBuffer key;
	ReadControl c1;

	int g=symmetric_key->generate(key,param,c);
	if(!g) g=storage->put(h,key,c1,type,1);

	delete symmetric_key;

	return g;
}

int CryptoLayer::generate_pks_key(CommBuffer& h,const CommBuffer& param, ReadControl& c,int type)const
{
	if(!crypto_random) return 1;

	PKSKey* pks_key = PKSKey::new_object(type,config.pks_key);
	if(!pks_key) return 2;

	pks_key->set(*crypto_random);

	CommBuffer priv_key;
	ReadControl c0,c1;

	int g=pks_key->generate(h,priv_key,param,c);
	if(!g) g=storage->put(priv_key,c0,h,c1,type,1);

	delete pks_key;

	return g;
}

int CryptoLayer::generate_mac_key(unsigned int& h,const CommBuffer& param, ReadControl& c,int type)const
{
	if(!crypto_random) return 1;

	MACKey* mac_key = MACKey::new_object(type,config.mac_key);
	if(!mac_key) return 2;

	mac_key->set(*crypto_random);

	CommBuffer key;
	ReadControl c1;

	int g=mac_key->generate(key,param,c);
	if(!g) g=storage->put(h,key,c1,type,1);


	delete mac_key;

	return g;
}


int CryptoLayer::put_secret(unsigned int& h,const CommBuffer& s,ReadControl& c, int type)const
{
	if(!storage) return 1;

	return storage->put(h,s,c,type,0);
}

int CryptoLayer::get_secret(int& type, CommBuffer& d,unsigned int h)const
{
	if(!storage) return 1;

	return storage->get(type,d,h,0);
}

int CryptoLayer::drop_secret(unsigned int h)const
{
	if(!storage) return 1;

	return storage->drop(h,0);
}

int CryptoLayer::drop_secret(const CommBuffer& h,ReadControl& c)const
{
	if(!storage) return 1;

	return storage->drop(h,c,1);
}

int CryptoLayer::append_random(CommBuffer& d,int bytes)const
{
	if(!crypto_random) return 1;

	if(d.space_left()<bytes)
		d.reallocate(d.allocated+bytes-d.space_left());

	for(int i=0;i<bytes;++i)
		d.append(1,crypto_random->byte());

	return 0;
}


CryptoHash* CryptoLayer::newCryptoHash(int type)const
{
	CryptoHash* z=new CryptoHash;
	if(!z) return 0;

	z->imp=CryptoHashImp::new_object(type,config.hash);
	if(!z->imp) 
	{
		delete z;
		return 0;
	}

	return z;
}

CryptoHash* CryptoLayer::newCryptoHash(int type,const CommBuffer& key,ReadControl& c)const
{
	CryptoHash* z=new CryptoHash;
	if(!z) return 0;

	z->imp=CryptoHashImp::new_object(type,config.hash);
	if(!z->imp)
	{
		delete z;
		return 0;
	}

	int g=z->imp->set_key(key,c);
	if(!g)
	{
		delete z;
		return 0;
	}

	return z;
}

CryptoHash* CryptoLayer::newCryptoHash(int& type,unsigned int h)const
{
	if(!storage) return 0;

	CryptoHash* z=new CryptoHash;
	if(!z) return 0;
	
	CommBuffer key;
	ReadControl c;

	int g=storage->get(type,key,h,1);
	if(!g)
	{
		delete z;
		return 0;
	}

	z->imp=CryptoHashImp::new_object(type,config.hash);
	if(!z->imp)
	{
		delete z;
		return 0;
	}

	g=z->imp->set_key(key,c);
	if(!g)
	{
		delete z;
		return 0;
	}

	return z;
}

CryptoSymmetric* CryptoLayer::newCryptoSymmetric(int type,const CommBuffer& key,ReadControl& c)const
{
	CryptoSymmetric* z=new CryptoSymmetric;
	if(!z) return 0;

	z->imp=CryptoSymmetricImp::new_object(type,config.symmetric);
	if(!z->imp)
	{
		delete z;
		return 0;
	}

	int g=z->imp->set_key(key,c);
	if(!g)
	{
		delete z;
		return 0;
	}

	return z;	
}

CryptoSymmetric* CryptoLayer::newCryptoSymmetric(int& type,unsigned int h)const
{
	CryptoSymmetric* z=new CryptoSymmetric;
	if(!z) return 0;

	CommBuffer key;
	ReadControl c;

	int g=storage->get(type,key,h,1);
	if(!g)
	{
		delete z;
		return 0;
	}

	z->imp=CryptoSymmetricImp::new_object(type,config.symmetric);
	if(!z->imp)
	{
		delete z;
		return 0;
	}

	g=z->imp->set_key(key,c);
	if(!g)
	{
		delete z;
		return 0;
	}

	return z;	
}

CryptoPKS* CryptoLayer::newCryptoPKS(int& type,const CommBuffer& pub_key,ReadControl& c)const
{
	if(!storage) return 0;

	CryptoPKS* z=new CryptoPKS;
	if(!z) return 0;

	z->storage=storage;
	z->pub_key.set(pub_key,c);
	
	z->imp=CryptoPKSImp::new_object(type,config.pks);
	if(!z->imp)
	{
		delete z;
		return 0;
	}

	return z;
}

CryptoSign* CryptoLayer::newCryptoSign(int& type,const CommBuffer& pub_key,ReadControl& c)const
{
	if(!storage) return 0;

	CryptoSign* z=new CryptoSign;
	if(!z) return 0;

	z->pub_key.set(pub_key,c);
	z->storage=storage;

	z->imp=CryptoSignImp::new_object(type,config.pks);
	if(!z->imp)
	{
		delete z;
		return 0;
	}

	return z;
}