2 * Copyright (c) 2000-2001 Apple Computer, Inc. All Rights Reserved.
4 * The contents of this file constitute Original Code as defined in and are
5 * subject to the Apple Public Source License Version 1.2 (the 'License').
6 * You may not use this file except in compliance with the License. Please obtain
7 * a copy of the License at http://www.apple.com/publicsource and read it before
10 * This Original Code and all software distributed under the License are
11 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS
12 * OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, INCLUDING WITHOUT
13 * LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
14 * PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. Please see the License for the
15 * specific language governing rights and limitations under the License.
22 Contains: Encryption, decryption and MACing of data
24 Written by: Doug Mitchell, based on Netscape RSARef 3.0
26 Copyright: (c) 1999 by Apple Computer, Inc., all rights reserved.
29 /* *********************************************************************
32 SSLRef 3.0 Final -- 11/19/96
34 Copyright (c)1996 by Netscape Communications Corp.
36 By retrieving this software you are bound by the licensing terms
37 disclosed in the file "LICENSE.txt". Please read it, and if you don't
38 accept the terms, delete this software.
40 SSLRef 3.0 was developed by Netscape Communications Corp. of Mountain
41 View, California <http://home.netscape.com/> and Consensus Development
42 Corporation of Berkeley, California <http://www.consensus.com/>.
44 *********************************************************************
46 File: sslrec.c Encryption, decryption and MACing of data
48 All the transformations which occur between plaintext and the
49 secured, authenticated data that goes out over the wire. Also,
50 detects incoming SSL 2 hello messages and hands them off to the SSL 2
51 record layer (and hands all SSL 2 reading & writing off to the SSL 2
54 ****************************************************************** */
68 #include "cryptType.h"
92 #ifndef _APPLE_GLUE_H_
93 #include "appleGlue.h"
100 * Lots of servers fail to provide closure alerts when they disconnect.
101 * For now we'll just accept it as long as it occurs on a clean record boundary
102 * (and the handshake is complete).
104 #define SSL_ALLOW_UNNOTICED_DISCONNECT 1
106 static SSLErr
DecryptSSLRecord(UInt8 type
, SSLBuffer
*payload
, SSLContext
*ctx
);
107 static SSLErr
VerifyMAC(UInt8 type
, SSLBuffer data
, UInt8
*compareMAC
, SSLContext
*ctx
);
108 static SSLErr
ComputeMAC(UInt8 type
, SSLBuffer data
, SSLBuffer mac
, sslUint64 seqNo
, SSLBuffer secret
, const HashReference
*macHash
, SSLContext
*ctx
);
109 static UInt8
* SSLEncodeUInt64(UInt8
*p
, sslUint64 value
);
112 * Attempt to read & decrypt an SSL record.
115 SSLReadRecord(SSLRecord
*rec
, SSLContext
*ctx
)
117 UInt32 len
, contentLen
;
119 SSLBuffer readData
, cipherFragment
;
121 if (!ctx
->partialReadBuffer
.data
|| ctx
->partialReadBuffer
.length
< 5)
122 { if (ctx
->partialReadBuffer
.data
)
123 if ((err
= SSLFreeBuffer(&ctx
->partialReadBuffer
, &ctx
->sysCtx
)) != 0)
124 { SSLFatalSessionAlert(alert_close_notify
, ctx
);
127 if ((err
= SSLAllocBuffer(&ctx
->partialReadBuffer
, DEFAULT_BUFFER_SIZE
, &ctx
->sysCtx
)) != 0)
128 { SSLFatalSessionAlert(alert_close_notify
, ctx
);
133 if (ctx
->negProtocolVersion
== SSL_Version_Undetermined
||
134 ctx
->negProtocolVersion
== SSL_Version_3_0_With_2_0_Hello
)
135 if (ctx
->amountRead
< 1)
136 { readData
.length
= 1 - ctx
->amountRead
;
137 readData
.data
= ctx
->partialReadBuffer
.data
+ ctx
->amountRead
;
138 len
= readData
.length
;
140 err
= sslIoRead(readData
, &len
, ctx
);
143 if (ERR(err
= ctx
->ioCtx
.read(readData
, &len
, ctx
->ioCtx
.ioRef
)) != 0)
145 { if (err
== SSLWouldBlockErr
)
146 ctx
->amountRead
+= len
;
148 SSLFatalSessionAlert(alert_close_notify
, ctx
);
151 ctx
->amountRead
+= len
;
154 /* In undetermined cases, if the first byte isn't in the range of SSL 3.0
155 * record types, this is an SSL 2.0 record
157 switch (ctx
->negProtocolVersion
)
158 { case SSL_Version_Undetermined
:
159 case SSL_Version_3_0_With_2_0_Hello
:
160 if (ctx
->partialReadBuffer
.data
[0] < SSL_smallest_3_0_type
||
161 ctx
->partialReadBuffer
.data
[0] > SSL_largest_3_0_type
)
162 return SSL2ReadRecord(rec
, ctx
);
165 case SSL_Version_2_0
:
166 return SSL2ReadRecord(rec
, ctx
);
171 if (ctx
->amountRead
< 5)
172 { readData
.length
= 5 - ctx
->amountRead
;
173 readData
.data
= ctx
->partialReadBuffer
.data
+ ctx
->amountRead
;
174 len
= readData
.length
;
176 err
= sslIoRead(readData
, &len
, ctx
);
179 if (ERR(err
= ctx
->ioCtx
.read(readData
, &len
, ctx
->ioCtx
.ioRef
)) != 0)
183 case SSLWouldBlockErr
:
184 ctx
->amountRead
+= len
;
186 #if SSL_ALLOW_UNNOTICED_DISCONNECT
187 case SSLConnectionClosedGraceful
:
188 /* legal if we're on record boundary and we've gotten past
190 if((ctx
->amountRead
== 0) && /* nothing pending */
191 (len
== 0) && /* nothing new */
192 (ctx
->state
== HandshakeClientReady
)) { /* handshake done */
194 * This means that the server has discionected without
195 * sending a closure alert notice. This is technically
196 * illegal per the SSL3 spec, but about half of the
197 * servers out there do it, so we report it as a separate
198 * error which most clients - including (currently)
199 * URLAccess - ignore by treating it the same as
200 * a SSLConnectionClosedGraceful error. Paranoid
201 * clients can detect it and handle it however they
204 SSLChangeHdskState(ctx
, SSLNoNotifyClose
);
205 err
= SSLConnectionClosedNoNotify
;
209 /* illegal disconnect */
210 err
= SSLConnectionClosedError
;
211 /* and drop thru to default: fatal alert */
213 #endif /* SSL_ALLOW_UNNOTICED_DISCONNECT */
215 SSLFatalSessionAlert(alert_close_notify
, ctx
);
220 ctx
->amountRead
+= len
;
223 CASSERT(ctx
->amountRead
>= 5);
225 progress
= ctx
->partialReadBuffer
.data
;
226 rec
->contentType
= *progress
++;
227 if (rec
->contentType
< SSL_smallest_3_0_type
||
228 rec
->contentType
> SSL_largest_3_0_type
)
229 return ERR(SSLProtocolErr
);
231 rec
->protocolVersion
= (SSLProtocolVersion
)SSLDecodeInt(progress
, 2);
233 contentLen
= SSLDecodeInt(progress
, 2);
235 if (contentLen
> (16384 + 2048)) /* Maximum legal length of an SSLCipherText payload */
236 { SSLFatalSessionAlert(alert_unexpected_message
, ctx
);
237 return ERR(SSLProtocolErr
);
240 if (ctx
->partialReadBuffer
.length
< 5 + contentLen
)
241 { if ((err
= SSLReallocBuffer(&ctx
->partialReadBuffer
, 5 + contentLen
, &ctx
->sysCtx
)) != 0)
242 { SSLFatalSessionAlert(alert_close_notify
, ctx
);
247 if (ctx
->amountRead
< 5 + contentLen
)
248 { readData
.length
= 5 + contentLen
- ctx
->amountRead
;
249 readData
.data
= ctx
->partialReadBuffer
.data
+ ctx
->amountRead
;
250 len
= readData
.length
;
252 err
= sslIoRead(readData
, &len
, ctx
);
255 if (ERR(err
= ctx
->ioCtx
.read(readData
, &len
, ctx
->ioCtx
.ioRef
)) != 0)
257 { if (err
== SSLWouldBlockErr
)
258 ctx
->amountRead
+= len
;
260 SSLFatalSessionAlert(alert_close_notify
, ctx
);
263 ctx
->amountRead
+= len
;
266 CASSERT(ctx
->amountRead
>= 5 + contentLen
);
268 cipherFragment
.data
= ctx
->partialReadBuffer
.data
+ 5;
269 cipherFragment
.length
= contentLen
;
271 /* Decrypt the payload & check the MAC, modifying the length of the buffer to indicate the
272 * amount of plaintext data after adjusting for the block size and removing the MAC
273 * (this function generates its own alerts)
275 if ((err
= DecryptSSLRecord(rec
->contentType
, &cipherFragment
, ctx
)) != 0)
278 /* We appear to have sucessfully received a record; increment the sequence number */
279 IncrementUInt64(&ctx
->readCipher
.sequenceNum
);
281 /* Allocate a buffer to return the plaintext in and return it */
282 if ((err
= SSLAllocBuffer(&rec
->contents
, cipherFragment
.length
, &ctx
->sysCtx
)) != 0)
283 { SSLFatalSessionAlert(alert_close_notify
, ctx
);
286 memcpy(rec
->contents
.data
, cipherFragment
.data
, cipherFragment
.length
);
288 ctx
->amountRead
= 0; /* We've used all the data in the cache */
293 /* SSLWriteRecord does not send alerts on failure, out of the assumption/fear
294 * that this might result in a loop (since sending an alert causes SSLWriteRecord
298 SSLWriteRecord(SSLRecord rec
, SSLContext
*ctx
)
301 WaitingRecord
*out
, *queue
;
302 SSLBuffer buf
, payload
, secret
, mac
;
304 UInt16 payloadSize
,blockSize
;
306 if (rec
.protocolVersion
== SSL_Version_2_0
)
307 return SSL2WriteRecord(rec
, ctx
);
309 CASSERT(rec
.protocolVersion
== SSL_Version_3_0
);
310 CASSERT(rec
.contents
.length
<= 16384);
313 /* Allocate a WaitingRecord to store our ready-to-send record in */
314 if ((err
= SSLAllocBuffer(&buf
, sizeof(WaitingRecord
), &ctx
->sysCtx
)) != 0)
316 out
= (WaitingRecord
*)buf
.data
;
319 /* Allocate enough room for the transmitted record, which will be:
320 * 5 bytes of header +
321 * encrypted contents +
323 * padding [block ciphers only] +
324 * padding length field (1 byte) [block ciphers only]
326 payloadSize
= (UInt16
) (rec
.contents
.length
+ ctx
->writeCipher
.hash
->digestSize
);
327 blockSize
= ctx
->writeCipher
.symCipher
->blockSize
;
329 { padding
= blockSize
- (payloadSize
% blockSize
) - 1;
330 payloadSize
+= padding
+ 1;
333 if ((err
= SSLAllocBuffer(&out
->data
, 5 + payloadSize
, &ctx
->sysCtx
)) != 0)
336 progress
= out
->data
.data
;
337 *(progress
++) = rec
.contentType
;
338 progress
= SSLEncodeInt(progress
, rec
.protocolVersion
, 2);
339 progress
= SSLEncodeInt(progress
, payloadSize
, 2);
341 /* Copy the contents into the output buffer */
342 memcpy(progress
, rec
.contents
.data
, rec
.contents
.length
);
343 payload
.data
= progress
;
344 payload
.length
= rec
.contents
.length
;
346 progress
+= rec
.contents
.length
;
347 /* MAC immediately follows data */
349 mac
.length
= ctx
->writeCipher
.hash
->digestSize
;
350 progress
+= mac
.length
;
353 if (mac
.length
> 0) /* Optimize away null case */
354 { secret
.data
= ctx
->writeCipher
.macSecret
;
355 secret
.length
= ctx
->writeCipher
.hash
->digestSize
;
356 if ((err
= ComputeMAC(rec
.contentType
, payload
, mac
, ctx
->writeCipher
.sequenceNum
, secret
, ctx
->writeCipher
.hash
, ctx
)) != 0)
360 /* Update payload to reflect encrypted data: contents, mac & padding */
361 payload
.length
= payloadSize
;
363 /* Fill in the padding bytes & padding length field with the padding value; the
364 * protocol only requires the last byte,
365 * but filling them all in avoids leaking data
367 if (ctx
->writeCipher
.symCipher
->blockSize
> 0)
368 for (i
= 1; i
<= padding
+ 1; ++i
)
369 payload
.data
[payload
.length
- i
] = padding
;
371 /* Encrypt the data */
372 DUMP_BUFFER_NAME("cleartext data", payload
);
373 /* _APPLE_CDSA_ change */
374 if ((err
= ctx
->writeCipher
.symCipher
->encrypt(payload
,
379 DUMP_BUFFER_NAME("encrypted data", payload
);
381 /* Enqueue the record to be written from the idle loop */
382 if (ctx
->recordWriteQueue
== 0)
383 ctx
->recordWriteQueue
= out
;
385 { queue
= ctx
->recordWriteQueue
;
386 while (queue
->next
!= 0)
391 /* Increment the sequence number */
392 IncrementUInt64(&ctx
->writeCipher
.sequenceNum
);
396 fail
: /* Only for if we fail between when the WaitingRecord is allocated and when it is queued */
397 SSLFreeBuffer(&out
->data
, &ctx
->sysCtx
);
398 buf
.data
= (UInt8
*)out
;
399 buf
.length
= sizeof(WaitingRecord
);
400 SSLFreeBuffer(&buf
, &ctx
->sysCtx
);
405 DecryptSSLRecord(UInt8 type
, SSLBuffer
*payload
, SSLContext
*ctx
)
409 if ((ctx
->readCipher
.symCipher
->blockSize
> 0) &&
410 ((payload
->length
% ctx
->readCipher
.symCipher
->blockSize
) != 0))
411 { SSLFatalSessionAlert(alert_unexpected_message
, ctx
);
412 return ERR(SSLProtocolErr
);
415 /* Decrypt in place */
416 DUMP_BUFFER_NAME("encrypted data", (*payload
));
417 /* _APPLE_CDSA_ change */
418 if ((err
= ctx
->readCipher
.symCipher
->decrypt(*payload
,
422 { SSLFatalSessionAlert(alert_close_notify
, ctx
);
425 DUMP_BUFFER_NAME("decrypted data", (*payload
));
427 /* Locate content within decrypted payload */
428 content
.data
= payload
->data
;
429 content
.length
= payload
->length
- ctx
->readCipher
.hash
->digestSize
;
430 if (ctx
->readCipher
.symCipher
->blockSize
> 0)
431 { /* padding can't be equal to or more than a block */
432 if (payload
->data
[payload
->length
- 1] >= ctx
->readCipher
.symCipher
->blockSize
)
433 { SSLFatalSessionAlert(alert_unexpected_message
, ctx
);
434 errorLog1("DecryptSSLRecord: bad padding length (%d)\n",
435 (unsigned)payload
->data
[payload
->length
- 1]);
436 return ERR(SSLProtocolErr
);
438 content
.length
-= 1 + payload
->data
[payload
->length
- 1]; /* Remove block size padding */
441 /* Verify MAC on payload */
442 if (ctx
->readCipher
.hash
->digestSize
> 0) /* Optimize away MAC for null case */
443 if ((err
= VerifyMAC(type
, content
, payload
->data
+ content
.length
, ctx
)) != 0)
444 { SSLFatalSessionAlert(alert_bad_record_mac
, ctx
);
448 *payload
= content
; /* Modify payload buffer to indicate content length */
454 SSLEncodeUInt64(UInt8
*p
, sslUint64 value
)
455 { p
= SSLEncodeInt(p
, value
.high
, 4);
456 return SSLEncodeInt(p
, value
.low
, 4);
460 VerifyMAC(UInt8 type
, SSLBuffer data
, UInt8
*compareMAC
, SSLContext
*ctx
)
462 UInt8 macData
[MAX_DIGEST_SIZE
];
463 SSLBuffer secret
, mac
;
465 secret
.data
= ctx
->readCipher
.macSecret
;
466 secret
.length
= ctx
->readCipher
.hash
->digestSize
;
468 mac
.length
= ctx
->readCipher
.hash
->digestSize
;
470 if ((err
= ComputeMAC(type
, data
, mac
, ctx
->readCipher
.sequenceNum
, secret
, ctx
->readCipher
.hash
, ctx
)) != 0)
473 if ((memcmp(mac
.data
, compareMAC
, mac
.length
)) != 0) {
474 errorLog0("VerifyMAC: Mac verify failure\n");
475 return ERR(SSLProtocolErr
);
481 ComputeMAC(UInt8 type
, SSLBuffer data
, SSLBuffer mac
, sslUint64 seqNo
, SSLBuffer secret
,
482 const HashReference
*macHash
, SSLContext
*ctx
)
484 UInt8 innerDigestData
[MAX_DIGEST_SIZE
];
485 UInt8 scratchData
[11], *progress
;
486 SSLBuffer digest
,digestCtx
,scratch
;
488 CASSERT(macHash
->macPadSize
<= MAX_MAC_PADDING
);
489 CASSERT(macHash
->digestSize
<= MAX_DIGEST_SIZE
);
490 CASSERT(SSLMACPad1
[0] == 0x36 && SSLMACPad2
[0] == 0x5C);
493 if ((err
= SSLAllocBuffer(&digestCtx
, macHash
->contextSize
, &ctx
->sysCtx
)) != 0)
496 /* MAC = hash( MAC_write_secret + pad_2 + hash( MAC_write_secret + pad_1 + seq_num + type + length + content ) ) */
497 if ((err
= macHash
->init(digestCtx
)) != 0)
499 if ((err
= macHash
->update(digestCtx
, secret
)) != 0) /* MAC secret */
501 scratch
.data
= SSLMACPad1
;
502 scratch
.length
= macHash
->macPadSize
;
503 if ((err
= macHash
->update(digestCtx
, scratch
)) != 0) /* pad1 */
505 progress
= scratchData
;
506 progress
= SSLEncodeUInt64(progress
, seqNo
);
508 progress
= SSLEncodeInt(progress
, data
.length
, 2);
509 scratch
.data
= scratchData
;
511 CASSERT(progress
= scratchData
+11);
512 if ((err
= macHash
->update(digestCtx
, scratch
)) != 0) /* sequenceNo, type & length */
514 if ((err
= macHash
->update(digestCtx
, data
)) != 0) /* content */
516 digest
.data
= innerDigestData
;
517 digest
.length
= macHash
->digestSize
;
518 if ((err
= macHash
->final(digestCtx
, digest
)) != 0) /* figure inner digest */
521 if ((err
= macHash
->init(digestCtx
)) != 0)
523 if ((err
= macHash
->update(digestCtx
, secret
)) != 0) /* MAC secret */
525 scratch
.data
= SSLMACPad2
;
526 scratch
.length
= macHash
->macPadSize
;
527 if ((err
= macHash
->update(digestCtx
, scratch
)) != 0) /* pad2 */
529 if ((err
= macHash
->update(digestCtx
, digest
)) != 0) /* inner digest */
531 if ((err
= macHash
->final(digestCtx
, mac
)) != 0) /* figure the mac */
534 err
= SSLNoErr
; /* redundant, I know */
537 SSLFreeBuffer(&digestCtx
, &ctx
->sysCtx
);