/***********************************************************************
 *
 *  Copyright (C) 2005-2006 Novell, Inc. All Rights Reserved.
 *
 *  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; version 2.1
 *  of the License.
 *
 *  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 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, Novell, Inc.
 *
 *  To contact Novell about this file by physical or electronic mail,
 *  you may find current contact information at www.novell.com.
 *
 ***********************************************************************/

#include "CryptManager.h"



void CryptManager::SetupFunctions(void *funList[])
{

	 //PK11SetPasswordFunc = (PK11_SetPasswordFunc) funList[0];
	 PK11GetInternalKeySlot = (PK11_GetInternalKeySlot) funList[1];
	 PK11FreeSlot = (PK11_FreeSlot) funList[2];
	 PK11Authenticate = (PK11_Authenticate) funList[3];
	 PK11CheckUserPassword =(PK11_CheckUserPassword) funList[4];
	 PK11SDRDecrypt = (PK11SDR_Decrypt) funList[5];
	 PK11SDREncrypt = (PK11SDR_Encrypt) funList[6];
	 PLBase64Encode = (PL_Base64Encode) funList[7];
	 PLBase64Decode = (PL_Base64Decode) funList[8];

}

int CryptManager::GetEncryptionPref()
{

return FPM_TRUE;
}


/**
*     This function encrypts the clear text data. First it performs TRIPLE DES encryption
*     and then performs base64 encoding on the encrypted data.
*   
*     @param(in)  clearData   clear text data to be encrypted
*     @param(out) finalData   encrypted data ( null terminated)
*
*	  @return     FPM_TRUE on success and FPM_FALSE on error.
*
*/
int CryptManager::EncryptString (char *clearData, char **finalData) 
{
int encryptDataLen = 0;
char *encryptData = NULL;
char *encodeData  = NULL;
int retValue;


	if( clearData == NULL )
	{
		PrintMessage(MESG_ERROR, "\n EncryptString : Text Data is NULL");
		return FPM_FALSE;
	}

	// Do the encryption if encryption pref is set otherwise just do base64 encoding...
	if ( GetEncryptionPref() ) 
	{
		PrintMessage(MESG_DEBUG, "\n EncryptString : Performing PK11 Encryption...");
		
		retValue = FPM_FALSE;
		if( ((retValue = CryptPK11EncryptString(clearData, strlen(clearData), &encryptData, &encryptDataLen)) != FPM_TRUE) || ( encryptData == NULL) )
		{
			PrintMessage(MESG_ERROR, "\n EncryptString : Failed to encrypt the string : %s ", clearData);
			return retValue;
		}

		if( (CryptBase64Encode(encryptData, encryptDataLen, finalData) != FPM_TRUE) || (*finalData == NULL) )
		{
			PrintMessage(MESG_ERROR, "\n EncryptString : BASE64 encoding failed");
			return FPM_FALSE;
		}
		
		PrintMessage(MESG_DEBUG, "\n EncryptString : Success  ");
	
		// WARNING : If you uncomment , then be ready for side effects , crashes..etc
		// Need full analysis of malloc for this data..
		// Free the allocated blocks...

		//if( encryptData )
		//	free( encryptData);
		
		return FPM_TRUE;
	}
	
	// otherwise do our own obscuring using Base64 encoding 
	PrintMessage(MESG_DEBUG, "\n EncryptString : Performing JUST base64 encoding...");
	
	if( (CryptBase64Encode(clearData, strlen(clearData), &encodeData) == FPM_FALSE) || (encodeData == NULL) )
	{
		PrintMessage(MESG_ERROR, "\n EncryptString : BASE64 encoding failed");
		return FPM_FALSE;
	}

	// We need to add the CRYPT_PREFIX at the begining of encoded data...
	// This will help during decrption process to identify type of encryption

	int prefixLen = strlen( CRYPT_PREFIX );
	int encodeLen = strlen( encodeData );
	*finalData = (char *)malloc( prefixLen + encodeLen + 1);
	
	if( *finalData == NULL )
	{
		PrintMessage(MESG_ERROR, "\n EncryptString : Insufficient memory");
		return FPM_FALSE;
	}

	// FinalData = CRYPT_PREFIX + Encoded Data + '\0'
	strcpy(*finalData, CRYPT_PREFIX);
	strcat(*finalData, encodeData);
	*(*finalData + prefixLen + encodeLen) = 0;
			  
	free(encodeData);

	return FPM_TRUE;
}




/**
*     This function decrypts the encrypted data. First it performs base64 decoding and
*     then performs TRIPLE DES decryption.
*   
*     @param(in)  cryptData   encrypted data 
*     @param(out) clearData   clear text data ( null terminated)
*
*	  @return    FPM_TRUE on success and FPM_FALSE on error.
*
*/

int CryptManager::DecryptString(char *cryptData, char **clearData)
{
int decodeLen = 0;
int finalLen = 0;
char *decodeData = NULL;
char *finalData = NULL;
int retValue;

	if( cryptData == NULL )
	{
		PrintMessage(MESG_ERROR, "\n DecryptString: CryptData is NULL...");
		return FPM_FALSE;
	}

	// treat zero-length crypt string as a special case 
	if(cryptData[0] == '\0') 
	{
		*clearData  = (char*) malloc(1);
		**clearData = 0;
		return FPM_TRUE;
	}
	
	// use PK11 encryption stuff if crypt doesn't starts with prefix 
	if( cryptData[0] != CRYPT_PREFIX[0] ) 
	{
		
		PrintMessage(MESG_DEBUG, "\n Performing PK11 Decryption ");
			
		// First do base64 decoding.....
		if(  (CryptBase64Decode(cryptData, &decodeData, &decodeLen) != FPM_TRUE) || (decodeData == NULL) )
		{
			PrintMessage(MESG_ERROR, "\n DecryptString : Base64 decoding of crypt data failed ");
			return FPM_FALSE;
		}

		PrintMessage(MESG_DEBUG, "\n DecryptString : base64data (%d) = %s ", decodeLen, decodeData);
		
		// Now do actual PK11 decryption	
		retValue = FPM_FALSE;
		retValue = CryptPK11DecryptString(decodeData, decodeLen, &finalData, &finalLen);

		if( retValue != FPM_TRUE ) 
		{
			PrintMessage(MESG_ERROR, "\n DecryptString : Failed to decrypt the string ");
			return retValue;
		}
		
			
		// WARNING : Decrypted string is not NULL terminated 
		// So we will create new NULL terminated string here...
	
		*clearData = (char*) malloc( finalLen + 1 );

		if( *clearData == NULL )
		{
			PrintMessage(MESG_ERROR, "\n DecryptString :Insufficient memory... ");
			return FPM_INSUFFICIENT_MEMORY;
		}
		else
		{
			PrintMessage(MESG_DEBUG, "\n DecryptString : Copying new data ....");
			memcpy(*clearData, finalData, finalLen);
			*(*clearData + finalLen) = 0;    // Null terminate the string....
		}
		
		/*  
		// Free the allocated memory
		// This is causing the problems currently...Later point we have to reanalyze the cause for this
		
		if( decodeData )
			free(decodeData);

		if( finalData )
			free(finalData);
		*/

		PrintMessage(MESG_DEBUG, "\n decryptString : finalLen = %d ", finalLen);

		return FPM_TRUE;
	}
	
	
	// otherwise do our own de-obscuring 
	PrintMessage(MESG_DEBUG, "\n  DecryptString : Performing simple Base64 Decoding ");

	unsigned int PREFIX_Len = strlen(CRYPT_PREFIX);
	if( strlen(cryptData) ==  PREFIX_Len )
	{
		*clearData = (char *)malloc(1);
		**clearData = '\0';
		return FPM_TRUE;
	}
	
	if(  CryptBase64Decode(&cryptData[PREFIX_Len], clearData, &decodeLen) == FPM_FALSE )
	{
		PrintMessage(MESG_ERROR, "\n DecryptString : Base64 decoding of crypt data failed ");
		return FPM_FALSE;
	}
	
	return FPM_TRUE;
}



/**
*     Performs base64 encoding of the encrypted data..
*   
*     @param(in)  cryptData    encrypted data 
*     @param(in)  cryptDataLen length of encrypted data
*     @param(out) encodeData   base64 encoded data
*
*	  @return     FPM_TRUE on success and FPM_FALSE on error.
*
*/

int CryptManager::CryptBase64Encode(char *cryptData, int cryptDataLen, char **encodeData)
{
    
	*encodeData = (*PLBase64Encode)((const char *)cryptData, cryptDataLen, NULL);
    
	if ( *encodeData == NULL ) 
	{ 
		PrintMessage(MESG_ERROR, "\n Base64 encoding failed ...");
		return FPM_FALSE;
	}

	return FPM_TRUE;
}





/**
*     Performs base64 decoding of the encrypted data..
*   
*     @param(in)  cryptData   encrypted data 
*     @param(out) decodeData  base64 decoded data
*     @param(out) decodeLen   length of base64 decoded data
*
*	  @return     FPM_TRUE on success and FPM_FALSE on error.
*
*/
int CryptManager::CryptBase64Decode(char *cryptData, char **decodeData, int *decodeLen)
{
    int len = strlen( cryptData );
    int adjust = 0;

	PrintMessage(MESG_DEBUG, "\n CryptBase64Decode : Length of crypt data = %d", len);
    
	// Compute length adjustment 
    if (cryptData[len-1] == '=') 
	{
      adjust++;
      if (cryptData[len-2] == '=') 
		  adjust++;
    }

    *decodeData = ( char *)(*PLBase64Decode)(cryptData, len, NULL);

    if( *decodeData == NULL )
	{
		PrintMessage(MESG_ERROR, "\n Base64 decoding failed ...");
		return FPM_FALSE;
	}
	
    *decodeLen = (len*3)/4 - adjust;
	
	PrintMessage(MESG_DEBUG, "\n CryptBase64Decode : Length of decoded data = %d", *decodeLen);
    
	return FPM_TRUE;
}


/**
*     Performs TRIPLE DES encryption of clear text data
*   
*     @param(in)  clearData     clear text data to be encrypted
*     @param(in)  clearDataLen  length of clear text data
*     @param(out) cryptData     TRIPLE DES encrypted data
*     @param(out) cryptDataLen  length of encrypted data
*
*	  @return     FPM_TRUE on success and FPM_FALSE on error.
*
*/
int CryptManager::CryptPK11EncryptString(char *clearData, int clearDataLen, char **cryptData, int *cryptDataLen)
{
  PK11SlotInfo *slot = 0;
  SECItem keyid;
  SECItem request;
  SECItem reply;
  SECStatus status;

	slot = (*PK11GetInternalKeySlot)();
	
	if (!slot) 
	{ 
		PrintMessage(MESG_ERROR, "\n CryptPK11EncryptString : PK11_GetInternalKeySlot failed  ...");
		return FPM_FALSE;
	}

	// PK11 authentication
	if ( (*PK11Authenticate)(slot, PR_TRUE, NULL) != SECSuccess)
	{
		// since we have specified password callback function , we won't come here...
		PrintMessage(MESG_ERROR, "\n CryptPK11EncryptString : PK11_Authenticate failed, possibly master password is wrong");
		(*PK11FreeSlot) (slot);
		return FPM_MASTERPASSWORD_WRONG;
	}

		
	// Use default key id 
	keyid.data = 0;
	keyid.len  = 0;
	request.data = (unsigned char *)clearData;
	request.len  = clearDataLen;
	reply.data = 0;
	reply.len  = 0;

	status = (*PK11SDREncrypt)(&keyid, &request, &reply, NULL);
	
	if (status != SECSuccess) 
	{ 
		PrintMessage(MESG_ERROR, "\n CryptPK11EncryptString : PK11SDR_Encrypt failed  ...");
		(*PK11FreeSlot) (slot);
		return FPM_FALSE;
	}


	*cryptData = (char*)reply.data;
	*cryptDataLen = reply.len;
	
	(*PK11FreeSlot) (slot);
	return FPM_TRUE;
}



/**
*     Performs TRIPLE DES decryption of base64 decoded data
*   
*     @param(in)  decodeData  base64 decoded data
*     @param(in)  decodeLen   length of base64 decoded data
*     @param(out) clearData   decrypted data
*     @param(out) finalLen    length of decrypted data
*
*	  @return     FPM_TRUE on success and FPM_FALSE on error.
*
*/
int CryptManager::CryptPK11DecryptString(char *decodeData, int decodeLen, char **clearData, int *finalLen)
{
	PK11SlotInfo *slot = 0;
	SECStatus status;
	SECItem request;
	SECItem reply;

	PrintMessage(MESG_DEBUG, "\n CryptPK11DecryptString entered  ...");

	// Find token with SDR key 
	slot = (*PK11GetInternalKeySlot)();
	
	if (!slot) 
	{ 
		PrintMessage(MESG_ERROR, "\n PK11_GetInternalKeySlot failed  ...");
		return FPM_FALSE;
	}

	PrintMessage(MESG_DEBUG, "\n  PK11_GetInternalKeySlot SUCCESS ...");
	
	// Force authentication
	if ( (*PK11Authenticate)(slot, PR_TRUE, NULL) != SECSuccess)
	{
		// since we have specified password callback function , we won't come here...
		PrintMessage(MESG_ERROR, "\n PK11_Authenticate failed, Probably master password is wrong");
		(*PK11FreeSlot) (slot);
		return FPM_MASTERPASSWORD_WRONG;
	}

	PrintMessage(MESG_DEBUG, "\n  PK11_Authenticate SUCCESS ...");

	// Decrypt the string
	request.data = (unsigned char *)decodeData;
	request.len = decodeLen;
	reply.data = 0;
	reply.len = 0;

	PrintMessage(MESG_DEBUG, "\n calling PK11SDR_Decrypt ...");

	status = (*PK11SDRDecrypt)(&request, &reply, NULL);

	if (status != SECSuccess) 
	{ 
		PrintMessage(MESG_ERROR, "\n PK11SDR_Decrypt failed  ...");
		(*PK11FreeSlot) (slot);
		return FPM_FALSE;
	}

	PrintMessage(MESG_DEBUG, "\n PK11SDR_Decrypt SUCCESS ");
	
	// WARNING : This string is not NULL terminated..
	*clearData = (char*)reply.data;
	*finalLen  = reply.len;

	// Free the slot
	(*PK11FreeSlot) (slot);

	return FPM_TRUE;
}