// UB_SEC.CPP
//
// Copyright (c) 1996-1999 Symbian Ltd.  All rights reserved.
//

//
// UB.SEC.CPP
//

#include "ub_std.h"

const TInt KSecurityStandardUid=0x10000103;

const TInt KMaxPasswordInternal=(KMaxPassword<<1);

const TInt KEncryptBlockSize=32;

class CSecurityModel : public CBoundedSecurityBase
	{
public:
// Encryption
	virtual void ConstructL() =0;
	virtual void SetL(const TDesC& aOldPassword,const TDesC& aNewPassword)=0;
	virtual TPtrC8 SecurityData() const =0;
	virtual CSecurityEncryptBase* NewEncryptL(const TDesC8& aInit) const =0;  
// Decryption
	static TBool Recognizer(const TDesC8& aSecurityData);
	static CSecurityModel* NewL(const TDesC8& aSecurityData);
	virtual void ConstructL(const TDesC8& aSecurityData) =0;
	virtual void PrepareL(const TDesC& aPassword) =0;
	virtual CSecurityDecryptBase* NewDecryptL(const TDesC8& aInit) const =0;  
// 
	virtual TInt IsEnabled() const =0;
	virtual void SetEnabledL(const TDesC& aPassword,TBool aIsEnabled) =0;
//
	virtual TInt MaxCipherLength(TInt aPlainTextLength) const =0;
protected:
	CSecurityModel();
	};

class TStandardSecurityData
	{
public:
	TStandardSecurityData();
	TStandardSecurityData::TStandardSecurityData(const TDesC8& aSecurityData);
	void MakePassword(const TDesC& aPassword,TBool aIsEnabled);
	TInt operator==(const TStandardSecurityData& aData) const;
	TBool IsValid();
public:
	TInt16 iEnabled;
private:
	TUint16 iCheck;
	TUid iAlgorithm;
	TText iPasswordInternal[KMaxPasswordInternal];
	};

class TStandardSecurityKey
	{
public:
	TStandardSecurityKey();
	void MakeKey(const TDesC& aPassword);
public:
	TBool iKeyMade;
	TText iKeyText[KMaxPasswordInternal];
	};

class CSecurityStandardEncrypt : public CSecurityEncryptBase
	{
public:
	CSecurityStandardEncrypt();
	void ConstructL(const TStandardSecurityKey& aKey,const TDesC8& aInit);
	TInt EncryptL(TDes8& aOutput,const TDesC8& aInput);
	TInt CompleteL(TDes8& aOutput,const TDesC8& aInput);
private:
	TStandardSecurityKey iKey;
	TInt iPos;
	TBuf8<KEncryptBlockSize> encryptBuf;
	};

class CSecurityStandardDecrypt : public CSecurityDecryptBase
	{
public:
	CSecurityStandardDecrypt();
	void ConstructL(const TStandardSecurityKey& aKey,const TDesC8& aInit);
	TInt DecryptL(TDes8& aOutput,const TDesC8& aInput);
private:
	TStandardSecurityKey iKey;
	TInt iPos;
	};

class CSecurityStandard : public CSecurityModel
	{
public:
	CSecurityStandard();
// Encryption
	void ConstructL();
	void SetL(const TDesC& aOldPassword,const TDesC& aNewPassword);
	TPtrC8 SecurityData() const;
	CSecurityEncryptBase* NewEncryptL(const TDesC8& aInit) const;  
// Decryption
	static TBool Recognizer(const TDesC8& aSecurityData);
	static CSecurityModel* NewL(const TDesC8& aSecurityData);
	void ConstructL(const TDesC8& aSecurityData);
	void PrepareL(const TDesC& aPassword);
	CSecurityDecryptBase* NewDecryptL(const TDesC8& aInit) const;  
// 
	TInt IsEnabled() const;
	void SetEnabledL(const TDesC& aPassword,TBool aIsEnabled);
//
	TInt MaxCipherLength(TInt aPlainTextLength) const;
private:
	TStandardSecurityData iData;
	TStandardSecurityKey iKey;
private:
	TBool PasswordRequired() const;
	};

LOCAL_C TUint multiply(TText* aPtr,TInt aLength,TInt aConstant)
//
// Do long multiply - for creating internal password or session key
//
	{

	TInt carry=0;
	TText *pE=aPtr+aLength;
	while (aPtr<pE)
		{
		TUint val=(*aPtr*aConstant)+carry;
		*aPtr++=(TText)val;
		carry=val>>(8*sizeof(TText));
		}
	return (carry);
	}

EXPORT_C CSecurityEncryptBase::CSecurityEncryptBase()
	{

	__DECLARE_NAME(_S("CSecurityEncryptBase"));
	}

EXPORT_C CSecurityDecryptBase::CSecurityDecryptBase()
	{

	__DECLARE_NAME(_S("CSecurityDecryptBase"));
	}

EXPORT_C CSecurityBase::CSecurityBase()
	{

	__DECLARE_NAME(_S("CSecurityBase"));
	}

CSecurityModel::CSecurityModel()
	{

	__DECLARE_NAME(_S("CSecurityModel"));
	}

EXPORT_C CBoundedSecurityBase* Security::NewL()
//
// Create a new security object (the one with the strongest algorithm
// currently available) with a default password of nothing and disabled,
// for encryption
//
	{

	CSecurityModel* pS;
	pS=new(ELeave) CSecurityStandard;
	TRAPD(r,pS->ConstructL());
	if (r!=KErrNone)
		{
		delete pS;
		User::Leave(r);
		}
	return (pS);
	}

EXPORT_C CBoundedSecurityBase* Security::NewL(const TDesC8& aSecurityData)
//
// Create a new security object with a security data for decryption
//
	{

	if (CSecurityStandard::Recognizer(aSecurityData))
		return(CSecurityStandard::NewL(aSecurityData));
/*
// for example, if stronger algorithms added
	if (CSecurityStrong::Recognizer(aSecurityData))
		return(CSecurityStrong::NewL(aSecurityData));
*/
	return (NULL);
	}

TBool CSecurityStandard::Recognizer(const TDesC8& aSecurityData)
//
// the security object recognizes it's own encrypting work
//
	{
	
	TStandardSecurityData securityData(aSecurityData);
	return (securityData.IsValid());
	}

CSecurityModel* CSecurityStandard::NewL(const TDesC8& aSecurityData)
//
// Create a new CSecurityStandard object
//
	{
	
	CSecurityStandard* pS=new(ELeave) CSecurityStandard;
	TRAPD(r,pS->ConstructL(aSecurityData));
	if (r!=KErrNone)
		{
		delete pS;
		User::Leave(r);
		}
	return(pS);
	}

CSecurityStandard::CSecurityStandard()
	{

	__DECLARE_NAME(_S("CSecurityStandard"));
	}

void CSecurityStandard::ConstructL()
//
// Default constructor
//
	{

	}

void CSecurityStandard::SetL(const TDesC& aOldPassword,const TDesC& aNewPassword)
//
// Set a new password, creating new internal password and decryption key
//
	{

	TStandardSecurityData data;
	data.MakePassword(aOldPassword,iData.iEnabled);
	if (!(iData==data))
		User::Leave(KErrGeneral);
	iData.MakePassword(aNewPassword,iData.iEnabled);
	iKey.MakeKey(aNewPassword);
	}

TPtrC8 CSecurityStandard::SecurityData() const
//
// Return the security object's security data 
//
	{

	return (TPtrC8((TUint8*)&iData,sizeof(TStandardSecurityData)));
	}

CSecurityEncryptBase* CSecurityStandard::NewEncryptL(const TDesC8& aInit) const
//
// Create a new encryption object, aInit is used as initialisation vector data
//  
	{

	if (PasswordRequired())
		User::Leave(KErrGeneral);
	CSecurityStandardEncrypt* pE=new(ELeave) CSecurityStandardEncrypt;
	pE->ConstructL(iKey,aInit);
	return (pE);
	}

void CSecurityStandard::ConstructL(const TDesC8& aSecurityData)
//
// Construct a security object with security data, ready for decryption
//
	{

	__ASSERT_ALWAYS(aSecurityData.Length()==sizeof(TStandardSecurityData),Panic(ESecurityData));
	Mem::Copy(&iData,aSecurityData.Ptr(),sizeof(TStandardSecurityData));
	}

void CSecurityStandard::PrepareL(const TDesC& aPassword)
//
// Prepare to start decrypting by making a decryption key
//
	{

	TStandardSecurityData data;
	data.MakePassword(aPassword,iData.iEnabled);
	if (!(iData==data))
		User::Leave(KErrGeneral);
	iKey.MakeKey(aPassword);
	}

CSecurityDecryptBase* CSecurityStandard::NewDecryptL(const TDesC8& aInit) const
//
// Create a new decryption object; aInit is used as initialisation vector data,
// and must be the same as that passed for encryption
//  
	{

	if (PasswordRequired())
		User::Leave(KErrGeneral);
	CSecurityStandardDecrypt* pD=new(ELeave) CSecurityStandardDecrypt;
	pD->ConstructL(iKey,aInit);
	return (pD);
	}

TInt CSecurityStandard::IsEnabled() const
//
// Check whether the password is enabled
//
	{

	return (iData.iEnabled);
	}

void CSecurityStandard::SetEnabledL(const TDesC& aPassword,TBool aIsEnabled)
//
// Set the password enabled state.
//
	{

	TStandardSecurityData data;
	data.MakePassword(aPassword,iData.iEnabled);
	if (!(iData==data))
		User::Leave(KErrGeneral);
	iData.iEnabled=(TInt16)aIsEnabled;
	}

TBool CSecurityStandard::PasswordRequired() const
//
// Test whether the session key has been made
//
	{

	return (!iKey.iKeyMade);
	}

TInt CSecurityStandard::MaxCipherLength(TInt aPlainTextLength) const
//
// Return the total block size of output when encrypting given input length
//
	{
	return (aPlainTextLength+(KEncryptBlockSize-1))&~(KEncryptBlockSize-1);	// block size is a power of two
	}


CSecurityStandardEncrypt::CSecurityStandardEncrypt()
//
// Constructor
//
	{

	__DECLARE_NAME(_S("CSecurityStandardEncrypt"));
	}

void CSecurityStandardEncrypt::ConstructL(const TStandardSecurityKey& aKey,const TDesC8& aInit)
//
// pass in, as aInit, something which will vary for each encryption object, such as
// stream uid numbers, to be used as initialisation vectors.
//
	{

	iKey=aKey;
	iPos=0;
	for (TInt i=0;i<aInit.Length();i++)
		iPos^=aInit[i];	
	}

TInt CSecurityStandardEncrypt::EncryptL(TDes8& aOutput,const TDesC8& aInput)
//
// Encrypt the data, return how much of the input data was consumed (this will not be
// all the input data if the maximum length of the output descriptor is too small)
//
	{

	aOutput.Zero();
	TText8* pB=(TText8*)aInput.Ptr();
	TText8* pE;
	if (aOutput.MaxLength()>=(((aInput.Length()+encryptBuf.Length())/encryptBuf.MaxLength())*encryptBuf.MaxLength()))
		pE=pB+aInput.Length();
	else 
		pE=pB+((aOutput.MaxLength()/encryptBuf.MaxLength())*encryptBuf.MaxLength())+encryptBuf.MaxLength()-encryptBuf.Length()-1;
	TInt eBufMaxLen=encryptBuf.MaxLength();
	while (pB<pE)
		{
		encryptBuf.Append((TText8)(*pB+iKey.iKeyText[(iPos++)&(KMaxPasswordInternal-1)]));
		pB++;
		if (encryptBuf.Length()==eBufMaxLen)
			{
			aOutput.Append(encryptBuf);
			encryptBuf.Zero();
			}
		}
	return (pE-(TText8*)aInput.Ptr());
	}

TInt CSecurityStandardEncrypt::CompleteL(TDes8& aOutput,const TDesC8& /*aInput*/)
//
// pad the last block to make it up to size, user must remove upon decryption.
// return how much of the input data was consumed.
//
	{

	aOutput.Zero();
	if (encryptBuf.Length()==0)
		return (0);
	if (aOutput.MaxLength()<encryptBuf.Length())
		return (KErrArgument);
	TInt outLength;
	if (aOutput.MaxLength()>=encryptBuf.MaxLength())
		outLength=encryptBuf.MaxLength();
	else
		outLength=aOutput.MaxLength();
	while (encryptBuf.Length()<outLength)
		encryptBuf.Append((TText8)('0'+iKey.iKeyText[(iPos++)&(KMaxPasswordInternal-1)]));
	aOutput.Append(encryptBuf);
	encryptBuf.Zero();
	return (0);
	}

	CSecurityStandardDecrypt::CSecurityStandardDecrypt()
//
// Constructor
//
	{

	__DECLARE_NAME(_S("CSecurityStandardDecrypt"));
	}

void CSecurityStandardDecrypt::ConstructL(const TStandardSecurityKey& aKey,const TDesC8& aInit)
//
// Provide the same initialisation vector as for encryption, via aInit
//
	{

	iKey=aKey;
	iPos=0;
	for (TInt i=0;i<aInit.Length();i++)
		iPos^=aInit[i];	
	}

TInt CSecurityStandardDecrypt::DecryptL(TDes8& aOutput,const TDesC8& aInput)
//
// Decrypt the data
//
	{

	aOutput.Zero();
	TText8 *pB=(TText8*)aInput.Ptr();
	TText8 *pE;
	if (aOutput.MaxLength()>=aInput.Length())
		pE=pB+aInput.Length();
	else 
		pE=pB+aOutput.MaxLength();
	while (pB<pE)
		{
		aOutput.Append((TText8)(*pB-iKey.iKeyText[(iPos++)&(KMaxPasswordInternal-1)]));
		pB++;
		}
	return (pE-(TText8*)aInput.Ptr());
	}

TStandardSecurityData::TStandardSecurityData()
//
// Constructor creating default internal password
//
	{

	MakePassword(TPtrC(),EFalse);
	TUid uid=TUid::Uid(KSecurityStandardUid);
	iAlgorithm=uid;
	}

TStandardSecurityData::TStandardSecurityData(const TDesC8& aSecurityData)
//
// Constructor taking existing piece of security data
//
	{
	if (aSecurityData.Length()!=sizeof(TStandardSecurityData))
		Mem::FillZ(this,sizeof(TStandardSecurityData));
	Mem::Copy(this,aSecurityData.Ptr(),sizeof(TStandardSecurityData));
	}

TBool TStandardSecurityData::IsValid()
//
// Check that this security data is a valid TStandardSecurityData
//
	{

	TUid uid=TUid::Uid(KSecurityStandardUid);
	return(iAlgorithm==uid);
	}

TInt TStandardSecurityData::operator==(const TStandardSecurityData& aData) const
//
// Equality
//
	{

	return (Mem::Compare(&iPasswordInternal[0],KMaxPasswordInternal,&aData.iPasswordInternal[0],KMaxPasswordInternal)==0 &&
		   iCheck==aData.iCheck && iEnabled==aData.iEnabled && iAlgorithm==aData.iAlgorithm);
	}

void TStandardSecurityData::MakePassword(const TDesC& aPassword,TBool aIsEnabled)
//
// Convert the password to its internal form.
//
	{

	const TInt KMultiplyConstant=137;
	TUint i=0;
	TUint j=0;
	TUint c=aPassword.Length();
	TText pass[KMaxPasswordInternal];
	while (j<c)
		{
		pass[i++]=(TText)(0x100+j-c);
		pass[i++]=aPassword[j++];
		}
	while (j<KMaxPassword)
		{
		pass[i++]=(TText)(0x100+j-c);
		pass[i++]=(TText)(0x100+j++-c);
		}
	iCheck=0;
	Mem::Crc(iCheck,&pass[0],KMaxPasswordInternal*sizeof(TText));
	iEnabled=(TInt16)aIsEnabled;
	TText npass[KMaxPasswordInternal];
	Mem::Copy(&npass[0],&pass[0],KMaxPasswordInternal*sizeof(TText));
	TText fpass[KMaxPasswordInternal];
	for (i=0;i<KMaxPasswordInternal;)
		fpass[i++]=(TText)multiply(&npass[0],KMaxPasswordInternal,KMultiplyConstant);
	for (i=0,j=KMaxPasswordInternal;i<KMaxPasswordInternal;i++)
		iPasswordInternal[i]=(TText)(fpass[--j]+pass[i]);
	}


TStandardSecurityKey::TStandardSecurityKey()
//
// Constructor
//
	{

	iKeyMade=EFalse;
	}

void TStandardSecurityKey::MakeKey(const TDesC& aPassword)
//
// Convert the password to an encryption/decryption session key.
//
	{

	const TInt KMultiplyConstantCrypt=161;
	TUint i=0;
	TUint j=0;
	TUint c=aPassword.Length();
	TText pass[KMaxPasswordInternal];
	while (j<c)
		{
		pass[i++]=(TText)(0x100+j-c);
		pass[i++]=aPassword[j++];
		}
	while (j<KMaxPassword)
		{
		pass[i++]=(TText)(0x100+j-c);
		pass[i++]=(TText)(0x100+j++-c);
		}
	TText npass[KMaxPasswordInternal];
	Mem::Copy(&npass[0],&pass[0],KMaxPasswordInternal*sizeof(TText));
	TText fpass[KMaxPasswordInternal];
	for (i=0;i<KMaxPasswordInternal;)
		fpass[i++]=(TText)multiply(&npass[0],KMaxPasswordInternal,KMultiplyConstantCrypt);
	for (i=0,j=KMaxPasswordInternal;i<KMaxPasswordInternal;i++)
		iKeyText[i]=(TText)(fpass[--j]+pass[i]);
	iKeyMade=ETrue;
	}

