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: Record encrypting/decrypting/MACing for SSL 2
24 Written by: Doug Mitchell, based on Netscape SSLRef 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: ssl2rec.c Record encrypting/decrypting/MACing for SSL 2
49 ****************************************************************** */
84 #ifndef _APPLE_GLUE_H_
85 #include "appleGlue.h"
91 static SSLErr
SSL2DecryptRecord(SSLBuffer
*payload
, SSLContext
*ctx
);
92 static SSLErr
SSL2VerifyMAC(SSLBuffer content
, UInt8
*compareMAC
, SSLContext
*ctx
);
93 static SSLErr
SSL2CalculateMAC(SSLBuffer secret
, SSLBuffer content
, UInt32 seqNo
, const HashReference
*hash
, SSLBuffer mac
, SSLContext
*ctx
);
97 SSL2ReadRecord(SSLRecord
*rec
, SSLContext
*ctx
)
99 UInt32 len
, contentLen
;
100 int padding
, headerSize
;
102 SSLBuffer readData
, cipherFragment
;
104 switch (ctx
->negProtocolVersion
)
105 { case SSL_Version_Undetermined
:
106 case SSL_Version_3_0_With_2_0_Hello
:
107 case SSL_Version_2_0
:
109 case SSL_Version_3_0
: /* We've negotiated a 3.0 session; we can send an alert */
110 SSLFatalSessionAlert(alert_unexpected_message
, ctx
);
111 return SSLProtocolErr
;
112 case SSL_Version_3_0_Only
: /* We haven't yet negotiated, but we don't want to support 2.0; just die without an alert */
113 return SSLProtocolErr
;
115 sslPanic("bad protocolVersion in ctx->protocolVersion");
118 if (!ctx
->partialReadBuffer
.data
|| ctx
->partialReadBuffer
.length
< 3)
119 { if (ctx
->partialReadBuffer
.data
)
120 if (ERR(err
= SSLFreeBuffer(&ctx
->partialReadBuffer
, &ctx
->sysCtx
)) != 0)
121 { SSLFatalSessionAlert(alert_close_notify
, ctx
);
124 if (ERR(err
= SSLAllocBuffer(&ctx
->partialReadBuffer
, DEFAULT_BUFFER_SIZE
, &ctx
->sysCtx
)) != 0)
125 { SSLFatalSessionAlert(alert_close_notify
, ctx
);
130 if (ctx
->amountRead
< 3)
131 { readData
.length
= 3 - ctx
->amountRead
;
132 readData
.data
= ctx
->partialReadBuffer
.data
+ ctx
->amountRead
;
133 len
= readData
.length
;
135 err
= sslIoRead(readData
, &len
, ctx
);
138 if (ERR(err
= ctx
->ioCtx
.read(readData
, &len
, ctx
->ioCtx
.ioRef
)) != 0)
140 { if (err
== SSLWouldBlockErr
)
141 ctx
->amountRead
+= len
;
142 if (err
== SSLIOErr
&& ctx
->amountRead
== 0) /* If the session closes on a record boundary, it's graceful */
143 err
= SSLConnectionClosedGraceful
;
146 ctx
->amountRead
+= len
;
149 rec
->contentType
= SSL_version_2_0_record
;
150 rec
->protocolVersion
= SSL_Version_2_0
;
151 progress
= ctx
->partialReadBuffer
.data
;
153 if (((*progress
) & 0x80) != 0) /* High bit on -> specifies 2-byte header */
155 contentLen
= ((progress
[0] & 0x7F) << 8) | progress
[1];
158 else if (((*progress
) & 0x40) != 0) /* Bit 6 on -> specifies security escape */
159 { return ERR(SSLProtocolErr
); /* No security escapes are defined */
161 else /* 3-byte header */
163 contentLen
= ((progress
[0] & 0x3F) << 8) | progress
[1];
164 padding
= progress
[2];
169 * FIXME - what's the max record size?
170 * and why doesn't SSLReadRecord parse the 2 or 3 byte header?
171 * Note: I see contentLen of 0 coming back from www.cduniverse.com when
172 * it's only been given SSL_RSA_EXPORT_WITH_DES40_CBC_SHA.
174 if((contentLen
== 0) || (contentLen
> 0xffff)) {
175 return SSLProtocolErr
;
179 progress
+= headerSize
;
181 if (ctx
->partialReadBuffer
.length
< headerSize
+ contentLen
)
182 { if (ERR(err
= SSLReallocBuffer(&ctx
->partialReadBuffer
, 5 + contentLen
, &ctx
->sysCtx
)) != 0)
186 if (ctx
->amountRead
< headerSize
+ contentLen
)
187 { readData
.length
= headerSize
+ contentLen
- ctx
->amountRead
;
188 readData
.data
= ctx
->partialReadBuffer
.data
+ ctx
->amountRead
;
189 len
= readData
.length
;
191 err
= sslIoRead(readData
, &len
, ctx
);
194 if (ERR(err
= ctx
->ioCtx
.read(readData
, &len
, ctx
->ioCtx
.ioRef
)) != 0)
196 { if (err
== SSLWouldBlockErr
)
197 ctx
->amountRead
+= len
;
200 ctx
->amountRead
+= len
;
203 cipherFragment
.data
= ctx
->partialReadBuffer
.data
+ headerSize
;
204 cipherFragment
.length
= contentLen
;
205 if (ERR(err
= SSL2DecryptRecord(&cipherFragment
, ctx
)) != 0)
208 cipherFragment
.length
-= padding
; /* Remove padding; MAC was removed by SSL2DecryptRecord */
210 IncrementUInt64(&ctx
->readCipher
.sequenceNum
);
212 /* Allocate a buffer to return the plaintext in and return it */
213 if (ERR(err
= SSLAllocBuffer(&rec
->contents
, cipherFragment
.length
, &ctx
->sysCtx
)) != 0)
215 memcpy(rec
->contents
.data
, cipherFragment
.data
, cipherFragment
.length
);
217 ctx
->amountRead
= 0; /* We've used all the data in the cache */
223 SSL2WriteRecord(SSLRecord rec
, SSLContext
*ctx
)
225 int padding
= 0, i
, headerSize
;
226 WaitingRecord
*out
, *queue
;
227 SSLBuffer buf
, content
, payload
, secret
, mac
;
229 UInt16 payloadSize
, blockSize
;
231 CASSERT(rec
.contents
.length
< 16384);
234 /* Allocate a WaitingRecord to store our ready-to-send record in */
235 if (ERR(err
= SSLAllocBuffer(&buf
, sizeof(WaitingRecord
), &ctx
->sysCtx
)) != 0)
237 out
= (WaitingRecord
*)buf
.data
;
241 payloadSize
= (UInt16
) (rec
.contents
.length
+ ctx
->writeCipher
.hash
->digestSize
);
242 blockSize
= ctx
->writeCipher
.symCipher
->blockSize
;
246 /* HEY! this netscape code could never work with a block cipher... */
247 padding
= blockSize
- (payloadSize
% blockSize
);
250 padding
= blockSize
- (payloadSize
% blockSize
) - 1;
252 if (padding
== blockSize
)
254 payloadSize
+= padding
;
262 if (ERR(err
= SSLAllocBuffer(&out
->data
, headerSize
+ payloadSize
, &ctx
->sysCtx
)) != 0)
264 progress
= out
->data
.data
;
267 progress
= SSLEncodeInt(progress
, payloadSize
| 0x8000, 2);
269 { progress
= SSLEncodeInt(progress
, payloadSize
, 2);
270 *progress
++ = padding
;
273 payload
.data
= progress
;
274 payload
.length
= payloadSize
;
277 mac
.length
= ctx
->writeCipher
.hash
->digestSize
;
278 progress
+= mac
.length
;
280 content
.data
= progress
;
281 content
.length
= rec
.contents
.length
+ padding
;
282 memcpy(progress
, rec
.contents
.data
, rec
.contents
.length
);
283 progress
+= rec
.contents
.length
;
286 *progress
++ = padding
;
288 CASSERT(progress
== out
->data
.data
+ out
->data
.length
);
290 secret
.data
= ctx
->writeCipher
.macSecret
;
291 secret
.length
= ctx
->writeCipher
.symCipher
->keySize
;
293 if (ERR(err
= SSL2CalculateMAC(secret
, content
, ctx
->writeCipher
.sequenceNum
.low
,
294 ctx
->writeCipher
.hash
, mac
, ctx
)) != 0)
297 /* APPLE_CDSA change...*/
298 if (ERR(err
= ctx
->writeCipher
.symCipher
->encrypt(payload
,
304 /* Enqueue the record to be written from the idle loop */
305 if (ctx
->recordWriteQueue
== 0)
306 ctx
->recordWriteQueue
= out
;
308 { queue
= ctx
->recordWriteQueue
;
309 while (queue
->next
!= 0)
314 /* Increment the sequence number */
315 IncrementUInt64(&ctx
->writeCipher
.sequenceNum
);
319 fail
: /* Only for if we fail between when the WaitingRecord is allocated and when it is queued */
320 SSLFreeBuffer(&out
->data
, 0);
321 buf
.data
= (UInt8
*)out
;
322 buf
.length
= sizeof(WaitingRecord
);
323 SSLFreeBuffer(&buf
, &ctx
->sysCtx
);
328 SSL2DecryptRecord(SSLBuffer
*payload
, SSLContext
*ctx
)
332 if (ctx
->readCipher
.symCipher
->blockSize
> 0)
333 if (payload
->length
% ctx
->readCipher
.symCipher
->blockSize
!= 0)
334 return ERR(SSLProtocolErr
);
336 /* Decrypt in place */
337 /* APPLE_CDSA change...*/
338 if (ERR(err
= ctx
->readCipher
.symCipher
->decrypt(*payload
,
344 if (ctx
->readCipher
.hash
->digestSize
> 0) /* Optimize away MAC for null case */
345 { content
.data
= payload
->data
+ ctx
->readCipher
.hash
->digestSize
; /* Data is after MAC */
346 content
.length
= payload
->length
- ctx
->readCipher
.hash
->digestSize
;
347 if (ERR(err
= SSL2VerifyMAC(content
, payload
->data
, ctx
)) != 0)
349 /* Adjust payload to remove MAC; caller is still responsible for removing padding [if any] */
356 #define IGNORE_MAC_FAILURE 0
359 SSL2VerifyMAC(SSLBuffer content
, UInt8
*compareMAC
, SSLContext
*ctx
)
361 UInt8 calculatedMAC
[MAX_DIGEST_SIZE
];
362 SSLBuffer secret
, mac
;
364 secret
.data
= ctx
->readCipher
.macSecret
;
365 secret
.length
= ctx
->readCipher
.symCipher
->keySize
;
366 mac
.data
= calculatedMAC
;
367 mac
.length
= ctx
->readCipher
.hash
->digestSize
;
368 if (ERR(err
= SSL2CalculateMAC(secret
, content
, ctx
->readCipher
.sequenceNum
.low
,
369 ctx
->readCipher
.hash
, mac
, ctx
)) != 0)
371 if (memcmp(mac
.data
, compareMAC
, mac
.length
) != 0) {
372 #if IGNORE_MAC_FAILURE
373 dprintf0("SSL2VerifyMAC: Mac verify failure\n");
376 errorLog0("SSL2VerifyMAC: Mac verify failure\n");
377 return ERR(SSLProtocolErr
);
383 #define LOG_MAC_DATA 0
385 static void logMacData(
391 printf("%s: ", field
);
392 for(i
=0; i
<data
->length
; i
++) {
393 printf("%02X", data
->data
[i
]);
400 #else /* LOG_MAC_DATA */
401 #define logMacData(f, d)
402 #endif /* LOG_MAC_DATA */
404 /* For SSL 2, the MAC is hash ( secret || content || sequence# )
405 * where secret is the decryption key for the message, content is
406 * the record data plus any padding used to round out the record
407 * size to an even multiple of the block size and sequence# is
408 * a monotonically increasing 32-bit unsigned integer.
411 SSL2CalculateMAC(SSLBuffer secret
, SSLBuffer content
, UInt32 seqNo
, const HashReference
*hash
, SSLBuffer mac
, SSLContext
*ctx
)
413 UInt8 sequenceNum
[4];
414 SSLBuffer seqData
, hashContext
;
416 SSLEncodeInt(sequenceNum
, seqNo
, 4);
417 seqData
.data
= sequenceNum
;
420 hashContext
.data
= 0;
421 if (ERR(err
= ReadyHash(hash
, &hashContext
, ctx
)) != 0)
423 if (ERR(err
= hash
->update(hashContext
, secret
)) != 0)
425 if (ERR(err
= hash
->update(hashContext
, content
)) != 0)
427 if (ERR(err
= hash
->update(hashContext
, seqData
)) != 0)
429 if (ERR(err
= hash
->final(hashContext
, mac
)) != 0)
432 logMacData("secret ", &secret
);
433 logMacData("seqData", &seqData
);
434 logMacData("mac ", &mac
);
438 ERR(SSLFreeBuffer(&hashContext
, &ctx
->sysCtx
));
443 SSL2SendError(SSL2ErrorCode error
, SSLContext
*ctx
)
448 rec
.contentType
= SSL_version_2_0_record
;
449 rec
.protocolVersion
= SSL_Version_2_0
;
450 rec
.contents
.data
= errorData
;
451 rec
.contents
.length
= 3;
452 errorData
[0] = ssl2_mt_error
;
453 SSLEncodeInt(errorData
+ 1, error
, 2);
455 ERR(err
= SSL2WriteRecord(rec
, ctx
));