/*
 * libretroshare/src/util: rsrecogn.cc
 *
 * RetroShare Utilities
 *
 * Copyright 2013 by Robert Fernie.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License Version 2.1 as published by the Free Software Foundation.
 *
 * 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
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
 * USA.
 *
 * Please report all bugs and problems to "retroshare@lunamutt.com".
 *
 */

#include "pqi/pqi_base.h"

#include "util/rsrecogn.h"
#include "util/radix64.h"
#include "util/rsstring.h"
#include "util/rsdir.h"

#include "gxs/gxssecurity.h"

#include <openssl/ssl.h>
#include <openssl/evp.h>

/***
 * #define DEBUG_RECOGN	1
 ***/

#define DEBUG_RECOGN	1

static std::string RecognKey = "MIICCgKCAgEA4/i2fy+zR27/H8fzphM8mR/Nz+yXjMJTtqKlCvEMQlyk7lKzDbKifNjGSiAXjSv3b9TRgMtje7hfEhs3//Oeu4KsCf6sz17aj2StBF579IdJTSUPDwq6jCsZ6NDEYpG8xz3FVV+Ac8q5Vpr/+jdg23ta09zq4aV8VIdIsroVOmZQqjwPcmQK57iWHd538i/XBtc2rnzbYq5bprnmtAKdx55gXVXDfALa0s6yR0HYvCaWguMEJhMIKWfi/9PEgLgwF9OmRwywc2TU/EdvYJo8fYHLfGk0PnYBuL1oSnn3cwAAef02W2JyCzQ84g30tLSUk+hC1LLi+iYj3x7IRR4q7Rlf/FYv/Q5fvjRtPT9eqM6fKyJ9ZO4NjlrSPFGydNbgABzP6WMhBzFjUkEKS27bGmr8Qxdj3Zp0TvR2IkyM6oM+6YknuM4RndUEgC1ZxtoIhugMjm6HdMQmoaHNK3kXewgQB90HHqzKA/J1gok3NcqL8Yls5g0LHepVHsU4cuaIqQr5yr665ZTLU2oqn1HIdkgydBYYUt6G3eWJKXYRbDhWPthGo/HK+W+iw6cTGWxzlCZS40EU9efxz4mDuhow67jOe704lBP3kiYXu05Y5uspaYnuvrvIwaRWBYapyR9UmKktnY8xJYrZvrcZgCovAbiodTzWeYg37xjFfGgYNI8CAwEAAQ==";

#define NUM_RECOGN_SIGN_KEYS	3
static std::string RecognSigningKeys[NUM_RECOGN_SIGN_KEYS] = 
{
	"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",


};



EVP_PKEY *RsRecogn::loadMasterKey()
{
	/* load master signing key */
    std::vector<uint8_t> decoded = Radix64::decode(RecognKey);
		
    const unsigned char *keyptr2 = decoded.data();
    long keylen2 = decoded.size();
	
	RSA *rsakey = d2i_RSAPublicKey(NULL, &(keyptr2), keylen2);

	if (!rsakey)
	{
#ifdef DEBUG_RECOGN
		std::cerr << "RsRecogn::loadMasterKeys() failed rsakey load";
		std::cerr << std::endl;
#endif // DEBUG_RECOGN
		return NULL;
	}	

	
	EVP_PKEY *signKey = EVP_PKEY_new();
	EVP_PKEY_assign_RSA(signKey, rsakey);

	return signKey;
}


bool	RsRecogn::loadSigningKeys(std::map<RsGxsId, RsGxsRecognSignerItem *> &signMap)
{

	EVP_PKEY *signKey = loadMasterKey();
	RsGxsRecognSerialiser recognSerialiser;

	if (!signKey)
	{
		std::cerr << "RsRecogn::loadSigningKeys() missing Master Key";
		return false;
	}


	time_t now = time(NULL);

	for(int i = 0; i < NUM_RECOGN_SIGN_KEYS; i++)
	{
        std::vector<uint8_t> signerbuf = Radix64::decode(RecognSigningKeys[i]);

        uint32_t pktsize = signerbuf.size();
        RsItem *pktitem = recognSerialiser.deserialise(signerbuf.data(), &pktsize);
		RsGxsRecognSignerItem *item = dynamic_cast<RsGxsRecognSignerItem *>(pktitem);

		if (!item)
		{
#ifdef DEBUG_RECOGN
			std::cerr << "RsRecogn::loadSigningKeys() failed to deserialise SignerItem";
			std::cerr << std::endl;
#endif // DEBUG_RECOGN
			continue;
		}

#ifdef DEBUG_RECOGN
		std::cerr << "RsRecogn::loadSigningKeys() SignerItem: ";
		std::cerr << std::endl;
		item->print(std::cerr);
#endif // DEBUG_RECOGN
		
		/* check dates */
		if ((item->key.startTS > (unsigned) now) || (item->key.endTS < (unsigned) now))
		{
#ifdef DEBUG_RECOGN
			std::cerr << "RsRecogn::loadSigningKeys() failed timestamp";
			std::cerr << std::endl;
			std::cerr << "RsRecogn::loadSigningKeys() key.startTS: " << item->key.startTS;
			std::cerr << std::endl;
			std::cerr << "RsRecogn::loadSigningKeys() now: " << now;
			std::cerr << std::endl;
			std::cerr << "RsRecogn::loadSigningKeys() key.endTS: " << item->key.endTS;
			std::cerr << std::endl;
#endif // DEBUG_RECOGN
			delete item;
			continue;
		}

		/* check signature */
		RsTlvKeySignature signature = item->sign;
		item->sign.TlvShallowClear();

		unsigned int siglen = signature.signData.bin_len;
		unsigned char *sigbuf = (unsigned char *) signature.signData.bin_data;

		/* store in */
		uint32_t datalen = recognSerialiser.size(item);
        
        	RsTemporaryMemory data(datalen) ;
            
            	if(!data)
                    return false ;
                
		uint32_t pktlen = datalen;
		int signOk = 0;
		
		if (recognSerialiser.serialise(item, data, &pktlen))
		{
			EVP_MD_CTX *mdctx = EVP_MD_CTX_create();

			EVP_VerifyInit(mdctx, EVP_sha1());
			EVP_VerifyUpdate(mdctx, data, pktlen);
			signOk = EVP_VerifyFinal(mdctx, sigbuf, siglen, signKey);
	
			EVP_MD_CTX_destroy(mdctx);

			item->sign = signature;
			signature.TlvShallowClear();

			if (signOk)
			{
#ifdef DEBUG_RECOGN
				std::cerr << "RsRecogn::loadSigningKeys() signature ok";
				std::cerr << std::endl;
#endif // DEBUG_RECOGN
				RsGxsId signerId = item->key.keyId;
				signMap[signerId] = item;
			}
		}
		
		if (!signOk)
		{
#ifdef DEBUG_RECOGN
			std::cerr << "RsRecogn::loadSigningKeys() signature failed";
			std::cerr << std::endl;
#endif // DEBUG_RECOGN
			delete item;
		}
	}

	/* clean up */
	EVP_PKEY_free(signKey);
	return true;
}



bool	RsRecogn::validateTagSignature(RsGxsRecognSignerItem *signer, RsGxsRecognTagItem *item)
{
	if (item->sign.keyId != signer->key.keyId)
	{
#ifdef DEBUG_RECOGN
		std::cerr << "RsRecogn::validateTagSignature() keyId mismatch";
		std::cerr << std::endl;
#endif // DEBUG_RECOGN
		return false;
	}
		
	const unsigned char *keyptr = (const unsigned char *) signer->key.keyData.bin_data;
	long keylen = signer->key.keyData.bin_len;
		
	/* extract admin key */
	RSA *rsakey = d2i_RSAPublicKey(NULL, &(keyptr), keylen);
	if (!rsakey)
	{
#ifdef DEBUG_RECOGN
		std::cerr << "RsRecogn::validateTagSignature() failed extract signkey";
		std::cerr << std::endl;
#endif // DEBUG_RECOGN
		return false;
	}
		
	/* check signature */
	RsTlvKeySignature signature = item->sign;
	item->sign.TlvShallowClear();
		
	unsigned int siglen = signature.signData.bin_len;
	unsigned char *sigbuf = (unsigned char *) signature.signData.bin_data;

	EVP_MD_CTX *mdctx = EVP_MD_CTX_create();
	EVP_PKEY *signKey = EVP_PKEY_new();
	EVP_PKEY_assign_RSA(signKey, rsakey);

		
	/* store in */
	RsGxsRecognSerialiser serialiser;

	uint32_t datalen = serialiser.size(item);
    
    	RsTemporaryMemory data(datalen) ;
        
        if(!data)
            return false ;
        
	int signOk = 0;
		
	uint32_t pktlen = datalen;
	if (serialiser.serialise(item, data, &pktlen))
	{
		
		EVP_VerifyInit(mdctx, EVP_sha1());
		EVP_VerifyUpdate(mdctx, data, pktlen);
		signOk = EVP_VerifyFinal(mdctx, sigbuf, siglen, signKey);
#ifdef DEBUG_RECOGN
		std::cerr << "RsRecogn::validateTagSignature() sign_result: " << signOk;
		std::cerr << std::endl;
#endif // DEBUG_RECOGN
	}
	else
	{
#ifdef DEBUG_RECOGN
		std::cerr << "RsRecogn::validateTagSignature() failed to serialise";
		std::cerr << std::endl;
#endif // DEBUG_RECOGN
	}
		
	// Clean up.
	item->sign = signature;
	signature.TlvShallowClear();
		
	EVP_MD_CTX_destroy(mdctx);
	EVP_PKEY_free(signKey);
		
	return (signOk == 1);		
}


bool rsa_sanity_check(RSA *rsa)
{
	std::cerr << "rsa_sanity_check()";
	std::cerr << std::endl;

	if (!rsa)
	{
		std::cerr << "rsa_sanity_check() RSA == NULL";
		std::cerr << std::endl;
		return false;
	}

	RSA *pubkey = RSAPublicKey_dup(rsa);

	std::string signId = RsRecogn::getRsaKeyId(rsa);
	std::string signId2 = RsRecogn::getRsaKeyId(pubkey);

	bool ok = true;
	if (signId != signId2)
	{
		std::cerr << "rsa_sanity_check() ERROR SignId Failure";
		std::cerr << std::endl;
		ok = false;
	}

	if (1 != RSA_check_key(rsa))
	{
		std::cerr << "rsa_sanity_check() ERROR RSA key is not private";
		std::cerr << std::endl;
		ok = false;
	}

#if 0
	if (1 == RSA_check_key(pubkey))
	{
		std::cerr << "rsa_sanity_check() ERROR RSA dup key is private";
		std::cerr << std::endl;
		ok = false;
	}
#endif

	RSA_free(pubkey);

	if (!ok)
	{
		exit(1);
	}

	return true;
}

		
#warning csoler: this code should be using GxsSecurity signature code. Not some own made signature call.

bool	RsRecogn::signTag(EVP_PKEY *signKey, RsGxsRecognTagItem *item)
{
	RsGxsRecognSerialiser serialiser;

	RSA *rsa = EVP_PKEY_get1_RSA(signKey);
	std::string signId = getRsaKeyId(rsa);
	rsa_sanity_check(rsa);
	RSA_free(rsa);

	item->sign.TlvClear();

	/* write out the item for signing */
	uint32_t len = serialiser.size(item);
	char *buf = new char[len];
	if (!serialiser.serialise(item, buf, &len))
	{
#ifdef DEBUG_RECOGN
		std::cerr << "RsRecogn::signTag() Failed serialise TagItem:";
		std::cerr << std::endl;
		item->print(std::cerr);
#endif // DEBUG_RECOGN
		delete []buf;
		return false;
	}

	/* calc and check signature */
	EVP_MD_CTX *mdctx = EVP_MD_CTX_create();

	EVP_SignInit(mdctx, EVP_sha1());
	EVP_SignUpdate(mdctx, buf, len);

	unsigned int siglen = EVP_PKEY_size(signKey);
	unsigned char sigbuf[siglen];
	EVP_SignFinal(mdctx, sigbuf, &siglen, signKey);

	/* save signature */
	item->sign.signData.setBinData(sigbuf, siglen);
	item->sign.keyId = RsGxsId(signId);

	/* clean up */
	EVP_MD_CTX_destroy(mdctx);
	delete []buf;

	return true;
}

#warning csoler: this code should be using GxsSecurity signature code. Not some own made signature call.

bool	RsRecogn::signSigner(EVP_PKEY *signKey, RsGxsRecognSignerItem *item)
{
	std::cerr << "RsRecogn::signSigner()";
	std::cerr << std::endl;

	RsGxsRecognSerialiser serialiser;

	std::cerr << "RsRecogn::signSigner() Checking Key";
	std::cerr << std::endl;

	RSA *rsa = EVP_PKEY_get1_RSA(signKey);
	std::string signId = getRsaKeyId(rsa);
	rsa_sanity_check(rsa);
	RSA_free(rsa);

	std::cerr << "RsRecogn::signSigner() Key Okay";
	std::cerr << std::endl;

	item->sign.TlvClear();

	/* write out the item for signing */
	uint32_t len = serialiser.size(item);
	char *buf = new char[len];
	if (!serialiser.serialise(item, buf, &len))
	{
#ifdef DEBUG_RECOGN
		std::cerr << "RsRecogn::signSigner() Failed serialise SignerItem:";
		std::cerr << std::endl;
		item->print(std::cerr);
#endif // DEBUG_RECOGN
		delete []buf;
		return false;
	}

	/* calc and check signature */
	EVP_MD_CTX *mdctx = EVP_MD_CTX_create();

	EVP_SignInit(mdctx, EVP_sha1());
	EVP_SignUpdate(mdctx, buf, len);

	unsigned int siglen = EVP_PKEY_size(signKey);
	unsigned char sigbuf[siglen];
	EVP_SignFinal(mdctx, sigbuf, &siglen, signKey);

	/* save signature */
	item->sign.signData.setBinData(sigbuf, siglen);
	item->sign.keyId = RsGxsId(signId);

	/* clean up */
	EVP_MD_CTX_destroy(mdctx);
	delete []buf;

	return true;
}

#warning csoler: this code should be using GxsSecurity signature code. Not some own made signature call.

bool	RsRecogn::signTagRequest(EVP_PKEY *signKey, RsGxsRecognReqItem *item)
{
	std::cerr << "RsRecogn::signTagRequest()";
	std::cerr << std::endl;

	RsGxsRecognSerialiser serialiser;

	RSA *rsa = EVP_PKEY_get1_RSA(signKey);
	std::string signId = getRsaKeyId(rsa);
	rsa_sanity_check(rsa);
	RSA_free(rsa);

	item->sign.TlvClear();

	/* write out the item for signing */
	uint32_t len = serialiser.size(item);
	char *buf = new char[len];
	if (!serialiser.serialise(item, buf, &len))
	{
#ifdef DEBUG_RECOGN
		std::cerr << "RsRecogn::signTagRequest() Failed serialise Tag Request:";
		std::cerr << std::endl;
		item->print(std::cerr);
#endif // DEBUG_RECOGN
		delete []buf;
		return false;
	}

	/* calc and check signature */
	EVP_MD_CTX *mdctx = EVP_MD_CTX_create();

	EVP_SignInit(mdctx, EVP_sha1());
	EVP_SignUpdate(mdctx, buf, len);

	unsigned int siglen = EVP_PKEY_size(signKey);
	unsigned char sigbuf[siglen];
	EVP_SignFinal(mdctx, sigbuf, &siglen, signKey);

	/* save signature */
	item->sign.signData.setBinData(sigbuf, siglen);
	item->sign.keyId = RsGxsId(signId);

	/* clean up */
	EVP_MD_CTX_destroy(mdctx);
	delete []buf;

	return true;
}


bool	RsRecogn::itemToRadix64(RsItem *item, std::string &radstr)
{
	RsGxsRecognSerialiser serialiser;

	/* write out the item for signing */
	uint32_t len = serialiser.size(item);
    
    	RsTemporaryMemory buf(len) ;
        
	if (!serialiser.serialise(item, buf, &len))
	{
		return false;
	}

	radstr.clear();
	Radix64::encode(buf, len, radstr);

	return true;
}


std::string RsRecogn::getRsaKeyId(RSA *pubkey)
{
#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER)
	int len = BN_num_bytes(pubkey -> n);
	unsigned char tmp[len];
	BN_bn2bin(pubkey -> n, tmp);
#else
    const BIGNUM *nn=NULL ;
    RSA_get0_key(pubkey,&nn,NULL,NULL) ;

	int len = BN_num_bytes(nn);
	unsigned char tmp[len];
	BN_bn2bin(nn, tmp);
#endif

    return RsDirUtil::sha1sum(tmp,len).toStdString();

#ifdef OLD_VERSION_REMOVED
    // (cyril) I removed this because this is cryptographically insane, as it allows to easily forge a RSA key with the same ID.

	// copy first CERTSIGNLEN bytes...
	if (len > CERTSIGNLEN)
	{
		len = CERTSIGNLEN;
	}

	std::string id;
	for(uint32_t i = 0; i < CERTSIGNLEN; i++)
	{
		rs_sprintf_append(id, "%02x", (uint16_t) (((uint8_t *) (tmp))[i]));
	}

	return id;
#endif
}



RsGxsRecognTagItem *RsRecogn::extractTag(const std::string &encoded)
{
	// Decode from Radix64 encoded Packet.
	uint32_t pktsize;

    std::vector<uint8_t> buffer = Radix64::decode(encoded);
    pktsize = buffer.size();

	if( buffer.empty() )
		return NULL;

	RsGxsRecognSerialiser serialiser;
    RsItem *item = serialiser.deserialise(buffer.data(), &pktsize);

	if (!item)
	{
#ifdef DEBUG_RECOGN
		std::cerr << "RsRecogn::extractTag() ERROR Deserialise failed";
		std::cerr << std::endl;
#endif // DEBUG_RECOGN
		return NULL;
	}

	RsGxsRecognTagItem *tagitem = dynamic_cast<RsGxsRecognTagItem *>(item);

	if (!tagitem)
	{
#ifdef DEBUG_RECOGN
		std::cerr << "RsRecogn::extractTag() ERROR Not TagItem, is: ";
		std::cerr << std::endl;
		item->print(std::cerr);
#endif // DEBUG_RECOGN
		delete item;
	}

	return tagitem;
}


bool RsRecogn::createTagRequest(const RsTlvPrivateRSAKey &key, const RsGxsId &id, const std::string &nickname, uint16_t tag_class, uint16_t tag_type, const std::string &comment, std::string &tag)
{
	EVP_PKEY *signKey = EVP_PKEY_new();
	RSA *rsakey = d2i_RSAPrivateKey(NULL, (const unsigned char **)&key.keyData.bin_data, key.keyData.bin_len);

        if (!rsakey)
        {
#ifdef DEBUG_RECOGN
		std::cerr << "RsRecogn::createTagRequest() Failed to extract key";
		std::cerr << std::endl;
#endif // DEBUG_RECOGN
                return false;
        }

        if (!EVP_PKEY_assign_RSA(signKey, rsakey))
        {
#ifdef DEBUG_RECOGN
		std::cerr << "RsRecogn::createTagRequest() Failed to assign key";
		std::cerr << std::endl;
#endif // DEBUG_RECOGN
                return false;
        }

	RsGxsRecognReqItem *item = new RsGxsRecognReqItem();

	item->issued_at = time(NULL);
	item->period = 365 * 24 * 3600;
	item->tag_class = tag_class;
	item->tag_type  = tag_type;

	item->nickname = nickname;
	item->identity = id;
	item->comment = comment;

	bool signOk = RsRecogn::signTagRequest(signKey,item);
	EVP_PKEY_free(signKey);

	if (!signOk)
	{
#ifdef DEBUG_RECOGN
		std::cerr << "RsRecogn::createTagRequest() Failed to sign Tag Request:";
		std::cerr << std::endl;
		item->print(std::cerr);
#endif // DEBUG_RECOGN
		delete item;
		return false;
	}

	/* write out the item for signing */
	RsGxsRecognSerialiser serialiser;
	uint32_t len = serialiser.size(item);
    RsTemporaryMemory buf(len) ;
	bool serOk = serialiser.serialise(item, buf, &len);

	if (serOk)
	{
		Radix64::encode(buf, len, tag);
	}

	if (!serOk)
	{
#ifdef DEBUG_RECOGN
		std::cerr << "RsRecogn::createTagRequest() Failed serialise Tag Request:";
		std::cerr << std::endl;
		item->print(std::cerr);
#endif // DEBUG_RECOGN
        delete item;
        return false;
    }

    delete item;
	return serOk;
}


