linklayerprotocols/pppnif/SPPP/mschap2.cpp
changeset 0 af10295192d8
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/linklayerprotocols/pppnif/SPPP/mschap2.cpp	Tue Jan 26 15:23:49 2010 +0200
@@ -0,0 +1,826 @@
+// 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);
+	}