|
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 // SSL3.0 and TLS1.0 Handshake Negotiation source file. |
|
15 // Describes the implementation of a Handshake Negotiation state machine. |
|
16 // |
|
17 // |
|
18 |
|
19 /** |
|
20 @file |
|
21 */ |
|
22 |
|
23 #include "tlshandshakeitem.h" |
|
24 #include "AlertProtocolEvents.h" |
|
25 #include "handshakereceiveevents.h" |
|
26 #include "handshaketransmitevents.h" |
|
27 #include "recordprotocolevents.h" |
|
28 #include "changecipherevents.h" |
|
29 #include "tlsconnection.h" |
|
30 #include "tlshandshake.h" |
|
31 |
|
32 void CHandshakeHeader::ComposeHeader( ETlsHandshakeMessage aHandshakeMessage, TInt aLength ) |
|
33 /** |
|
34 * This method composes a handshake header. |
|
35 */ |
|
36 { |
|
37 LOG(Log::Printf(_L("CHandshakeHeader::ComposeHeader()"));) |
|
38 |
|
39 __ASSERT_DEBUG( iPtr8 != NULL, TlsPanic(ETlsPanicNullPointerToHandshakeHeaderBuffer) ); |
|
40 iPtr8[KTlsHandshakeTypeOffset] = static_cast<TUint8>(aHandshakeMessage); |
|
41 |
|
42 TBigEndian value; |
|
43 value.SetValue( iPtr8 + KTlsHandshakeLengthOffset, KTlsHandshakeBodyLength, aLength ); |
|
44 } |
|
45 |
|
46 // |
|
47 CHandshake* CHandshake::NewL(CTlsConnection& aTlsConnection) |
|
48 /** |
|
49 * This method creates a Handshake negotiation object. |
|
50 */ |
|
51 { |
|
52 LOG(Log::Printf(_L("CHandshake::NewL()"));) |
|
53 |
|
54 CHandshake* self = new(ELeave) CHandshake(aTlsConnection); |
|
55 LOG(Log::Printf(_L("self %x - %x"), self, (TUint)self + sizeof( CHandshake ));) |
|
56 CleanupStack::PushL(self); |
|
57 self->ConstructL(aTlsConnection); |
|
58 CleanupStack::Pop(); |
|
59 return self; |
|
60 } |
|
61 |
|
62 CHandshake::~CHandshake() |
|
63 /** |
|
64 * Destructor. |
|
65 * Destroys the list of handshake messages to transmit, the hash objects and the |
|
66 * 'Send Alert' object |
|
67 */ |
|
68 { |
|
69 LOG(Log::Printf(_L("CHandshake::~CHandshake()"));) |
|
70 |
|
71 DestroyTxList(); |
|
72 delete iSHA1Verify; |
|
73 delete iMD5Verify; |
|
74 delete iSendAlert; |
|
75 delete iServerCert; |
|
76 } |
|
77 |
|
78 void CHandshake::DestroyTxList() |
|
79 { |
|
80 LOG(Log::Printf(_L("CHandshake::DestroyTxList() of transmitted handshake messages"));) |
|
81 |
|
82 CTlsEvent* listItem; |
|
83 |
|
84 iTxListIter.SetToFirst(); |
|
85 while ( (listItem = iTxListIter++) != NULL ) |
|
86 { |
|
87 iTransmitList.Remove(*listItem); |
|
88 delete listItem; |
|
89 }; |
|
90 } |
|
91 void CHandshake::ConstructL(CTlsConnection& aTlsConnection) |
|
92 /** |
|
93 * Standard 2 phase construction. |
|
94 * This method creates and returns pointers to Hash objects; |
|
95 * Re-constructs the record parser and composer objects (setting them up |
|
96 * specifically for Handshake negotiation); |
|
97 * Creates an Alert object (for sending Alerts) and |
|
98 * Initiates transmission of the first asynchronous event, the Client Hello message. |
|
99 * |
|
100 * @param aTlsConnection The connection that initiated the handshake negotiation. |
|
101 */ |
|
102 { |
|
103 LOG(Log::Printf(_L("CHandshake::ConstructL()"));) |
|
104 |
|
105 iSHA1Verify = CSHA1::NewL(); |
|
106 iMD5Verify = CMD5::NewL(); |
|
107 |
|
108 iSendAlert = new(ELeave)CSendAlert( *this, aTlsConnection.RecordComposer() ); |
|
109 LOG(Log::Printf(_L("iSendAlert %x - %x"), SendAlert(), (TUint)iSendAlert + sizeof( CSendAlert ));) |
|
110 aTlsConnection.RecordParser().ReConstructL( this, TPtr8(NULL, 0), *iSendAlert ); |
|
111 aTlsConnection.RecordComposer().ReConstructL( this, 0 ); |
|
112 iActiveEvent = InitiateTransmitL(); // pointer to the 1st event/message to transmit |
|
113 } |
|
114 |
|
115 void CHandshake::StartL( TRequestStatus* aClientStatus, MStateMachineNotify* aStateMachineNotify ) |
|
116 /** |
|
117 * This method starts the handshake negotiation state machine by calling the |
|
118 * Start() method of the base state machine class (CStateMachine). |
|
119 * |
|
120 * @param aClientStatus Pointer to a TRequestStatus object that completes when |
|
121 * handshake negotiation is completed. |
|
122 * @param aStateMachineNotify Pointer to a MStateMachineNotify interface object. |
|
123 */ |
|
124 { |
|
125 LOG(Log::Printf(_L("CHandshake::StartL()"));) |
|
126 |
|
127 // Obtain a reference to a Record parser object and use its Handshake parser |
|
128 // to set the user's data descriptor and its length (CRecordParser::iUserData |
|
129 // and iUserMaxLength). |
|
130 CRecordParser& RecordParser = iTlsConnection.RecordParser(); |
|
131 CHandshakeParser* pParser = RecordParser.HandshakeParser(); |
|
132 pParser->SetMessageAsUserDataL( KTlsHandshakeHeaderSize ); |
|
133 |
|
134 CStateMachine::Start( aClientStatus, (CAsynchEvent*)iSendAlert, aStateMachineNotify ); |
|
135 } |
|
136 |
|
137 CTlsEvent* CHandshake::InitiateReceiveL() |
|
138 /** |
|
139 * This method creates a list of Handshake messages that are expected from |
|
140 * the Server. |
|
141 */ |
|
142 { |
|
143 LOG(Log::Printf(_L("CHandshake::InitiateReceiveL()"));) |
|
144 |
|
145 CTLSProvider& tlsProvider = iTlsConnection.TlsProvider(); |
|
146 |
|
147 CRecordParser& recordParser = iTlsConnection.RecordParser(); |
|
148 CHandshakeParser* pParser = recordParser.HandshakeParser(); |
|
149 __ASSERT_DEBUG( pParser != 0, TlsPanic(ETlsPanicNullPointerToHandshakeRecordParser)); |
|
150 __ASSERT_DEBUG( History() != 0, TlsPanic(ETlsPanicNullStateMachineHistory)); |
|
151 |
|
152 pParser->DestroyRxList(); |
|
153 CHandshakeReceive* rxItem; |
|
154 |
|
155 if ( History() & ETlsFinishedRecv ) // Handshake negotiation is complete |
|
156 { |
|
157 return NULL; |
|
158 } |
|
159 |
|
160 if ( History() == ETlsClientHelloSent ) // and nothing else has happened |
|
161 { |
|
162 rxItem = new(ELeave)CServerHello( tlsProvider, *this, recordParser ); |
|
163 //coverity[leave_without_push] |
|
164 LOG(Log::Printf(_L("CServerHello %x - %x"), rxItem, (TUint)rxItem + sizeof( CServerHello ));) |
|
165 pParser->AddToList( *rxItem ); |
|
166 } |
|
167 else if ( History() & ETlsFullHandshake ) // Full handshake |
|
168 { |
|
169 if ( History() & ETlsServerHelloDoneRecv) // Server hello done received |
|
170 { |
|
171 rxItem = new(ELeave)CRecvFinished( tlsProvider, *this, recordParser ); |
|
172 //coverity[leave_without_push] |
|
173 LOG(Log::Printf(_L("CRecvFinished %x - %x"), rxItem, (TUint)rxItem + sizeof( CRecvFinished ));) |
|
174 pParser->AddToList( *rxItem ); |
|
175 } |
|
176 else // Start of the handshake |
|
177 { |
|
178 if ( (History() & ETlsUsingPskKeyExchange) == 0) |
|
179 { |
|
180 rxItem = new(ELeave)CServerCertificate( tlsProvider, *this, recordParser ); |
|
181 //coverity[leave_without_push] |
|
182 LOG(Log::Printf(_L("CServerCertificate %x - %x"), rxItem, (TUint)rxItem + sizeof( CServerCertificate ));) |
|
183 pParser->AddToList( *rxItem ); |
|
184 } |
|
185 |
|
186 rxItem = new(ELeave)CServerKeyExch( tlsProvider, *this, recordParser ); |
|
187 //coverity[leave_without_push] |
|
188 LOG(Log::Printf(_L("CServerKeyExch %x - %x"), rxItem, (TUint)rxItem + sizeof( CServerKeyExch ));) |
|
189 pParser->AddToList( *rxItem ); |
|
190 |
|
191 if ( (History() & ETlsUsingPskKeyExchange) == 0) |
|
192 { |
|
193 rxItem = new(ELeave)CCertificateReq( tlsProvider, *this, recordParser ); |
|
194 //coverity[leave_without_push] |
|
195 LOG(Log::Printf(_L("CCertificateReq %x - %x"), rxItem, (TUint)rxItem + sizeof( CCertificateReq ));) |
|
196 pParser->AddToList( *rxItem ); |
|
197 } |
|
198 |
|
199 rxItem = new(ELeave)CServerHelloDone( tlsProvider, *this, recordParser ); |
|
200 //coverity[leave_without_push] |
|
201 LOG(Log::Printf(_L("CServerHelloDone %x - %x"), rxItem, (TUint)rxItem + sizeof( CServerHelloDone ));) |
|
202 pParser->AddToList( *rxItem ); |
|
203 } |
|
204 } |
|
205 else if ( History() & ETlsAbbreviatedHandshake ) // Abbreviated handshake |
|
206 { |
|
207 rxItem = new(ELeave)CRecvFinished( tlsProvider, *this, recordParser ); |
|
208 //coverity[leave_without_push] |
|
209 LOG(Log::Printf(_L("CRecvFinished %x - %x"), rxItem, (TUint)rxItem + sizeof( CRecvFinished ));) |
|
210 pParser->AddToList( *rxItem ); |
|
211 } |
|
212 |
|
213 return &recordParser; |
|
214 } |
|
215 |
|
216 CTlsEvent* CHandshake::InitiateTransmitL() |
|
217 /** |
|
218 * This method initiates transmission of SSL/TLS events. |
|
219 * It destroys the current list of events/messages and checks its current |
|
220 * position in terms of the state machine's history. |
|
221 * It also gets references to the TLS provider interface and the Record composer |
|
222 * object. (It needs access to cryptography services for the message items and it |
|
223 * needs a record composer to build a TLS record to transmit) |
|
224 * |
|
225 * This method is called whenever the protocol has to transmit messages/events, |
|
226 * i.e., at the start of Handshake negotiations and the start of Client authentication. |
|
227 */ |
|
228 { |
|
229 LOG(Log::Printf(_L("CHandshake::InitiateTransmitL()"));) |
|
230 |
|
231 DestroyTxList(); |
|
232 |
|
233 // Handshake negotiation is complete |
|
234 if ( History() & ETlsFinishedSent ) |
|
235 return NULL; |
|
236 |
|
237 CTLSProvider& tlsProvider = iTlsConnection.TlsProvider(); |
|
238 CRecordComposer& recordComposer = iTlsConnection.RecordComposer(); |
|
239 CTlsEvent* txItem; |
|
240 |
|
241 if ( History() == ETlsHandshakeStart ) |
|
242 { |
|
243 // Create a ClientHello message, initialise it and add it to the Transmit list |
|
244 txItem = new(ELeave)CClientHello( tlsProvider, *this, recordComposer ); |
|
245 //coverity[leave_without_push] |
|
246 LOG(Log::Printf(_L("CClientHello %x - %x"), txItem, (TUint)txItem + sizeof( CClientHello ));) |
|
247 AddToList( *txItem ); |
|
248 } |
|
249 else if ( (History() & ETlsClientKeyExchSent) || (History() & ETlsAbbreviatedHandshake) ) |
|
250 { |
|
251 // As fixed DH is not supported, the Certificate Verify message is always sent, |
|
252 // providing a non-NULL Client Certificate message is sent. |
|
253 if ( CertificateVerifyReqd() ) |
|
254 { |
|
255 txItem = new(ELeave)CCertificateVerify( tlsProvider, *this, recordComposer ); |
|
256 //coverity[leave_without_push] |
|
257 LOG(Log::Printf(_L("CCertificateVerify %x - %x"), txItem, (TUint)txItem + sizeof( CClientHello ));) |
|
258 AddToList( *txItem ); |
|
259 } |
|
260 |
|
261 txItem = new(ELeave)CSendChangeCipherSpec( tlsProvider, *this, recordComposer ); |
|
262 //coverity[leave_without_push] |
|
263 LOG(Log::Printf(_L("CSendChangeCipherSpec %x - %x"), txItem, (TUint)txItem + sizeof( CClientHello ));) |
|
264 AddToList( *txItem ); |
|
265 |
|
266 txItem = new(ELeave)CSendFinished( tlsProvider, *this, recordComposer ); |
|
267 //coverity[leave_without_push] |
|
268 LOG(Log::Printf(_L("CSendFinished %x - %x"), txItem, (TUint)txItem + sizeof( CClientHello ));) |
|
269 AddToList( *txItem ); |
|
270 } |
|
271 else |
|
272 { |
|
273 if ( History() & ETlsCertificateReqRecv ) |
|
274 { |
|
275 txItem = new(ELeave)CClientCertificate( tlsProvider, *this, recordComposer ); |
|
276 //coverity[leave_without_push] |
|
277 LOG(Log::Printf(_L("CClientCertificate %x - %x"), txItem, (TUint)txItem + sizeof( CClientHello ));) |
|
278 AddToList( *txItem ); |
|
279 } |
|
280 txItem = new(ELeave)CClientKeyExch( tlsProvider, *this, recordComposer ); |
|
281 //coverity[leave_without_push] |
|
282 LOG(Log::Printf(_L("CClientKeyExch %x - %x"), txItem, (TUint)txItem + sizeof( CClientHello ));) |
|
283 AddToList( *txItem ); |
|
284 } |
|
285 |
|
286 iTxListIter.SetToFirst(); |
|
287 return NextTxEvent(); |
|
288 } |
|
289 |
|
290 void CHandshake::SetNegotiatedVersion( const TTLSProtocolVersion* aTlsVersion ) |
|
291 { |
|
292 iTlsConnection.RecordComposer().SetVersion( aTlsVersion ); |
|
293 } |
|
294 |
|
295 void CHandshake::UpdateVerify( TDesC8& aMessage ) |
|
296 /** |
|
297 * When the first Finished message is computed, it is necessary to copy the iMD5Verify |
|
298 * and iSHA1Verify to new CMD5 & CSHA1 to see all ::Final and be able to update the members |
|
299 * to compute the second Finished message. |
|
300 */ |
|
301 { |
|
302 LOG(Log::Printf(_L("CHandshake::UpdateVerify()"));) |
|
303 |
|
304 iMD5Verify->Update( aMessage ); |
|
305 iSHA1Verify->Update( aMessage ); |
|
306 } |
|
307 |
|
308 void CHandshake::DoCancel() |
|
309 { |
|
310 LOG(Log::Printf(_L("CHandshake::DoCancel()"));) |
|
311 |
|
312 if ( iTlsConnection.TlsSession() ) |
|
313 { |
|
314 iTlsConnection.TlsSession()->CancelRequest(); |
|
315 } |
|
316 |
|
317 iTlsConnection.RecordComposer().CancelAll(); |
|
318 iTlsConnection.RecordParser().CancelAll(); |
|
319 } |
|
320 |
|
321 void CHandshake::Cancel(TInt aLastError) |
|
322 { |
|
323 LOG(Log::Printf(_L("CHandshake::Cancel(TInt)"));) |
|
324 CStateMachine::Cancel(aLastError); |
|
325 if (!iErrorEvent) |
|
326 { |
|
327 OnCompletion(); // This will cause the instance to be deleted |
|
328 } |
|
329 } |
|
330 |
|
331 void CHandshake::AddToList( CTlsEvent& aMsgItem ) |
|
332 { |
|
333 LOG(Log::Printf(_L("CHandshake::AddToList()"));) |
|
334 |
|
335 TxMessageList().AddLast(aMsgItem); |
|
336 } |
|
337 |
|
338 void CHandshake::GetServerAddrInfo( TTLSServerAddr& serverInfo ) |
|
339 { |
|
340 iTlsConnection.GetServerAddrInfo( serverInfo ); |
|
341 } |
|
342 |
|
343 CTLSSession*& CHandshake::TlsSession() |
|
344 { |
|
345 return iTlsConnection.TlsSession(); |
|
346 } |
|
347 |
|
348 void CHandshake::ResetCryptoAttributes() |
|
349 { |
|
350 iTlsConnection.ResetCryptoAttributes(); |
|
351 } |
|
352 |
|
353 TBool CHandshake::SessionReUse() const |
|
354 /** |
|
355 * This method returns a boolean which indicates whether a session should be reused. |
|
356 */ |
|
357 { |
|
358 LOG(Log::Printf(_L("CHandshake::SessionReUse()"));) |
|
359 return iTlsConnection.SessionReUse(); |
|
360 } |
|
361 |
|
362 CExtensionNode::CExtensionNode(CItemBase* aNext) : |
|
363 CConstItem(aNext, KTlsExtensionTypeLength) |
|
364 { |
|
365 } |
|
366 |
|
367 CExtensionNode::~CExtensionNode() |
|
368 { |
|
369 } |
|
370 |
|
371 TInt CExtensionNode::ExtensionLength() |
|
372 /** |
|
373 For ParseL (internalisation) to work correctly this function must return the total extension length. |
|
374 Any derived class must override this function by calling this one and then adding on its extra members. |
|
375 Unfortunately we can not walk the record linked list to perform this calculation because GetItemLength |
|
376 is not virtual (and not define in CItemBase). |
|
377 */ |
|
378 { |
|
379 return GetItemLength(); |
|
380 } |
|
381 |
|
382 |
|
383 CKnownExtensionNode::CKnownExtensionNode() : |
|
384 CExtensionNode(&iOpaqueData), |
|
385 iOpaqueData(NULL, KTlsExtensionLength) |
|
386 { |
|
387 } |
|
388 |
|
389 void CKnownExtensionNode::ConstructOpaqueDataWrapperL(CItemBase* aNext) |
|
390 { |
|
391 iOpaqueData.AddNodeL(aNext); |
|
392 } |
|
393 |
|
394 CKnownExtensionNode::~CKnownExtensionNode() |
|
395 { |
|
396 // Must not delete the opaque data because it points to members of a class derived from this one... |
|
397 iOpaqueData.DropNodes(); |
|
398 } |
|
399 |
|
400 CCompoundList::~CCompoundList() |
|
401 { |
|
402 TInt nodeCount = iRecords.Count(); |
|
403 for(TInt i=0; i<nodeCount; ++i) |
|
404 { |
|
405 delete iRecords[i].First(); |
|
406 } |
|
407 iRecords.Reset(); |
|
408 } |
|
409 |
|
410 void CCompoundList::AddNodeL( CItemBase* aNode ) |
|
411 /** |
|
412 * Adds the item to the tail of the compound list. |
|
413 |
|
414 * Once added the item is owned by the iNodes pointer array |
|
415 * |
|
416 * @param aItemBase the item to be added |
|
417 */ |
|
418 { |
|
419 // Add node to end of list. |
|
420 // Each node is managed by a TRecord object |
|
421 TRecord record(aNode); |
|
422 iRecords.AppendL(record); |
|
423 } |
|
424 |
|
425 TInt CCompoundList::CalcTotalInitialiseLength() const |
|
426 /** |
|
427 * Calculates the length of the data buffer to hold the data for all items in the |
|
428 dynamic record. |
|
429 |
|
430 * An initial value must be assigned to the items' headers. |
|
431 * |
|
432 * @return the buffer length |
|
433 */ |
|
434 { |
|
435 TInt n = CCompoundListHeader::CalcTotalInitialiseLength(); |
|
436 TInt nodeCount = iRecords.Count(); |
|
437 for(TInt i=0; i<nodeCount; ++i) |
|
438 { |
|
439 n += iRecords[i].CalcTotalInitialiseLength(); |
|
440 } |
|
441 return n; |
|
442 } |
|
443 |
|
444 void CCompoundList::InitialiseL( TPtr8& aBuf8 ) |
|
445 /** |
|
446 * Initialises the pointers and length for all the items in the dynamic record |
|
447 * |
|
448 * @param aBuf8 An 8 bit modifiable pointer descriptor to hold the item's data |
|
449 */ |
|
450 { |
|
451 TInt tmp = aBuf8.Length(); |
|
452 CCompoundListHeader::InitialiseL( aBuf8 ); |
|
453 TInt n = aBuf8.Length(); |
|
454 |
|
455 TInt nodeCount = iRecords.Count(); |
|
456 for(TInt i=0; i<nodeCount; ++i) |
|
457 { |
|
458 iRecords[i].InitialiseL( aBuf8 ); |
|
459 } |
|
460 CCompoundListHeader::SetBigEndian( aBuf8.Length() - n ); |
|
461 } |
|
462 |
|
463 void CCompoundList::ParseL( TPtr8& /*aDes8*/ ) |
|
464 /** |
|
465 * Checks the input and initialises the items' pointers to point to the beginning |
|
466 * of theirs input buffers |
|
467 * |
|
468 * @param aDes8 An 8 bit modifiable pointer descriptor representing the descriptor to be checked |
|
469 */ |
|
470 { |
|
471 ASSERT(false); |
|
472 } |
|
473 |