diff -r 000000000000 -r af10295192d8 networksecurity/tls/protocol/handshakereceiveevents.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/networksecurity/tls/protocol/handshakereceiveevents.cpp Tue Jan 26 15:23:49 2010 +0200 @@ -0,0 +1,835 @@ +// 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: +// Implementation file for Received handshake events - ServerHello, +// Server Certificate, Server KeyExchange, ServerHelloDone and Server +// Finished messages. It also contains the implementation for the +// CHandshakeParser class (parses received handshake messages). +// +// + +/** + @file +*/ + +#include "tlshandshakeitem.h" +#include "handshakereceiveevents.h" +#include "recordprotocolevents.h" +#include "tlshandshake.h" +#include "tlsconnection.h" +#include + +#ifdef SYMBIAN_ENABLE_SPLIT_HEADERS +#include +#endif + +CHandshakeParser::~CHandshakeParser() +/** + * Destructor. + * Destroys the Received messages list, nulls and deletes its pointers + * and descriptors. + */ +{ + LOG(Log::Printf(_L("CHandshakeParser::~CHandshakeParser()"));) + + DestroyRxList(); + iRecordParser.SetUserData( NULL ); + iRecordParser.SetUserMaxLength( 0 ); + delete iMessage; +} + +void CHandshakeParser::DestroyRxList() +{ + LOG(Log::Printf(_L("CHandshakeParser::DestroyRxList() of expected handshake messages"));) + + CHandshakeReceive* listItem; + + iRxListIter.SetToFirst(); + while ( (listItem = iRxListIter++) != NULL ) + { + iMessageList.Remove(*listItem); + delete listItem; + }; +} + +void CHandshakeParser::AddToList( CHandshakeReceive& aRxMsgItem ) +{ + LOG(Log::Printf(_L("CHandshakeParser::AddToList()"));) + + iMessageList.AddLast(aRxMsgItem); +} + +CTlsEvent* CHandshakeParser::LookUpEventL( const TUint8 aHandshakeType ) +/** + * This method is called from CHandshakeParser::ParseHeaderL(). It is used to determine + * which handshake message (event) will process a received message. + * + * @param aHandshakeType Constant TUint8 representing the Handshake message type. + * @return CTlsEvent* A pointer to the event that will process the received message. + */ +{ + LOG(Log::Printf(_L("CHandshakeParser::LookUpEventL()"));) + + CHandshakeReceive* pEvent; + iRxListIter.SetToFirst(); + + while ( (pEvent = iRxListIter) != 0 && !pEvent->AcceptMessage( aHandshakeType ) ) + { + iRxListIter++; + } + if ( !pEvent ) + { + User::Leave( KErrSSLAlertUnexpectedMessage ); + } + + return pEvent; +} + +TInt CHandshakeParser::ParseHeaderL() +/** + * This method parses a handshake message header and determines which event will + * process a received message. It also extracts the message length (which is in + * big endian format) from the message header. + * + * @return TInt An integer representing the handshake message length. + */ +{ + LOG(Log::Printf(_L("CHandshakeParser::ParseHeaderL()"));) + + iNext = LookUpEventL( iMessageType = iMessagePtr[KTlsHandshakeTypeOffset] ); + + TBigEndian value; + TInt nLength = value.GetValue( iMessagePtr.Ptr() + KTlsHandshakeLengthOffset, KTlsHandshakeBodyLength ); + if ( nLength > KTlsMaxHandshakeBodySize ) + { + User::Leave( KErrSSLAlertIllegalParameter ); + } + LOG(RFileLogger::HexDump(KSSLLogDir,KSSLLogFileName,EFileLoggingModeAppend, NULL, NULL, iMessagePtr.Ptr(), iMessagePtr.Length() )); + return nLength; +} + +void CHandshakeParser::SetMessageAsUserDataL( TInt aWaitingFor ) +{ + if ( !iMessage ) + { + iMessage = HBufC8::NewL( 128 ); + iMessagePtr.Set( iMessage->Des() ); + } + iRecordParser.SetUserData( &iMessagePtr ); + LOG(Log::Printf(_L("CHandshakeParser::SetMessageAsUserDataL() - aWaitingFor = %d, %d"), aWaitingFor, iMessagePtr.Length());) + iRecordParser.SetUserMaxLength( aWaitingFor ); + iWaitingFor = aWaitingFor; +} + +TPtr8 CHandshakeParser::Message() +/** + * This method returns a pointer to a heap descriptor which contains (or + * will contain) a received Handshake protocol message. We don't want to return a reference to HBuf + * that's why thi akward TPtr construction. The caller MUST process the buffer before returning + * to active scheduler. + */ +{ + TPtr8 ptr( const_cast(iMessagePtr.Ptr()), iMessagePtr.Length(), iMessagePtr.MaxLength() ); + iMessagePtr.SetLength( 0 ); + LOG(Log::Printf(_L("CHandshakeParser::Message() - %d, %d"), iMessage->Length(), iMessagePtr.Length());) + return ptr; //we cannot return iMessagePtr since it's reset to zero +} + +CAsynchEvent* CHandshakeParser::ProcessNextL( TRequestStatus& aStatus ) +{ + // Note that a Finished message is treated differently (see CRecvFinished::ProcessL) + // update verify happens only whilst negotiating + if ( iMessageType != ETlsFinishedMsg && iStateMachine->History() != KTlsApplicationData ) + { + Handshake().UpdateVerify( iMessagePtr ); + } + + SetMessageAsUserDataL( KTlsHandshakeHeaderSize ); + return iNext->ProcessL( aStatus ); + } + +TBool CHandshakeParser::AcceptRecord( TInt aRecordType ) const +/** + * This virtual method determines whether the first byte of a Record protocol header + * (content type) can be accepted by an event (in iExpRecordTypes). + * + * @param aRecordType Integer specifying the Record protocol content type + * @return TBool Boolean indicating whether or not the record should be accepted by + * this event. + */ +{ + LOG(Log::Printf(_L("CHandshakeParser::AcceptRecord()"));) + + return aRecordType == ETlsHandshakeContentType; +} + +CAsynchEvent* CHandshakeParser::ProcessL( TRequestStatus& aStatus ) +/** + * This asynchronous message parses a received handshake message. It first processes + * the message header (extracts the message length) and then the message body. + * + * @param aStatus Request status for this event. + * @return CAsynchEvent* A pointer to an asynchronous event which will process + * the handshake message. + */ +{ + + //********TODO: Remove this If condition when CR741 is submitted*******/ + if(iMessage == NULL) + { + TRequestStatus* p=&aStatus; + User::RequestComplete( p, KErrNone ); + return LookUpEventL(ETlsHelloRequestMsg); + /* + Recordparser received a handshake message in application data mode which the + handshake parser framework isnt primed to do. hence this "null" message. + + The iMessage buffer is initialized when and during the initial or + client renegotiation handshake and will not be initialized during APP data + transfer. Since we currently don't support renegotiations from server, + any handshake message about to be processed in app mode is stopped here, + returning an alert. + */ + } + //*********************************************************************/ + + __ASSERT_DEBUG( iMessage, TlsPanic(ETlsPanicNullHandshakeMsg) ); + + // Do we have enough data? + if ( iMessagePtr.Length() == iWaitingFor ) + { + if ( iMessagePtr.Length() == KTlsHandshakeHeaderSize ) + { // The handshake header has arrived + LOG(Log::Printf(_L("CHandshakeParser::ProcessL() - msg header received"));) + LOG(RFileLogger::HexDump(KSSLLogDir,KSSLLogFileName,EFileLoggingModeAppend, NULL, NULL, iMessagePtr.Ptr(), iMessagePtr.Length() )); + iWaitingFor += ParseHeaderL(); // Append message length + + if ( iWaitingFor > iMessagePtr.MaxLength() ) + { + iMessage = iMessage->ReAllocL( iWaitingFor ); + iMessagePtr.Set( iMessage->Des() ); + } + else if ( iWaitingFor == KTlsHandshakeHeaderSize ) + { // A zero length message was received => proceed at once + // The message length will be set to zero after the message has been processed + // see ParseHeaderL iNext must be != NULL => assert not necessary + return ProcessNextL( aStatus ); + } + } + else + { // Must be the message body so process it. The message buffer length and + // iUserMaxLength will be reset after the message has been processed. + LOG(Log::Printf(_L("CHandshakeParser::ProcessL() - msg body received"));) + iWaitingFor = KTlsHandshakeHeaderSize; // reset + __ASSERT_DEBUG( iNext, TlsPanic(ETlsPanicNoDataToProcess) ); //see ParseHeaderL + + return ProcessNextL( aStatus ); + } + } + + //read again + LOG(Log::Printf(_L("CHandshakeParser::ProcessL() - read again iWaitingFor = %d, %d, %d"), iWaitingFor, iMessage->Length(), iMessagePtr.Length());) + SetMessageAsUserDataL( iWaitingFor ); + return iRecordParser.ProcessL( aStatus ); +} + +// +// +CHandshakeReceive::~CHandshakeReceive() +{ + LOG(Log::Printf(_L("CHandshakeReceive::~CHandshakeReceive()"));) + delete iHandshakeMessage; + iHandshakeMessage = NULL; +} + +// +// +CServerHello::~CServerHello() +{ + iCipherList.Close(); +} + +TBool CServerHello::AcceptMessage( const TUint8 aHandshakeType ) const +/** + * This method determines whether a Server Hello message (event) should be accepted. + * It asserts that the Client Hello message has been sent. This is the only condition + * that must be met before a Server Hello message can be received immediately afterwards. + * @param aHandshakeType A handshake message type + * @return TBool Boolean indicating whether a message should be accepted + */ + { + LOG(Log::Printf(_L("CServerHello::AcceptMessage()"));) + + __ASSERT_DEBUG( iStateMachine->History() & ETlsClientHelloSent, TlsPanic(ETlsPanicClientHelloMsgNotSent) ); + return !iHandshakeMessage && aHandshakeType == ETlsServerHelloMsg; +} + +CAsynchEvent* CServerHello::ProcessL( TRequestStatus& aStatus ) +/** + * This method parses a received Server Hello message. It extracts the items in the + * message and passes the information on to Security for processing. + * For a Server Hello message, the only item of variable length is the Session Id + * (hence this item is preceded by its length part). + * + * The next event to be processed depends on whether this is a full handshake or an + * abbreviated handshake. + * + * @param aStatus Request status for this event. + * @return CAsynchEvent* A pointer to the next asynchronous event to be processed. + */ +{ + LOG(Log::Printf(_L("CServerHello::ProcessL()"));) + if ( iRecordParser.ReadActive() && !iCipherListRead ) + {//server's accepted renegotiation => close the old provider to reset token & attributes + //we still keep the encryptor & decryptor for record level in CRecordParser::iActiveTlsSession + //& CRecordComposer::iActiveTlsSession + iTlsProvider->ReConnectL(); + //!!!need to find the session again to force token enumeration this is extremely silly!!! + //!!!hoping to get the same session at least which should be implied + + iTlsProvider->CipherSuitesL(iCipherList, aStatus); + iCipherListRead = ETrue; + return this; + } + iCipherList.Close(); + iHandshakeMessage = new(ELeave)CServerHelloMsgWithOptionalExtensions; + LOG(Log::Printf(_L("iHandshakeMessage %x - %x"), iHandshakeMessage, (TUint)iHandshakeMessage + sizeof( CServerHelloMsg ));) + + TPtr8 ptr( iRecordParser.HandshakeParser()->Message() ); + iHandshakeMessage->iRecord.ParseL( ptr ); // Set each item's pointer to the correct part of the received message +#ifdef _DEBUG + LOG(iHandshakeMessage->iRecord.Dump( KSSLLogDir,KSSLLogFileName);) +#endif + + CServerHelloMsg* pHelloMsg = static_cast(iHandshakeMessage); + + // Extract the Server Hello data items and fill the TLSProvider's data structure + + TPtr8 body = pHelloMsg->iVersion.GetBodyDes(); + CTlsCryptoAttributes& cryptoAttributes = *iTlsProvider->Attributes(); + cryptoAttributes.iNegotiatedProtocol.iMajor = body[0]; + cryptoAttributes.iNegotiatedProtocol.iMinor = body[1]; + + TDes8& desSessionId = cryptoAttributes.iSessionNameAndID.iSessionId; + TInt resumeSession = desSessionId.Compare( pHelloMsg->iSessionId.GetBodyDes() ); + + Handshake().SetNegotiatedVersion( &cryptoAttributes.iNegotiatedProtocol ); + + cryptoAttributes.iMasterSecretInput.iServerRandom = pHelloMsg->iRandom.GetBodyDes(); + + User::LeaveIfError(pHelloMsg->iSessionId.GetBodyDes().Length() <= 32 ? KErrNone:KErrSSLAlertIllegalParameter); + + desSessionId.Copy( pHelloMsg->iSessionId.GetBodyDes() ); + LOG(RFileLogger::HexDump(KSSLLogDir,KSSLLogFileName,EFileLoggingModeAppend, NULL, NULL, desSessionId.Ptr(), desSessionId.Length() )); + + body.Zero(); + body = pHelloMsg->iCipherSuite.GetBodyDes(); + //we have to get back the cipher from our proposed list + //current cipher suites start with 00 followed by something != 00 => the below test is sufficient + //for the known ciphers. In case the server gives us something unknown the security will not be + //able to find/use any token and should return an error further down the handshake. + User::LeaveIfError( body.Length() == 2 && + cryptoAttributes.iProposedCiphers.Find( body ) != KErrNotFound ? KErrNone : KErrSSLAlertIllegalParameter ); + cryptoAttributes.iCurrentCipherSuite.iLoByte = body[1]; + cryptoAttributes.iCurrentCipherSuite.iHiByte = body[0]; + + + body.Zero(); + body = pHelloMsg->iCompression.GetBodyDes(); + User::LeaveIfError( body[0] == NULL ? KErrNone : KErrSSLAlertUnexpectedMessage ); + cryptoAttributes.iCompressionMethod = (TTLSCompressionMethod) body[0]; + + // Update the history and decide on whether it's a full or abbreviated handshake. + // Abbreviated handshake is only when Client and Server Session Id match, AND the server's + // Session id is NOT zero (i.e. when it is a resumable session). + iStateMachine->UpdateHistory( ETlsServerHelloRecv ); + + if ( resumeSession == 0 && desSessionId.Length() ) + {//server hello done won't be sent => create security parameters here + LOG(Log::Printf(_L("Abbreviated Handshake"));) + iStateMachine->UpdateHistory( ETlsAbbreviatedHandshake ); // Abbreviated Handshake + iTlsProvider->CreateL( Handshake().TlsSession(), aStatus); + } + else + { + LOG(Log::Printf(_L("Full Handshake"));) + iStateMachine->UpdateHistory( ETlsFullHandshake ); // Full Handshake + TRequestStatus* p=&aStatus; + User::RequestComplete( p, KErrNone ); + } + + const TTLSCipherSuiteMapping *pCipherDetails = cryptoAttributes.iCurrentCipherSuite.CipherDetails(); + if(pCipherDetails) + { + if(pCipherDetails->iKeyExAlg == EPsk) + { + // Using PSK for key exchange which impacts list of legal messages and ordering checks + iStateMachine->UpdateHistory( ETlsUsingPskKeyExchange ); + } + // Set key exchange type in the CTLSPublicKeyParams structure which will be passed to the TLS token. + iTlsProvider->Attributes()->iPublicKeyParams->iKeyType = pCipherDetails->iKeyExAlg; + } + + // Call InitiateReceiveL() to set up the list of expected messages + return Handshake().InitiateReceiveL(); + +} + +// +// +TBool CCertificateReq::AcceptMessage( const TUint8 aHandshakeType ) const +/** + * This method determines whether a Certificate Request message (event) should + * be accepted. A server certificate must have been received before a certificate + * request can be made. + * + * @param aHandshakeType A handshake message type + * @return TBool Boolean indicating whether a message should be accepted + */ +{ + LOG(Log::Printf(_L("CCertificateReq::AcceptMessage()"));) + return !iHandshakeMessage && iStateMachine->History() & ETlsServerCertificateRecv && aHandshakeType == ETlsCertificateReqMsg; +} + +CAsynchEvent* CCertificateReq::ProcessL( TRequestStatus& aStatus ) +/** + * This asynchronous method processes a received Certificate Request message. + * + * @param aStatus Request status for this event. + * @return CAsynchEvent* A pointer to the next asynchronous event to be processed. + */ +{ + LOG(Log::Printf(_L("CCertificateReq::ProcessL()"));) + + __ASSERT_DEBUG( !iHandshakeMessage, TlsPanic( ETlsPanicHandshakeMsgAlreadyExists) ); + iHandshakeMessage = new(ELeave) CCertificateReqMsg; + LOG(Log::Printf(_L("iHandshakeMessage %x - %x"), iHandshakeMessage, (TUint)iHandshakeMessage + sizeof( CCertificateReqMsg ));) + TPtr8 ptr( iRecordParser.HandshakeParser()->Message() ); + LOG(Log::Printf(_L("ptr.Length() %d"), ptr.Length() );) + + iHandshakeMessage->iRecord.ParseL( ptr ); +#ifdef _DEBUG + LOG(iHandshakeMessage->iRecord.Dump( KSSLLogDir,KSSLLogFileName);) +#endif + + // Update the Handshake history and set the Certificate Types + iStateMachine->UpdateHistory( ETlsCertificateReqRecv ); + CCertificateReqMsg* pCertReqMsg = (CCertificateReqMsg*)iHandshakeMessage; + + TPtr8 body = pCertReqMsg->iClientCertificateTypes.GetBodyDes(); + CTlsCryptoAttributes& cryptoAttributes = *iTlsProvider->Attributes(); + + for (TInt loop = 0; loop < body.Length(); ++loop) + { + User::LeaveIfError( cryptoAttributes.iReqCertTypes.Append( (TTLSClientCertType) body[loop] ) ); + } + + // Set the list of CA Distinguished Names + CListNode* listNode = pCertReqMsg->iDistinguishedNames.First(); + + __ASSERT_DEBUG( cryptoAttributes.iDistinguishedCANames.Count() == 0, TlsPanic(ETlsPanicNoCA )); + while ( listNode ) + { + TPtr8 listPtr = listNode->GetBodyDes(); + LOG(Log::Printf(_L("DistinguishedCAName") );) + LOG(RFileLogger::HexDump(KSSLLogDir,KSSLLogFileName,EFileLoggingModeAppend, NULL, NULL, listPtr.Ptr(), listPtr.Length() )); + + // Append the item into the Distinguished Names array and get the next list item + HBufC8* buf = listPtr.AllocL(); + CleanupStack::PushL(buf); + User::LeaveIfError(cryptoAttributes.iDistinguishedCANames.Append(buf) ); + CleanupStack::Pop(); + listNode = listNode->Next(); + } + iTlsProvider->Attributes()->iClientAuthenticate = ETrue; + + TRequestStatus* p=&aStatus; + User::RequestComplete( p, KErrNone ); + return &iRecordParser; +} + +// +// +TBool CServerCertificate::AcceptMessage( const TUint8 aHandshakeType ) const +/** + * This method determines whether a Server Certificate message (event) should + * be accepted. + * + * @param aHandshakeType A handshake message type + * @return TBool Boolean indicating whether a message should be accepted + */ +{ + LOG(Log::Printf(_L("CServerCertificate::AcceptMessage()"));) + + // Assert that a Server Hello message has been received + __ASSERT_DEBUG( iStateMachine->History() & ETlsServerHelloRecv, TlsPanic(ETlsPanicServerHelloMsgNotReceived)); + return !iHandshakeMessage && aHandshakeType == ETlsServerCertificateMsg; +} + +CAsynchEvent* CServerCertificate::ProcessL( TRequestStatus& aStatus ) +/** + * This asynchronous method processes a Server Certificate message. The contents of the + * message are passed as an uninterpreted buffer to the Security subsystem. + * + * @param aStatus Request status for this event. + * @return CAsynchEvent* A pointer to the next asynchronous event to be processed. + */ +{ + CX509Certificate*& serverCert = Handshake().ServerCert(); + if ( serverCert ) + {//store signing alg + //cannot convert from 'enum TAlgorithmId' to 'enum TTLSSignatureAlgorithm => + //=> CTlsProvider MUST use the same enum as security -> the same applies to keyExchange alg + const CSubjectPublicKeyInfo& publicKeyInfo = serverCert->PublicKey(); + + TAlgorithmId& signAlgorithm = Handshake().SignatureAlg(); + signAlgorithm = publicKeyInfo.AlgorithmId(); + if ( signAlgorithm == ERSA ) + { + iTlsProvider->Attributes()->isignatureAlgorithm = ERsaSigAlg; + } + else if ( signAlgorithm == EDSA ) + { + iTlsProvider->Attributes()->isignatureAlgorithm = EDsa; + } + else + { + LOG(Log::Printf(_L("Unknown signing algorithm and ridiculous enum redefinitions."));) + User::Leave( KErrSSLAlertIllegalParameter ); + } + TRequestStatus* p=&aStatus; + User::RequestComplete( p, KErrNone ); + return &iRecordParser; + } + LOG(Log::Printf(_L("CServerCertificate::ProcessL()"));) + + // Update the Handshake history and pass the server certificate chain to TLS Provider + iStateMachine->UpdateHistory( ETlsServerCertificateRecv ); + + TPtr8 ptr( iRecordParser.HandshakeParser()->Message() ); + TPtr8 ptrEncoded( ptr ); + +#ifdef _DEBUG + if (iHandshakeMessage) + { + LOG(Log::Printf(_L("ERROR: iHandshakeMessage %x - %x, should be NULL"), iHandshakeMessage, (TUint)iHandshakeMessage + sizeof( CCertificateMsg ));) + } + __ASSERT_DEBUG( !iHandshakeMessage, TlsPanic(ETlsPanicHandshakeMsgAlreadyExists)); +#endif + + iHandshakeMessage = new(ELeave) CCertificateMsg; + LOG(Log::Printf(_L("iHandshakeMessage %x - %x"), iHandshakeMessage, (TUint)iHandshakeMessage + sizeof( CCertificateMsg ));) + iHandshakeMessage->iRecord.ParseL( ptr ); // Set each item's pointer to the correct part of the received message +#ifdef _DEBUG + LOG(iHandshakeMessage->iRecord.Dump( KSSLLogDir,KSSLLogFileName);) +#endif + //prepare certificate chain for security (this should've really been TLS provider's work + CListNode* pNode = ((CCertificateMsg*)iHandshakeMessage)->iCertificateList.First(); + User::LeaveIfError( pNode ? KErrNone : KErrSSLAlertBadCertificate ); + //iPtr1 + iPtr2 setup to delete handshake header+cert list length + TUint8* iPtr1 = iHandshakeMessage->Ptr(); + TUint8* iPtr2 = pNode->Ptr(); + TInt nDel = 0; + TInt nToDel = iPtr2 - iPtr1; + TInt nPos = iPtr1 - iHandshakeMessage->Ptr(); + ptrEncoded.Delete( nPos, nToDel ); + do + { + nDel += nToDel; + iPtr1 = pNode->Ptr() - nDel; + iPtr2 = pNode->GetBodyPtr() - nDel; + nToDel = iPtr2 - iPtr1; + nPos = iPtr1 - iHandshakeMessage->Ptr(); + ptrEncoded.Delete( nPos, nToDel ); + pNode = pNode->Next(); + } + while ( pNode ); + LOG(RFileLogger::HexDump(KSSLLogDir,KSSLLogFileName,EFileLoggingModeAppend, NULL, NULL, ptrEncoded.Ptr(), ptrEncoded.Length() )); + iTlsProvider->VerifyServerCertificate( ptrEncoded, serverCert, aStatus); + + + return this; +} + +// +// +TBool CServerKeyExch::AcceptMessage( const TUint8 aHandshakeType ) const +/** + * This method determines whether a Server Key exchange message should be accepted. + * As server authentication is mandatory for the current implementation, either a Server + * certificate must have been received, or we must be using PSK to key exchange and authenticate. + * + * @param aHandshakeType A handshake message type + * @return TBool Boolean indicating whether a message should be accepted + */ +{ + LOG(Log::Printf(_L("CServerKeyExch::AcceptMessage()"));) + + return !iHandshakeMessage && + ((iStateMachine->History() & ETlsServerCertificateRecv) || (iStateMachine->History() & ETlsUsingPskKeyExchange)) && + aHandshakeType == ETlsServerKeyExchMsg; +} + +void CServerKeyExch::CreateMessageL( TTLSKeyExchangeAlgorithm aKeyExchange, TAlgorithmId aSignAlgorithm ) + { + if ( aKeyExchange == ERsa ) // Provider's enum for RSA differs from Security, hence the 2 enums values + { + if ( aSignAlgorithm == ERSA ) + { + iHandshakeMessage = new(ELeave) CRsaRsaServerKeyExchMsg; +LOG(Log::Printf(_L("iHandshakeMessage %x - %x"), iHandshakeMessage, (TUint)iHandshakeMessage + sizeof( CRsaRsaServerKeyExchMsg ));) + } + else if ( aSignAlgorithm == EDSA) + { + iHandshakeMessage = new(ELeave) CRsaDsaServerKeyExchMsg; +LOG(Log::Printf(_L("iHandshakeMessage %x - %x"), iHandshakeMessage, (TUint)iHandshakeMessage + sizeof( CRsaDsaServerKeyExchMsg ));) + } + } + else if ( aKeyExchange == EDHE ) + { + if ( aSignAlgorithm == ERSA ) + { + iHandshakeMessage = new(ELeave) CDhRsaServerKeyExchMsg; +LOG(Log::Printf(_L("iHandshakeMessage %x - %x"), iHandshakeMessage, (TUint)iHandshakeMessage + sizeof( CDhRsaServerKeyExchMsg ));) + } + else if ( aSignAlgorithm == EDSA) + { + iHandshakeMessage = new(ELeave) CDhDsaServerKeyExchMsg; +LOG(Log::Printf(_L("iHandshakeMessage %x - %x"), iHandshakeMessage, (TUint)iHandshakeMessage + sizeof( CDhDsaServerKeyExchMsg ));) + } + } + else if ( aKeyExchange == EPsk ) + { + iHandshakeMessage = new(ELeave) CPskServerKeyExchMsg; +LOG(Log::Printf(_L("iHandshakeMessage %x - %x"), iHandshakeMessage, (TUint)iHandshakeMessage + sizeof( CPskServerKeyExchMsg ));) + } + + if ( !iHandshakeMessage ) + { + LOG(Log::Printf(_L("CServerKeyExch::ProcessL() - Unknown signing algorithm"));) + User::Leave(KErrSSLAlertIllegalParameter); + } + } + +CAsynchEvent* CServerKeyExch::ProcessL( TRequestStatus& aStatus ) +/** + * This message processes a Server Key exchange message. + * The protocol needs the Key exchange algorithm from the cipher suite and the signing + * algorithm, if relevant, from the certificate, in order to interpret and process this + * message correctly. + * + * @param aStatus Request status for this event. + * @return CAsynchEvent* A pointer to the next asynchronous event to be processed. + */ +{ + LOG(Log::Printf(_L("CServerKeyExch::ProcessL()"));) + + // Get the Key exchange Algorithm and set this value in the Provider. + const TTLSCipherSuite cipherSuite = iTlsProvider->Attributes()->iCurrentCipherSuite; + + TTLSKeyExchangeAlgorithm keyExchange = iTlsProvider->Attributes()->iPublicKeyParams->iKeyType; + + TAlgorithmId& signAlgorithm = Handshake().SignatureAlg(); + + // Create and parse the Server Key exchange message. Set all necessary information. + CreateMessageL( keyExchange, signAlgorithm ); + TPtr8 ptr( iRecordParser.HandshakeParser()->Message() ); + iHandshakeMessage->iRecord.ParseL( ptr ); +#ifdef _DEBUG + LOG(iHandshakeMessage->iRecord.Dump( KSSLLogDir,KSSLLogFileName);) +#endif + CServerKeyExchMsg* pKeyExcMsg = (CServerKeyExchMsg*)iHandshakeMessage; + + pKeyExcMsg->CopyParamsL( iTlsProvider->Attributes() ); + + if(!(iStateMachine->History() & ETlsUsingPskKeyExchange)) + { + // Under pre-shared keys, the ServerKeyExch message does not have certificates (see RFC 4279). In + // this case, we do not process the certs. + TBuf8 msgDigest; + pKeyExcMsg->ComputeDigestL( iTlsProvider->Attributes()->iMasterSecretInput.iClientRandom, + iTlsProvider->Attributes()->iMasterSecretInput.iServerRandom, + msgDigest ); + TPtr8 signature( pKeyExcMsg->Signature() ); + CX509Certificate* serverCert = Handshake().ServerCert(); + __ASSERT_DEBUG( serverCert, TlsPanic(ETlsPanicNullServerCertificate) ); + const CSubjectPublicKeyInfo& publicKeyInfo = serverCert->PublicKey(); + + User::LeaveIfError( iTlsProvider->VerifySignatureL(publicKeyInfo, msgDigest, signature) ? + KErrNone : KErrSSLAlertBadCertificate ); + } + + // Update the Handshake history. Clear message buffer at the end and return the next + // item to be processed. + iStateMachine->UpdateHistory( ETlsServerKeyExchRecv ); + + TRequestStatus* p=&aStatus; + User::RequestComplete( p, KErrNone ); + return &iRecordParser; +} + +TBool CServerHelloDone::AcceptMessage( const TUint8 aHandshakeType ) const +/** + * This method decides whether a 'ServerHelloDone' message can be accepted. + * This message can only be received after a Server hello message. However, + * as this protocol implementation requires that a Server be authenticated, + * this message can only be accepted after a Server's certificate has been received, or we must + * be using PSK to key exchange and authenticate. + * + * @param aHandshakeType A handshake message type + * @return TBool Boolean indicating whether a message should be accepted + */ +{ + LOG(Log::Printf(_L("CServerHelloDone::AcceptMessage()"));) + + return !iHandshakeMessage && + ((iStateMachine->History() & ETlsServerCertificateRecv) || (iStateMachine->History() & ETlsUsingPskKeyExchange)) && + aHandshakeType == ETlsServerHelloDoneMsg; +} + +CAsynchEvent* CServerHelloDone::ProcessL( TRequestStatus& aStatus ) +/** + * This method processes a Server Hello Done message. + * Once this message has been received, the Server's params (from the negotiation) + * can begin to be processed. + * + * @param aStatus Request status for this event. + * @return CAsynchEvent* A pointer to the next asynchronous event to be processed. + */ +{ + LOG(Log::Printf(_L("CServerHelloDone::ProcessL()"));) + + iHandshakeMessage = new(ELeave)CServerHelloDoneMsg; + LOG(Log::Printf(_L("iHandshakeMessage %x - %x"), iHandshakeMessage, (TUint)iHandshakeMessage + sizeof( CServerHelloDoneMsg ));) + TPtr8 ptr( iRecordParser.HandshakeParser()->Message() ); + iHandshakeMessage->iRecord.ParseL( ptr ); // Set each item's pointer to the correct part of the received message +#ifdef _DEBUG + LOG(iHandshakeMessage->iRecord.Dump( KSSLLogDir,KSSLLogFileName);) +#endif + + // Update the Handshake history + // A ServerHelloDone message always has an empty body + User::LeaveIfError( ptr.Length() ? KErrSSLAlertUnexpectedMessage : KErrNone ); + iStateMachine->UpdateHistory( ETlsServerHelloDoneRecv ); + + // Call InitiateTransmitL() to start protocol transmissions. + // Set the next event to be processed as the Record composer object + iTlsProvider->CreateL( Handshake().TlsSession(), aStatus); + return Handshake().InitiateTransmitL(); +} + +// +// +TBool CRecvFinished::AcceptMessage( const TUint8 aHandshakeType ) const +/** + * This method decides whether a received 'Finished' message can be accepted. + * A 'Finished' message can only be accepted after a 'ChangeCipherSpec' message + * has been received. + * + * @param aHandshakeType A handshake message type + * @return TBool Boolean indicating whether a message should be accepted + */ +{ + LOG(Log::Printf(_L("CRecvFinished::AcceptMessage()"));) + __ASSERT_DEBUG( iStateMachine->History() & ETlsChangeCipherRecv, TlsPanic(ETlsPanicChangeCipherMsgNotReceived) ); + return !iHandshakeMessage && aHandshakeType == ETlsFinishedMsg; +} + +CAsynchEvent* CRecvFinished::ProcessL( TRequestStatus& aStatus ) +/** + * This asynchronous method processes a received Server Finished message. + * Note that the Finished message takes an integer which is the size of the message + * body only (i.e. minus the Handshake header). + */ +{ + LOG(Log::Printf(_L("CRecvFinished::ProcessL()"));) + + TPtr8 ptr( iRecordParser.HandshakeParser()->Message() ); + TInt nLen = ptr.Length(); + iHandshakeMessage = new(ELeave)CFinishedMsg( nLen - KTlsHandshakeHeaderSize); + LOG(Log::Printf(_L("iHandshakeMessage %x - %x"), iHandshakeMessage, (TUint)iHandshakeMessage + sizeof( CFinishedMsg ));) + + iHandshakeMessage->iRecord.ParseL( ptr ); // Set each item's pointer to the correct part of the received message +#ifdef _DEBUG + LOG(iHandshakeMessage->iRecord.Dump( KSSLLogDir,KSSLLogFileName);) +#endif + CFinishedMsg* pFinishedMsg = (CFinishedMsg*)iHandshakeMessage; + TPtr8 body = pFinishedMsg->iFinishedData.GetBodyDes(); + + // Pass the information to the Provider + iShaPtr = static_cast( Handshake().SHA1Verify()->CopyL() ); + iMd5Ptr = static_cast( Handshake().MD5Verify()->CopyL() ); + iTlsProvider->TlsSessionPtr()->VerifyServerFinishedMsgL( iMd5Ptr, iShaPtr, body, aStatus); + + ptr.Set( iRecordParser.HandshakeParser()->Message() ); + ptr.SetLength( nLen ); + Handshake().UpdateVerify( ptr ); + iStateMachine->UpdateHistory( ETlsFinishedRecv ); + + return Handshake().InitiateTransmitL(); +} + + +CRecvFinished::~CRecvFinished() +/** + * Destructor. + */ +{ + LOG(Log::Printf(_L("CRecvFinished::~CRecvFinished()"));) + delete iShaPtr; + delete iMd5Ptr; +} + +CGenericExtensionList::CGenericExtensionList( CItemBase* aNext ) + : CCompoundList(aNext, KTlsExtensionLength) +{ +} + +void CGenericExtensionList::ParseL( TPtr8& aDes8 ) +{ + if(aDes8.Length() == 0) + { + return; // No Extension list at all + } + CCompoundListHeader::ParseL( aDes8 ); + TInt nLenExpected = CCompoundListHeader::GetBigEndian(); + + if(nLenExpected > aDes8.Length()) + { + User::Leave( KErrBadDescriptor ); + } + + while ( nLenExpected > 0 ) + { + CGenericExtension *ext = CGenericExtension::NewLC( 0 ); + TRecord record(ext); + + record.ParseL( aDes8 ); + + AddNodeL(ext); + CleanupStack::Pop(ext); + + nLenExpected -= ext->ExtensionLength(); + } + if(nLenExpected < 0) + { + User::Leave( KErrBadDescriptor ); + } +} + +CGenericExtension* CGenericExtensionList::Node(TInt aIndex) const +{ + return static_cast(CCompoundList::Node(aIndex)); +} + +// End of file