|
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 // Implementation file for Received handshake events - ServerHello, |
|
15 // Server Certificate, Server KeyExchange, ServerHelloDone and Server |
|
16 // Finished messages. It also contains the implementation for the |
|
17 // CHandshakeParser class (parses received handshake messages). |
|
18 // |
|
19 // |
|
20 |
|
21 /** |
|
22 @file |
|
23 */ |
|
24 |
|
25 #include "tlshandshakeitem.h" |
|
26 #include "handshakereceiveevents.h" |
|
27 #include "recordprotocolevents.h" |
|
28 #include "tlshandshake.h" |
|
29 #include "tlsconnection.h" |
|
30 #include <signed.h> |
|
31 |
|
32 #ifdef SYMBIAN_ENABLE_SPLIT_HEADERS |
|
33 #include <tlstypedef_internal.h> |
|
34 #endif |
|
35 |
|
36 CHandshakeParser::~CHandshakeParser() |
|
37 /** |
|
38 * Destructor. |
|
39 * Destroys the Received messages list, nulls and deletes its pointers |
|
40 * and descriptors. |
|
41 */ |
|
42 { |
|
43 LOG(Log::Printf(_L("CHandshakeParser::~CHandshakeParser()"));) |
|
44 |
|
45 DestroyRxList(); |
|
46 iRecordParser.SetUserData( NULL ); |
|
47 iRecordParser.SetUserMaxLength( 0 ); |
|
48 delete iMessage; |
|
49 } |
|
50 |
|
51 void CHandshakeParser::DestroyRxList() |
|
52 { |
|
53 LOG(Log::Printf(_L("CHandshakeParser::DestroyRxList() of expected handshake messages"));) |
|
54 |
|
55 CHandshakeReceive* listItem; |
|
56 |
|
57 iRxListIter.SetToFirst(); |
|
58 while ( (listItem = iRxListIter++) != NULL ) |
|
59 { |
|
60 iMessageList.Remove(*listItem); |
|
61 delete listItem; |
|
62 }; |
|
63 } |
|
64 |
|
65 void CHandshakeParser::AddToList( CHandshakeReceive& aRxMsgItem ) |
|
66 { |
|
67 LOG(Log::Printf(_L("CHandshakeParser::AddToList()"));) |
|
68 |
|
69 iMessageList.AddLast(aRxMsgItem); |
|
70 } |
|
71 |
|
72 CTlsEvent* CHandshakeParser::LookUpEventL( const TUint8 aHandshakeType ) |
|
73 /** |
|
74 * This method is called from CHandshakeParser::ParseHeaderL(). It is used to determine |
|
75 * which handshake message (event) will process a received message. |
|
76 * |
|
77 * @param aHandshakeType Constant TUint8 representing the Handshake message type. |
|
78 * @return CTlsEvent* A pointer to the event that will process the received message. |
|
79 */ |
|
80 { |
|
81 LOG(Log::Printf(_L("CHandshakeParser::LookUpEventL()"));) |
|
82 |
|
83 CHandshakeReceive* pEvent; |
|
84 iRxListIter.SetToFirst(); |
|
85 |
|
86 while ( (pEvent = iRxListIter) != 0 && !pEvent->AcceptMessage( aHandshakeType ) ) |
|
87 { |
|
88 iRxListIter++; |
|
89 } |
|
90 if ( !pEvent ) |
|
91 { |
|
92 User::Leave( KErrSSLAlertUnexpectedMessage ); |
|
93 } |
|
94 |
|
95 return pEvent; |
|
96 } |
|
97 |
|
98 TInt CHandshakeParser::ParseHeaderL() |
|
99 /** |
|
100 * This method parses a handshake message header and determines which event will |
|
101 * process a received message. It also extracts the message length (which is in |
|
102 * big endian format) from the message header. |
|
103 * |
|
104 * @return TInt An integer representing the handshake message length. |
|
105 */ |
|
106 { |
|
107 LOG(Log::Printf(_L("CHandshakeParser::ParseHeaderL()"));) |
|
108 |
|
109 iNext = LookUpEventL( iMessageType = iMessagePtr[KTlsHandshakeTypeOffset] ); |
|
110 |
|
111 TBigEndian value; |
|
112 TInt nLength = value.GetValue( iMessagePtr.Ptr() + KTlsHandshakeLengthOffset, KTlsHandshakeBodyLength ); |
|
113 if ( nLength > KTlsMaxHandshakeBodySize ) |
|
114 { |
|
115 User::Leave( KErrSSLAlertIllegalParameter ); |
|
116 } |
|
117 LOG(RFileLogger::HexDump(KSSLLogDir,KSSLLogFileName,EFileLoggingModeAppend, NULL, NULL, iMessagePtr.Ptr(), iMessagePtr.Length() )); |
|
118 return nLength; |
|
119 } |
|
120 |
|
121 void CHandshakeParser::SetMessageAsUserDataL( TInt aWaitingFor ) |
|
122 { |
|
123 if ( !iMessage ) |
|
124 { |
|
125 iMessage = HBufC8::NewL( 128 ); |
|
126 iMessagePtr.Set( iMessage->Des() ); |
|
127 } |
|
128 iRecordParser.SetUserData( &iMessagePtr ); |
|
129 LOG(Log::Printf(_L("CHandshakeParser::SetMessageAsUserDataL() - aWaitingFor = %d, %d"), aWaitingFor, iMessagePtr.Length());) |
|
130 iRecordParser.SetUserMaxLength( aWaitingFor ); |
|
131 iWaitingFor = aWaitingFor; |
|
132 } |
|
133 |
|
134 TPtr8 CHandshakeParser::Message() |
|
135 /** |
|
136 * This method returns a pointer to a heap descriptor which contains (or |
|
137 * will contain) a received Handshake protocol message. We don't want to return a reference to HBuf |
|
138 * that's why thi akward TPtr construction. The caller MUST process the buffer before returning |
|
139 * to active scheduler. |
|
140 */ |
|
141 { |
|
142 TPtr8 ptr( const_cast<TUint8*>(iMessagePtr.Ptr()), iMessagePtr.Length(), iMessagePtr.MaxLength() ); |
|
143 iMessagePtr.SetLength( 0 ); |
|
144 LOG(Log::Printf(_L("CHandshakeParser::Message() - %d, %d"), iMessage->Length(), iMessagePtr.Length());) |
|
145 return ptr; //we cannot return iMessagePtr since it's reset to zero |
|
146 } |
|
147 |
|
148 CAsynchEvent* CHandshakeParser::ProcessNextL( TRequestStatus& aStatus ) |
|
149 { |
|
150 // Note that a Finished message is treated differently (see CRecvFinished::ProcessL) |
|
151 // update verify happens only whilst negotiating |
|
152 if ( iMessageType != ETlsFinishedMsg && iStateMachine->History() != KTlsApplicationData ) |
|
153 { |
|
154 Handshake().UpdateVerify( iMessagePtr ); |
|
155 } |
|
156 |
|
157 SetMessageAsUserDataL( KTlsHandshakeHeaderSize ); |
|
158 return iNext->ProcessL( aStatus ); |
|
159 } |
|
160 |
|
161 TBool CHandshakeParser::AcceptRecord( TInt aRecordType ) const |
|
162 /** |
|
163 * This virtual method determines whether the first byte of a Record protocol header |
|
164 * (content type) can be accepted by an event (in iExpRecordTypes). |
|
165 * |
|
166 * @param aRecordType Integer specifying the Record protocol content type |
|
167 * @return TBool Boolean indicating whether or not the record should be accepted by |
|
168 * this event. |
|
169 */ |
|
170 { |
|
171 LOG(Log::Printf(_L("CHandshakeParser::AcceptRecord()"));) |
|
172 |
|
173 return aRecordType == ETlsHandshakeContentType; |
|
174 } |
|
175 |
|
176 CAsynchEvent* CHandshakeParser::ProcessL( TRequestStatus& aStatus ) |
|
177 /** |
|
178 * This asynchronous message parses a received handshake message. It first processes |
|
179 * the message header (extracts the message length) and then the message body. |
|
180 * |
|
181 * @param aStatus Request status for this event. |
|
182 * @return CAsynchEvent* A pointer to an asynchronous event which will process |
|
183 * the handshake message. |
|
184 */ |
|
185 { |
|
186 |
|
187 //********TODO: Remove this If condition when CR741 is submitted*******/ |
|
188 if(iMessage == NULL) |
|
189 { |
|
190 TRequestStatus* p=&aStatus; |
|
191 User::RequestComplete( p, KErrNone ); |
|
192 return LookUpEventL(ETlsHelloRequestMsg); |
|
193 /* |
|
194 Recordparser received a handshake message in application data mode which the |
|
195 handshake parser framework isnt primed to do. hence this "null" message. |
|
196 |
|
197 The iMessage buffer is initialized when and during the initial or |
|
198 client renegotiation handshake and will not be initialized during APP data |
|
199 transfer. Since we currently don't support renegotiations from server, |
|
200 any handshake message about to be processed in app mode is stopped here, |
|
201 returning an alert. |
|
202 */ |
|
203 } |
|
204 //*********************************************************************/ |
|
205 |
|
206 __ASSERT_DEBUG( iMessage, TlsPanic(ETlsPanicNullHandshakeMsg) ); |
|
207 |
|
208 // Do we have enough data? |
|
209 if ( iMessagePtr.Length() == iWaitingFor ) |
|
210 { |
|
211 if ( iMessagePtr.Length() == KTlsHandshakeHeaderSize ) |
|
212 { // The handshake header has arrived |
|
213 LOG(Log::Printf(_L("CHandshakeParser::ProcessL() - msg header received"));) |
|
214 LOG(RFileLogger::HexDump(KSSLLogDir,KSSLLogFileName,EFileLoggingModeAppend, NULL, NULL, iMessagePtr.Ptr(), iMessagePtr.Length() )); |
|
215 iWaitingFor += ParseHeaderL(); // Append message length |
|
216 |
|
217 if ( iWaitingFor > iMessagePtr.MaxLength() ) |
|
218 { |
|
219 iMessage = iMessage->ReAllocL( iWaitingFor ); |
|
220 iMessagePtr.Set( iMessage->Des() ); |
|
221 } |
|
222 else if ( iWaitingFor == KTlsHandshakeHeaderSize ) |
|
223 { // A zero length message was received => proceed at once |
|
224 // The message length will be set to zero after the message has been processed |
|
225 // see ParseHeaderL iNext must be != NULL => assert not necessary |
|
226 return ProcessNextL( aStatus ); |
|
227 } |
|
228 } |
|
229 else |
|
230 { // Must be the message body so process it. The message buffer length and |
|
231 // iUserMaxLength will be reset after the message has been processed. |
|
232 LOG(Log::Printf(_L("CHandshakeParser::ProcessL() - msg body received"));) |
|
233 iWaitingFor = KTlsHandshakeHeaderSize; // reset |
|
234 __ASSERT_DEBUG( iNext, TlsPanic(ETlsPanicNoDataToProcess) ); //see ParseHeaderL |
|
235 |
|
236 return ProcessNextL( aStatus ); |
|
237 } |
|
238 } |
|
239 |
|
240 //read again |
|
241 LOG(Log::Printf(_L("CHandshakeParser::ProcessL() - read again iWaitingFor = %d, %d, %d"), iWaitingFor, iMessage->Length(), iMessagePtr.Length());) |
|
242 SetMessageAsUserDataL( iWaitingFor ); |
|
243 return iRecordParser.ProcessL( aStatus ); |
|
244 } |
|
245 |
|
246 // |
|
247 // |
|
248 CHandshakeReceive::~CHandshakeReceive() |
|
249 { |
|
250 LOG(Log::Printf(_L("CHandshakeReceive::~CHandshakeReceive()"));) |
|
251 delete iHandshakeMessage; |
|
252 iHandshakeMessage = NULL; |
|
253 } |
|
254 |
|
255 // |
|
256 // |
|
257 CServerHello::~CServerHello() |
|
258 { |
|
259 iCipherList.Close(); |
|
260 } |
|
261 |
|
262 TBool CServerHello::AcceptMessage( const TUint8 aHandshakeType ) const |
|
263 /** |
|
264 * This method determines whether a Server Hello message (event) should be accepted. |
|
265 * It asserts that the Client Hello message has been sent. This is the only condition |
|
266 * that must be met before a Server Hello message can be received immediately afterwards. |
|
267 * @param aHandshakeType A handshake message type |
|
268 * @return TBool Boolean indicating whether a message should be accepted |
|
269 */ |
|
270 { |
|
271 LOG(Log::Printf(_L("CServerHello::AcceptMessage()"));) |
|
272 |
|
273 __ASSERT_DEBUG( iStateMachine->History() & ETlsClientHelloSent, TlsPanic(ETlsPanicClientHelloMsgNotSent) ); |
|
274 return !iHandshakeMessage && aHandshakeType == ETlsServerHelloMsg; |
|
275 } |
|
276 |
|
277 CAsynchEvent* CServerHello::ProcessL( TRequestStatus& aStatus ) |
|
278 /** |
|
279 * This method parses a received Server Hello message. It extracts the items in the |
|
280 * message and passes the information on to Security for processing. |
|
281 * For a Server Hello message, the only item of variable length is the Session Id |
|
282 * (hence this item is preceded by its length part). |
|
283 * |
|
284 * The next event to be processed depends on whether this is a full handshake or an |
|
285 * abbreviated handshake. |
|
286 * |
|
287 * @param aStatus Request status for this event. |
|
288 * @return CAsynchEvent* A pointer to the next asynchronous event to be processed. |
|
289 */ |
|
290 { |
|
291 LOG(Log::Printf(_L("CServerHello::ProcessL()"));) |
|
292 if ( iRecordParser.ReadActive() && !iCipherListRead ) |
|
293 {//server's accepted renegotiation => close the old provider to reset token & attributes |
|
294 //we still keep the encryptor & decryptor for record level in CRecordParser::iActiveTlsSession |
|
295 //& CRecordComposer::iActiveTlsSession |
|
296 iTlsProvider->ReConnectL(); |
|
297 //!!!need to find the session again to force token enumeration this is extremely silly!!! |
|
298 //!!!hoping to get the same session at least which should be implied |
|
299 |
|
300 iTlsProvider->CipherSuitesL(iCipherList, aStatus); |
|
301 iCipherListRead = ETrue; |
|
302 return this; |
|
303 } |
|
304 iCipherList.Close(); |
|
305 iHandshakeMessage = new(ELeave)CServerHelloMsgWithOptionalExtensions; |
|
306 LOG(Log::Printf(_L("iHandshakeMessage %x - %x"), iHandshakeMessage, (TUint)iHandshakeMessage + sizeof( CServerHelloMsg ));) |
|
307 |
|
308 TPtr8 ptr( iRecordParser.HandshakeParser()->Message() ); |
|
309 iHandshakeMessage->iRecord.ParseL( ptr ); // Set each item's pointer to the correct part of the received message |
|
310 #ifdef _DEBUG |
|
311 LOG(iHandshakeMessage->iRecord.Dump( KSSLLogDir,KSSLLogFileName);) |
|
312 #endif |
|
313 |
|
314 CServerHelloMsg* pHelloMsg = static_cast<CServerHelloMsg*>(iHandshakeMessage); |
|
315 |
|
316 // Extract the Server Hello data items and fill the TLSProvider's data structure |
|
317 |
|
318 TPtr8 body = pHelloMsg->iVersion.GetBodyDes(); |
|
319 CTlsCryptoAttributes& cryptoAttributes = *iTlsProvider->Attributes(); |
|
320 cryptoAttributes.iNegotiatedProtocol.iMajor = body[0]; |
|
321 cryptoAttributes.iNegotiatedProtocol.iMinor = body[1]; |
|
322 |
|
323 TDes8& desSessionId = cryptoAttributes.iSessionNameAndID.iSessionId; |
|
324 TInt resumeSession = desSessionId.Compare( pHelloMsg->iSessionId.GetBodyDes() ); |
|
325 |
|
326 Handshake().SetNegotiatedVersion( &cryptoAttributes.iNegotiatedProtocol ); |
|
327 |
|
328 cryptoAttributes.iMasterSecretInput.iServerRandom = pHelloMsg->iRandom.GetBodyDes(); |
|
329 |
|
330 User::LeaveIfError(pHelloMsg->iSessionId.GetBodyDes().Length() <= 32 ? KErrNone:KErrSSLAlertIllegalParameter); |
|
331 |
|
332 desSessionId.Copy( pHelloMsg->iSessionId.GetBodyDes() ); |
|
333 LOG(RFileLogger::HexDump(KSSLLogDir,KSSLLogFileName,EFileLoggingModeAppend, NULL, NULL, desSessionId.Ptr(), desSessionId.Length() )); |
|
334 |
|
335 body.Zero(); |
|
336 body = pHelloMsg->iCipherSuite.GetBodyDes(); |
|
337 //we have to get back the cipher from our proposed list |
|
338 //current cipher suites start with 00 followed by something != 00 => the below test is sufficient |
|
339 //for the known ciphers. In case the server gives us something unknown the security will not be |
|
340 //able to find/use any token and should return an error further down the handshake. |
|
341 User::LeaveIfError( body.Length() == 2 && |
|
342 cryptoAttributes.iProposedCiphers.Find( body ) != KErrNotFound ? KErrNone : KErrSSLAlertIllegalParameter ); |
|
343 cryptoAttributes.iCurrentCipherSuite.iLoByte = body[1]; |
|
344 cryptoAttributes.iCurrentCipherSuite.iHiByte = body[0]; |
|
345 |
|
346 |
|
347 body.Zero(); |
|
348 body = pHelloMsg->iCompression.GetBodyDes(); |
|
349 User::LeaveIfError( body[0] == NULL ? KErrNone : KErrSSLAlertUnexpectedMessage ); |
|
350 cryptoAttributes.iCompressionMethod = (TTLSCompressionMethod) body[0]; |
|
351 |
|
352 // Update the history and decide on whether it's a full or abbreviated handshake. |
|
353 // Abbreviated handshake is only when Client and Server Session Id match, AND the server's |
|
354 // Session id is NOT zero (i.e. when it is a resumable session). |
|
355 iStateMachine->UpdateHistory( ETlsServerHelloRecv ); |
|
356 |
|
357 if ( resumeSession == 0 && desSessionId.Length() ) |
|
358 {//server hello done won't be sent => create security parameters here |
|
359 LOG(Log::Printf(_L("Abbreviated Handshake"));) |
|
360 iStateMachine->UpdateHistory( ETlsAbbreviatedHandshake ); // Abbreviated Handshake |
|
361 iTlsProvider->CreateL( Handshake().TlsSession(), aStatus); |
|
362 } |
|
363 else |
|
364 { |
|
365 LOG(Log::Printf(_L("Full Handshake"));) |
|
366 iStateMachine->UpdateHistory( ETlsFullHandshake ); // Full Handshake |
|
367 TRequestStatus* p=&aStatus; |
|
368 User::RequestComplete( p, KErrNone ); |
|
369 } |
|
370 |
|
371 const TTLSCipherSuiteMapping *pCipherDetails = cryptoAttributes.iCurrentCipherSuite.CipherDetails(); |
|
372 if(pCipherDetails) |
|
373 { |
|
374 if(pCipherDetails->iKeyExAlg == EPsk) |
|
375 { |
|
376 // Using PSK for key exchange which impacts list of legal messages and ordering checks |
|
377 iStateMachine->UpdateHistory( ETlsUsingPskKeyExchange ); |
|
378 } |
|
379 // Set key exchange type in the CTLSPublicKeyParams structure which will be passed to the TLS token. |
|
380 iTlsProvider->Attributes()->iPublicKeyParams->iKeyType = pCipherDetails->iKeyExAlg; |
|
381 } |
|
382 |
|
383 // Call InitiateReceiveL() to set up the list of expected messages |
|
384 return Handshake().InitiateReceiveL(); |
|
385 |
|
386 } |
|
387 |
|
388 // |
|
389 // |
|
390 TBool CCertificateReq::AcceptMessage( const TUint8 aHandshakeType ) const |
|
391 /** |
|
392 * This method determines whether a Certificate Request message (event) should |
|
393 * be accepted. A server certificate must have been received before a certificate |
|
394 * request can be made. |
|
395 * |
|
396 * @param aHandshakeType A handshake message type |
|
397 * @return TBool Boolean indicating whether a message should be accepted |
|
398 */ |
|
399 { |
|
400 LOG(Log::Printf(_L("CCertificateReq::AcceptMessage()"));) |
|
401 return !iHandshakeMessage && iStateMachine->History() & ETlsServerCertificateRecv && aHandshakeType == ETlsCertificateReqMsg; |
|
402 } |
|
403 |
|
404 CAsynchEvent* CCertificateReq::ProcessL( TRequestStatus& aStatus ) |
|
405 /** |
|
406 * This asynchronous method processes a received Certificate Request message. |
|
407 * |
|
408 * @param aStatus Request status for this event. |
|
409 * @return CAsynchEvent* A pointer to the next asynchronous event to be processed. |
|
410 */ |
|
411 { |
|
412 LOG(Log::Printf(_L("CCertificateReq::ProcessL()"));) |
|
413 |
|
414 __ASSERT_DEBUG( !iHandshakeMessage, TlsPanic( ETlsPanicHandshakeMsgAlreadyExists) ); |
|
415 iHandshakeMessage = new(ELeave) CCertificateReqMsg; |
|
416 LOG(Log::Printf(_L("iHandshakeMessage %x - %x"), iHandshakeMessage, (TUint)iHandshakeMessage + sizeof( CCertificateReqMsg ));) |
|
417 TPtr8 ptr( iRecordParser.HandshakeParser()->Message() ); |
|
418 LOG(Log::Printf(_L("ptr.Length() %d"), ptr.Length() );) |
|
419 |
|
420 iHandshakeMessage->iRecord.ParseL( ptr ); |
|
421 #ifdef _DEBUG |
|
422 LOG(iHandshakeMessage->iRecord.Dump( KSSLLogDir,KSSLLogFileName);) |
|
423 #endif |
|
424 |
|
425 // Update the Handshake history and set the Certificate Types |
|
426 iStateMachine->UpdateHistory( ETlsCertificateReqRecv ); |
|
427 CCertificateReqMsg* pCertReqMsg = (CCertificateReqMsg*)iHandshakeMessage; |
|
428 |
|
429 TPtr8 body = pCertReqMsg->iClientCertificateTypes.GetBodyDes(); |
|
430 CTlsCryptoAttributes& cryptoAttributes = *iTlsProvider->Attributes(); |
|
431 |
|
432 for (TInt loop = 0; loop < body.Length(); ++loop) |
|
433 { |
|
434 User::LeaveIfError( cryptoAttributes.iReqCertTypes.Append( (TTLSClientCertType) body[loop] ) ); |
|
435 } |
|
436 |
|
437 // Set the list of CA Distinguished Names |
|
438 CListNode* listNode = pCertReqMsg->iDistinguishedNames.First(); |
|
439 |
|
440 __ASSERT_DEBUG( cryptoAttributes.iDistinguishedCANames.Count() == 0, TlsPanic(ETlsPanicNoCA )); |
|
441 while ( listNode ) |
|
442 { |
|
443 TPtr8 listPtr = listNode->GetBodyDes(); |
|
444 LOG(Log::Printf(_L("DistinguishedCAName") );) |
|
445 LOG(RFileLogger::HexDump(KSSLLogDir,KSSLLogFileName,EFileLoggingModeAppend, NULL, NULL, listPtr.Ptr(), listPtr.Length() )); |
|
446 |
|
447 // Append the item into the Distinguished Names array and get the next list item |
|
448 HBufC8* buf = listPtr.AllocL(); |
|
449 CleanupStack::PushL(buf); |
|
450 User::LeaveIfError(cryptoAttributes.iDistinguishedCANames.Append(buf) ); |
|
451 CleanupStack::Pop(); |
|
452 listNode = listNode->Next(); |
|
453 } |
|
454 iTlsProvider->Attributes()->iClientAuthenticate = ETrue; |
|
455 |
|
456 TRequestStatus* p=&aStatus; |
|
457 User::RequestComplete( p, KErrNone ); |
|
458 return &iRecordParser; |
|
459 } |
|
460 |
|
461 // |
|
462 // |
|
463 TBool CServerCertificate::AcceptMessage( const TUint8 aHandshakeType ) const |
|
464 /** |
|
465 * This method determines whether a Server Certificate message (event) should |
|
466 * be accepted. |
|
467 * |
|
468 * @param aHandshakeType A handshake message type |
|
469 * @return TBool Boolean indicating whether a message should be accepted |
|
470 */ |
|
471 { |
|
472 LOG(Log::Printf(_L("CServerCertificate::AcceptMessage()"));) |
|
473 |
|
474 // Assert that a Server Hello message has been received |
|
475 __ASSERT_DEBUG( iStateMachine->History() & ETlsServerHelloRecv, TlsPanic(ETlsPanicServerHelloMsgNotReceived)); |
|
476 return !iHandshakeMessage && aHandshakeType == ETlsServerCertificateMsg; |
|
477 } |
|
478 |
|
479 CAsynchEvent* CServerCertificate::ProcessL( TRequestStatus& aStatus ) |
|
480 /** |
|
481 * This asynchronous method processes a Server Certificate message. The contents of the |
|
482 * message are passed as an uninterpreted buffer to the Security subsystem. |
|
483 * |
|
484 * @param aStatus Request status for this event. |
|
485 * @return CAsynchEvent* A pointer to the next asynchronous event to be processed. |
|
486 */ |
|
487 { |
|
488 CX509Certificate*& serverCert = Handshake().ServerCert(); |
|
489 if ( serverCert ) |
|
490 {//store signing alg |
|
491 //cannot convert from 'enum TAlgorithmId' to 'enum TTLSSignatureAlgorithm => |
|
492 //=> CTlsProvider MUST use the same enum as security -> the same applies to keyExchange alg |
|
493 const CSubjectPublicKeyInfo& publicKeyInfo = serverCert->PublicKey(); |
|
494 |
|
495 TAlgorithmId& signAlgorithm = Handshake().SignatureAlg(); |
|
496 signAlgorithm = publicKeyInfo.AlgorithmId(); |
|
497 if ( signAlgorithm == ERSA ) |
|
498 { |
|
499 iTlsProvider->Attributes()->isignatureAlgorithm = ERsaSigAlg; |
|
500 } |
|
501 else if ( signAlgorithm == EDSA ) |
|
502 { |
|
503 iTlsProvider->Attributes()->isignatureAlgorithm = EDsa; |
|
504 } |
|
505 else |
|
506 { |
|
507 LOG(Log::Printf(_L("Unknown signing algorithm and ridiculous enum redefinitions."));) |
|
508 User::Leave( KErrSSLAlertIllegalParameter ); |
|
509 } |
|
510 TRequestStatus* p=&aStatus; |
|
511 User::RequestComplete( p, KErrNone ); |
|
512 return &iRecordParser; |
|
513 } |
|
514 LOG(Log::Printf(_L("CServerCertificate::ProcessL()"));) |
|
515 |
|
516 // Update the Handshake history and pass the server certificate chain to TLS Provider |
|
517 iStateMachine->UpdateHistory( ETlsServerCertificateRecv ); |
|
518 |
|
519 TPtr8 ptr( iRecordParser.HandshakeParser()->Message() ); |
|
520 TPtr8 ptrEncoded( ptr ); |
|
521 |
|
522 #ifdef _DEBUG |
|
523 if (iHandshakeMessage) |
|
524 { |
|
525 LOG(Log::Printf(_L("ERROR: iHandshakeMessage %x - %x, should be NULL"), iHandshakeMessage, (TUint)iHandshakeMessage + sizeof( CCertificateMsg ));) |
|
526 } |
|
527 __ASSERT_DEBUG( !iHandshakeMessage, TlsPanic(ETlsPanicHandshakeMsgAlreadyExists)); |
|
528 #endif |
|
529 |
|
530 iHandshakeMessage = new(ELeave) CCertificateMsg; |
|
531 LOG(Log::Printf(_L("iHandshakeMessage %x - %x"), iHandshakeMessage, (TUint)iHandshakeMessage + sizeof( CCertificateMsg ));) |
|
532 iHandshakeMessage->iRecord.ParseL( ptr ); // Set each item's pointer to the correct part of the received message |
|
533 #ifdef _DEBUG |
|
534 LOG(iHandshakeMessage->iRecord.Dump( KSSLLogDir,KSSLLogFileName);) |
|
535 #endif |
|
536 //prepare certificate chain for security (this should've really been TLS provider's work |
|
537 CListNode* pNode = ((CCertificateMsg*)iHandshakeMessage)->iCertificateList.First(); |
|
538 User::LeaveIfError( pNode ? KErrNone : KErrSSLAlertBadCertificate ); |
|
539 //iPtr1 + iPtr2 setup to delete handshake header+cert list length |
|
540 TUint8* iPtr1 = iHandshakeMessage->Ptr(); |
|
541 TUint8* iPtr2 = pNode->Ptr(); |
|
542 TInt nDel = 0; |
|
543 TInt nToDel = iPtr2 - iPtr1; |
|
544 TInt nPos = iPtr1 - iHandshakeMessage->Ptr(); |
|
545 ptrEncoded.Delete( nPos, nToDel ); |
|
546 do |
|
547 { |
|
548 nDel += nToDel; |
|
549 iPtr1 = pNode->Ptr() - nDel; |
|
550 iPtr2 = pNode->GetBodyPtr() - nDel; |
|
551 nToDel = iPtr2 - iPtr1; |
|
552 nPos = iPtr1 - iHandshakeMessage->Ptr(); |
|
553 ptrEncoded.Delete( nPos, nToDel ); |
|
554 pNode = pNode->Next(); |
|
555 } |
|
556 while ( pNode ); |
|
557 LOG(RFileLogger::HexDump(KSSLLogDir,KSSLLogFileName,EFileLoggingModeAppend, NULL, NULL, ptrEncoded.Ptr(), ptrEncoded.Length() )); |
|
558 iTlsProvider->VerifyServerCertificate( ptrEncoded, serverCert, aStatus); |
|
559 |
|
560 |
|
561 return this; |
|
562 } |
|
563 |
|
564 // |
|
565 // |
|
566 TBool CServerKeyExch::AcceptMessage( const TUint8 aHandshakeType ) const |
|
567 /** |
|
568 * This method determines whether a Server Key exchange message should be accepted. |
|
569 * As server authentication is mandatory for the current implementation, either a Server |
|
570 * certificate must have been received, or we must be using PSK to key exchange and authenticate. |
|
571 * |
|
572 * @param aHandshakeType A handshake message type |
|
573 * @return TBool Boolean indicating whether a message should be accepted |
|
574 */ |
|
575 { |
|
576 LOG(Log::Printf(_L("CServerKeyExch::AcceptMessage()"));) |
|
577 |
|
578 return !iHandshakeMessage && |
|
579 ((iStateMachine->History() & ETlsServerCertificateRecv) || (iStateMachine->History() & ETlsUsingPskKeyExchange)) && |
|
580 aHandshakeType == ETlsServerKeyExchMsg; |
|
581 } |
|
582 |
|
583 void CServerKeyExch::CreateMessageL( TTLSKeyExchangeAlgorithm aKeyExchange, TAlgorithmId aSignAlgorithm ) |
|
584 { |
|
585 if ( aKeyExchange == ERsa ) // Provider's enum for RSA differs from Security, hence the 2 enums values |
|
586 { |
|
587 if ( aSignAlgorithm == ERSA ) |
|
588 { |
|
589 iHandshakeMessage = new(ELeave) CRsaRsaServerKeyExchMsg; |
|
590 LOG(Log::Printf(_L("iHandshakeMessage %x - %x"), iHandshakeMessage, (TUint)iHandshakeMessage + sizeof( CRsaRsaServerKeyExchMsg ));) |
|
591 } |
|
592 else if ( aSignAlgorithm == EDSA) |
|
593 { |
|
594 iHandshakeMessage = new(ELeave) CRsaDsaServerKeyExchMsg; |
|
595 LOG(Log::Printf(_L("iHandshakeMessage %x - %x"), iHandshakeMessage, (TUint)iHandshakeMessage + sizeof( CRsaDsaServerKeyExchMsg ));) |
|
596 } |
|
597 } |
|
598 else if ( aKeyExchange == EDHE ) |
|
599 { |
|
600 if ( aSignAlgorithm == ERSA ) |
|
601 { |
|
602 iHandshakeMessage = new(ELeave) CDhRsaServerKeyExchMsg; |
|
603 LOG(Log::Printf(_L("iHandshakeMessage %x - %x"), iHandshakeMessage, (TUint)iHandshakeMessage + sizeof( CDhRsaServerKeyExchMsg ));) |
|
604 } |
|
605 else if ( aSignAlgorithm == EDSA) |
|
606 { |
|
607 iHandshakeMessage = new(ELeave) CDhDsaServerKeyExchMsg; |
|
608 LOG(Log::Printf(_L("iHandshakeMessage %x - %x"), iHandshakeMessage, (TUint)iHandshakeMessage + sizeof( CDhDsaServerKeyExchMsg ));) |
|
609 } |
|
610 } |
|
611 else if ( aKeyExchange == EPsk ) |
|
612 { |
|
613 iHandshakeMessage = new(ELeave) CPskServerKeyExchMsg; |
|
614 LOG(Log::Printf(_L("iHandshakeMessage %x - %x"), iHandshakeMessage, (TUint)iHandshakeMessage + sizeof( CPskServerKeyExchMsg ));) |
|
615 } |
|
616 |
|
617 if ( !iHandshakeMessage ) |
|
618 { |
|
619 LOG(Log::Printf(_L("CServerKeyExch::ProcessL() - Unknown signing algorithm"));) |
|
620 User::Leave(KErrSSLAlertIllegalParameter); |
|
621 } |
|
622 } |
|
623 |
|
624 CAsynchEvent* CServerKeyExch::ProcessL( TRequestStatus& aStatus ) |
|
625 /** |
|
626 * This message processes a Server Key exchange message. |
|
627 * The protocol needs the Key exchange algorithm from the cipher suite and the signing |
|
628 * algorithm, if relevant, from the certificate, in order to interpret and process this |
|
629 * message correctly. |
|
630 * |
|
631 * @param aStatus Request status for this event. |
|
632 * @return CAsynchEvent* A pointer to the next asynchronous event to be processed. |
|
633 */ |
|
634 { |
|
635 LOG(Log::Printf(_L("CServerKeyExch::ProcessL()"));) |
|
636 |
|
637 // Get the Key exchange Algorithm and set this value in the Provider. |
|
638 const TTLSCipherSuite cipherSuite = iTlsProvider->Attributes()->iCurrentCipherSuite; |
|
639 |
|
640 TTLSKeyExchangeAlgorithm keyExchange = iTlsProvider->Attributes()->iPublicKeyParams->iKeyType; |
|
641 |
|
642 TAlgorithmId& signAlgorithm = Handshake().SignatureAlg(); |
|
643 |
|
644 // Create and parse the Server Key exchange message. Set all necessary information. |
|
645 CreateMessageL( keyExchange, signAlgorithm ); |
|
646 TPtr8 ptr( iRecordParser.HandshakeParser()->Message() ); |
|
647 iHandshakeMessage->iRecord.ParseL( ptr ); |
|
648 #ifdef _DEBUG |
|
649 LOG(iHandshakeMessage->iRecord.Dump( KSSLLogDir,KSSLLogFileName);) |
|
650 #endif |
|
651 CServerKeyExchMsg* pKeyExcMsg = (CServerKeyExchMsg*)iHandshakeMessage; |
|
652 |
|
653 pKeyExcMsg->CopyParamsL( iTlsProvider->Attributes() ); |
|
654 |
|
655 if(!(iStateMachine->History() & ETlsUsingPskKeyExchange)) |
|
656 { |
|
657 // Under pre-shared keys, the ServerKeyExch message does not have certificates (see RFC 4279). In |
|
658 // this case, we do not process the certs. |
|
659 TBuf8<KTlsMd5Length + KTlsShaLength> msgDigest; |
|
660 pKeyExcMsg->ComputeDigestL( iTlsProvider->Attributes()->iMasterSecretInput.iClientRandom, |
|
661 iTlsProvider->Attributes()->iMasterSecretInput.iServerRandom, |
|
662 msgDigest ); |
|
663 TPtr8 signature( pKeyExcMsg->Signature() ); |
|
664 CX509Certificate* serverCert = Handshake().ServerCert(); |
|
665 __ASSERT_DEBUG( serverCert, TlsPanic(ETlsPanicNullServerCertificate) ); |
|
666 const CSubjectPublicKeyInfo& publicKeyInfo = serverCert->PublicKey(); |
|
667 |
|
668 User::LeaveIfError( iTlsProvider->VerifySignatureL(publicKeyInfo, msgDigest, signature) ? |
|
669 KErrNone : KErrSSLAlertBadCertificate ); |
|
670 } |
|
671 |
|
672 // Update the Handshake history. Clear message buffer at the end and return the next |
|
673 // item to be processed. |
|
674 iStateMachine->UpdateHistory( ETlsServerKeyExchRecv ); |
|
675 |
|
676 TRequestStatus* p=&aStatus; |
|
677 User::RequestComplete( p, KErrNone ); |
|
678 return &iRecordParser; |
|
679 } |
|
680 |
|
681 TBool CServerHelloDone::AcceptMessage( const TUint8 aHandshakeType ) const |
|
682 /** |
|
683 * This method decides whether a 'ServerHelloDone' message can be accepted. |
|
684 * This message can only be received after a Server hello message. However, |
|
685 * as this protocol implementation requires that a Server be authenticated, |
|
686 * this message can only be accepted after a Server's certificate has been received, or we must |
|
687 * be using PSK to key exchange and authenticate. |
|
688 * |
|
689 * @param aHandshakeType A handshake message type |
|
690 * @return TBool Boolean indicating whether a message should be accepted |
|
691 */ |
|
692 { |
|
693 LOG(Log::Printf(_L("CServerHelloDone::AcceptMessage()"));) |
|
694 |
|
695 return !iHandshakeMessage && |
|
696 ((iStateMachine->History() & ETlsServerCertificateRecv) || (iStateMachine->History() & ETlsUsingPskKeyExchange)) && |
|
697 aHandshakeType == ETlsServerHelloDoneMsg; |
|
698 } |
|
699 |
|
700 CAsynchEvent* CServerHelloDone::ProcessL( TRequestStatus& aStatus ) |
|
701 /** |
|
702 * This method processes a Server Hello Done message. |
|
703 * Once this message has been received, the Server's params (from the negotiation) |
|
704 * can begin to be processed. |
|
705 * |
|
706 * @param aStatus Request status for this event. |
|
707 * @return CAsynchEvent* A pointer to the next asynchronous event to be processed. |
|
708 */ |
|
709 { |
|
710 LOG(Log::Printf(_L("CServerHelloDone::ProcessL()"));) |
|
711 |
|
712 iHandshakeMessage = new(ELeave)CServerHelloDoneMsg; |
|
713 LOG(Log::Printf(_L("iHandshakeMessage %x - %x"), iHandshakeMessage, (TUint)iHandshakeMessage + sizeof( CServerHelloDoneMsg ));) |
|
714 TPtr8 ptr( iRecordParser.HandshakeParser()->Message() ); |
|
715 iHandshakeMessage->iRecord.ParseL( ptr ); // Set each item's pointer to the correct part of the received message |
|
716 #ifdef _DEBUG |
|
717 LOG(iHandshakeMessage->iRecord.Dump( KSSLLogDir,KSSLLogFileName);) |
|
718 #endif |
|
719 |
|
720 // Update the Handshake history |
|
721 // A ServerHelloDone message always has an empty body |
|
722 User::LeaveIfError( ptr.Length() ? KErrSSLAlertUnexpectedMessage : KErrNone ); |
|
723 iStateMachine->UpdateHistory( ETlsServerHelloDoneRecv ); |
|
724 |
|
725 // Call InitiateTransmitL() to start protocol transmissions. |
|
726 // Set the next event to be processed as the Record composer object |
|
727 iTlsProvider->CreateL( Handshake().TlsSession(), aStatus); |
|
728 return Handshake().InitiateTransmitL(); |
|
729 } |
|
730 |
|
731 // |
|
732 // |
|
733 TBool CRecvFinished::AcceptMessage( const TUint8 aHandshakeType ) const |
|
734 /** |
|
735 * This method decides whether a received 'Finished' message can be accepted. |
|
736 * A 'Finished' message can only be accepted after a 'ChangeCipherSpec' message |
|
737 * has been received. |
|
738 * |
|
739 * @param aHandshakeType A handshake message type |
|
740 * @return TBool Boolean indicating whether a message should be accepted |
|
741 */ |
|
742 { |
|
743 LOG(Log::Printf(_L("CRecvFinished::AcceptMessage()"));) |
|
744 __ASSERT_DEBUG( iStateMachine->History() & ETlsChangeCipherRecv, TlsPanic(ETlsPanicChangeCipherMsgNotReceived) ); |
|
745 return !iHandshakeMessage && aHandshakeType == ETlsFinishedMsg; |
|
746 } |
|
747 |
|
748 CAsynchEvent* CRecvFinished::ProcessL( TRequestStatus& aStatus ) |
|
749 /** |
|
750 * This asynchronous method processes a received Server Finished message. |
|
751 * Note that the Finished message takes an integer which is the size of the message |
|
752 * body only (i.e. minus the Handshake header). |
|
753 */ |
|
754 { |
|
755 LOG(Log::Printf(_L("CRecvFinished::ProcessL()"));) |
|
756 |
|
757 TPtr8 ptr( iRecordParser.HandshakeParser()->Message() ); |
|
758 TInt nLen = ptr.Length(); |
|
759 iHandshakeMessage = new(ELeave)CFinishedMsg( nLen - KTlsHandshakeHeaderSize); |
|
760 LOG(Log::Printf(_L("iHandshakeMessage %x - %x"), iHandshakeMessage, (TUint)iHandshakeMessage + sizeof( CFinishedMsg ));) |
|
761 |
|
762 iHandshakeMessage->iRecord.ParseL( ptr ); // Set each item's pointer to the correct part of the received message |
|
763 #ifdef _DEBUG |
|
764 LOG(iHandshakeMessage->iRecord.Dump( KSSLLogDir,KSSLLogFileName);) |
|
765 #endif |
|
766 CFinishedMsg* pFinishedMsg = (CFinishedMsg*)iHandshakeMessage; |
|
767 TPtr8 body = pFinishedMsg->iFinishedData.GetBodyDes(); |
|
768 |
|
769 // Pass the information to the Provider |
|
770 iShaPtr = static_cast<CSHA1*>( Handshake().SHA1Verify()->CopyL() ); |
|
771 iMd5Ptr = static_cast<CMD5*>( Handshake().MD5Verify()->CopyL() ); |
|
772 iTlsProvider->TlsSessionPtr()->VerifyServerFinishedMsgL( iMd5Ptr, iShaPtr, body, aStatus); |
|
773 |
|
774 ptr.Set( iRecordParser.HandshakeParser()->Message() ); |
|
775 ptr.SetLength( nLen ); |
|
776 Handshake().UpdateVerify( ptr ); |
|
777 iStateMachine->UpdateHistory( ETlsFinishedRecv ); |
|
778 |
|
779 return Handshake().InitiateTransmitL(); |
|
780 } |
|
781 |
|
782 |
|
783 CRecvFinished::~CRecvFinished() |
|
784 /** |
|
785 * Destructor. |
|
786 */ |
|
787 { |
|
788 LOG(Log::Printf(_L("CRecvFinished::~CRecvFinished()"));) |
|
789 delete iShaPtr; |
|
790 delete iMd5Ptr; |
|
791 } |
|
792 |
|
793 CGenericExtensionList::CGenericExtensionList( CItemBase* aNext ) |
|
794 : CCompoundList(aNext, KTlsExtensionLength) |
|
795 { |
|
796 } |
|
797 |
|
798 void CGenericExtensionList::ParseL( TPtr8& aDes8 ) |
|
799 { |
|
800 if(aDes8.Length() == 0) |
|
801 { |
|
802 return; // No Extension list at all |
|
803 } |
|
804 CCompoundListHeader::ParseL( aDes8 ); |
|
805 TInt nLenExpected = CCompoundListHeader::GetBigEndian(); |
|
806 |
|
807 if(nLenExpected > aDes8.Length()) |
|
808 { |
|
809 User::Leave( KErrBadDescriptor ); |
|
810 } |
|
811 |
|
812 while ( nLenExpected > 0 ) |
|
813 { |
|
814 CGenericExtension *ext = CGenericExtension::NewLC( 0 ); |
|
815 TRecord record(ext); |
|
816 |
|
817 record.ParseL( aDes8 ); |
|
818 |
|
819 AddNodeL(ext); |
|
820 CleanupStack::Pop(ext); |
|
821 |
|
822 nLenExpected -= ext->ExtensionLength(); |
|
823 } |
|
824 if(nLenExpected < 0) |
|
825 { |
|
826 User::Leave( KErrBadDescriptor ); |
|
827 } |
|
828 } |
|
829 |
|
830 CGenericExtension* CGenericExtensionList::Node(TInt aIndex) const |
|
831 { |
|
832 return static_cast<CGenericExtension*>(CCompoundList::Node(aIndex)); |
|
833 } |
|
834 |
|
835 // End of file |