cellularsrvapitest/datatransferhaitest/esock/src/T_DataVerify.cpp
author Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
Fri, 19 Feb 2010 23:59:33 +0200
branchRCL_3
changeset 9 962e6306d9d2
parent 0 3553901f7fa8
child 24 6638e7f4bd8f
permissions -rw-r--r--
Revision: 201007 Kit: 201007

/*
* Copyright (c) 2005-2009 Nokia Corporation and/or its subsidiary(-ies). 
* All rights reserved.
* This component and the accompanying materials are made available
* under the terms of "Eclipse Public License v1.0"
* which accompanies this distribution, and is available
* at the URL "http://www.eclipse.org/legal/epl-v10.html".
*
* Initial Contributors:
* Nokia Corporation - initial contribution.
*
* Contributors:
*
* Description:
*
*/


#include <e32base.h>
#include <f32file.h>

#include <hash.h> // CryptoHW (md5hash)

#include "T_DataVerify.h"



/*@{*/
//Command LIT's
_LIT(KCmdVerifyData,	"VerifyData");
/*@}*/

/*@{*/
//LIT's for reading params from ini file
_LIT( KDataVerifyType,  "VerifyType" );
_LIT( KDataVerifyFile,  "VerifyFile" );
_LIT( KChecksum,     	"Checksum" );
_LIT( KMD5Type,			"MD5" );        // Use MD5 with data verify
/*@}*/


namespace DataVerify
	{
	const TInt KMD5Size = 32;
    const TInt KMD5length  = 32;
    const TInt KBufsize    = 512;
    const TInt KCharlength = 2;
	}

typedef TBuf8<DataVerify::KMD5length> THashBuf;

CT_DataVerify::CT_DataVerify():
	iMD5(NULL)
	{
	}

CT_DataVerify* CT_DataVerify::NewL()
	{
	CT_DataVerify* ret = new (ELeave) CT_DataVerify();
	CleanupStack::PushL(ret);
	ret->ConstructL();
	CleanupStack::Pop(ret);
	return ret;
	}

CT_DataVerify::~CT_DataVerify()
	{
    iFs.Close();
    if(iMD5)
    	{
    	delete iMD5;
    	iMD5 = NULL;
    	}
	}

void CT_DataVerify::ConstructL()
	{
    User::LeaveIfError( iFs.Connect() );
    iMD5 = CMD5::NewL();
	}

TAny* CT_DataVerify::GetObject()
	{
	return NULL;
	}

TBool CT_DataVerify::DoCommandL(const TTEFFunction& aCommand, const TTEFSectionName& aSection, const TInt /*aAsyncErrorIndex*/)
	{
	TBool retVal=ETrue;
	
	if (aCommand==KCmdVerifyData)
		{	
		DoCmdVerifyData(aSection);	
		}
	else
		{
		ERR_PRINTF1(_L("Unknown command"));
		retVal=EFalse;
		}

	return retVal;
	}


/**
 * Verifies that the downloaded file is not corrupted by using a previously calculated checksum.
 * @param aSection - The section in config file to look for the Data Verify Type, the file to verify and the checksum.
 * @return error - Error code. KErrNone if checksum is equal.
 */
void CT_DataVerify::DoCmdVerifyData(const TTEFSectionName& aSection)
    {
    INFO_PRINTF1(_L("*START* CT_TransferData::DoCmdVerifyData"));
    
    TBool dataOk =ETrue;
    TPtrC type;
    if(!GetStringFromConfig(aSection, KDataVerifyType, type ))
    	{
    	ERR_PRINTF2(_L("Error in getting parameter %S from INI file"), &KDataVerifyType);
    	SetBlockResult(EFail);
    	dataOk = EFalse;
    	}
    
    TPtrC filename;
    if(!GetStringFromConfig(aSection, KDataVerifyFile, filename ))
    	{
    	ERR_PRINTF2(_L("Error in getting parameter %S from INI file"), &KDataVerifyFile);
    	SetBlockResult(EFail);
    	dataOk = EFalse;
    	}
    
    TPtrC checksumParameter;
    if(!GetStringFromConfig(aSection, KChecksum, checksumParameter ))
    	{
    	ERR_PRINTF2(_L("Error in getting parameter %S from INI file"), &KChecksum);
    	SetBlockResult(EFail);
    	dataOk = EFalse;
    	}
    if(dataOk)
    	{
    	TBuf8<DataVerify::KMD5Size> checksum;
    	checksum.Append(checksumParameter);

        if (type == KMD5Type)
            {
            INFO_PRINTF1(_L("MD5 selected"));
            TRAPD(error, VerifyChecksumL(checksum, filename));
            if (error == KErrNone)
                {
                INFO_PRINTF1(_L("Data verify succeeded"));
                }
            else
            	{
                ERR_PRINTF2(_L("VerifyData failed [%d]"), error);
                SetError(error);
            	}
            }
    	}

    
    INFO_PRINTF1(_L("*END* CT_TransferData::DoCmdVerifyData"));
    }


/**
 * Verify Data integrity
 *
 * @param aMethod			The verification method
 * @param aFileName			File to verify
 * @param aChecksum			Checksum
 *
 * @return					N/A
 *
 * @leave					System wide error
 */
void CT_DataVerify::VerifyData( const TDataVerifyMethod aMethod, const TFileName& aFileName, const TDesC& aChecksum )
	{
	TBool dataOk = ETrue;
	
	if( aFileName.Length() > KMaxFileName)
		{
		ERR_PRINTF2(_L("Illegal filename: %S"), &aFileName);
		SetBlockResult(EFail);
		dataOk = EFalse;
		}
	if(dataOk)
		{
		THashBuf checksum;
		checksum.Append( aChecksum );
		
		// Use MD5 to verify file
		if( aMethod == EDataVerifyMethodMD5 )
			{
			// MD5 selected
			TRAPD(error, VerifyChecksumL( checksum, aFileName ));
			if(error == KErrNone)
				{
				INFO_PRINTF1(_L("Data verify succeeded."));
				}
			else
				{
				ERR_PRINTF2(_L("Checksum verification left with error %d"), error);
				SetBlockResult(EFail);
				}
			}
		else
			{
			ERR_PRINTF1(_L("Verification method not supported"));
			SetBlockResult(EFail);
			}
		}
	}

/**
 * Verify File integrity
 *
 * @param aReferenceFileName	Reference file against to verify integrity
 * @param aDataFileName			File to verify
 *
 * @return						N/A
 *
 * @leave						System wide error
 */
void CT_DataVerify::VerifyFileL( const TFileName& aReferenceFileName, const TFileName& aDataFileName )
	{
	THashBuf refHash;

	// hash the first file
	HashFileL( aReferenceFileName, refHash );

	THashBuf dataHash;
	// hash the second file
	HashFileL( aDataFileName, dataHash );

	// compare hashes together
	if( !CompareBuffers( refHash, dataHash ) )
		{
		// Data was corrupted
		User::Leave( KErrCorrupt );
		}
	}

/**
 * Verify Buffer integrity
 *
 * @param aReferenceFileName	Reference file against to verify integrity
 * @param aData					Buffer to verify
 *
 * @return						N/A
 *
 * @leave						System wide error
 */
void CT_DataVerify::VerifyBufferL( const TFileName& aReferenceFileName, const TDesC8& aData )
    {
	HBufC8* refdata = 0;
	TPtr8 ptr = refdata->Des();
	// read file into buffer, file must fit into HBufC8
	ReadFileL( aReferenceFileName, ptr );

	// compare read file with given buffer
	if( !CompareBuffers( refdata->Des(), aData ) )
		{
		// Data was corrupted
		User::Leave( KErrCorrupt );
		}
    }

/**
 * Verify Buffer integrity
 *
 * @param aReferenceBuffer		Reference buffer against to verify integrity
 * @param aData					Buffer to verify
 *
 * @return						N/A
 *
 * @leave						System wide error
 */
void CT_DataVerify::VerifyBufferL( const TDesC8& aReferenceData, const TDesC8& aData )
	{
	// Compare buffers
	if ( !CompareBuffers( aReferenceData, aData ) )
		{
		// Data was corrupted
		User::Leave( KErrCorrupt );
		}
    }

/**
 * Verify Checksum integrity
 *
 * @param aReferenceMD5Checksum		Reference checksum
 * @param aDataFileName				File to verify
 *
 * @return							N/A
 *
 * @leave							System wide error
 */
void CT_DataVerify::VerifyChecksumL( const TDesC8& aReferenceMD5Checksum, const TFileName& aDataFileName )
	{
	// MD5 checksum is always 128 bit (32 char) long
	if( aReferenceMD5Checksum.Length() != DataVerify::KMD5length )
		{
		User::Leave( KErrBadDescriptor );
		}

	THashBuf hash;
	// hash file
	HashFileL( aDataFileName, hash );

	// converting MD5 checksum to binary is necessary because Hash() function
	// returns hash in binary format and reference checksum given in parameter
	// is a 32 character long string
	THashBuf conversion;
	ConvertString2HexL( aReferenceMD5Checksum, conversion );

	if( !CompareBuffers( conversion, hash ) )
		{
		// Data was corrupted
		User::Leave( KErrCorrupt );
		}
	}

/**
 * Verify Checksum integrity
 *
 * @param aReferenceMD5Checksum		Reference checksum
 * @param aData						Data to verify
 *
 * @return							N/A
 *
 * @leave							System wide error
 */
void CT_DataVerify::VerifyChecksumL( const TDesC8& aReferenceMD5Checksum, const TDesC8& aData )
    {
	// MD5 checksum is always 128 bit (32 char) long
	if( aReferenceMD5Checksum.Length() != DataVerify::KMD5length )
		{
		// MD5 Checksum was not correct length (32 characters)
		User::Leave( KErrBadDescriptor );
		}

	// hash the given buffer
	TPtrC8 hash = iMD5->Hash(aData);

	// converting MD5 checksum to binary is necessary because Hash() function
	// returns hash in binary format and reference checksum given in parameter
	// is a 32 character long string
	THashBuf conversion;
	ConvertString2HexL( aReferenceMD5Checksum, conversion );

	// Compare hashs together
	if( !CompareBuffers( conversion, hash ) )
		{
		// Data was corrupted
		User::Leave( KErrCorrupt );
		}
	}

/**
 * Compare Buffers
 *
 * @param aReferenceBuffer		Reference buffer
 * @param aDataBuffer			Buffer to compare
 *
 * @return						ETrue if buffer contents match
 */
TBool CT_DataVerify::CompareBuffers( const TDesC8& aReferenceBuffer, const TDesC8& aDataBuffer )
	{
	TBool res = EFalse;
	TInt result = aReferenceBuffer.Compare( aDataBuffer );
	
	if( result == 0 )
		{
		// Buffer contents match
		res = ETrue;
		}
	return res;
	}

/**
 * Read File
 *
 * @param aFileName			File name to read
 * @param aBuf				Buffer to read file content
 *
 * @return					N/A
 *
 * @leave					System wide error
 */
void CT_DataVerify::ReadFileL(const TFileName& aFileName, TDes8& aBuf)
    {
    RFile file;
    User::LeaveIfError( file.Open( iFs, aFileName, EFileRead ) );
    CleanupClosePushL( file );

    // Get the file size
    TInt size;
    User::LeaveIfError( file.Size( size ) );

    // allocate memory from heap for file content
    HBufC8* buf = HBufC8::NewLC( size );
    aBuf = buf->Des();

    // read file content into buffer, file may be too big for HBufC8
    User::LeaveIfError( file.Read( aBuf ) );

    CleanupStack::Pop( buf );
    CleanupStack::PopAndDestroy( &file ); // file.Close
    }

/**
 * Hash file in pieces
 *
 * @param aFileName			File name to be hashed
 * @param aDes				Hash value
 *
 * @return					N/A
 *
 * @leave					System wide error
 */
void CT_DataVerify::HashFileL( const TFileName& aFileName, TDes8& aDes )
	{
	RFile file;
	User::LeaveIfError( file.Open( iFs, aFileName, EFileRead ) );
	CleanupClosePushL( file );

	TBuf8<DataVerify::KBufsize> buf;

	TInt error = KErrNone;
	iMD5->Reset();
	// File can be hashed in pieces
	while( error == KErrNone ) // loop until something goes wrong
		{
		error = file.Read( buf, DataVerify::KBufsize );
		if( buf.Length() != 0 )
			{
			// MD5 checksum is calculated in pieces
			iMD5->Update(buf);
			}
		else
			{
			// file ended, end loop
			break;
			}
		}
	// Return calculated MD5 checksum
	aDes = iMD5->Final();

	CleanupStack::PopAndDestroy(&file); // file.Close
	}

/**
 * Write File
 *
 * @param aFileName			File name
 * @param aData				Data to be written into file name.
 *
 * @return					N/A
 *
 * @leave					System wide error
 */
void CT_DataVerify::WriteFileL( const TFileName& aFileName, const TDesC8& aData )
    {
    RFile file;
    // Create file, replace if exists
    User::LeaveIfError( file.Replace( iFs, aFileName, EFileWrite|EFileStream ) );
    CleanupClosePushL( file );
    // Write data into file
    User::LeaveIfError( file.Write( aData ) );
    // Commit write
    User::LeaveIfError( file.Flush() );

    CleanupStack::PopAndDestroy( &file ); // file.Close
    }

/**
 * Convert a string to Hexadecimal
 *
 * @param aData			String descriptor
 * @param aDes			Descriptor where Hex value is stored
 *
 * @return				N/A
 *
 * @leave				System wide error
 */
void CT_DataVerify::ConvertString2HexL( const TDesC8& aData, TDes8& aDes )
    {
	TBuf8<DataVerify::KCharlength> charBuf;

	// Check that buffer is even number
	if( ( aData.Length() % DataVerify::KCharlength ) != 0 )
		{
		User::Leave( KErrBadDescriptor );
		}

	// Go through the data and convert it two characters at a time
	// buffer overflow does not occur because buffer is checked to be even number first
	for( TInt i = 0, limit = aData.Length()-DataVerify::KCharlength; i <= limit; i+=DataVerify::KCharlength )
		{
		// Clean char buffer first
		charBuf.Delete( 0, charBuf.Length() );
		
		// Add KCharlength characters into buffer
		for ( TInt j = 0; j < DataVerify::KCharlength; j++ )
			{
			charBuf.Append( aData[i+j] );
			}

		TUint number;
		TLex8 converter = TLex8( charBuf );
		// Two characters together represent a hex number in MD5 checksum
		User::LeaveIfError( converter.Val( number, EHex ) );

		aDes.Append( number );
		}
	}