cryptlib  3.4.1
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Macros
ssl_hsc.c
Go to the documentation of this file.
1 /****************************************************************************
2 * *
3 * cryptlib SSL v3/TLS Handshake Completion Management *
4 * Copyright Peter Gutmann 1998-2009 *
5 * *
6 ****************************************************************************/
7 
8 #if defined( INC_ALL )
9  #include "crypt.h"
10  #include "misc_rw.h"
11  #include "session.h"
12  #include "ssl.h"
13 #else
14  #include "crypt.h"
15  #include "enc_dec/misc_rw.h"
16  #include "session/session.h"
17  #include "session/ssl.h"
18 #endif /* Compiler-specific includes */
19 
20 #ifdef USE_SSL
21 
22 /* Pre-encoded finished message header that we can use for message hashing:
23 
24  byte ID = SSL_HAND_FINISHED
25  uint24 len = 16 + 20 (SSL), 12 (TLS) */
26 
27 #define FINISHED_TEMPLATE_SIZE 4
28 
29 static const BYTE finishedTemplateSSL[] = \
31 static const BYTE finishedTemplateTLS[] = \
33 
34 /****************************************************************************
35 * *
36 * Utility Functions *
37 * *
38 ****************************************************************************/
39 
40 /* Destroy cloned hash contexts, used to clean up dual-hash (SSL, TLS 1.0-1.1)
41  or single-hash (TLS 1.2+) contexts */
42 
43 static void destroyHashContexts( IN_HANDLE const CRYPT_CONTEXT hashContext1,
44  IN_HANDLE const CRYPT_CONTEXT hashContext2,
45  IN_HANDLE const CRYPT_CONTEXT hashContext3 )
46  {
47  REQUIRES_V( ( isHandleRangeValid( hashContext1 ) && \
48  isHandleRangeValid( hashContext2 ) && \
49  hashContext3 == CRYPT_ERROR ) || \
50  ( hashContext1 == CRYPT_ERROR && \
51  hashContext2 == CRYPT_ERROR && \
52  isHandleRangeValid( hashContext3 ) ) );
53 
54  if( hashContext1 != CRYPT_ERROR )
55  krnlSendNotifier( hashContext1, IMESSAGE_DECREFCOUNT );
56  if( hashContext2 != CRYPT_ERROR )
57  krnlSendNotifier( hashContext2, IMESSAGE_DECREFCOUNT );
58  if( hashContext3 != CRYPT_ERROR )
59  krnlSendNotifier( hashContext3, IMESSAGE_DECREFCOUNT );
60  }
61 
62 /****************************************************************************
63 * *
64 * Read/Write Handshake Completion Messages *
65 * *
66 ****************************************************************************/
67 
68 /* Read/write the handshake completion data (change cipherspec + finished) */
69 
70 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2 ) ) \
71 static int readHandshakeCompletionData( INOUT SESSION_INFO *sessionInfoPtr,
72  IN_BUFFER( hashValuesLength ) \
73  const BYTE *hashValues,
74  IN_LENGTH_SHORT const int hashValuesLength )
75  {
76  STREAM stream;
77  BYTE macBuffer[ MD5MAC_SIZE + SHA1MAC_SIZE + 8 ];
78  const int macValueLength = \
79  ( sessionInfoPtr->version <= SSL_MINOR_VERSION_SSL ) ? \
81  int length, value, status;
82 
83  assert( isWritePtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) );
84  assert( isReadPtr( hashValues, hashValuesLength ) );
85 
86  REQUIRES( hashValuesLength == macValueLength );
87 
88  /* Process the other side's change cipher spec:
89 
90  byte type = SSL_MSG_CHANGE_CIPHER_SPEC
91  byte[2] version = { 0x03, 0x0n }
92  uint16 len = 1
93  byte 1 */
94  status = readHSPacketSSL( sessionInfoPtr, NULL, &length,
96  if( cryptStatusError( status ) )
97  {
98  /* If we don't get the change cipherspec at this point this may be
99  because the server asked us for client authentication but we
100  skipped it because we don't have a certificate, in which case
101  we return extended error information indicating this */
102  if( !isServer( sessionInfoPtr ) && \
103  ( sessionInfoPtr->protocolFlags & SSL_PFLAG_CLIAUTHSKIPPED ) )
104  {
105  retExtErrAlt( status,
106  ( status, SESSION_ERRINFO,
107  ", probably due to missing client "
108  "authentication" ) );
109  }
110  return( status );
111  }
112  sMemConnect( &stream, sessionInfoPtr->receiveBuffer, length );
113  value = sgetc( &stream );
114  sMemDisconnect( &stream );
115  if( value != 1 )
116  {
119  "Invalid change cipher spec packet payload, expected "
120  "0x01, got 0x%02X", value ) );
121  }
122 
123  /* Change cipher spec was the last message not subject to security
124  encapsulation so we turn on security for the read channel after
125  seeing it. In addition if we're using TLS 1.1+ explicit IVs the
126  effective header size changes because of the extra IV data, so we
127  record the size of the additional IV data and update the receive
128  buffer start offset to accomodate it */
129  sessionInfoPtr->flags |= SESSION_ISSECURE_READ;
130  if( sessionInfoPtr->version >= SSL_MINOR_VERSION_TLS11 && \
131  sessionInfoPtr->cryptBlocksize > 1 )
132  {
133  sessionInfoPtr->sessionSSL->ivSize = sessionInfoPtr->cryptBlocksize;
134  sessionInfoPtr->receiveBufStartOfs += sessionInfoPtr->sessionSSL->ivSize;
135  }
136  if( sessionInfoPtr->protocolFlags & SSL_PFLAG_GCM )
137  {
138  /* If we're using GCM then the IV is partially explicit and
139  partially implicit, and unrelated to the cipher block size */
140  sessionInfoPtr->sessionSSL->ivSize = \
141  GCM_IV_SIZE - sessionInfoPtr->sessionSSL->gcmSaltSize;
142  sessionInfoPtr->receiveBufStartOfs += sessionInfoPtr->sessionSSL->ivSize;
143  }
144 
145  /* Process the other side's finished message. Since this is the first
146  chance that we have to test whether our crypto keys are set up
147  correctly, we report problems with decryption or MACing or a failure
148  to find any recognisable header as a wrong key rather than a bad data
149  error. In addition we signal the fact that the other side may
150  respond unexpectedly because of the use of encryption to
151  readHSPacketSSL() by specifying a special-case packet type, see the
152  comment in readHSPacketSSL() for how this is handled and why it's
153  necessary:
154 
155  byte ID = SSL_HAND_FINISHED
156  uint24 len
157  SSLv3 TLS
158  byte[16] MD5 MAC byte[12] hashedMAC
159  byte[20] SHA-1 MAC */
160  status = readHSPacketSSL( sessionInfoPtr, NULL, &length,
162  if( cryptStatusError( status ) )
163  return( status );
164  status = unwrapPacketSSL( sessionInfoPtr, sessionInfoPtr->receiveBuffer,
165  length, &length, SSL_MSG_HANDSHAKE );
166  if( cryptStatusError( status ) )
167  {
168  if( status == CRYPT_ERROR_BADDATA || \
169  status == CRYPT_ERROR_SIGNATURE )
170  {
174  "Decrypted data was corrupt, probably due to "
175  "incorrect encryption keys being negotiated "
176  "during the handshake: " ) );
177  }
178  return( status );
179  }
180  sMemConnect( &stream, sessionInfoPtr->receiveBuffer, length );
181  status = checkHSPacketHeader( sessionInfoPtr, &stream, &length,
182  SSL_HAND_FINISHED, macValueLength );
183  if( cryptStatusOK( status ) )
184  {
185  if( length != macValueLength )
186  {
187  /* A length mis-match can only be an overflow, since an
188  underflow would be caught by checkHSPacketHeader() */
189  status = CRYPT_ERROR_OVERFLOW;
190  }
191  else
192  status = sread( &stream, macBuffer, macValueLength );
193  }
194  sMemDisconnect( &stream );
195  if( cryptStatusError( status ) )
196  {
197  if( status == CRYPT_ERROR_BADDATA )
198  {
201  "Invalid handshake finished packet, probably due to "
202  "incorrect encryption keys being negotiated during "
203  "the handshake" ) );
204  }
205  return( status );
206  }
207 
208  /* Make sure that the dual MAC/hashed MAC of all preceding messages is
209  valid */
210  if( !compareDataConstTime( hashValues, macBuffer, macValueLength ) )
211  {
214  "Bad MAC for handshake messages, handshake messages were "
215  "corrupted/modified" ) );
216  }
217 
218  return( CRYPT_OK );
219  }
220 
221 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2, 3 ) ) \
222 static int writeHandshakeCompletionData( INOUT SESSION_INFO *sessionInfoPtr,
224  IN_BUFFER( hashValuesLength ) \
225  const BYTE *hashValues,
226  IN_LENGTH_SHORT const int hashValuesLength,
227  const BOOLEAN continuedStream )
228  {
229  STREAM *stream = &handshakeInfo->stream;
230  int offset = 0, ccsEndPos, status;
231 
232  assert( isWritePtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) );
233  assert( isWritePtr( handshakeInfo, sizeof( SSL_HANDSHAKE_INFO ) ) );
234  assert( isReadPtr( hashValues, hashValuesLength ) );
235 
236  REQUIRES( hashValuesLength > 0 && \
237  hashValuesLength < MAX_INTLENGTH_SHORT );
238 
239  /* Build the change cipher spec packet:
240 
241  byte type = SSL_MSG_CHANGE_CIPHER_SPEC
242  byte[2] version = { 0x03, 0x0n }
243  uint16 len = 1
244  byte 1
245 
246  Since change cipher spec is its own protocol, we use SSL-level packet
247  encoding rather than handshake protocol-level encoding */
248  if( continuedStream )
249  {
250  status = continuePacketStreamSSL( stream, sessionInfoPtr,
252  &offset );
253  }
254  else
255  {
256  status = openPacketStreamSSL( stream, sessionInfoPtr,
259  }
260  if( cryptStatusError( status ) )
261  return( status );
262  status = sputc( stream, 1 );
263  if( cryptStatusOK( status ) )
264  status = completePacketStreamSSL( stream, offset );
265  if( cryptStatusError( status ) )
266  {
267  sMemDisconnect( stream );
268  return( status );
269  }
270 
271  /* Change cipher spec was the last message not subject to security
272  encapsulation so we turn on security for the write channel after
273  seeing it. In addition if we're using TLS 1.1+ explicit IVs the
274  effective header size changes because of the extra IV data, so we
275  record the size of the additional IV data and update the receive
276  buffer start offset to accomodate it */
277  sessionInfoPtr->flags |= SESSION_ISSECURE_WRITE;
278  if( sessionInfoPtr->version >= SSL_MINOR_VERSION_TLS11 && \
279  sessionInfoPtr->cryptBlocksize > 1 )
280  {
281  sessionInfoPtr->sessionSSL->ivSize = sessionInfoPtr->cryptBlocksize;
282  sessionInfoPtr->sendBufStartOfs += sessionInfoPtr->sessionSSL->ivSize;
283  }
284  if( sessionInfoPtr->protocolFlags & SSL_PFLAG_GCM )
285  {
286  /* If we're using GCM then the IV is partially explicit and
287  partially implicit, and unrelated to the cipher block size */
288  sessionInfoPtr->sessionSSL->ivSize = \
289  GCM_IV_SIZE - sessionInfoPtr->sessionSSL->gcmSaltSize;
290  sessionInfoPtr->sendBufStartOfs += sessionInfoPtr->sessionSSL->ivSize;
291  }
292 
293  /* Build the finished packet. The initiator sends the MAC of the
294  contents of every handshake packet before the finished packet, the
295  responder sends the MAC of the contents of every packet before its own
296  finished packet but including the MAC of the initiator's packet
297  contents:
298 
299  byte ID = SSL_HAND_FINISHED
300  uint24 len
301  SSLv3 TLS
302  byte[16] MD5 MAC byte[12] hashedMAC
303  byte[20] SHA-1 MAC */
304  status = continuePacketStreamSSL( stream, sessionInfoPtr,
305  SSL_MSG_HANDSHAKE, &ccsEndPos );
306  if( cryptStatusOK( status ) )
307  status = continueHSPacketStream( stream, SSL_HAND_FINISHED,
308  &offset );
309  if( cryptStatusOK( status ) )
310  status = swrite( stream, hashValues, hashValuesLength );
311  if( cryptStatusOK( status ) )
312  status = completeHSPacketStream( stream, offset );
313  if( cryptStatusOK( status ) )
314  status = wrapPacketSSL( sessionInfoPtr, stream, ccsEndPos );
315  if( cryptStatusOK( status ) )
316  status = sendPacketSSL( sessionInfoPtr, stream,
317  TRUE );
318  sMemDisconnect( stream );
319 
320  return( status );
321  }
322 
323 /****************************************************************************
324 * *
325 * Complete the SSL/TLS Handshake *
326 * *
327 ****************************************************************************/
328 
329 /* Complete the handshake with the client or server. The logic gets a bit
330  complex here because the roles of the client and server are reversed if
331  we're resuming a session:
332 
333  Normal Resumed
334  Client Server Client Server
335  ------ ------ ------ ------
336  <--- ... Hello --->
337  KeyEx ---> <--- Hello
338  ------------------------------------------ completeHandshakeSSL()
339  CCS ---> <--- CCS
340  Fin ---> <--- Fin
341  <--- CCS CCS --->
342  <--- Fin Fin --->
343 
344  Because of this the handshake-completion step treats the two sides as
345  initiator and responder rather than client and server. The overall flow
346  is then:
347 
348  dualMAC/MAC( initiator );
349  if( !initiator )
350  read initiator CCS + Fin;
351  dualMAC/MAC( responder );
352  send initiator/responder CCS + Fin;
353  if( initiator )
354  read responder CCS + Fin; */
355 
356 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2 ) ) \
357 int completeHandshakeSSL( INOUT SESSION_INFO *sessionInfoPtr,
358  INOUT SSL_HANDSHAKE_INFO *handshakeInfo,
359  const BOOLEAN isClient,
360  const BOOLEAN isResumedSession )
361  {
362  const CRYPT_CONTEXT initiatorMD5context = handshakeInfo->md5context;
363  const CRYPT_CONTEXT initiatorSHA1context = handshakeInfo->sha1context;
364  const CRYPT_CONTEXT initiatorSHA2context = handshakeInfo->sha2context;
365  CRYPT_CONTEXT responderMD5context = CRYPT_ERROR;
366  CRYPT_CONTEXT responderSHA1context = CRYPT_ERROR;
367  CRYPT_CONTEXT responderSHA2context = CRYPT_ERROR;
368  BYTE masterSecret[ SSL_SECRET_SIZE + 8 ];
369  BYTE initiatorHashes[ ( CRYPT_MAX_HASHSIZE * 2 ) + 8 ];
370  BYTE responderHashes[ ( CRYPT_MAX_HASHSIZE * 2 ) + 8 ];
371  const void *sslInitiatorString, *sslResponderString;
372  const void *tlsInitiatorString, *tlsResponderString;
373  const BOOLEAN isInitiator = isResumedSession ? !isClient : isClient;
374  int initiatorHashLength, responderHashLength;
375  int sslLabelLength, tlsLabelLength, status;
376 
377  assert( isWritePtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) );
378  assert( isWritePtr( handshakeInfo, sizeof( SSL_HANDSHAKE_INFO ) ) );
379 
380  REQUIRES( MAX_KEYBLOCK_SIZE >= ( sessionInfoPtr->authBlocksize + \
381  handshakeInfo->cryptKeysize +
382  sessionInfoPtr->cryptBlocksize ) * 2 );
383  REQUIRES( handshakeInfo->authAlgo == CRYPT_ALGO_NONE || \
384  ( isEccAlgo( handshakeInfo->keyexAlgo ) && \
385  handshakeInfo->premasterSecretSize >= MIN_PKCSIZE_ECC ) || \
386  ( !isEccAlgo( handshakeInfo->keyexAlgo ) && \
387  handshakeInfo->premasterSecretSize >= SSL_SECRET_SIZE ) );
388 
389  /* Perform the necessary juggling of values for the reversed message
390  flow of resumed sessions */
391  if( isResumedSession )
392  {
393  /* Resumed session, initiator = server, responder = client */
394  sslInitiatorString = SSL_SENDER_SERVERLABEL;
395  sslResponderString = SSL_SENDER_CLIENTLABEL;
396  tlsInitiatorString = "server finished";
397  tlsResponderString = "client finished";
398  }
399  else
400  {
401  /* Normal session, initiator = client, responder = server */
402  sslInitiatorString = SSL_SENDER_CLIENTLABEL;
403  sslResponderString = SSL_SENDER_SERVERLABEL;
404  tlsInitiatorString = "client finished";
405  tlsResponderString = "server finished";
406  }
407  sslLabelLength = SSL_SENDERLABEL_SIZE;
408  tlsLabelLength = 15;
409 
410  /* Initialise and load cryptovariables into all encryption contexts */
411  status = initCryptoSSL( sessionInfoPtr, handshakeInfo, masterSecret,
412  SSL_SECRET_SIZE, isClient, isResumedSession );
413  if( cryptStatusError( status ) )
414  return( status );
415 
416  /* At this point the hashing of the initiator and responder diverge.
417  The initiator sends its change cipherspec and finished messages
418  first, so the hashing stops there, while the responder has to keep
419  hasing the initiator's messages until it's its turn to send its
420  change cipherspec and finished messages. To handle this we clone
421  the initiator's hash context(s) so that we can contine the hashing
422  after the initiator has wrapped things up */
423  if( sessionInfoPtr->version < SSL_MINOR_VERSION_TLS12 )
424  {
425  status = cloneHashContext( initiatorMD5context,
426  &responderMD5context );
427  if( cryptStatusOK( status ) )
428  {
429  status = cloneHashContext( initiatorSHA1context,
430  &responderSHA1context );
431  if( cryptStatusError( status ) )
432  krnlSendNotifier( responderMD5context, IMESSAGE_DECREFCOUNT );
433  }
434  }
435  else
436  {
437  status = cloneHashContext( initiatorSHA2context,
438  &responderSHA2context );
439  }
440  if( cryptStatusError( status ) )
441  {
442  zeroise( masterSecret, SSL_SECRET_SIZE );
443  return( status );
444  }
445 
446  /* Complete the dual-MAC/MAC of the initiator-side messages and, if
447  we're the responder, check that the MACs match the ones supplied by
448  the initiator */
449  if( sessionInfoPtr->version <= SSL_MINOR_VERSION_SSL )
450  {
451  status = completeSSLDualMAC( initiatorMD5context, initiatorSHA1context,
452  initiatorHashes, CRYPT_MAX_HASHSIZE * 2,
453  &initiatorHashLength, sslInitiatorString,
454  sslLabelLength, masterSecret, SSL_SECRET_SIZE );
455  }
456  else
457  {
458  if( sessionInfoPtr->version < SSL_MINOR_VERSION_TLS12 )
459  {
460  status = completeTLSHashedMAC( initiatorMD5context,
461  initiatorSHA1context, initiatorHashes,
462  CRYPT_MAX_HASHSIZE * 2, &initiatorHashLength,
463  tlsInitiatorString, tlsLabelLength, masterSecret,
464  SSL_SECRET_SIZE );
465  }
466  else
467  {
468  status = completeTLS12HashedMAC( initiatorSHA2context,
469  initiatorHashes, CRYPT_MAX_HASHSIZE,
470  &initiatorHashLength, tlsInitiatorString,
471  tlsLabelLength, masterSecret, SSL_SECRET_SIZE );
472  }
473  }
474  if( cryptStatusOK( status ) && !isInitiator )
475  {
476  status = readHandshakeCompletionData( sessionInfoPtr,
477  initiatorHashes,
478  initiatorHashLength );
479  }
480  if( cryptStatusError( status ) )
481  {
482  zeroise( masterSecret, SSL_SECRET_SIZE );
483  destroyHashContexts( responderMD5context, responderSHA1context,
484  responderSHA2context );
485  return( status );
486  }
487 
488  /* Now that we have the initiator MACs, complete the dual-hashing/
489  hashing and dual-MAC/MAC of the responder-side messages and destroy
490  the master secret. We haven't created the full message yet at this
491  point so we manually hash the individual pieces so that we can
492  finally get rid of the master secret */
493  if( sessionInfoPtr->version >= SSL_MINOR_VERSION_TLS12 )
494  {
495  status = krnlSendMessage( responderSHA2context, IMESSAGE_CTX_HASH,
496  ( MESSAGE_CAST ) finishedTemplateTLS, FINISHED_TEMPLATE_SIZE );
497  if( cryptStatusOK( status ) )
498  status = krnlSendMessage( responderSHA2context, IMESSAGE_CTX_HASH,
499  initiatorHashes, initiatorHashLength );
500  }
501  else
502  {
503  const BYTE *finishedTemplate = \
504  ( sessionInfoPtr->version <= SSL_MINOR_VERSION_SSL ) ? \
505  finishedTemplateSSL : finishedTemplateTLS;
506 
507  status = krnlSendMessage( responderMD5context, IMESSAGE_CTX_HASH,
508  ( MESSAGE_CAST ) finishedTemplate, FINISHED_TEMPLATE_SIZE );
509  if( cryptStatusOK( status ) )
510  {
511  status = krnlSendMessage( responderSHA1context, IMESSAGE_CTX_HASH,
512  ( MESSAGE_CAST ) finishedTemplate, FINISHED_TEMPLATE_SIZE );
513  }
514  if( cryptStatusOK( status ) )
515  status = krnlSendMessage( responderMD5context, IMESSAGE_CTX_HASH,
516  initiatorHashes, initiatorHashLength );
517  if( cryptStatusOK( status ) )
518  status = krnlSendMessage( responderSHA1context, IMESSAGE_CTX_HASH,
519  initiatorHashes, initiatorHashLength );
520  }
521  if( cryptStatusError( status ) )
522  {
523  zeroise( masterSecret, SSL_SECRET_SIZE );
524  destroyHashContexts( responderMD5context, responderSHA1context,
525  responderSHA2context );
526  return( status );
527  }
528  if( sessionInfoPtr->version <= SSL_MINOR_VERSION_SSL )
529  {
530  status = completeSSLDualMAC( responderMD5context, responderSHA1context,
531  responderHashes, CRYPT_MAX_HASHSIZE * 2,
532  &responderHashLength, sslResponderString,
533  sslLabelLength, masterSecret, SSL_SECRET_SIZE );
534  }
535  else
536  {
537  if( sessionInfoPtr->version < SSL_MINOR_VERSION_TLS12 )
538  {
539  status = completeTLSHashedMAC( responderMD5context,
540  responderSHA1context, responderHashes,
541  CRYPT_MAX_HASHSIZE * 2, &responderHashLength,
542  tlsResponderString, tlsLabelLength, masterSecret,
543  SSL_SECRET_SIZE );
544  }
545  else
546  {
547  status = completeTLS12HashedMAC( responderSHA2context,
548  responderHashes, CRYPT_MAX_HASHSIZE * 2,
549  &responderHashLength, tlsResponderString,
550  tlsLabelLength, masterSecret, SSL_SECRET_SIZE );
551  }
552  }
553  zeroise( masterSecret, SSL_SECRET_SIZE );
554  destroyHashContexts( responderMD5context, responderSHA1context,
555  responderSHA2context );
556  if( cryptStatusError( status ) )
557  return( status );
558 
559  /* Send our MACs to the other side and read back their response if
560  necessary. The initiatorHashLength is the same as the
561  responderHashLength (it's just a naming difference based on the
562  role that we're playing) so we use initiatorHashLength for both */
563  status = writeHandshakeCompletionData( sessionInfoPtr, handshakeInfo,
564  isInitiator ? initiatorHashes : \
565  responderHashes,
566  initiatorHashLength,
567  /* Same as responderHashLength */
568  ( isClient && !isResumedSession ) || \
569  ( !isClient && isResumedSession ) );
570  if( cryptStatusError( status ) || !isInitiator )
571  return( status );
572  return( readHandshakeCompletionData( sessionInfoPtr, responderHashes,
573  initiatorHashLength ) );
574  }
575 #endif /* USE_SSL */