cryptlib  3.4.1
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Macros
ssl_wr.c
Go to the documentation of this file.
1 /****************************************************************************
2 * *
3 * cryptlib SSL v3/TLS Session Write Routines *
4 * Copyright Peter Gutmann 1998-2010 *
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 /****************************************************************************
23 * *
24 * Sub-packet Management Routines *
25 * *
26 ****************************************************************************/
27 
28 /* Open and complete an SSL packet:
29 
30  offset packetEndOfs
31  | |
32  v v
33  +---+---+---+----+--------------------------+
34  |ID |Ver|Len|(IV)| |
35  +---+---+---+----+--------------------------+
36 
37  An initial openXXX() starts a new packet at the start of a stream and
38  continueXXX() adds another packet after an existing one, or (for the
39  xxxHSXXX() variants) adds a handshake sub-packet within an existing
40  packet. The continueXXX() operations return the start offset of the new
41  packet within the stream, openXXX() always starts at the start of the SSL
42  send buffer so the start offset is an implied 0. completeXXX() then goes
43  back to the given offset and deposits the appropriate length value in the
44  header that was written earlier. So typical usage (with error checking
45  omitted for clarity) would be:
46 
47  // Change-cipher-spec packet
48  openPacketStreamSSL( CRYPT_USE_DEFAULT, SSL_MSG_CHANGE_CIPHER_SPEC );
49  write( stream, ... );
50  completePacketStreamSSL( stream, 0 );
51 
52  // Finished handshake sub-packet within a handshake packet
53  continuePacketStreamSSL( SSL_MSG_HANDSHAKE );
54  offset = continueHSPacketStream( SSL_HAND_FINISHED );
55  write( stream, ... );
56  completeHSPacketStream( stream, offset );
57  // (Packet stream is completed by wrapPacketSSL()) */
58 
59 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2 ) ) \
60 static int startPacketStream( INOUT STREAM *stream,
63  SSL_HAND_LAST ) const int packetType )
64  {
65  SSL_INFO *sslInfo = sessionInfoPtr->sessionSSL;
66  int status;
67 
68  assert( isWritePtr( stream, sizeof( STREAM ) ) );
69  assert( isReadPtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) );
70 
71  REQUIRES( packetType >= SSL_MSG_FIRST && packetType <= SSL_MSG_LAST );
72 
73  /* Write the packet header:
74 
75  byte ID = packetType
76  byte[2] version = { 0x03, 0x0n }
77  uint16 len = 0 (placeholder)
78  [ byte[] iv - TLS 1.1+ only ] */
79  sputc( stream, packetType );
80  sputc( stream, SSL_MAJOR_VERSION );
81  sputc( stream, sessionInfoPtr->version );
82  status = writeUint16( stream, 0 ); /* Placeholder */
83  if( cryptStatusError( status ) )
84  return( status );
85  if( ( sessionInfoPtr->flags & SESSION_ISSECURE_WRITE ) && \
86  sslInfo->ivSize > 0 )
87  {
89  BYTE iv[ CRYPT_MAX_IVSIZE + 8 ];
90 
91  setMessageData( &msgData, iv, sslInfo->ivSize );
93  &msgData, CRYPT_IATTRIBUTE_RANDOM_NONCE );
94  status = swrite( stream, iv, sslInfo->ivSize );
95  }
96  return( status );
97  }
98 
99 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2 ) ) \
100 int openPacketStreamSSL( OUT STREAM *stream,
101  const SESSION_INFO *sessionInfoPtr,
102  IN_LENGTH_OPT const int bufferSize,
104  SSL_HAND_LAST ) const int packetType )
105  {
106  const int streamSize = ( bufferSize == CRYPT_USE_DEFAULT ) ? \
107  sessionInfoPtr->sendBufSize - EXTRA_PACKET_SIZE : \
108  bufferSize + sessionInfoPtr->sendBufStartOfs;
109 
110  assert( isWritePtr( stream, sizeof( STREAM ) ) );
111  assert( isReadPtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) && \
112  isWritePtr( sessionInfoPtr->sendBuffer, streamSize ) );
113 
114  REQUIRES( bufferSize == CRYPT_USE_DEFAULT || \
115  ( packetType == SSL_MSG_APPLICATION_DATA && \
116  bufferSize == 0 ) || \
117  ( bufferSize > 0 && bufferSize < MAX_INTLENGTH ) );
118  /* When wrapping up data packets we only write the implicit-
119  length header so the buffer size is zero */
120  REQUIRES( packetType >= SSL_MSG_FIRST && packetType <= SSL_MSG_LAST );
121  REQUIRES( streamSize >= sessionInfoPtr->sendBufStartOfs && \
122  streamSize <= sessionInfoPtr->sendBufSize - EXTRA_PACKET_SIZE );
123 
124  /* Create the stream */
125  sMemOpen( stream, sessionInfoPtr->sendBuffer, streamSize );
126  return( startPacketStream( stream, sessionInfoPtr, packetType ) );
127  }
128 
129 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2, 4 ) ) \
130 int continuePacketStreamSSL( INOUT STREAM *stream,
131  const SESSION_INFO *sessionInfoPtr,
133  SSL_HAND_LAST ) const int packetType,
135  {
136  const int offset = stell( stream );
137  int status;
138 
139  assert( isWritePtr( stream, sizeof( STREAM ) ) );
140  assert( isReadPtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) );
141  assert( isWritePtr( packetOffset, sizeof( int ) ) );
142 
143  REQUIRES( stell( stream ) >= SSL_HEADER_SIZE );
144  REQUIRES( packetType >= SSL_MSG_FIRST && packetType <= SSL_MSG_LAST );
145 
146  /* Clear return value */
147  *packetOffset = 0;
148 
149  /* Continue the stream */
150  status = startPacketStream( stream, sessionInfoPtr, packetType );
151  if( cryptStatusError( status ) )
152  return( status );
153  *packetOffset = offset;
154 
155  return( CRYPT_OK );
156  }
157 
159 int completePacketStreamSSL( INOUT STREAM *stream,
160  IN_LENGTH_Z const int offset )
161  {
162  const int packetEndOffset = stell( stream );
163  int status;
164 
165  assert( isWritePtr( stream, sizeof( STREAM ) ) );
166 
167  REQUIRES( ( offset == 0 || offset >= SSL_HEADER_SIZE ) && \
168  offset <= packetEndOffset - ( ID_SIZE + VERSIONINFO_SIZE ) );
169 
170  /* Update the length field at the start of the packet */
171  sseek( stream, offset + ID_SIZE + VERSIONINFO_SIZE );
172  status = writeUint16( stream, ( packetEndOffset - offset ) - \
173  SSL_HEADER_SIZE );
174  sseek( stream, packetEndOffset );
175 
176  return( status );
177  }
178 
179 /* Start and complete a handshake packet within an SSL packet. Since this
180  continues an existing packet stream that's been opened using
181  openPacketStreamSSL(), it's denoted as continueXXX() rather than
182  openXXX() */
183 
184 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 3 ) ) \
185 int continueHSPacketStream( INOUT STREAM *stream,
187  SSL_HAND_LAST ) const int packetType,
188  OUT_LENGTH_SHORT_Z int *packetOffset )
189  {
190  const int offset = stell( stream );
191  int status;
192 
193  assert( isWritePtr( stream, sizeof( STREAM ) ) );
194  assert( isWritePtr( packetOffset, sizeof( int ) ) );
195 
196  REQUIRES( packetType >= SSL_HAND_FIRST && packetType <= SSL_HAND_LAST );
197 
198  /* Clear return value */
199  *packetOffset = 0;
200 
201  /* Write the handshake packet header:
202 
203  byte ID = packetType
204  uint24 len = 0 (placeholder) */
205  sputc( stream, packetType );
206  status = writeUint24( stream, 0 );
207  if( cryptStatusError( status ) )
208  return( status );
209  *packetOffset = offset;
210 
211  return( CRYPT_OK );
212  }
213 
215 int completeHSPacketStream( INOUT STREAM *stream,
216  IN_LENGTH const int offset )
217  {
218  const int packetEndOffset = stell( stream );
219  int status;
220 
221  assert( isWritePtr( stream, sizeof( STREAM ) ) );
222 
223  REQUIRES( offset >= SSL_HEADER_SIZE && \
224  offset <= packetEndOffset - ( ID_SIZE + LENGTH_SIZE ) );
225  /* HELLO_DONE has size zero so
226  offset == packetEndOffset - HDR_SIZE */
227 
228  /* Update the length field at the start of the packet */
229  sseek( stream, offset + ID_SIZE );
230  status = writeUint24( stream, packetEndOffset - \
231  ( offset + ID_SIZE + LENGTH_SIZE ) );
232  sseek( stream, packetEndOffset );
233  DEBUG_PRINT(( "Wrote %s (%d) handshake packet, length %ld.\n", \
234  getSSLHSPacketName( DEBUG_GET_STREAMBYTE( stream, offset ) ),
235  DEBUG_GET_STREAMBYTE( stream, offset ),
236  ( packetEndOffset - offset ) - ( ID_SIZE + LENGTH_SIZE ) ));
237  DEBUG_DUMP_STREAM( stream, offset + ( ID_SIZE + LENGTH_SIZE ),
238  ( packetEndOffset - offset ) - ( ID_SIZE + LENGTH_SIZE ) );
239 
240  return( status );
241  }
242 
243 /****************************************************************************
244 * *
245 * Write/Wrap a Packet *
246 * *
247 ****************************************************************************/
248 
249 /* Wrap an SSL data packet:
250 
251  sendBuffer hdrPtr dataPtr
252  | | |------------------- MAC'd
253  v v v================================ Encrypted
254  +-------+-----+-----+-------------------+-----+-----+
255  |///////| hdr | IV | data | MAC | pad |
256  +-------+-----+-----+-------------------+-----+-----+
257  ^<----+---->|<- payloadLength ->^ |
258  | | <-------- bMaxLen -|---------->
259  offset sBufStartOfs stell( stream )
260 
261  sendBuffer hdrPtr dataPtr
262  | | |
263  v v v============================== AuthEnc'd
264  +-------+-----+-----+-----------------------+-----+
265  |///////| hdr | IV | data | ICV |
266  +-------+-----+-----+-----------------------+-----+
267  ^<----+---->|<- payloadLength ----->^ |
268  | | <-------- bMaxLen -----|---->
269  offset sBufStartOfs stell( stream )
270 
271  This MACs/ICVs the data, adds the IV if necessary, pads and encrypts, and
272  updates the header */
273 
274 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2 ) ) \
275 int wrapPacketSSL( INOUT SESSION_INFO *sessionInfoPtr,
276  INOUT STREAM *stream,
277  IN_LENGTH_Z const int offset )
278  {
279  SSL_INFO *sslInfo = sessionInfoPtr->sessionSSL;
280  STREAM lengthStream;
281  const int payloadLength = stell( stream ) - \
282  ( offset + sessionInfoPtr->sendBufStartOfs );
283  int bufMaxLen = payloadLength + sMemDataLeft( stream );
284  BYTE lengthBuffer[ UINT16_SIZE + 8 ];
285  BYTE *dataPtr, *headerPtr;
286  int length, status;
287 
288  assert( isWritePtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) );
289  assert( isWritePtr( stream, sizeof( STREAM ) ) );
290 
291  REQUIRES( sessionInfoPtr->flags & SESSION_ISSECURE_WRITE );
292  REQUIRES( sStatusOK( stream ) );
293  REQUIRES( offset >= 0 && \
294  offset <= stell( stream ) - \
295  ( payloadLength + \
296  sessionInfoPtr->sendBufStartOfs ) );
297  REQUIRES( payloadLength >= 0 && payloadLength <= MAX_PACKET_SIZE && \
298  payloadLength < sessionInfoPtr->sendBufSize - \
299  ( sessionInfoPtr->sendBufStartOfs + \
300  sslInfo->ivSize ) );
301 
302  /* Get pointers into the data stream for the crypto processing */
303  status = sMemGetDataBlockAbs( stream, offset, ( void ** ) &headerPtr,
304  SSL_HEADER_SIZE + sslInfo->ivSize + \
305  bufMaxLen );
306  if( cryptStatusError( status ) )
307  return( status );
308  dataPtr = headerPtr + SSL_HEADER_SIZE + sslInfo->ivSize;
309  ENSURES( *headerPtr >= SSL_MSG_FIRST && *headerPtr <= SSL_MSG_LAST );
310 
311  /* MAC the payload if we're not using GCM, which combines the MAC with
312  the encryption */
313  if( !( sessionInfoPtr->protocolFlags & SSL_PFLAG_GCM ) )
314  {
315  if( sessionInfoPtr->version == SSL_MINOR_VERSION_SSL )
316  status = createMacSSL( sessionInfoPtr, dataPtr, bufMaxLen,
317  &length, payloadLength, *headerPtr );
318  else
319  status = createMacTLS( sessionInfoPtr, dataPtr, bufMaxLen,
320  &length, payloadLength, *headerPtr );
321  if( cryptStatusError( status ) )
322  return( status );
323  }
324  else
325  {
326  /* GCM is a stream cipher so the length is the same as the payload
327  length */
328  length = payloadLength;
329  }
330 
331  /* If it's TLS 1.1+ and we're using a block cipher, adjust for the
332  explicit IV that precedes the data. This is because the IV load is
333  handled implicitly by encrypting it as part of the data. We know
334  that the resulting values are within bounds because
335  dataPtr = headerPtr + hdr + IV */
336  if( sslInfo->ivSize > 0 && \
337  !( sessionInfoPtr->protocolFlags & SSL_PFLAG_GCM ) )
338  {
339  REQUIRES( sessionInfoPtr->sendBufStartOfs >= SSL_HEADER_SIZE + \
340  sslInfo->ivSize );
341  dataPtr -= sslInfo->ivSize;
342  length += sslInfo->ivSize;
343  bufMaxLen += sslInfo->ivSize;
344  ENSURES( length > 0 && length <= bufMaxLen )
345  }
346  DEBUG_PRINT(( "Wrote %s (%d) packet, length %ld.\n",
347  getSSLPacketName( *headerPtr ), *headerPtr, length ));
348  DEBUG_DUMP_DATA( dataPtr, length );
349 
350  /* If we're using GCM then the IV has to be assembled from implicit and
351  explicit components and set explicitly. The reason why it's twelve
352  bytes is because AES-GCM preferentially uses 96 bits of IV followed
353  by 32 bits of 000...1, with other lengths being possible but then the
354  data has to be cryptographically reduced to 96 bits before
355  processing, so TLS specifies a fixed length of 96 bits:
356 
357  |<--- 12 bytes ---->|
358  +-------+-----------+
359  | Salt | Nonce |
360  +-------+-----------+
361  |<- 4 ->|<--- 8 --->|
362 
363  In addition we have to process the packet metadata that's normally
364  MACed as GCM AAD */
365  if( sessionInfoPtr->protocolFlags & SSL_PFLAG_GCM )
366  {
367  BYTE iv[ CRYPT_MAX_IVSIZE + 8 ];
369 
370  /* Set the GCM IV from a combination of the secret salt value and
371  the explicitly-communicated IV */
372  memcpy( iv, sslInfo->gcmWriteSalt, sslInfo->gcmSaltSize );
373  memcpy( iv + sslInfo->gcmSaltSize, dataPtr - sslInfo->ivSize,
374  sslInfo->ivSize );
375  setMessageData( &msgData, iv, GCM_IV_SIZE );
376  status = krnlSendMessage( sessionInfoPtr->iCryptOutContext,
378  &msgData, CRYPT_CTXINFO_IV );
379  if( cryptStatusError( status ) )
380  return( status );
381 
382  /* Process the packet metadata as GCM AAD */
383  status = macDataTLSGCM( sessionInfoPtr->iCryptOutContext,
384  sslInfo->writeSeqNo, sessionInfoPtr->version,
385  length, *headerPtr );
386  if( cryptStatusError( status ) )
387  return( status );
388  sslInfo->writeSeqNo++;
389  }
390 
391  /* Pad and encrypt the payload */
392  status = encryptData( sessionInfoPtr, dataPtr, bufMaxLen, &length,
393  length );
394  if( cryptStatusError( status ) )
395  return( status );
396 
397  /* If we're using GCM then we have to adjust the length to account for
398  the IV data at the start (for non-GCM modes this is handled
399  implicitly by making the IV part of the data to encrypt) */
400  if( sessionInfoPtr->protocolFlags & SSL_PFLAG_GCM )
401  length += sslInfo->ivSize;
402 
403  /* Insert the final packet payload length into the packet header. We
404  directly copy the data in because the stream may have been opened in
405  read-only mode if we're using it to write pre-assembled packet data
406  that's been passed in by the caller */
407  sMemOpen( &lengthStream, lengthBuffer, UINT16_SIZE );
408  status = writeUint16( &lengthStream, length );
409  sMemDisconnect( &lengthStream );
410  if( cryptStatusError( status ) )
411  return( status );
412  memcpy( headerPtr + ID_SIZE + VERSIONINFO_SIZE, lengthBuffer,
413  UINT16_SIZE );
414 
415  /* Sync the stream information to match the new payload size */
416  return( sSkip( stream, length - ( sslInfo->ivSize + payloadLength ) ) );
417  }
418 
419 /* Wrap up and send an SSL packet */
420 
421 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2 ) ) \
422 int sendPacketSSL( INOUT SESSION_INFO *sessionInfoPtr,
423  INOUT STREAM *stream, const BOOLEAN sendOnly )
424  {
425  const int length = stell( stream );
426  void *dataPtr;
427  int status;
428 
429  assert( isReadPtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) );
430  assert( isWritePtr( stream, sizeof( STREAM ) ) );
431 
432  REQUIRES( sStatusOK( stream ) );
433  REQUIRES( stell( stream ) >= SSL_HEADER_SIZE );
434 
435  /* Update the length field at the start of the packet if necessary */
436  if( !sendOnly )
437  {
438  status = completePacketStreamSSL( stream, 0 );
439  if( cryptStatusError( status ) )
440  return( status );
441  }
442 
443  /* Send the packet to the peer */
444  status = sMemGetDataBlockAbs( stream, 0, &dataPtr, length );
445  if( cryptStatusError( status ) )
446  return( status );
447  ANALYSER_HINT( dataPtr != NULL );
448  status = swrite( &sessionInfoPtr->stream, dataPtr, length );
449  if( cryptStatusError( status ) )
450  {
451  sNetGetErrorInfo( &sessionInfoPtr->stream,
452  &sessionInfoPtr->errorInfo );
453  return( status );
454  }
455  return( CRYPT_OK ); /* swrite() returns a byte count */
456  }
457 
458 /****************************************************************************
459 * *
460 * Send SSL Alerts *
461 * *
462 ****************************************************************************/
463 
464 /* Send a close alert, with appropriate protection if necessary */
465 
466 STDC_NONNULL_ARG( ( 1 ) ) \
467 static void sendAlert( INOUT SESSION_INFO *sessionInfoPtr,
469  SSL_ALERTLEVEL_FATAL ) const int alertLevel,
471  SSL_ALERT_LAST ) const int alertType,
472  const BOOLEAN alertReceived )
473  {
474  STREAM stream;
475  int length = DUMMY_INIT, status;
476 
477  assert( isWritePtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) );
478 
479  REQUIRES_V( alertLevel == SSL_ALERTLEVEL_WARNING || \
480  alertLevel == SSL_ALERTLEVEL_FATAL );
481  REQUIRES_V( alertType >= SSL_ALERT_FIRST && \
482  alertType <= SSL_ALERT_LAST );
483 
484  /* Make sure that we only send a single alert. Normally we do this
485  automatically on shutdown, but we may have already sent it earlier
486  as part of an error-handler */
487  if( sessionInfoPtr->protocolFlags & SSL_PFLAG_ALERTSENT )
488  return;
489  sessionInfoPtr->protocolFlags |= SSL_PFLAG_ALERTSENT;
490 
491  /* Create the alert. We can't really do much with errors at this point,
492  although we can throw an exception in the debug version to draw
493  attention to the fact that there's a problem. The one error type
494  that we don't complain about is an access permission problem, which
495  can occur when cryptlib is shutting down, for example when the
496  current thread is blocked waiting for network traffic and another
497  thread shuts things down */
498  status = openPacketStreamSSL( &stream, sessionInfoPtr,
500  if( cryptStatusOK( status ) )
501  {
502  sputc( &stream, alertLevel );
503  status = sputc( &stream, alertType );
504  }
505  if( cryptStatusOK( status ) )
506  {
507  if( sessionInfoPtr->flags & SESSION_ISSECURE_WRITE )
508  {
509  status = wrapPacketSSL( sessionInfoPtr, &stream, 0 );
510  assert( cryptStatusOK( status ) || \
511  status == CRYPT_ERROR_PERMISSION );
512  }
513  else
514  status = completePacketStreamSSL( &stream, 0 );
515  if( cryptStatusOK( status ) )
516  length = stell( &stream );
517  sMemDisconnect( &stream );
518  }
519  /* Fall through with status passed on to the following code */
520 
521  /* Send the alert. Note that we didn't exit on an error status in the
522  previous operation (for the reasons given in the comment earlier)
523  since we can at least perform a clean shutdown even if the creation
524  of the close alert fails */
525  if( cryptStatusOK( status ) )
526  status = sendCloseNotification( sessionInfoPtr,
527  sessionInfoPtr->sendBuffer, length );
528  else
529  status = sendCloseNotification( sessionInfoPtr, NULL, 0 );
530  if( cryptStatusError( status ) || alertReceived )
531  return;
532 
533  /* Read back the other side's close alert acknowledgement. Again, since
534  we're closing down the session anyway there's not much that we can do
535  in response to an error */
536  ( void ) readHSPacketSSL( sessionInfoPtr, NULL, &length,
537  SSL_MSG_ALERT );
538  }
539 
540 STDC_NONNULL_ARG( ( 1 ) ) \
541 void sendCloseAlert( INOUT SESSION_INFO *sessionInfoPtr,
542  const BOOLEAN alertReceived )
543  {
544  assert( isWritePtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) );
545 
546  sendAlert( sessionInfoPtr, SSL_ALERTLEVEL_WARNING,
547  SSL_ALERT_CLOSE_NOTIFY, alertReceived );
548  }
549 
550 STDC_NONNULL_ARG( ( 1 ) ) \
551 void sendHandshakeFailAlert( INOUT SESSION_INFO *sessionInfoPtr )
552  {
553  assert( isWritePtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) );
554 
555  /* We set the alertReceived flag to true when sending a handshake
556  failure alert to avoid waiting to get back an ack, since this
557  alert type isn't acknowledged by the other side */
558  sendAlert( sessionInfoPtr, SSL_ALERTLEVEL_FATAL,
560  }
561 #endif /* USE_SSL */