linklayerprotocols/pppnif/SPPP/mschap2.cpp
author hgs
Thu, 08 Jul 2010 15:21:49 +0530
changeset 39 45fd446095e6
parent 0 af10295192d8
permissions -rw-r--r--
201027_01

// Copyright (c) 2003-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:
// Extensions, Version 2 (MS-CHAP-V2) - RFC 2759, except the
// authenticator-controlled authentication retry mechanisms and the
// password changing mechanisms - this is in accordance with the
// requirements.
// 
//

/**
 @file
 @brief Source file for the implementation of Microsoft PPP CHAP
 @internalComponent 
*/

#include "mschap2.h"
#include "MSCHAP.H"

// using SHA-1
#include <hash.h>

// using DES
#include <symmetric.h>

// using random
#include <random.h>
// using MD4
#include "MD4.H"
#include "PPPConfig.h"


CPppMsChap2::CPppMsChap2()
/**
   Constructor.
   @internalComponent
*/
	: iResponseValue(KPppMsChap2ResponseValueSize),
	iAuthenticatorResponse(KPppMsChap2AuthenticatorResponseSize)
	{
	}


CPppMsChap2::~CPppMsChap2()
/**
   Destructor.
   @internalComponent
*/
	{
#ifdef _UNICODE
	delete iUserName;
#endif
	}

void CPppMsChap2::InitL(CPppLcp* aLcp)
/**
   @copydoc CPppChap::InitL(CPppLcp*)
   @see CPppChap::InitL(CPppLcp*)
   @internalComponent
*/
	{
	CPppChap::InitL(aLcp);
	}


void CPppMsChap2::CheckChallengePacketL(RMBufPacket& aPacket)
/**
   @copydoc CPppChap::CheckChallengePacketL(RMBufPacket&)
   @copydoc CPppChap::CheckChallengePacketL(RMBufPacket&)
   @internalComponent
*/
	{
	__ASSERT_ALWAYS(aPacket.Length() >= KPppChapCodeFieldSize +
				KPppChapIdFieldSize + 
				KPppChapLengthFieldSize +
				KPppChapValueSizeFieldSize + 
				KPppMsChap2AuthenticatorChallengeSize,
			User::Leave(KErrUnderflow));

	__ASSERT_ALWAYS(*(aPacket.First()->Ptr() +
							KPppChapCodeFieldSize +
							KPppChapIdFieldSize +
							KPppChapLengthFieldSize) == 
						KPppMsChap2AuthenticatorChallengeSize,
				User::Leave(KErrOverflow));
	}


void CPppMsChap2::MakeResponseL(TUint8 /*aChallengeId*/, 
				const TDesC8& aChallengeValue, 
				TPtrC8& aResponseValue, 
				TPtrC8& aResponseName)
/**
   @copydoc CPppChap::MakeResponseL(TUint8,const TDesC8&,TPtrC8&,TPtrC8&)
   @see CPppChap::MakeResponseL(TUint8,const TDesC8&,TPtrC8&,TPtrC8&)
   @internalComponent
*/
	{
	ASSERT(aChallengeValue.Length() ==
			KPppMsChap2AuthenticatorChallengeSize);

	TPtr8 peerChallenge(const_cast<TUint8*>(iResponseValue.Ptr()),
				KPppMsChap2PeerChallengeSize,
				KPppMsChap2PeerChallengeSize);
	GeneratePeerChallengeL(peerChallenge);

// The NT username shall be char and not be longer than
// KPppMsChapMaxNTUserNameLength
    const CCredentialsConfig* credentials = iPppLcp->GetCredentials();
	const TDesC& username = credentials->GetUserName();
	__ASSERT_ALWAYS(username.Length() <=
				KPppMsChapMaxNTUserNameLength,
			User::Leave(KErrTooBig));

// The NT password shall be Unicode and not be longer than
// KPppMsChapMaxNTPasswordLength
	const TDesC& password = credentials->GetPassword();
	__ASSERT_ALWAYS(password.Length() <=
				KPppMsChapMaxNTPasswordLength,
			User::Leave(KErrTooBig));

#ifdef _UNICODE
// The MS-CHAP-V2 routines require the username to be represented as
// 0-to-256-char (RFC 2759), so convert the username.
	delete iUserName;
	iUserName = 0;
	iUserName = HBufC8::NewL(username.Length());
	iUserName->Des().Copy(username);
	aResponseName.Set(*iUserName);

	TPtrC16 uniPassword(password);
#else //! _UNICODE
	aResponseName.Set(username);

// The MS-CHAP-V2 routines require the password to be represented as
// 0-to-256-unicode-char (RFC 2759), so convert the password to
// Unicode.
	HBufC16& uniPassword = *HBufC16::NewLC(password.Length());
	uniPassword.Des().Copy(password);
#endif //! _UNICODE

	TPtr8 ntResponse(const_cast<TUint8*>(iResponseValue.Ptr()) +
				KPppMsChap2PeerChallengeSize +
				KPppMsChap2ResponseReservedSize,
			KPppMsChap2NTResponseSize,
			KPppMsChap2NTResponseSize);

	GenerateNTResponseL(aChallengeValue, 
			peerChallenge, 
			aResponseName,
			uniPassword, 
			ntResponse);

	aResponseValue.Set(iResponseValue);

	GenerateAuthenticatorResponseL(uniPassword, 
					ntResponse,
					peerChallenge, 
					aChallengeValue,
					aResponseName,
					iAuthenticatorResponse);

#ifndef _UNICODE
	CleanupStack::PopAndDestroy(&uniPassword);
#endif //! _UNICODE

	ASSERT(aResponseValue.Length() == KPppMsChap2ResponseValueSize);
	ASSERT(aResponseName.Length() >= KPppChapMinNameSize &&
		   aResponseName.Length() <= KPppMsChapMaxNTUserNameLength);
	}


void CPppMsChap2::SuccessL(RMBufPacket& aPacket)
/**
   @copydoc CPppChap::SuccessL(RMBufPacket&)
   @see CPppChap::SuccessL(RMBufPacket&)
   @internalComponent
*/
	{
	__ASSERT_ALWAYS(aPacket.Length() >= KPppChapCodeFieldSize +
				KPppChapIdFieldSize + 
				KPppChapLengthFieldSize +
				KPppMsChap2AuthenticatorResponseSize, 
			User::Leave(KErrUnderflow));

// check the id
	if (!CheckIdentifier(aPacket))
		User::Leave(KErrGeneral);

// no more retries
	TimerCancel();

	// Read the length of the MS-CHAP-V2 Failure packet and compute
	// the length of the CHAP Message field and go past the CHAP Code
	// field, the CHAP Identifier field and the CHAP Length field, in
	// order to read the CHAP Message field.

	TPtrC8 authResponse(aPacket.First()->Ptr() + 
					KPppChapCodeFieldSize + 
					KPppChapIdFieldSize + 
					KPppChapLengthFieldSize,
			KPppMsChap2AuthenticatorResponseSize);

// 	TPtrC8 message(ptr + KPppChapCodeFieldSize + KPppChapIdFieldSize + KPppChapLengthFieldSize + KPppMsChap2AuthenticatorResponseSize + 3, BigEndian::Get16(ptr + KPppChapCodeFieldSize + KPppChapIdFieldSize) - KPppChapCodeFieldSize - KPppChapIdFieldSize - KPppChapLengthFieldSize - KPppMsChap2AuthenticatorResponseSize - 3);

	if (authResponse!=iAuthenticatorResponse)
		DoFail(KErrIfAuthenticationFailure);
	else
		DoSucceed();
	}


void CPppMsChap2::FailureL(RMBufPacket& aPacket)
/**
   @copydoc CPppChap::FailureL(RMBufPacket&)
   @see CPppChap::FailureL(RMBufPacket&)
   @internalComponent
*/
	{
	__ASSERT_ALWAYS(aPacket.Length() >= KPppChapCodeFieldSize +
				KPppChapIdFieldSize + 
				KPppChapLengthFieldSize +
				KPppMsChap2AuthenticatorChallengeSize*2
				+ 2,
			User::Leave(KErrUnderflow));

// check the id
	if (!CheckIdentifier(aPacket))
		User::Leave(KErrGeneral);

	TimerCancel();

#ifndef _DEBUG

// The authenticator-controlled authentication retry mechanisms and
// the password changing mechanisms have not been implemented in this
// release - this is in accordance with the project requirements.
// Consequently simply fail.

	DoFail(KErrIfAuthenticationFailure);

#else // _DEBUG

// Read the length of the MS-CHAP-V2 Failure packet and compute the
// length of the CHAP Message field and go past the CHAP Code field,
// the CHAP Identifier field and the CHAP Length field, in order to
// read the CHAP Message field
	TPtrC8 failureMessage(aPacket.First()->Ptr() +
					KPppChapCodeFieldSize +
					KPppChapIdFieldSize +
					KPppChapLengthFieldSize, 
				aPacket.Length() - 
					KPppChapCodeFieldSize -
					KPppChapIdFieldSize -
					KPppChapLengthFieldSize);

	if (failureMessage.Length()==0)
		{
		DoFail(KErrIfAuthenticationFailure);
		return;
		}

	TUint msChapError;
	TUint8 isRetryAllowed;
	TUint8 passwordProtoVersion;
	TPtrC8 message;
	TInt sysError=KErrIfAuthenticationFailure;

	ProcessFailureMessageL(failureMessage, 
				msChapError,
				isRetryAllowed, 
				iChallengeRef,
				passwordProtoVersion, 
				message);

	sysError=TranslateMsChapError(msChapError);

// The code only handles KPppMsChapAuthenticationFailure, and no other
// MS-CHAP specific errors.  In particular, this code does not handle
// KPppMsChapErrorPasswordExpired, since the password changing
// mechanisms have not been implemented in this release - this is in
// accordance with the project requirements.
	if (msChapError != KPppMsChapAuthenticationFailure)
		{
		DoFail(sysError);
		return;
		}

	if (!isRetryAllowed)
		{
		DoFail(sysError);
		return;
		}

// The authenticator-controlled authentication retry mechanisms and
// the password changing mechanisms have not been implemented in this
// release - this is in accordance with the project requirements.
// Consequently simply fail.
	DoFail(sysError);

#endif // _DEBUG
	}


inline void CPppMsChap2::ProcessFailureMessageL(
				const TDesC8& aFailureMessage, 
				TUint& aErrorCode, 
				TUint8& aRetryFlag, 
				TDes8& aAuthChallenge, 
				TUint8& aPasswordProtoVersion, 
				TPtrC8& aMessage)
/**
   Processes a MS-CHAP-V2 Failure Message.
   @param aFailureMessage [in] A MS-CHAP-V2 Failure Message.  The
   Failure Message needs to be in the format specified in RFC 2759:
   "E=eeeeeeeeee R=r C=cccccccccccccccccccccccccccccccc V=vvvvvvvvvv
   M=<msg>".
   @param aErrorCode [out] The MS-CHAP-V2 Failure error code.
   @param aRetryFlag [out] The retry flag.  The flag will be set to
   "1" if a retry is allowed, and "0" if not.  When the authenticator
   sets this flag to "1" it disables short timeouts, expecting the
   peer to prompt the user for new credentials and resubmit the
   response.
   @param aAuthChallenge [out] The new Authenticator Challenge Value.
   @param aPasswordProtoVersion [out] The password changing protocol
   supported by the peer.
   @param aMessage [out] A failure text message.
   @internalComponent
*/
	{
	ASSERT(aAuthChallenge.Length() ==
			KPppMsChap2AuthenticatorChallengeSize);
	
	TLex8 input(aFailureMessage);

	if (input.Get() != 'E')
		User::Leave(KErrGeneral);

	if (input.Get() != '=')
		User::Leave(KErrGeneral);


// RFC 2759: ""eeeeeeeeee" is the ASCII representation of a decimal
// error code (need not be 10 digits) corresponding to one of those
// listed below, though implementations should deal with codes not on
// this list gracefully."

	
	TInt ret;
	if ((ret = input.Val(aErrorCode))!=KErrNone)
		if (ret!= KErrOverflow)
			User::Leave(KErrGeneral);
		else
// Gracefully handle unusually large, yet valid, MS-CHAP-V2 specific
// error code values.  This code only handles the MS-CHAP-V2 specific
// error code values specified in RFC 2759.
			aErrorCode=0;

	input.SkipSpace();

	if (input.Get() != 'R')
		User::Leave(KErrGeneral);

	if (input.Get() != '=')
		User::Leave(KErrGeneral);

	if (input.Val(aRetryFlag, EDecimal)!=KErrNone)
		User::Leave(KErrGeneral);

	input.SkipSpace();

	if (input.Get() != 'C')
		User::Leave(KErrGeneral);

	if (input.Get() != '=')
		User::Leave(KErrGeneral);

	TPtrC8 token(input.NextToken());
// This field is 32 hexadecimal digits representing an ASCII
// representation of a new challenge value.  Each octet is represented
// in 2 hexadecimal digits.
	if (token.Length() != KPppMsChap2AuthenticatorChallengeSize*2)
		User::Leave(KErrGeneral);

	TLex8 lex;
	TUint8 octet;
	TUint8* pChallengeOctet = 
			const_cast<TUint8*>(aAuthChallenge.Ptr());
	TUint8 i = 0;
	do
		{
		lex.Assign(token.Mid(i*2, 2));
		if (lex.Val(octet, EHex) != KErrNone)
			User::Leave(KErrGeneral);

		*(pChallengeOctet + i) = octet;
		} 
	while (++i < KPppMsChap2AuthenticatorChallengeSize);

	input.SkipSpace();

	if (input.Get() != 'V')
		User::Leave(KErrGeneral);

	if (input.Get() != '=')
		User::Leave(KErrGeneral);


// RFC 2759: "The "vvvvvvvvvv" is the ASCII representation of a
// decimal version code (need not be 10 digits) indicating the
// password changing protocol version supported on the server.  For
// MS-CHAP-V2, this value SHOULD always be 3."


	if ((ret = input.Val(aPasswordProtoVersion, EDecimal)) != 
			KErrNone)
		if (ret != KErrOverflow)
			User::Leave(KErrGeneral);
		else
// Gracefully handle unusually large, yet valid, password changing
// protocol version values.  This code only handles the password
// changing protocol version values specified in RFC 2759.
			aPasswordProtoVersion=0;

	input.SkipSpace();

	switch (input.Get())
		{
	case 'M':
		if (input.Get() != '=')
			User::Leave(KErrGeneral);

		aMessage.Set(input.NextToken());
		break;

	case 0:
		break;

	default:
		User::Leave(KErrGeneral);
		}

	ASSERT(aAuthChallenge.Length() ==
		   KPppMsChap2AuthenticatorChallengeSize);
	}


TInt CPppMsChap2::TranslateMsChapError(TUint aMsChapError)
/**
   Translates a MS-CHAP-V2 error code into a Symbian OS PPP NIF
   specific error code.
   @param aMsChapError A MS-CHAP-V2 error code.
   @return The Symbian OS PPP NIF specific error code corresponding to
   the MS-CHAP error code.
   @internalComponent
*/
	{
	return CPppMsChap::TranslateMsChapError(aMsChapError);
	}


inline void CPppMsChap2::RetryPasswordL()
/**
   Retries the authentication.
   @internalComponent
*/
	{
	++iCurrentId;

	iResponseRetryCount = 0;
	RespondL();
	}


inline void CPppMsChap2::GeneratePeerChallengeL(TDes8& aChallenge)
/**
   Generates a MS-CHAP-V2 Peer Challenge.
   @param aChallenge [out] A MS-CHAP-V2 Peer Challenge (16 octets).
   @internalComponent
*/
	{
	TRandom::RandomL(aChallenge);
	}

inline void CPppMsChap2::GenerateNTResponseL(
				const TDesC8& aAuthenticatorChallenge,
				const TDesC8& aPeerChallenge, 
				const TDesC8& aUserName, 
				const TDesC16& aPassword, 
				TDes8& aResponse)
/**
   Generates a MS-CHAP-V2 NT-Response.
   @param aAuthenticatorChallenge [in] The MS-CHAP-V2 authenticator
   challenge (16 octets).
   @param aPeerChallenge [in] The MS-CHAP-V2 peer challenge (16
   octets).
   @param aUserName [in] The Microsoft Windows NT username (0 to 256
   char).
   @param aPassword [in] The Microsoft Windows NT password (0 to 256
   unicode char).
   @param aResponse [out] The MS-CHAP-V2 Challenge Response,
   NT-Response (24 octets).
   @note This function implements the GenerateNTResponse routine
   specified in RFC 2759.
   @internalComponent
*/
	{
	ASSERT(aAuthenticatorChallenge.Length() ==
		KPppMsChap2AuthenticatorChallengeSize);
	ASSERT(aPeerChallenge.Length() ==
		KPppMsChap2PeerChallengeSize);
	ASSERT(aUserName.Length() <= KPppMsChapMaxNTUserNameLength);
	ASSERT(aPassword.Length() <= KPppMsChapMaxNTPasswordLength);
	ASSERT(aResponse.Length() == KPppMsChap2NTResponseSize);

	HBufC8* challengeHashBuf =
			HBufC8::NewMaxLC(KPppMsChap2ChallengeHashSize);
	TPtr8 challengeHash(challengeHashBuf->Des());
	ChallengeHashL(aPeerChallenge,
			aAuthenticatorChallenge, 
			aUserName,
			challengeHash);

	HBufC8* paddedPasswordHashBuf =
			HBufC8::NewLC(KPppMsChap2PaddedHashSize);
	TPtr8 paddablePasswordHash(paddedPasswordHashBuf->Des());

	paddablePasswordHash.SetLength(KPppMsChap2HashSize);
	NtPasswordHashL(aPassword, paddablePasswordHash);

	ChallengeResponseL(challengeHash, 
			paddablePasswordHash,
			aResponse);

	CleanupStack::PopAndDestroy(paddedPasswordHashBuf);
	CleanupStack::PopAndDestroy(challengeHashBuf);

	ASSERT(aResponse.Length()==KPppMsChap2NTResponseSize);
	}


void CPppMsChap2::ChallengeHashL(const TDesC8& aPeerChallenge, 
				const TDesC8& aAuthenticatorChallenge,
				const TDesC8& aUserName, 
				TDes8& aChallengeHash)
/**
   Computes the hash of the Peer Challenge, Authenticator Challenge
   and username using SHA-1.
   @param aPeerChallenge [in] The Peer Challenge (16 octets).
   @param aAuthenticatorChallenge [in] The Authenticator Challenge (16
   octets).
   @param aUserName [in] The Microsoft Windows NT username (0 to 256
   char).
   @param aChallengeHash [out] The hash of the peer challenge,
   authenticator challenge and username, computed using SHA-1 (8
   octets).
   @note This function implements the ChallengeHash routine specified
   in RFC 2759.
   @internalComponent
*/
	{
	ASSERT(aPeerChallenge.Length() ==
		KPppMsChap2PeerChallengeSize);
	ASSERT(aAuthenticatorChallenge.Length() ==
		   KPppMsChap2AuthenticatorChallengeSize);
	ASSERT(aUserName.Length() <= KPppMsChapMaxNTUserNameLength);
	ASSERT(aChallengeHash.Length()==KPppMsChap2ChallengeHashSize);

	CSHA1* sha1 = CSHA1::NewL();
	CleanupStack::PushL(sha1);

// RFC 2759: "Only the user name (as presented by the peer and
// excluding any prepended domain name)"
	TPtrC8 userName(aUserName);
	TInt i = aUserName.Locate('\\');
	if (i >= 0 && i < userName.Length() - 1)
		userName.Set(aUserName.Mid(i + 1));
	else if (i >= userName.Length() - 1)
		User::Leave(KErrGeneral);


	sha1->Update(aPeerChallenge);
	sha1->Update(aAuthenticatorChallenge);

	aChallengeHash.Copy(sha1->Final(userName).Ptr(),
				KPppMsChap2ChallengeHashSize);


	CleanupStack::PopAndDestroy(sha1);

	ASSERT(aChallengeHash.Length()==KPppMsChap2ChallengeHashSize);
	}


void CPppMsChap2::NtPasswordHashL(const TDesC16& aPassword,
				TDes8& aPasswordHash)
/**
   Computes the hash of the Microsoft Windows NT password using MD4.
   @param aPassword [in] The Microsoft Windows NT password (0 to 256
   Unicode char).
   @param aPasswordHash [out] The MD4 hash of the Microsoft Windows NT
   password (16 octets).
   @note This function implements the NtPasswordHash routine specified
   in RFC 2759.
   @internalComponent
*/
	{
	ASSERT(aPassword.Length() <= KPppMsChapMaxNTPasswordLength);
	ASSERT(aPasswordHash.Length()==KPppMsChap2HashSize);

// The following code does not use the Symbian Security subsystem
// components, because they do not provide a MD4 implementation yet.
// This is a provisional solution until the Symbian Security subsystem
// components will provide a MD4 implementation.

	CMd4* md4 = CMd4::NewL();
	CleanupStack::PushL(md4);


// The following code assumes that the data in TDesC16 descriptors is
// stored in little endian byte order, which is currently a
// characteristic of Symbian OS, so the reinterpret_cast is assumed to
// be safe here.
	md4->Input(TPtrC8(reinterpret_cast<const TUint8*>(
					aPassword.Ptr()), 
			aPassword.Length()*2));
	md4->Output(aPasswordHash);
	
	CleanupStack::PopAndDestroy(md4);

	ASSERT(aPasswordHash.Length()==KPppMsChap2HashSize);
	}


inline void CPppMsChap2::ChallengeResponseL(
					const TDesC8& aChallengeHash,
					TDes8& aPaddablePasswordHash, 
					TDes8& aResponse)
/**
   Computes the challenge response using DES.
   @param aChallengeHash [in] The hash of the peer challenge,
   authenticator challenge and username, computed using SHA-1 (8
   octets).
   @param aPaddablePasswordHash [in/out] The hash of the password in a
   paddable buffer (16 octets in buffer with at least 21 octets
   maximum length).
   @param aResponse [out] The challenge response (24 octets).
   @note This function implements the ChallengeResponse routine
   specified in RFC 2759.
   @internalComponent
*/
	{
	CPppMsChap::ChallengeResponseL(aChallengeHash,
					aPaddablePasswordHash, 
					aResponse);
	}


void CPppMsChap2::DesEncryptL(const TDesC8& aClear, 
				const TDesC8& aKey, 
				TDes8& aCypher)
/**
   Encrypts a plaintext into a ciphertext using the DES encryption
   algorithm in ECB mode.
   @param aClear [in] A plaintext (8 octets).
   @param aKey [in] A key (7 octets).
   @param aCypher [out] The ciphertext (8 octets).
   @note This function implements the DesEncrypt routine specified in
   RFC 2759.
   @internalComponent
*/
	{
	CPppMsChap::DesEncryptL(aClear, aKey, aCypher);
	}


inline void CPppMsChap2::HashNtPasswordHashL(
					const TDesC8& aPasswordHash, 
					TDes8& aPasswordHashHash)
/**
   Computes the hash of the hash of the Microsoft Windows NT password
   using MD4.
   @param aPasswordHash [in] The hash of the Microsoft Windows NT
   password (16 octets).
   @param aPasswordHashHash [out] The hash of the hash of the
   Microsoft Windows NT password, computed using MD4 (16 octets).
   @note This function implements the HashNtPasswordHash routine
   specified in RFC 2759.
   @internalComponent
*/
	{
	ASSERT(aPasswordHash.Length()==KPppMsChap2HashSize);
	ASSERT(aPasswordHashHash.Length()==KPppMsChap2HashSize);

	CMd4* md4 = CMd4::NewL();
	CleanupStack::PushL(md4);
	
// 	aPasswordHashHash.Copy(md4.Final(aPasswordHash));

	md4->Input(aPasswordHash);
	md4->Output(aPasswordHashHash);
	
	CleanupStack::PopAndDestroy(md4);

	ASSERT(aPasswordHashHash.Length()==KPppMsChap2HashSize);
	}


inline void CPppMsChap2::GenerateAuthenticatorResponseL(
				const TDesC16& aPassword, 
				const TDesC8& aNTResponse, 
				const TDesC8& aPeerChallenge,
				const TDesC8& aAuthenticatorChallenge,
				const TDesC8& aUserName, 
				TDes8& aAuthenticatorResponse)
/**
   Generates the expected MS-CHAP-V2 Authenticator Response Value.
   @param aPassword [in] The Microsoft Windows NT password (0 to 256
   Unicode char).
   @param aNTResponse [in] The MS-CHAP-V2 NT-Response (24 octets).
   @param aPeerChallenge [in] The Peer Challenge (16 octets).
   @param aAuthenticatorChallenge [in] The Authenticator Challenge (16
   octets).
   @param aUserName [in] The Microsoft Windows NT username (0 to 256
   char).
   @param aAuthenticatorResponse [out] The expected MS-CHAP-V2
   Authenticator Response Value encoded in the format
   "S=<auth_string>" as specified in RFC 2759 (42 octets).
   @note This function implements the GenerateAuthenticatorResponse
   routine specified in RFC 2759.
   @internalComponent
*/
	{
	ASSERT(aPassword.Length()<=KPppMsChapMaxNTPasswordLength);
	ASSERT(aNTResponse.Length() == KPppMsChap2NTResponseSize);
	ASSERT(aPeerChallenge.Length() ==
		KPppMsChap2PeerChallengeSize);
	ASSERT(aAuthenticatorChallenge.Length() ==
		   KPppMsChap2AuthenticatorChallengeSize);
	ASSERT(aUserName.Length()<=KPppMsChapMaxNTUserNameLength);
	ASSERT(aAuthenticatorResponse.Length() ==
		   KPppMsChap2AuthenticatorResponseSize);

	HBufC8* passwordHashBuf=HBufC8::NewMaxLC(KPppMsChap2HashSize);
	TPtr8 passwordHash(passwordHashBuf->Des());

	NtPasswordHashL(aPassword, passwordHash);

	HashNtPasswordHashL(passwordHash, passwordHash);

	CSHA1* sha1 = CSHA1::NewL();
	CleanupStack::PushL(sha1);

// A magic string literal specified in RFC 2759 used in reponse
// generation by the GenerateAuthenticatorResponse routine for SHA-1
// encryption.
	_LIT8(KMagic1, "Magic server to client signing constant");


	sha1->Update(passwordHash);
	sha1->Update(aNTResponse);
	TPtrC8 hash(sha1->Final(KMagic1));


	HBufC8* challengeHashBuf =
			HBufC8::NewMaxLC(KPppMsChap2ChallengeHashSize);
	TPtr8 challengeHash(challengeHashBuf->Des());
	ChallengeHashL(aPeerChallenge, 
			aAuthenticatorChallenge, 
			aUserName,
			challengeHash);

// Another magic string literal specified in RFC 2759 used in reponse
// generation by the GenerateAuthenticatorResponse routine for SHA-1
// encryption.
	_LIT8(KMagic2, "Pad to make it do more than one iteration");


	sha1->Update(hash);
	sha1->Update(challengeHash);
	const TUint8* pHash = sha1->Final(KMagic2).Ptr();

	
	_LIT8(KFormat,
		  "S=%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X"
			"%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X");
	aAuthenticatorResponse.Format(KFormat, 
					*pHash, 
					*(pHash + 1),
					*(pHash + 2), 
					*(pHash + 3), 
					*(pHash + 4), 
					*(pHash + 5), 
					*(pHash + 6), 
					*(pHash + 7), 
					*(pHash + 8), 
					*(pHash + 9), 
					*(pHash + 10),
					*(pHash + 11), 
					*(pHash + 12), 
					*(pHash + 13), 
					*(pHash + 14),
					*(pHash + 15), 
					*(pHash + 16), 
					*(pHash + 17), 
					*(pHash + 18),
					*(pHash + 19));

	CleanupStack::PopAndDestroy(challengeHashBuf);

	CleanupStack::PopAndDestroy(sha1);
	CleanupStack::PopAndDestroy(passwordHashBuf);

	ASSERT(aAuthenticatorResponse.Length() ==
		   KPppMsChap2AuthenticatorResponseSize);
	}