linklayerprotocols/pppnif/SPPP/mschap2.cpp
changeset 0 af10295192d8
equal deleted inserted replaced
-1:000000000000 0:af10295192d8
       
     1 // Copyright (c) 2003-2009 Nokia Corporation and/or its subsidiary(-ies).
       
     2 // All rights reserved.
       
     3 // This component and the accompanying materials are made available
       
     4 // under the terms of "Eclipse Public License v1.0"
       
     5 // which accompanies this distribution, and is available
       
     6 // at the URL "http://www.eclipse.org/legal/epl-v10.html".
       
     7 //
       
     8 // Initial Contributors:
       
     9 // Nokia Corporation - initial contribution.
       
    10 //
       
    11 // Contributors:
       
    12 //
       
    13 // Description:
       
    14 // Extensions, Version 2 (MS-CHAP-V2) - RFC 2759, except the
       
    15 // authenticator-controlled authentication retry mechanisms and the
       
    16 // password changing mechanisms - this is in accordance with the
       
    17 // requirements.
       
    18 // 
       
    19 //
       
    20 
       
    21 /**
       
    22  @file
       
    23  @brief Source file for the implementation of Microsoft PPP CHAP
       
    24  @internalComponent 
       
    25 */
       
    26 
       
    27 #include "mschap2.h"
       
    28 #include "MSCHAP.H"
       
    29 
       
    30 // using SHA-1
       
    31 #include <hash.h>
       
    32 
       
    33 // using DES
       
    34 #include <symmetric.h>
       
    35 
       
    36 // using random
       
    37 #include <random.h>
       
    38 // using MD4
       
    39 #include "MD4.H"
       
    40 #include "PPPConfig.h"
       
    41 
       
    42 
       
    43 CPppMsChap2::CPppMsChap2()
       
    44 /**
       
    45    Constructor.
       
    46    @internalComponent
       
    47 */
       
    48 	: iResponseValue(KPppMsChap2ResponseValueSize),
       
    49 	iAuthenticatorResponse(KPppMsChap2AuthenticatorResponseSize)
       
    50 	{
       
    51 	}
       
    52 
       
    53 
       
    54 CPppMsChap2::~CPppMsChap2()
       
    55 /**
       
    56    Destructor.
       
    57    @internalComponent
       
    58 */
       
    59 	{
       
    60 #ifdef _UNICODE
       
    61 	delete iUserName;
       
    62 #endif
       
    63 	}
       
    64 
       
    65 void CPppMsChap2::InitL(CPppLcp* aLcp)
       
    66 /**
       
    67    @copydoc CPppChap::InitL(CPppLcp*)
       
    68    @see CPppChap::InitL(CPppLcp*)
       
    69    @internalComponent
       
    70 */
       
    71 	{
       
    72 	CPppChap::InitL(aLcp);
       
    73 	}
       
    74 
       
    75 
       
    76 void CPppMsChap2::CheckChallengePacketL(RMBufPacket& aPacket)
       
    77 /**
       
    78    @copydoc CPppChap::CheckChallengePacketL(RMBufPacket&)
       
    79    @copydoc CPppChap::CheckChallengePacketL(RMBufPacket&)
       
    80    @internalComponent
       
    81 */
       
    82 	{
       
    83 	__ASSERT_ALWAYS(aPacket.Length() >= KPppChapCodeFieldSize +
       
    84 				KPppChapIdFieldSize + 
       
    85 				KPppChapLengthFieldSize +
       
    86 				KPppChapValueSizeFieldSize + 
       
    87 				KPppMsChap2AuthenticatorChallengeSize,
       
    88 			User::Leave(KErrUnderflow));
       
    89 
       
    90 	__ASSERT_ALWAYS(*(aPacket.First()->Ptr() +
       
    91 							KPppChapCodeFieldSize +
       
    92 							KPppChapIdFieldSize +
       
    93 							KPppChapLengthFieldSize) == 
       
    94 						KPppMsChap2AuthenticatorChallengeSize,
       
    95 				User::Leave(KErrOverflow));
       
    96 	}
       
    97 
       
    98 
       
    99 void CPppMsChap2::MakeResponseL(TUint8 /*aChallengeId*/, 
       
   100 				const TDesC8& aChallengeValue, 
       
   101 				TPtrC8& aResponseValue, 
       
   102 				TPtrC8& aResponseName)
       
   103 /**
       
   104    @copydoc CPppChap::MakeResponseL(TUint8,const TDesC8&,TPtrC8&,TPtrC8&)
       
   105    @see CPppChap::MakeResponseL(TUint8,const TDesC8&,TPtrC8&,TPtrC8&)
       
   106    @internalComponent
       
   107 */
       
   108 	{
       
   109 	ASSERT(aChallengeValue.Length() ==
       
   110 			KPppMsChap2AuthenticatorChallengeSize);
       
   111 
       
   112 	TPtr8 peerChallenge(const_cast<TUint8*>(iResponseValue.Ptr()),
       
   113 				KPppMsChap2PeerChallengeSize,
       
   114 				KPppMsChap2PeerChallengeSize);
       
   115 	GeneratePeerChallengeL(peerChallenge);
       
   116 
       
   117 // The NT username shall be char and not be longer than
       
   118 // KPppMsChapMaxNTUserNameLength
       
   119     const CCredentialsConfig* credentials = iPppLcp->GetCredentials();
       
   120 	const TDesC& username = credentials->GetUserName();
       
   121 	__ASSERT_ALWAYS(username.Length() <=
       
   122 				KPppMsChapMaxNTUserNameLength,
       
   123 			User::Leave(KErrTooBig));
       
   124 
       
   125 // The NT password shall be Unicode and not be longer than
       
   126 // KPppMsChapMaxNTPasswordLength
       
   127 	const TDesC& password = credentials->GetPassword();
       
   128 	__ASSERT_ALWAYS(password.Length() <=
       
   129 				KPppMsChapMaxNTPasswordLength,
       
   130 			User::Leave(KErrTooBig));
       
   131 
       
   132 #ifdef _UNICODE
       
   133 // The MS-CHAP-V2 routines require the username to be represented as
       
   134 // 0-to-256-char (RFC 2759), so convert the username.
       
   135 	delete iUserName;
       
   136 	iUserName = 0;
       
   137 	iUserName = HBufC8::NewL(username.Length());
       
   138 	iUserName->Des().Copy(username);
       
   139 	aResponseName.Set(*iUserName);
       
   140 
       
   141 	TPtrC16 uniPassword(password);
       
   142 #else //! _UNICODE
       
   143 	aResponseName.Set(username);
       
   144 
       
   145 // The MS-CHAP-V2 routines require the password to be represented as
       
   146 // 0-to-256-unicode-char (RFC 2759), so convert the password to
       
   147 // Unicode.
       
   148 	HBufC16& uniPassword = *HBufC16::NewLC(password.Length());
       
   149 	uniPassword.Des().Copy(password);
       
   150 #endif //! _UNICODE
       
   151 
       
   152 	TPtr8 ntResponse(const_cast<TUint8*>(iResponseValue.Ptr()) +
       
   153 				KPppMsChap2PeerChallengeSize +
       
   154 				KPppMsChap2ResponseReservedSize,
       
   155 			KPppMsChap2NTResponseSize,
       
   156 			KPppMsChap2NTResponseSize);
       
   157 
       
   158 	GenerateNTResponseL(aChallengeValue, 
       
   159 			peerChallenge, 
       
   160 			aResponseName,
       
   161 			uniPassword, 
       
   162 			ntResponse);
       
   163 
       
   164 	aResponseValue.Set(iResponseValue);
       
   165 
       
   166 	GenerateAuthenticatorResponseL(uniPassword, 
       
   167 					ntResponse,
       
   168 					peerChallenge, 
       
   169 					aChallengeValue,
       
   170 					aResponseName,
       
   171 					iAuthenticatorResponse);
       
   172 
       
   173 #ifndef _UNICODE
       
   174 	CleanupStack::PopAndDestroy(&uniPassword);
       
   175 #endif //! _UNICODE
       
   176 
       
   177 	ASSERT(aResponseValue.Length() == KPppMsChap2ResponseValueSize);
       
   178 	ASSERT(aResponseName.Length() >= KPppChapMinNameSize &&
       
   179 		   aResponseName.Length() <= KPppMsChapMaxNTUserNameLength);
       
   180 	}
       
   181 
       
   182 
       
   183 void CPppMsChap2::SuccessL(RMBufPacket& aPacket)
       
   184 /**
       
   185    @copydoc CPppChap::SuccessL(RMBufPacket&)
       
   186    @see CPppChap::SuccessL(RMBufPacket&)
       
   187    @internalComponent
       
   188 */
       
   189 	{
       
   190 	__ASSERT_ALWAYS(aPacket.Length() >= KPppChapCodeFieldSize +
       
   191 				KPppChapIdFieldSize + 
       
   192 				KPppChapLengthFieldSize +
       
   193 				KPppMsChap2AuthenticatorResponseSize, 
       
   194 			User::Leave(KErrUnderflow));
       
   195 
       
   196 // check the id
       
   197 	if (!CheckIdentifier(aPacket))
       
   198 		User::Leave(KErrGeneral);
       
   199 
       
   200 // no more retries
       
   201 	TimerCancel();
       
   202 
       
   203 	// Read the length of the MS-CHAP-V2 Failure packet and compute
       
   204 	// the length of the CHAP Message field and go past the CHAP Code
       
   205 	// field, the CHAP Identifier field and the CHAP Length field, in
       
   206 	// order to read the CHAP Message field.
       
   207 
       
   208 	TPtrC8 authResponse(aPacket.First()->Ptr() + 
       
   209 					KPppChapCodeFieldSize + 
       
   210 					KPppChapIdFieldSize + 
       
   211 					KPppChapLengthFieldSize,
       
   212 			KPppMsChap2AuthenticatorResponseSize);
       
   213 
       
   214 // 	TPtrC8 message(ptr + KPppChapCodeFieldSize + KPppChapIdFieldSize + KPppChapLengthFieldSize + KPppMsChap2AuthenticatorResponseSize + 3, BigEndian::Get16(ptr + KPppChapCodeFieldSize + KPppChapIdFieldSize) - KPppChapCodeFieldSize - KPppChapIdFieldSize - KPppChapLengthFieldSize - KPppMsChap2AuthenticatorResponseSize - 3);
       
   215 
       
   216 	if (authResponse!=iAuthenticatorResponse)
       
   217 		DoFail(KErrIfAuthenticationFailure);
       
   218 	else
       
   219 		DoSucceed();
       
   220 	}
       
   221 
       
   222 
       
   223 void CPppMsChap2::FailureL(RMBufPacket& aPacket)
       
   224 /**
       
   225    @copydoc CPppChap::FailureL(RMBufPacket&)
       
   226    @see CPppChap::FailureL(RMBufPacket&)
       
   227    @internalComponent
       
   228 */
       
   229 	{
       
   230 	__ASSERT_ALWAYS(aPacket.Length() >= KPppChapCodeFieldSize +
       
   231 				KPppChapIdFieldSize + 
       
   232 				KPppChapLengthFieldSize +
       
   233 				KPppMsChap2AuthenticatorChallengeSize*2
       
   234 				+ 2,
       
   235 			User::Leave(KErrUnderflow));
       
   236 
       
   237 // check the id
       
   238 	if (!CheckIdentifier(aPacket))
       
   239 		User::Leave(KErrGeneral);
       
   240 
       
   241 	TimerCancel();
       
   242 
       
   243 #ifndef _DEBUG
       
   244 
       
   245 // The authenticator-controlled authentication retry mechanisms and
       
   246 // the password changing mechanisms have not been implemented in this
       
   247 // release - this is in accordance with the project requirements.
       
   248 // Consequently simply fail.
       
   249 
       
   250 	DoFail(KErrIfAuthenticationFailure);
       
   251 
       
   252 #else // _DEBUG
       
   253 
       
   254 // Read the length of the MS-CHAP-V2 Failure packet and compute the
       
   255 // length of the CHAP Message field and go past the CHAP Code field,
       
   256 // the CHAP Identifier field and the CHAP Length field, in order to
       
   257 // read the CHAP Message field
       
   258 	TPtrC8 failureMessage(aPacket.First()->Ptr() +
       
   259 					KPppChapCodeFieldSize +
       
   260 					KPppChapIdFieldSize +
       
   261 					KPppChapLengthFieldSize, 
       
   262 				aPacket.Length() - 
       
   263 					KPppChapCodeFieldSize -
       
   264 					KPppChapIdFieldSize -
       
   265 					KPppChapLengthFieldSize);
       
   266 
       
   267 	if (failureMessage.Length()==0)
       
   268 		{
       
   269 		DoFail(KErrIfAuthenticationFailure);
       
   270 		return;
       
   271 		}
       
   272 
       
   273 	TUint msChapError;
       
   274 	TUint8 isRetryAllowed;
       
   275 	TUint8 passwordProtoVersion;
       
   276 	TPtrC8 message;
       
   277 	TInt sysError=KErrIfAuthenticationFailure;
       
   278 
       
   279 	ProcessFailureMessageL(failureMessage, 
       
   280 				msChapError,
       
   281 				isRetryAllowed, 
       
   282 				iChallengeRef,
       
   283 				passwordProtoVersion, 
       
   284 				message);
       
   285 
       
   286 	sysError=TranslateMsChapError(msChapError);
       
   287 
       
   288 // The code only handles KPppMsChapAuthenticationFailure, and no other
       
   289 // MS-CHAP specific errors.  In particular, this code does not handle
       
   290 // KPppMsChapErrorPasswordExpired, since the password changing
       
   291 // mechanisms have not been implemented in this release - this is in
       
   292 // accordance with the project requirements.
       
   293 	if (msChapError != KPppMsChapAuthenticationFailure)
       
   294 		{
       
   295 		DoFail(sysError);
       
   296 		return;
       
   297 		}
       
   298 
       
   299 	if (!isRetryAllowed)
       
   300 		{
       
   301 		DoFail(sysError);
       
   302 		return;
       
   303 		}
       
   304 
       
   305 // The authenticator-controlled authentication retry mechanisms and
       
   306 // the password changing mechanisms have not been implemented in this
       
   307 // release - this is in accordance with the project requirements.
       
   308 // Consequently simply fail.
       
   309 	DoFail(sysError);
       
   310 
       
   311 #endif // _DEBUG
       
   312 	}
       
   313 
       
   314 
       
   315 inline void CPppMsChap2::ProcessFailureMessageL(
       
   316 				const TDesC8& aFailureMessage, 
       
   317 				TUint& aErrorCode, 
       
   318 				TUint8& aRetryFlag, 
       
   319 				TDes8& aAuthChallenge, 
       
   320 				TUint8& aPasswordProtoVersion, 
       
   321 				TPtrC8& aMessage)
       
   322 /**
       
   323    Processes a MS-CHAP-V2 Failure Message.
       
   324    @param aFailureMessage [in] A MS-CHAP-V2 Failure Message.  The
       
   325    Failure Message needs to be in the format specified in RFC 2759:
       
   326    "E=eeeeeeeeee R=r C=cccccccccccccccccccccccccccccccc V=vvvvvvvvvv
       
   327    M=<msg>".
       
   328    @param aErrorCode [out] The MS-CHAP-V2 Failure error code.
       
   329    @param aRetryFlag [out] The retry flag.  The flag will be set to
       
   330    "1" if a retry is allowed, and "0" if not.  When the authenticator
       
   331    sets this flag to "1" it disables short timeouts, expecting the
       
   332    peer to prompt the user for new credentials and resubmit the
       
   333    response.
       
   334    @param aAuthChallenge [out] The new Authenticator Challenge Value.
       
   335    @param aPasswordProtoVersion [out] The password changing protocol
       
   336    supported by the peer.
       
   337    @param aMessage [out] A failure text message.
       
   338    @internalComponent
       
   339 */
       
   340 	{
       
   341 	ASSERT(aAuthChallenge.Length() ==
       
   342 			KPppMsChap2AuthenticatorChallengeSize);
       
   343 	
       
   344 	TLex8 input(aFailureMessage);
       
   345 
       
   346 	if (input.Get() != 'E')
       
   347 		User::Leave(KErrGeneral);
       
   348 
       
   349 	if (input.Get() != '=')
       
   350 		User::Leave(KErrGeneral);
       
   351 
       
   352 
       
   353 // RFC 2759: ""eeeeeeeeee" is the ASCII representation of a decimal
       
   354 // error code (need not be 10 digits) corresponding to one of those
       
   355 // listed below, though implementations should deal with codes not on
       
   356 // this list gracefully."
       
   357 
       
   358 	
       
   359 	TInt ret;
       
   360 	if ((ret = input.Val(aErrorCode))!=KErrNone)
       
   361 		if (ret!= KErrOverflow)
       
   362 			User::Leave(KErrGeneral);
       
   363 		else
       
   364 // Gracefully handle unusually large, yet valid, MS-CHAP-V2 specific
       
   365 // error code values.  This code only handles the MS-CHAP-V2 specific
       
   366 // error code values specified in RFC 2759.
       
   367 			aErrorCode=0;
       
   368 
       
   369 	input.SkipSpace();
       
   370 
       
   371 	if (input.Get() != 'R')
       
   372 		User::Leave(KErrGeneral);
       
   373 
       
   374 	if (input.Get() != '=')
       
   375 		User::Leave(KErrGeneral);
       
   376 
       
   377 	if (input.Val(aRetryFlag, EDecimal)!=KErrNone)
       
   378 		User::Leave(KErrGeneral);
       
   379 
       
   380 	input.SkipSpace();
       
   381 
       
   382 	if (input.Get() != 'C')
       
   383 		User::Leave(KErrGeneral);
       
   384 
       
   385 	if (input.Get() != '=')
       
   386 		User::Leave(KErrGeneral);
       
   387 
       
   388 	TPtrC8 token(input.NextToken());
       
   389 // This field is 32 hexadecimal digits representing an ASCII
       
   390 // representation of a new challenge value.  Each octet is represented
       
   391 // in 2 hexadecimal digits.
       
   392 	if (token.Length() != KPppMsChap2AuthenticatorChallengeSize*2)
       
   393 		User::Leave(KErrGeneral);
       
   394 
       
   395 	TLex8 lex;
       
   396 	TUint8 octet;
       
   397 	TUint8* pChallengeOctet = 
       
   398 			const_cast<TUint8*>(aAuthChallenge.Ptr());
       
   399 	TUint8 i = 0;
       
   400 	do
       
   401 		{
       
   402 		lex.Assign(token.Mid(i*2, 2));
       
   403 		if (lex.Val(octet, EHex) != KErrNone)
       
   404 			User::Leave(KErrGeneral);
       
   405 
       
   406 		*(pChallengeOctet + i) = octet;
       
   407 		} 
       
   408 	while (++i < KPppMsChap2AuthenticatorChallengeSize);
       
   409 
       
   410 	input.SkipSpace();
       
   411 
       
   412 	if (input.Get() != 'V')
       
   413 		User::Leave(KErrGeneral);
       
   414 
       
   415 	if (input.Get() != '=')
       
   416 		User::Leave(KErrGeneral);
       
   417 
       
   418 
       
   419 // RFC 2759: "The "vvvvvvvvvv" is the ASCII representation of a
       
   420 // decimal version code (need not be 10 digits) indicating the
       
   421 // password changing protocol version supported on the server.  For
       
   422 // MS-CHAP-V2, this value SHOULD always be 3."
       
   423 
       
   424 
       
   425 	if ((ret = input.Val(aPasswordProtoVersion, EDecimal)) != 
       
   426 			KErrNone)
       
   427 		if (ret != KErrOverflow)
       
   428 			User::Leave(KErrGeneral);
       
   429 		else
       
   430 // Gracefully handle unusually large, yet valid, password changing
       
   431 // protocol version values.  This code only handles the password
       
   432 // changing protocol version values specified in RFC 2759.
       
   433 			aPasswordProtoVersion=0;
       
   434 
       
   435 	input.SkipSpace();
       
   436 
       
   437 	switch (input.Get())
       
   438 		{
       
   439 	case 'M':
       
   440 		if (input.Get() != '=')
       
   441 			User::Leave(KErrGeneral);
       
   442 
       
   443 		aMessage.Set(input.NextToken());
       
   444 		break;
       
   445 
       
   446 	case 0:
       
   447 		break;
       
   448 
       
   449 	default:
       
   450 		User::Leave(KErrGeneral);
       
   451 		}
       
   452 
       
   453 	ASSERT(aAuthChallenge.Length() ==
       
   454 		   KPppMsChap2AuthenticatorChallengeSize);
       
   455 	}
       
   456 
       
   457 
       
   458 TInt CPppMsChap2::TranslateMsChapError(TUint aMsChapError)
       
   459 /**
       
   460    Translates a MS-CHAP-V2 error code into a Symbian OS PPP NIF
       
   461    specific error code.
       
   462    @param aMsChapError A MS-CHAP-V2 error code.
       
   463    @return The Symbian OS PPP NIF specific error code corresponding to
       
   464    the MS-CHAP error code.
       
   465    @internalComponent
       
   466 */
       
   467 	{
       
   468 	return CPppMsChap::TranslateMsChapError(aMsChapError);
       
   469 	}
       
   470 
       
   471 
       
   472 inline void CPppMsChap2::RetryPasswordL()
       
   473 /**
       
   474    Retries the authentication.
       
   475    @internalComponent
       
   476 */
       
   477 	{
       
   478 	++iCurrentId;
       
   479 
       
   480 	iResponseRetryCount = 0;
       
   481 	RespondL();
       
   482 	}
       
   483 
       
   484 
       
   485 inline void CPppMsChap2::GeneratePeerChallengeL(TDes8& aChallenge)
       
   486 /**
       
   487    Generates a MS-CHAP-V2 Peer Challenge.
       
   488    @param aChallenge [out] A MS-CHAP-V2 Peer Challenge (16 octets).
       
   489    @internalComponent
       
   490 */
       
   491 	{
       
   492 	TRandom::RandomL(aChallenge);
       
   493 	}
       
   494 
       
   495 inline void CPppMsChap2::GenerateNTResponseL(
       
   496 				const TDesC8& aAuthenticatorChallenge,
       
   497 				const TDesC8& aPeerChallenge, 
       
   498 				const TDesC8& aUserName, 
       
   499 				const TDesC16& aPassword, 
       
   500 				TDes8& aResponse)
       
   501 /**
       
   502    Generates a MS-CHAP-V2 NT-Response.
       
   503    @param aAuthenticatorChallenge [in] The MS-CHAP-V2 authenticator
       
   504    challenge (16 octets).
       
   505    @param aPeerChallenge [in] The MS-CHAP-V2 peer challenge (16
       
   506    octets).
       
   507    @param aUserName [in] The Microsoft Windows NT username (0 to 256
       
   508    char).
       
   509    @param aPassword [in] The Microsoft Windows NT password (0 to 256
       
   510    unicode char).
       
   511    @param aResponse [out] The MS-CHAP-V2 Challenge Response,
       
   512    NT-Response (24 octets).
       
   513    @note This function implements the GenerateNTResponse routine
       
   514    specified in RFC 2759.
       
   515    @internalComponent
       
   516 */
       
   517 	{
       
   518 	ASSERT(aAuthenticatorChallenge.Length() ==
       
   519 		KPppMsChap2AuthenticatorChallengeSize);
       
   520 	ASSERT(aPeerChallenge.Length() ==
       
   521 		KPppMsChap2PeerChallengeSize);
       
   522 	ASSERT(aUserName.Length() <= KPppMsChapMaxNTUserNameLength);
       
   523 	ASSERT(aPassword.Length() <= KPppMsChapMaxNTPasswordLength);
       
   524 	ASSERT(aResponse.Length() == KPppMsChap2NTResponseSize);
       
   525 
       
   526 	HBufC8* challengeHashBuf =
       
   527 			HBufC8::NewMaxLC(KPppMsChap2ChallengeHashSize);
       
   528 	TPtr8 challengeHash(challengeHashBuf->Des());
       
   529 	ChallengeHashL(aPeerChallenge,
       
   530 			aAuthenticatorChallenge, 
       
   531 			aUserName,
       
   532 			challengeHash);
       
   533 
       
   534 	HBufC8* paddedPasswordHashBuf =
       
   535 			HBufC8::NewLC(KPppMsChap2PaddedHashSize);
       
   536 	TPtr8 paddablePasswordHash(paddedPasswordHashBuf->Des());
       
   537 
       
   538 	paddablePasswordHash.SetLength(KPppMsChap2HashSize);
       
   539 	NtPasswordHashL(aPassword, paddablePasswordHash);
       
   540 
       
   541 	ChallengeResponseL(challengeHash, 
       
   542 			paddablePasswordHash,
       
   543 			aResponse);
       
   544 
       
   545 	CleanupStack::PopAndDestroy(paddedPasswordHashBuf);
       
   546 	CleanupStack::PopAndDestroy(challengeHashBuf);
       
   547 
       
   548 	ASSERT(aResponse.Length()==KPppMsChap2NTResponseSize);
       
   549 	}
       
   550 
       
   551 
       
   552 void CPppMsChap2::ChallengeHashL(const TDesC8& aPeerChallenge, 
       
   553 				const TDesC8& aAuthenticatorChallenge,
       
   554 				const TDesC8& aUserName, 
       
   555 				TDes8& aChallengeHash)
       
   556 /**
       
   557    Computes the hash of the Peer Challenge, Authenticator Challenge
       
   558    and username using SHA-1.
       
   559    @param aPeerChallenge [in] The Peer Challenge (16 octets).
       
   560    @param aAuthenticatorChallenge [in] The Authenticator Challenge (16
       
   561    octets).
       
   562    @param aUserName [in] The Microsoft Windows NT username (0 to 256
       
   563    char).
       
   564    @param aChallengeHash [out] The hash of the peer challenge,
       
   565    authenticator challenge and username, computed using SHA-1 (8
       
   566    octets).
       
   567    @note This function implements the ChallengeHash routine specified
       
   568    in RFC 2759.
       
   569    @internalComponent
       
   570 */
       
   571 	{
       
   572 	ASSERT(aPeerChallenge.Length() ==
       
   573 		KPppMsChap2PeerChallengeSize);
       
   574 	ASSERT(aAuthenticatorChallenge.Length() ==
       
   575 		   KPppMsChap2AuthenticatorChallengeSize);
       
   576 	ASSERT(aUserName.Length() <= KPppMsChapMaxNTUserNameLength);
       
   577 	ASSERT(aChallengeHash.Length()==KPppMsChap2ChallengeHashSize);
       
   578 
       
   579 	CSHA1* sha1 = CSHA1::NewL();
       
   580 	CleanupStack::PushL(sha1);
       
   581 
       
   582 // RFC 2759: "Only the user name (as presented by the peer and
       
   583 // excluding any prepended domain name)"
       
   584 	TPtrC8 userName(aUserName);
       
   585 	TInt i = aUserName.Locate('\\');
       
   586 	if (i >= 0 && i < userName.Length() - 1)
       
   587 		userName.Set(aUserName.Mid(i + 1));
       
   588 	else if (i >= userName.Length() - 1)
       
   589 		User::Leave(KErrGeneral);
       
   590 
       
   591 
       
   592 	sha1->Update(aPeerChallenge);
       
   593 	sha1->Update(aAuthenticatorChallenge);
       
   594 
       
   595 	aChallengeHash.Copy(sha1->Final(userName).Ptr(),
       
   596 				KPppMsChap2ChallengeHashSize);
       
   597 
       
   598 
       
   599 	CleanupStack::PopAndDestroy(sha1);
       
   600 
       
   601 	ASSERT(aChallengeHash.Length()==KPppMsChap2ChallengeHashSize);
       
   602 	}
       
   603 
       
   604 
       
   605 void CPppMsChap2::NtPasswordHashL(const TDesC16& aPassword,
       
   606 				TDes8& aPasswordHash)
       
   607 /**
       
   608    Computes the hash of the Microsoft Windows NT password using MD4.
       
   609    @param aPassword [in] The Microsoft Windows NT password (0 to 256
       
   610    Unicode char).
       
   611    @param aPasswordHash [out] The MD4 hash of the Microsoft Windows NT
       
   612    password (16 octets).
       
   613    @note This function implements the NtPasswordHash routine specified
       
   614    in RFC 2759.
       
   615    @internalComponent
       
   616 */
       
   617 	{
       
   618 	ASSERT(aPassword.Length() <= KPppMsChapMaxNTPasswordLength);
       
   619 	ASSERT(aPasswordHash.Length()==KPppMsChap2HashSize);
       
   620 
       
   621 // The following code does not use the Symbian Security subsystem
       
   622 // components, because they do not provide a MD4 implementation yet.
       
   623 // This is a provisional solution until the Symbian Security subsystem
       
   624 // components will provide a MD4 implementation.
       
   625 
       
   626 	CMd4* md4 = CMd4::NewL();
       
   627 	CleanupStack::PushL(md4);
       
   628 
       
   629 
       
   630 // The following code assumes that the data in TDesC16 descriptors is
       
   631 // stored in little endian byte order, which is currently a
       
   632 // characteristic of Symbian OS, so the reinterpret_cast is assumed to
       
   633 // be safe here.
       
   634 	md4->Input(TPtrC8(reinterpret_cast<const TUint8*>(
       
   635 					aPassword.Ptr()), 
       
   636 			aPassword.Length()*2));
       
   637 	md4->Output(aPasswordHash);
       
   638 	
       
   639 	CleanupStack::PopAndDestroy(md4);
       
   640 
       
   641 	ASSERT(aPasswordHash.Length()==KPppMsChap2HashSize);
       
   642 	}
       
   643 
       
   644 
       
   645 inline void CPppMsChap2::ChallengeResponseL(
       
   646 					const TDesC8& aChallengeHash,
       
   647 					TDes8& aPaddablePasswordHash, 
       
   648 					TDes8& aResponse)
       
   649 /**
       
   650    Computes the challenge response using DES.
       
   651    @param aChallengeHash [in] The hash of the peer challenge,
       
   652    authenticator challenge and username, computed using SHA-1 (8
       
   653    octets).
       
   654    @param aPaddablePasswordHash [in/out] The hash of the password in a
       
   655    paddable buffer (16 octets in buffer with at least 21 octets
       
   656    maximum length).
       
   657    @param aResponse [out] The challenge response (24 octets).
       
   658    @note This function implements the ChallengeResponse routine
       
   659    specified in RFC 2759.
       
   660    @internalComponent
       
   661 */
       
   662 	{
       
   663 	CPppMsChap::ChallengeResponseL(aChallengeHash,
       
   664 					aPaddablePasswordHash, 
       
   665 					aResponse);
       
   666 	}
       
   667 
       
   668 
       
   669 void CPppMsChap2::DesEncryptL(const TDesC8& aClear, 
       
   670 				const TDesC8& aKey, 
       
   671 				TDes8& aCypher)
       
   672 /**
       
   673    Encrypts a plaintext into a ciphertext using the DES encryption
       
   674    algorithm in ECB mode.
       
   675    @param aClear [in] A plaintext (8 octets).
       
   676    @param aKey [in] A key (7 octets).
       
   677    @param aCypher [out] The ciphertext (8 octets).
       
   678    @note This function implements the DesEncrypt routine specified in
       
   679    RFC 2759.
       
   680    @internalComponent
       
   681 */
       
   682 	{
       
   683 	CPppMsChap::DesEncryptL(aClear, aKey, aCypher);
       
   684 	}
       
   685 
       
   686 
       
   687 inline void CPppMsChap2::HashNtPasswordHashL(
       
   688 					const TDesC8& aPasswordHash, 
       
   689 					TDes8& aPasswordHashHash)
       
   690 /**
       
   691    Computes the hash of the hash of the Microsoft Windows NT password
       
   692    using MD4.
       
   693    @param aPasswordHash [in] The hash of the Microsoft Windows NT
       
   694    password (16 octets).
       
   695    @param aPasswordHashHash [out] The hash of the hash of the
       
   696    Microsoft Windows NT password, computed using MD4 (16 octets).
       
   697    @note This function implements the HashNtPasswordHash routine
       
   698    specified in RFC 2759.
       
   699    @internalComponent
       
   700 */
       
   701 	{
       
   702 	ASSERT(aPasswordHash.Length()==KPppMsChap2HashSize);
       
   703 	ASSERT(aPasswordHashHash.Length()==KPppMsChap2HashSize);
       
   704 
       
   705 	CMd4* md4 = CMd4::NewL();
       
   706 	CleanupStack::PushL(md4);
       
   707 	
       
   708 // 	aPasswordHashHash.Copy(md4.Final(aPasswordHash));
       
   709 
       
   710 	md4->Input(aPasswordHash);
       
   711 	md4->Output(aPasswordHashHash);
       
   712 	
       
   713 	CleanupStack::PopAndDestroy(md4);
       
   714 
       
   715 	ASSERT(aPasswordHashHash.Length()==KPppMsChap2HashSize);
       
   716 	}
       
   717 
       
   718 
       
   719 inline void CPppMsChap2::GenerateAuthenticatorResponseL(
       
   720 				const TDesC16& aPassword, 
       
   721 				const TDesC8& aNTResponse, 
       
   722 				const TDesC8& aPeerChallenge,
       
   723 				const TDesC8& aAuthenticatorChallenge,
       
   724 				const TDesC8& aUserName, 
       
   725 				TDes8& aAuthenticatorResponse)
       
   726 /**
       
   727    Generates the expected MS-CHAP-V2 Authenticator Response Value.
       
   728    @param aPassword [in] The Microsoft Windows NT password (0 to 256
       
   729    Unicode char).
       
   730    @param aNTResponse [in] The MS-CHAP-V2 NT-Response (24 octets).
       
   731    @param aPeerChallenge [in] The Peer Challenge (16 octets).
       
   732    @param aAuthenticatorChallenge [in] The Authenticator Challenge (16
       
   733    octets).
       
   734    @param aUserName [in] The Microsoft Windows NT username (0 to 256
       
   735    char).
       
   736    @param aAuthenticatorResponse [out] The expected MS-CHAP-V2
       
   737    Authenticator Response Value encoded in the format
       
   738    "S=<auth_string>" as specified in RFC 2759 (42 octets).
       
   739    @note This function implements the GenerateAuthenticatorResponse
       
   740    routine specified in RFC 2759.
       
   741    @internalComponent
       
   742 */
       
   743 	{
       
   744 	ASSERT(aPassword.Length()<=KPppMsChapMaxNTPasswordLength);
       
   745 	ASSERT(aNTResponse.Length() == KPppMsChap2NTResponseSize);
       
   746 	ASSERT(aPeerChallenge.Length() ==
       
   747 		KPppMsChap2PeerChallengeSize);
       
   748 	ASSERT(aAuthenticatorChallenge.Length() ==
       
   749 		   KPppMsChap2AuthenticatorChallengeSize);
       
   750 	ASSERT(aUserName.Length()<=KPppMsChapMaxNTUserNameLength);
       
   751 	ASSERT(aAuthenticatorResponse.Length() ==
       
   752 		   KPppMsChap2AuthenticatorResponseSize);
       
   753 
       
   754 	HBufC8* passwordHashBuf=HBufC8::NewMaxLC(KPppMsChap2HashSize);
       
   755 	TPtr8 passwordHash(passwordHashBuf->Des());
       
   756 
       
   757 	NtPasswordHashL(aPassword, passwordHash);
       
   758 
       
   759 	HashNtPasswordHashL(passwordHash, passwordHash);
       
   760 
       
   761 	CSHA1* sha1 = CSHA1::NewL();
       
   762 	CleanupStack::PushL(sha1);
       
   763 
       
   764 // A magic string literal specified in RFC 2759 used in reponse
       
   765 // generation by the GenerateAuthenticatorResponse routine for SHA-1
       
   766 // encryption.
       
   767 	_LIT8(KMagic1, "Magic server to client signing constant");
       
   768 
       
   769 
       
   770 	sha1->Update(passwordHash);
       
   771 	sha1->Update(aNTResponse);
       
   772 	TPtrC8 hash(sha1->Final(KMagic1));
       
   773 
       
   774 
       
   775 	HBufC8* challengeHashBuf =
       
   776 			HBufC8::NewMaxLC(KPppMsChap2ChallengeHashSize);
       
   777 	TPtr8 challengeHash(challengeHashBuf->Des());
       
   778 	ChallengeHashL(aPeerChallenge, 
       
   779 			aAuthenticatorChallenge, 
       
   780 			aUserName,
       
   781 			challengeHash);
       
   782 
       
   783 // Another magic string literal specified in RFC 2759 used in reponse
       
   784 // generation by the GenerateAuthenticatorResponse routine for SHA-1
       
   785 // encryption.
       
   786 	_LIT8(KMagic2, "Pad to make it do more than one iteration");
       
   787 
       
   788 
       
   789 	sha1->Update(hash);
       
   790 	sha1->Update(challengeHash);
       
   791 	const TUint8* pHash = sha1->Final(KMagic2).Ptr();
       
   792 
       
   793 	
       
   794 	_LIT8(KFormat,
       
   795 		  "S=%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X"
       
   796 			"%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X");
       
   797 	aAuthenticatorResponse.Format(KFormat, 
       
   798 					*pHash, 
       
   799 					*(pHash + 1),
       
   800 					*(pHash + 2), 
       
   801 					*(pHash + 3), 
       
   802 					*(pHash + 4), 
       
   803 					*(pHash + 5), 
       
   804 					*(pHash + 6), 
       
   805 					*(pHash + 7), 
       
   806 					*(pHash + 8), 
       
   807 					*(pHash + 9), 
       
   808 					*(pHash + 10),
       
   809 					*(pHash + 11), 
       
   810 					*(pHash + 12), 
       
   811 					*(pHash + 13), 
       
   812 					*(pHash + 14),
       
   813 					*(pHash + 15), 
       
   814 					*(pHash + 16), 
       
   815 					*(pHash + 17), 
       
   816 					*(pHash + 18),
       
   817 					*(pHash + 19));
       
   818 
       
   819 	CleanupStack::PopAndDestroy(challengeHashBuf);
       
   820 
       
   821 	CleanupStack::PopAndDestroy(sha1);
       
   822 	CleanupStack::PopAndDestroy(passwordHashBuf);
       
   823 
       
   824 	ASSERT(aAuthenticatorResponse.Length() ==
       
   825 		   KPppMsChap2AuthenticatorResponseSize);
       
   826 	}