securityanddataprivacytools/securitytools/certapp/encdec/encdec.cpp
author Santosh Patil <santosh.v.patil@nokia.com>
Wed, 08 Jul 2009 11:25:26 +0100
changeset 0 2c201484c85f
child 8 35751d3474b7
permissions -rw-r--r--
Move the Security package to EPL, and add the implementations of the cryptographic algorithms

/*
* Copyright (c) 2008-2009 Nokia Corporation and/or its subsidiary(-ies).
* All rights reserved.
* This component and the accompanying materials are made available
* under the terms of the License "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 "encdec.h"
#include <sstream>
#include <errno.h>
#include <s32ucmp.h>
#include "stringconv.h"
#include <stdio.h>
#include "x509utils.h"

RDecodeReadStream::RDecodeReadStream(CFileStore *aStore, RReadStream &aReadStream)
	: iStore(aStore), iCertBaseName(), iReadStream(aReadStream), iHumanReadable(false), iToken(), 
	  iPrefetchedTokenIsValid(false), iPrefetchedToken()
{
}

RDecodeReadStream::RDecodeReadStream(const std::string &aCertBaseName, RReadStream &aReadStream)
	: iStore(0), iCertBaseName(aCertBaseName), iReadStream(aReadStream), iHumanReadable(true), iToken(), 
	  iPrefetchedTokenIsValid(false), iPrefetchedToken()
{
}

void RDecodeReadStream::RawRead(void *aPtr, TUint32 aLength)
{
	iReadStream.ReadL((TUint8 *)aPtr, aLength);
}

void RDecodeReadStream::CheckName(const std::string &aExpected)
{
	ReadNextToken();

	if(iToken != aExpected)
		{
		dbg << Log::Indent() << "Expected token '" << aExpected <<"' but got token '" << iToken << "'" << Log::Endl();
		FatalError();
		}
	   
}

TUint32 ReadUnsignedNumber(std::string &aStr, size_t aSize)
{
	TUint32 val;
	std::istringstream ss(aStr);

	if((aStr.length() > 2) && 
	   (aStr[0] == '0') && 
	   ((aStr[1] == 'x') || (aStr[1] == 'X')))
		{
		// Hex number starting with 0x
		char ch;
		ss >> ch >> ch; // Discard the 0x
		ss >> std::hex >> val;
		}
	else
		{
		// Decimal number
		ss >> val;
		}

	// Now work out if we consumed the entire token without error
	if(ss.fail())
		{
		// Number decode failed
		dbg << Log::Indent() << "Failed to decode '" << aStr << "' as a number" << Log::Endl();
		FatalError();
		}

	// Make sure we consumed all data
	if(! ss.eof())
		{
		// Trailing chars on numeric token
		FatalError();
		}

	if(aSize != 4)
		{
		// Check range
		// nb the following check would fail if aSize==4 because x>>32 == x if sizeof(x)==4
		if((val >> (8*aSize)) != 0)
			{
			// Higher order bits are set above the size of the variable we
			// are returning into
			FatalError();
			}
		}
return val;
}


TUint32 RDecodeReadStream::ReadUnsignedNumber(size_t aSize)
{
BULLSEYE_OFF
	if((TUint32(aSize)>4))
		{
		FatalError();
		}
BULLSEYE_RESTORE

	ReadNextToken();

	return ::ReadUnsignedNumber(iToken, aSize);
}

const std::string &RDecodeReadStream::Token() const
{
	return iToken;
}

void RDecodeReadStream::ReadNextToken()
{
	if(iPrefetchedTokenIsValid)
		{
		// Copy prefetched token to current token
		iToken = iPrefetchedToken;
		iPrefetchedToken.clear();
		iPrefetchedTokenIsValid = false;
		return;
		}
		
	GetToken(iToken);
}

const std::string &RDecodeReadStream::PeakToken()
{
	if(!iPrefetchedTokenIsValid)
		{
		GetToken(iPrefetchedToken);
		iPrefetchedTokenIsValid = true;
		}

	return iPrefetchedToken;
}





bool RDecodeReadStream::HumanReadable() const
{
	return iHumanReadable;
}


void RDecodeReadStream::Close()
{
	iReadStream.Close();
}

void RDecodeReadStream::GetToken(std::string &aToken)
{
	aToken.clear();

	TUint8 ch;
	do 
		{
		iReadStream >> ch;
		if(ch == '#')
			{
			// Skip comment
			++ch;
			while((ch != '\r') && (ch != '\n'))
				{
				iReadStream >> ch;
				} 

			}
		} while((ch == ' ') || (ch == '\t') || (ch == '\r') || (ch == '\n'));
	
	if(ch == '"')
		{
		// Read a string
		iReadStream >> ch; // read first char
		while(ch != '"')
			{
			if(ch=='\\')
				{
				// \X causes X to always be saved (even if X is ")
				iReadStream >> ch;
				}
			
			aToken.push_back(ch);
			iReadStream >> ch;
			}
		// At this point ch contains WS so it can be discarded.
		return;
		}

	// Read a non-string token
	while((ch != ' ') && (ch != '\t') && (ch != '\r') && (ch != '\n'))
		{
		aToken.push_back(ch);
		iReadStream >> ch;
		}
	// At this point ch contains WS so it can be discarded.
	return;
}





REncodeWriteStream::REncodeWriteStream(CFileStore *aStore, RWriteStream &aWriteStream)
	: iStore(aStore),
	  iCertBaseName(), // not used for STORE based streams
	  iWriteStream(&aWriteStream),
	  iLogStream(0), 
	  iHumanReadable(false), iPemOut(false), iVerbose(false), iIndentLevel(0)
{
}

REncodeWriteStream::REncodeWriteStream(const std::string &aCertBaseName, RWriteStream &aWriteStream)
	: iStore(0), iCertBaseName(aCertBaseName), iWriteStream(&aWriteStream),
	  iLogStream(0), 
	  iHumanReadable(true), iPemOut(false), iVerbose(false), iIndentLevel(0)
{
}

REncodeWriteStream::REncodeWriteStream(Log &aLog)
	: iStore(0), iWriteStream(0), iLogStream(&aLog.Stream()), 
	  iHumanReadable(true), iPemOut(false), iVerbose(false), iIndentLevel(aLog.IndentLevel())
{
}

void REncodeWriteStream::WriteBin(const void *aPtr, TUint32 aLength)
{
	if(iWriteStream)
		{
		iWriteStream->WriteL((TUint8 *)aPtr, aLength);
		}
	if(iLogStream)
		{
		iLogStream->write((const char *)aPtr, aLength);
		iLogStream->flush();
		}
}

void REncodeWriteStream::WriteQuotedUtf8(const void *aStr, TUint32 aLength)
{
	std::string tmp((const char *)aStr, aLength);
	
	// Insert a backslash before any backslash chars
	size_t pos = 0;
	while((pos = tmp.find('\\', pos)) != std::string::npos)
		{
		tmp.insert(pos, "\\", 1);
		pos += 2;
		}

	// Insert a backslash before any double quote chars
	pos = 0;
	while((pos = tmp.find('"', pos)) != std::string::npos)
		{
		tmp.insert(pos, "\\", 1);
		pos += 2;
		}

	if(iWriteStream)
		{
		iWriteStream->WriteL((TUint8 *)tmp.data(), tmp.size());
		}
	if(iLogStream)
		{
		iLogStream->write(tmp.data(), tmp.size());
		iLogStream->flush();
		}
}

void REncodeWriteStream::WriteByte(TUint8 aByte)
{
	WriteBin(&aByte, 1);
}

void REncodeWriteStream::WriteCStr(const void *aCstr)
{
	WriteBin(aCstr, strlen((const char *)aCstr));
}

void REncodeWriteStream::WriteHexNumber(TUint32 aNumber)
{
	char buf[20];
	int len = sprintf(buf, "0x%x", aNumber);
	WriteBin(buf, len);
}


void REncodeWriteStream::WriteSpace()
{
BULLSEYE_OFF
	if(!iHumanReadable) return;
BULLSEYE_RESTORE
	WriteByte(' ');
}

void REncodeWriteStream::WriteLineEnd()
{
BULLSEYE_OFF
	if(!iHumanReadable) return;
BULLSEYE_RESTORE
#ifdef linux
	WriteByte('\n');
#else
	WriteBin("\r\n", 2);
#endif
}


void REncodeWriteStream::WriteIndent()
{
BULLSEYE_OFF
	if(!iHumanReadable) return;
BULLSEYE_RESTORE
	for(int i=0; i<iIndentLevel; ++i)
		{
		WriteByte('\t');
		}
}

void REncodeWriteStream::IncIndent()
{
	prog.IncIndent();
	++iIndentLevel;
BULLSEYE_OFF
	if(iIndentLevel < 0) FatalError();
BULLSEYE_RESTORE
}

void REncodeWriteStream::DecIndent()
{
	prog.DecIndent();
	--iIndentLevel;
BULLSEYE_OFF
	if(iIndentLevel < 0) FatalError();
BULLSEYE_RESTORE
}

bool REncodeWriteStream::HumanReadable() const
{
	return iHumanReadable;
}

bool &REncodeWriteStream::PemOut()
{
	return iPemOut;
}

bool &REncodeWriteStream::Verbose()
{
	return iVerbose;
}



std::string REncodeWriteStream::CertFileName(TUint32 aFormat, TUint32 aCertNumber)
{
	std::stringstream ss;
	ss << iCertBaseName;
	ss << "cert";
	ss << aCertNumber;
	if(aFormat == EX509Certificate)
		{
		if(iPemOut)
			{
			ss << ".pem";
			}
		else
			{
			ss << ".der";
			}
		}
	else
		{
		ss << ".dat";
		}
	
	return ss.str();
}



void REncodeWriteStream::Close()
{
BULLSEYE_OFF
	if(iWriteStream)
BULLSEYE_RESTORE
		{
		iWriteStream->Close();
		}
}

CFileStore *REncodeWriteStream::StoreObject()
{
BULLSEYE_OFF
	if(iStore == 0) FatalError();
BULLSEYE_RESTORE
	return iStore;
}

RWriteStream &REncodeWriteStream::StoreWriteStream()
{
BULLSEYE_OFF
	if(iWriteStream == 0) FatalError();
BULLSEYE_RESTORE
	return *iWriteStream;
}

bool REncodeWriteStream::Quiet() const
{
	return iLogStream != 0;
}

TUint8 fromHex(TUint8 ch)
	/**
	   Convert a single hex char from ascii
	 */
{
	// convert to lowercase
	if((ch >= 'A') && (ch <= 'F'))
		{
		ch -= ('A' -'a');
		}
	// Handle a-f
	if((ch >= 'a') && (ch <= 'f'))
		{
		return ch - 'a' + 10;
		}

	// Handle 0-9
	if((ch >= '0') && (ch <= '9'))
		{
		return ch - '0';
		}

	// Illegal
	FatalError();
	return 0xff;
}

EncDecContainerItem::~EncDecContainerItem()
{
}


#ifdef _BullseyeCoverage
#pragma BullseyeCoverage off
#endif
std::string EncDecContainerItem::ItemName() const
{
	// Should never be called
	exit(-1);
	std::string tmp;
	return tmp;
}

void EncDecContainerItem::SetItemName(const std::string &)
{
	// Should never be called
	exit(-1);
}
#ifdef _BullseyeCoverage
#pragma BullseyeCoverage restore
#endif



EncDecContainer::EncDecContainer(const char *aContainerName, EncDecContainerItemFactoryFunc *aFactory)
	: iName(aContainerName), iFactory(aFactory), iArray()
{
}

EncDecContainer::~EncDecContainer()
{
	reset();
}

void EncDecContainer::push_back(EncDecContainerItem *aItem)
{
	iArray.push_back(aItem);
}

const EncDecContainerItem &EncDecContainer::operator[](TUint32 aIndex) const
{
	return *iArray[aIndex];
}

EncDecContainerItem &EncDecContainer::operator[](TUint32 aIndex)
{
	return *iArray[aIndex];
}

TUint32 EncDecContainer::size() const
{
	return iArray.size();
}

void EncDecContainer::reset()
{
	while(!iArray.empty())
		{
		EncDecContainerItem *p = iArray.back();
		iArray.pop_back();
		delete p;
		}
}



void EncDecContainer::Encode(REncodeWriteStream &aWriteStream) const
{
	// Calculate Start/End container names
	std::string startName("Start");
	startName.append(iName);
	std::string endName("End");
	endName.append(iName);

	prog << Log::Indent() << "Writing " << startName << Log::Endl();

	TUint32 arrayLength = iArray.size();

	if(aWriteStream.HumanReadable())
		{
		// Human
		aWriteStream.WriteIndent();
		aWriteStream.WriteBin(startName.data(), startName.size());
		aWriteStream.WriteLineEnd();
		}
	else
		{
		// Binary
		// Write length of array
		aWriteStream.WriteBin(&arrayLength, sizeof(arrayLength));
		}
	
	aWriteStream.IncIndent();

	// Write array entries
	for(TUint32 i=0; i < arrayLength; ++i)
		{
		std::ostringstream entryComment;
		entryComment << "# Entry " << i+1;
		prog << Log::Indent() << entryComment.str() << Log::Endl();
		
		bool bracketItem = (iArray[i]->ItemType() != 0);
		if(aWriteStream.HumanReadable())
			{
			if(bracketItem)
				{
				std::ostringstream itemStart;
				itemStart << "Start" << iArray[i]->ItemType() << " " << iArray[i]->ItemName();
				prog << Log::Indent() << itemStart.str() << Log::Endl();
				
				aWriteStream.WriteIndent();
				aWriteStream.WriteCStr("Start");
				aWriteStream.WriteCStr(iArray[i]->ItemType());
				aWriteStream.WriteCStr(" \"");
				aWriteStream.WriteQuotedUtf8(iArray[i]->ItemName().data(), iArray[i]->ItemName().size());
				aWriteStream.WriteCStr("\"");
				aWriteStream.WriteLineEnd();
				}
			else
				{
				aWriteStream.WriteIndent();
				aWriteStream.WriteBin(entryComment.str().data(), entryComment.str().size());
				aWriteStream.WriteLineEnd();
				}
			}
		aWriteStream.IncIndent();
		iArray[i]->Encode(aWriteStream);
		aWriteStream.DecIndent();
		if(bracketItem && aWriteStream.HumanReadable())
			{
			std::ostringstream tmp;
			tmp << "End" << iArray[i]->ItemType();

			prog << Log::Indent() << tmp.str() << Log::Endl();

			aWriteStream.WriteIndent();
			aWriteStream.WriteBin(tmp.str().data(), tmp.str().size());
			aWriteStream.WriteLineEnd();
			}
		}

	aWriteStream.DecIndent();

	if(aWriteStream.HumanReadable())
		{
		// Human
		aWriteStream.WriteIndent();
		aWriteStream.WriteCStr("End");
		aWriteStream.WriteBin(iName.data(), iName.size());
		aWriteStream.WriteLineEnd();
		}

	prog << Log::Indent() << endName << Log::Endl();

	return;
}

void EncDecContainer::Decode(RDecodeReadStream &aReadStream)
{
	// Calculate Start/End container names
	std::string startName("Start");
	startName.append(iName);
	std::string endName("End");
	endName.append(iName);

	prog << Log::Indent() << "Reading " << startName << Log::Endl();

	if(aReadStream.HumanReadable())
		{
		// Human

		// Check/consume container StartX
		aReadStream.CheckName(startName);

		prog.IncIndent();
		
		// Create items until we find the container EndX
		TUint32 entryNum=1;
		while(aReadStream.PeakToken() != endName)
			{
			// Progress message
			prog << Log::Indent() << "# Entry " << std::dec << entryNum++ << std::hex << Log::Endl();

			EncDecContainerItem *item = iFactory();
			bool bracketedItem = (item->ItemType() != 0);
			std::string itemName;
			if(bracketedItem && aReadStream.HumanReadable())
				{
				// Bracketed item, so read ItemType tag and ItemName
				std::string itemStart("Start");
				itemStart.append(item->ItemType());
				aReadStream.CheckName(itemStart);
				prog << Log::Indent() << itemStart;

				// Read item name
				aReadStream.ReadNextToken();
				itemName = aReadStream.Token();
				}
		
			prog.IncIndent();
			item->Decode(aReadStream);
			iArray.push_back(item);
			prog.DecIndent();
			if(bracketedItem && aReadStream.HumanReadable())
				{
				// Bracketed item, so read End ItemType tag
				std::string itemEnd("End");
				itemEnd.append(item->ItemType());
				aReadStream.CheckName(itemEnd);
				// and set item name
				item->SetItemName(itemName);
				}
			}

		// Check/consume container EndX
		aReadStream.CheckName(endName);

		prog.DecIndent();
		}
	else
		{
		// Binary
		// Read number of entries
		TUint32 arrayLength;
		aReadStream.RawRead(&arrayLength, sizeof(arrayLength));

		prog.IncIndent();
		for(TUint32 i=0; i < arrayLength; ++i)
			{
			EncDecContainerItem *item = iFactory();
			prog << Log::Indent() << "# Entry " << std::dec << i+1 << std::hex << Log::Endl();
			prog.IncIndent();
			item->Decode(aReadStream);
			prog.DecIndent();
			iArray.push_back(item);
			}
		prog.DecIndent();
		}

	prog << Log::Indent() << endName << Log::Endl();

}




REncodeWriteStream& operator<<(REncodeWriteStream& aStream, const EncDecContainer &aContainer)
{
	aContainer.Encode(aStream);
	return aStream;
}

RDecodeReadStream& operator>>(RDecodeReadStream& aStream,EncDecContainer &aContainer)
{
	aContainer.Decode(aStream);
	return aStream;
}

//
// TUid
//
void EncodeHuman(REncodeWriteStream& aStream,const TUid &aUid)
{
	aStream.WriteHexNumber(aUid.iUid);
}
void DecodeHuman(RDecodeReadStream& aStream,TUid &aUid)
{
	aUid.iUid = aStream.ReadUnsignedNumber(sizeof(aUid.iUid));
}

//
// TName
//
void EncodeHuman(REncodeWriteStream& aStream,const TName &aName)
{
	// Compress the internal UTF-16 to human readable UTF-8
	;
	TInt outputBytes = 0;
	TUint8 *outBuf = cstrFromUtf16(aName.Ptr(), aName.Length(), outputBytes);
	
	aStream.WriteByte('"');
	aStream.WriteQuotedUtf8(outBuf, outputBytes);
	aStream.WriteByte('"');

	delete [] outBuf;
}
void DecodeHuman(RDecodeReadStream& aStream,TName &aName)
{
	aStream.ReadNextToken();

	// Expand UTF-8 into internal UTF-16LE representation
	TInt outputWords = 0;
	TText *outputBuf = utf16FromUtf8((const TUint8 *)aStream.Token().data(), aStream.Token().size(), outputWords);
	memcpy((void *)aName.Ptr(), outputBuf, outputWords*2);
	aName.SetLength(outputWords);
	delete [] outputBuf;
}

void readContainer(const std::string &aFileName, bool aHuman, EncDecContainer &container)
{
	if(aHuman)
		{
		FileReadStream fileReadStream(aFileName, true);
		std::string certBaseName(aFileName);
		size_t dotLoc = certBaseName.rfind('.');
		if(dotLoc != std::string::npos)
			{
			certBaseName.erase(dotLoc);
			}
		certBaseName += '_';
		RDecodeReadStream inStream(certBaseName, fileReadStream); // human readable
		inStream >> container;
		inStream.Close();
		}
	else
		{
		RFs fs;
		User::LeaveIfError(fs.Connect());
		CleanupClosePushL(fs);
		
		CPermanentFileStore* store = CPermanentFileStore::OpenLC(fs, _L16(aFileName.c_str()),EFileShareAny | EFileRead);
		store->SetTypeL(KPermanentFileStoreLayoutUid);

		RStoreReadStream rootStream;
		rootStream.OpenLC(*store, store->Root());
		TUint32 dataStreamId;
		rootStream >> dataStreamId;
		CleanupStack::PopAndDestroy(&rootStream); 

		RStoreReadStream dataStream;
		dataStream.OpenLC(*store, dataStreamId);

		RDecodeReadStream readStream(store, dataStream); // binary store

		readStream >> container;

		CleanupStack::PopAndDestroy(&dataStream); 
		CleanupStack::PopAndDestroy(store); 
		CleanupStack::PopAndDestroy(&fs); 
		}
}


void writeContainer(const char *aFileName, bool aHuman, bool aPemOut, bool aVerbose, const EncDecContainer &container)
{
	prog << Log::Indent() << "Writing output file '" << aFileName << "'" << Log::Endl();
	prog.IncIndent();
	if(aHuman)
		{
		FileWriteStream fileWriteStream(aFileName);
		// Calculate based name by striping last .xx extension and appending an underscore.
		std::string certBaseName(aFileName);
		size_t dotLoc = certBaseName.rfind('.');
		if(dotLoc != std::string::npos)
			{
			certBaseName.erase(dotLoc);
			}
		certBaseName += '_';
		
		REncodeWriteStream outStream(certBaseName, fileWriteStream); // human readable
		outStream.PemOut() = aPemOut;
		outStream.Verbose() = aVerbose;
		outStream << container;
		outStream.Close();
		}
	else
		{
		RFs fs;
		User::LeaveIfError(fs.Connect());
		CleanupClosePushL(fs);

		CFileStore* store = CPermanentFileStore::ReplaceLC(fs,_L16(aFileName),EFileShareAny | EFileRead | EFileWrite);
		store->SetTypeL(KPermanentFileStoreLayoutUid);
		
		RStoreWriteStream dataStream;
		TStreamId dataStreamId = dataStream.CreateLC(*store);

		REncodeWriteStream outStream(store, dataStream); // binary
		outStream << container;
		outStream.Close();

		dataStream.CommitL();
		dataStream.Close();

		RStoreWriteStream rootStream;
		TStreamId rootId = rootStream.CreateLC(*store);
		rootStream << dataStreamId;
		rootStream.CommitL();
		rootStream.Close();
		

		store->SetRootL(rootId);
		store->CommitL();
		CleanupStack::PopAndDestroy(&dataStream); 
		CleanupStack::PopAndDestroy(store); 
		CleanupStack::PopAndDestroy(&fs); 
		}
	prog.DecIndent();
}


// End of file