networksecurity/tls/protocol/tlshandshake.cpp
changeset 0 af10295192d8
child 67 bb2423252ea3
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/networksecurity/tls/protocol/tlshandshake.cpp	Tue Jan 26 15:23:49 2010 +0200
@@ -0,0 +1,473 @@
+// 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:
+// SSL3.0 and TLS1.0 Handshake Negotiation source file.
+// Describes the implementation of a Handshake Negotiation state machine.
+// 
+//
+
+/**
+ @file
+*/
+  
+#include "tlshandshakeitem.h"
+#include "AlertProtocolEvents.h"
+#include "handshakereceiveevents.h"
+#include "handshaketransmitevents.h"
+#include "recordprotocolevents.h"
+#include "changecipherevents.h"
+#include "tlsconnection.h"
+#include "tlshandshake.h"  
+
+void CHandshakeHeader::ComposeHeader( ETlsHandshakeMessage aHandshakeMessage, TInt aLength )
+/**
+ * This method composes a handshake header.
+ */ 
+{
+	LOG(Log::Printf(_L("CHandshakeHeader::ComposeHeader()"));)
+	
+	__ASSERT_DEBUG( iPtr8 != NULL, TlsPanic(ETlsPanicNullPointerToHandshakeHeaderBuffer) );
+	iPtr8[KTlsHandshakeTypeOffset] = static_cast<TUint8>(aHandshakeMessage);
+   
+	TBigEndian value; 
+	value.SetValue( iPtr8 + KTlsHandshakeLengthOffset, KTlsHandshakeBodyLength, aLength );	
+}
+
+//
+CHandshake* CHandshake::NewL(CTlsConnection& aTlsConnection)
+/**
+ * This method creates a Handshake negotiation object.
+ */
+{
+	LOG(Log::Printf(_L("CHandshake::NewL()"));)
+
+	CHandshake* self = new(ELeave) CHandshake(aTlsConnection);
+  	LOG(Log::Printf(_L("self %x - %x"), self, (TUint)self + sizeof( CHandshake ));)
+	CleanupStack::PushL(self);
+	self->ConstructL(aTlsConnection);
+	CleanupStack::Pop();
+	return self;
+}
+
+CHandshake::~CHandshake()
+/**
+ * Destructor.
+ * Destroys the list of handshake messages to transmit, the hash objects and the
+ * 'Send Alert' object
+ */
+{
+	LOG(Log::Printf(_L("CHandshake::~CHandshake()"));)
+
+	DestroyTxList();
+	delete iSHA1Verify;
+	delete iMD5Verify;
+	delete iSendAlert;
+   delete iServerCert;
+}
+
+void CHandshake::DestroyTxList()
+{
+	LOG(Log::Printf(_L("CHandshake::DestroyTxList() of transmitted handshake messages"));)
+
+	CTlsEvent* listItem;
+    
+    iTxListIter.SetToFirst(); 
+    while ( (listItem = iTxListIter++) != NULL )
+        {
+        iTransmitList.Remove(*listItem);
+        delete listItem;
+        };
+}
+void CHandshake::ConstructL(CTlsConnection& aTlsConnection)
+/**
+ * Standard 2 phase construction.
+ * This method creates and returns pointers to Hash objects; 
+ * Re-constructs the record parser and composer objects (setting them up 
+ * specifically for Handshake negotiation);
+ * Creates an Alert object (for sending Alerts) and
+ * Initiates transmission of the first asynchronous event, the Client Hello message.
+ *
+ * @param aTlsConnection The connection that initiated the handshake negotiation.
+ */
+{
+	LOG(Log::Printf(_L("CHandshake::ConstructL()"));)
+   
+	iSHA1Verify = CSHA1::NewL();
+	iMD5Verify = CMD5::NewL();
+   
+	iSendAlert = new(ELeave)CSendAlert( *this, aTlsConnection.RecordComposer() );
+  	LOG(Log::Printf(_L("iSendAlert %x - %x"), SendAlert(), (TUint)iSendAlert + sizeof( CSendAlert ));)
+	aTlsConnection.RecordParser().ReConstructL( this, TPtr8(NULL, 0), *iSendAlert );
+	aTlsConnection.RecordComposer().ReConstructL( this, 0 );
+	iActiveEvent = InitiateTransmitL(); // pointer to the 1st event/message to transmit
+}
+
+void CHandshake::StartL( TRequestStatus* aClientStatus, MStateMachineNotify* aStateMachineNotify )
+/**
+ * This method starts the handshake negotiation state machine by calling the 
+ * Start() method of the base state machine class (CStateMachine).
+ *
+ * @param aClientStatus Pointer to a TRequestStatus object that completes when  
+ * handshake negotiation is completed.
+ * @param aStateMachineNotify Pointer to a MStateMachineNotify interface object.
+ */
+{
+	LOG(Log::Printf(_L("CHandshake::StartL()"));)
+
+	// Obtain a reference to a Record parser object and use its Handshake parser
+	// to set the user's data descriptor and its length (CRecordParser::iUserData 
+	// and iUserMaxLength).
+	CRecordParser& RecordParser = iTlsConnection.RecordParser();
+	CHandshakeParser* pParser = RecordParser.HandshakeParser();
+	pParser->SetMessageAsUserDataL( KTlsHandshakeHeaderSize );
+
+	CStateMachine::Start( aClientStatus, (CAsynchEvent*)iSendAlert, aStateMachineNotify );
+}
+
+CTlsEvent* CHandshake::InitiateReceiveL()
+/**
+ * This method creates a list of Handshake messages that are expected from 
+ * the Server.
+ */
+{
+	LOG(Log::Printf(_L("CHandshake::InitiateReceiveL()"));)
+
+	CTLSProvider& tlsProvider = iTlsConnection.TlsProvider();
+
+	CRecordParser& recordParser = iTlsConnection.RecordParser();
+	CHandshakeParser* pParser = recordParser.HandshakeParser();
+	__ASSERT_DEBUG( pParser != 0, TlsPanic(ETlsPanicNullPointerToHandshakeRecordParser));
+	__ASSERT_DEBUG( History() != 0, TlsPanic(ETlsPanicNullStateMachineHistory));
+	
+	pParser->DestroyRxList();
+	CHandshakeReceive* rxItem;
+
+	if ( History() & ETlsFinishedRecv ) // Handshake negotiation is complete
+	{
+		return NULL;
+	}
+	
+	if ( History() == ETlsClientHelloSent ) // and nothing else has happened
+	{
+		rxItem = new(ELeave)CServerHello( tlsProvider, *this, recordParser );
+		//coverity[leave_without_push]
+     	LOG(Log::Printf(_L("CServerHello %x - %x"), rxItem, (TUint)rxItem + sizeof( CServerHello ));)
+		pParser->AddToList( *rxItem );
+	}
+	else if ( History() & ETlsFullHandshake )			// Full handshake
+	{
+		if ( History() & ETlsServerHelloDoneRecv)		// Server hello done received
+		{
+			rxItem = new(ELeave)CRecvFinished( tlsProvider, *this, recordParser );
+			//coverity[leave_without_push]
+     	LOG(Log::Printf(_L("CRecvFinished %x - %x"), rxItem, (TUint)rxItem + sizeof( CRecvFinished ));)
+			pParser->AddToList( *rxItem );
+		}
+		else											// Start of the handshake
+		{
+			if ( (History() & ETlsUsingPskKeyExchange) == 0)
+				{
+				rxItem = new(ELeave)CServerCertificate( tlsProvider, *this, recordParser );
+				//coverity[leave_without_push]
+     			LOG(Log::Printf(_L("CServerCertificate %x - %x"), rxItem, (TUint)rxItem + sizeof( CServerCertificate ));)
+				pParser->AddToList( *rxItem );
+				}
+			
+			rxItem = new(ELeave)CServerKeyExch( tlsProvider, *this, recordParser );
+			//coverity[leave_without_push]
+     		LOG(Log::Printf(_L("CServerKeyExch %x - %x"), rxItem, (TUint)rxItem + sizeof( CServerKeyExch ));)
+			pParser->AddToList( *rxItem );
+
+			if ( (History() & ETlsUsingPskKeyExchange) == 0)
+				{
+				rxItem = new(ELeave)CCertificateReq( tlsProvider, *this, recordParser );
+				//coverity[leave_without_push]
+   	  			LOG(Log::Printf(_L("CCertificateReq %x - %x"), rxItem, (TUint)rxItem + sizeof( CCertificateReq ));)
+				pParser->AddToList( *rxItem );
+				}
+			
+			rxItem = new(ELeave)CServerHelloDone( tlsProvider, *this, recordParser );
+			//coverity[leave_without_push]
+     		LOG(Log::Printf(_L("CServerHelloDone %x - %x"), rxItem, (TUint)rxItem + sizeof( CServerHelloDone ));)
+			pParser->AddToList( *rxItem );
+		}
+	}
+	else if ( History() & ETlsAbbreviatedHandshake )	// Abbreviated handshake	
+	{	
+		rxItem = new(ELeave)CRecvFinished( tlsProvider, *this, recordParser );
+		//coverity[leave_without_push]
+     	LOG(Log::Printf(_L("CRecvFinished %x - %x"), rxItem, (TUint)rxItem + sizeof( CRecvFinished ));)
+		pParser->AddToList( *rxItem );
+	}
+	
+	return &recordParser;
+}
+
+CTlsEvent* CHandshake::InitiateTransmitL()
+/**
+ * This method initiates transmission of SSL/TLS events.
+ * It destroys the current list of events/messages and checks its current 
+ * position in terms of the state machine's history. 
+ * It also gets references to the TLS provider interface and the Record composer
+ * object. (It needs access to cryptography services for the message items and it 
+ * needs a record composer to build a TLS record to transmit)
+ * 
+ * This method is called whenever the protocol has to transmit messages/events, 
+ * i.e., at the start of Handshake negotiations and the start of Client authentication. 
+ */
+{
+	LOG(Log::Printf(_L("CHandshake::InitiateTransmitL()"));)
+
+	DestroyTxList();
+
+	// Handshake negotiation is complete
+	if ( History() & ETlsFinishedSent )
+		return NULL;
+
+	CTLSProvider& tlsProvider = iTlsConnection.TlsProvider();
+	CRecordComposer& recordComposer = iTlsConnection.RecordComposer();
+	CTlsEvent* txItem;
+
+	if ( History() == ETlsHandshakeStart )
+	{
+		// Create a ClientHello message, initialise it and add it to the Transmit list
+		txItem = new(ELeave)CClientHello( tlsProvider, *this, recordComposer );
+		//coverity[leave_without_push]
+     	LOG(Log::Printf(_L("CClientHello %x - %x"), txItem, (TUint)txItem + sizeof( CClientHello ));)
+		AddToList( *txItem );
+	}
+	else if ( (History() & ETlsClientKeyExchSent) || (History() & ETlsAbbreviatedHandshake) )
+	{
+		// As fixed DH is not supported, the Certificate Verify message is always sent,
+		// providing a non-NULL Client Certificate message is sent.
+		if ( CertificateVerifyReqd() )
+		{	
+			txItem = new(ELeave)CCertificateVerify( tlsProvider, *this, recordComposer );
+			//coverity[leave_without_push]
+     	LOG(Log::Printf(_L("CCertificateVerify %x - %x"), txItem, (TUint)txItem + sizeof( CClientHello ));)
+			AddToList( *txItem );
+		}
+
+		txItem = new(ELeave)CSendChangeCipherSpec( tlsProvider, *this, recordComposer );
+		//coverity[leave_without_push]
+     	LOG(Log::Printf(_L("CSendChangeCipherSpec %x - %x"), txItem, (TUint)txItem + sizeof( CClientHello ));)
+		AddToList( *txItem );
+
+		txItem = new(ELeave)CSendFinished( tlsProvider, *this, recordComposer );
+		//coverity[leave_without_push]
+     	LOG(Log::Printf(_L("CSendFinished %x - %x"), txItem, (TUint)txItem + sizeof( CClientHello ));)
+		AddToList( *txItem );
+	}
+	else
+	{
+		if ( History() & ETlsCertificateReqRecv )
+		{	
+			txItem = new(ELeave)CClientCertificate( tlsProvider, *this, recordComposer );
+			//coverity[leave_without_push]
+     	LOG(Log::Printf(_L("CClientCertificate %x - %x"), txItem, (TUint)txItem + sizeof( CClientHello ));)
+			AddToList( *txItem );
+		}
+		txItem = new(ELeave)CClientKeyExch( tlsProvider, *this, recordComposer );
+		//coverity[leave_without_push]
+     	LOG(Log::Printf(_L("CClientKeyExch %x - %x"), txItem, (TUint)txItem + sizeof( CClientHello ));)
+		AddToList( *txItem );
+	}
+
+   iTxListIter.SetToFirst();
+   return NextTxEvent();
+}
+
+void CHandshake::SetNegotiatedVersion( const TTLSProtocolVersion* aTlsVersion )
+{
+	iTlsConnection.RecordComposer().SetVersion( aTlsVersion );
+}
+
+void CHandshake::UpdateVerify( TDesC8& aMessage )
+/**
+ * When the first Finished message is computed, it is necessary to copy the iMD5Verify
+ * and iSHA1Verify to new CMD5 & CSHA1 to see all ::Final and be able to update the members 
+ * to compute the second Finished message.
+ */
+{
+	LOG(Log::Printf(_L("CHandshake::UpdateVerify()"));)	
+
+	iMD5Verify->Update( aMessage );
+	iSHA1Verify->Update( aMessage );
+}
+
+void CHandshake::DoCancel()
+	{
+	LOG(Log::Printf(_L("CHandshake::DoCancel()"));)
+
+    if ( iTlsConnection.TlsSession() )
+        {
+        iTlsConnection.TlsSession()->CancelRequest();
+        }
+
+	iTlsConnection.RecordComposer().CancelAll();
+	iTlsConnection.RecordParser().CancelAll();	
+	}
+
+void CHandshake::Cancel(TInt aLastError)
+    {
+	LOG(Log::Printf(_L("CHandshake::Cancel(TInt)"));)
+	CStateMachine::Cancel(aLastError);
+	if (!iErrorEvent)
+		{
+		OnCompletion(); // This will cause the instance to be deleted
+		}
+    }
+
+void CHandshake::AddToList( CTlsEvent& aMsgItem )
+{
+	LOG(Log::Printf(_L("CHandshake::AddToList()"));)
+
+    TxMessageList().AddLast(aMsgItem);
+}
+
+void CHandshake::GetServerAddrInfo( TTLSServerAddr& serverInfo )
+{
+   iTlsConnection.GetServerAddrInfo( serverInfo );
+}
+
+CTLSSession*& CHandshake::TlsSession()
+{
+   return iTlsConnection.TlsSession();
+}
+
+void CHandshake::ResetCryptoAttributes()
+{
+   iTlsConnection.ResetCryptoAttributes();
+}
+
+TBool CHandshake::SessionReUse() const
+/** 
+ * This method returns a boolean which indicates whether a session should be reused.
+ */
+{
+	LOG(Log::Printf(_L("CHandshake::SessionReUse()"));)
+	return iTlsConnection.SessionReUse();
+}
+
+CExtensionNode::CExtensionNode(CItemBase* aNext) :
+	CConstItem(aNext, KTlsExtensionTypeLength)
+{
+}
+
+CExtensionNode::~CExtensionNode()
+{
+}
+
+TInt CExtensionNode::ExtensionLength()
+	/**
+		For ParseL (internalisation) to work correctly this function must return the total extension length.
+		Any derived class must override this function by calling this one and then adding on its extra members.
+		Unfortunately we can not walk the record linked list to perform this calculation because GetItemLength
+		is not virtual (and not define in CItemBase).
+	*/
+{
+	return GetItemLength();
+}
+
+
+CKnownExtensionNode::CKnownExtensionNode() :
+	CExtensionNode(&iOpaqueData),
+	iOpaqueData(NULL, KTlsExtensionLength)
+{
+}
+
+void CKnownExtensionNode::ConstructOpaqueDataWrapperL(CItemBase* aNext)
+{
+	iOpaqueData.AddNodeL(aNext);
+}
+
+CKnownExtensionNode::~CKnownExtensionNode()
+{
+	// Must not delete the opaque data because it points to members of a class derived from this one...
+	iOpaqueData.DropNodes();
+}
+
+CCompoundList::~CCompoundList()
+	{
+	TInt nodeCount = iRecords.Count();
+	for(TInt i=0; i<nodeCount; ++i)
+		{
+		delete iRecords[i].First();
+		}
+	iRecords.Reset();
+	}
+
+void CCompoundList::AddNodeL( CItemBase* aNode )
+/** 
+ * Adds the item to the tail of the compound list. 
+ 
+ * Once added the item is owned by the iNodes pointer array
+ *
+ * @param aItemBase the item to be added
+ */
+	{
+	// Add node to end of list. 
+	// Each node is managed by a TRecord object
+	TRecord record(aNode);
+	iRecords.AppendL(record);
+	}
+
+TInt CCompoundList::CalcTotalInitialiseLength() const
+/** 
+ * Calculates the length of the data buffer to hold the data for all items in the 
+   dynamic record.
+ 
+ * An initial value must be assigned to the items' headers.
+ *
+ * @return the buffer length
+ */
+	{
+	TInt n = CCompoundListHeader::CalcTotalInitialiseLength();
+	TInt nodeCount = iRecords.Count();
+	for(TInt i=0; i<nodeCount; ++i)
+		{
+		n += iRecords[i].CalcTotalInitialiseLength();
+		}
+	return n;
+	}
+
+void CCompoundList::InitialiseL( TPtr8& aBuf8 )
+/** 
+ * Initialises the pointers and length for all the items in the dynamic record
+ *
+ * @param aBuf8  An 8 bit modifiable pointer descriptor to hold the item's data
+ */
+	{
+	TInt tmp = aBuf8.Length();
+	CCompoundListHeader::InitialiseL( aBuf8 );
+	TInt n = aBuf8.Length();
+
+	TInt nodeCount = iRecords.Count();
+	for(TInt i=0; i<nodeCount; ++i)
+		{
+		iRecords[i].InitialiseL( aBuf8 );
+		}
+	CCompoundListHeader::SetBigEndian( aBuf8.Length() - n );
+	}
+
+void CCompoundList::ParseL( TPtr8& /*aDes8*/ )
+/** 
+ * Checks the input and initialises the items' pointers to point to the beginning 
+ * of theirs input buffers
+ *
+ * @param aDes8 An 8 bit modifiable pointer descriptor representing the descriptor to be checked
+ */
+	{
+	ASSERT(false);
+	}
+