]>
git.saurik.com Git - apple/security.git/blob - SecureTransport/ssl2Record.cpp
40ea26a8102b8f34d57563f72ae7d3054e89105c
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
26 Copyright: (c) 1999 by Apple Computer, Inc., all rights reserved.
31 #include "sslRecord.h"
32 #include "sslMemory.h"
33 #include "sslContext.h"
34 #include "sslAlertMessage.h"
37 #include "sslDigests.h"
41 static OSStatus
SSL2DecryptRecord(
44 static OSStatus
SSL2VerifyMAC(
48 static OSStatus
SSL2CalculateMAC(
52 const HashReference
&hash
,
58 SSL2ReadRecord(SSLRecord
&rec
, SSLContext
*ctx
)
60 UInt32 len
, contentLen
;
61 int padding
, headerSize
;
63 SSLBuffer readData
, cipherFragment
;
65 switch (ctx
->negProtocolVersion
)
66 { case SSL_Version_Undetermined
:
69 case SSL_Version_3_0
: /* We've negotiated a 3.0 session;
70 * we can send an alert */
72 SSLFatalSessionAlert(SSL_AlertUnexpectedMsg
, ctx
);
73 return errSSLProtocol
;
75 sslErrorLog("bad protocolVersion in ctx->protocolVersion");
76 return errSSLInternal
;
79 if (!ctx
->partialReadBuffer
.data
|| ctx
->partialReadBuffer
.length
< 3)
80 { if (ctx
->partialReadBuffer
.data
)
81 if ((err
= SSLFreeBuffer(ctx
->partialReadBuffer
, ctx
)) != 0)
82 { SSL2SendError(SSL2_ErrNoCipher
, ctx
);
85 if ((err
= SSLAllocBuffer(ctx
->partialReadBuffer
, DEFAULT_BUFFER_SIZE
, ctx
)) != 0)
86 { SSL2SendError(SSL2_ErrNoCipher
, ctx
);
91 if (ctx
->amountRead
< 3)
92 { readData
.length
= 3 - ctx
->amountRead
;
93 readData
.data
= ctx
->partialReadBuffer
.data
+ ctx
->amountRead
;
94 len
= readData
.length
;
95 err
= sslIoRead(readData
, &len
, ctx
);
97 { if (err
== errSSLWouldBlock
)
98 ctx
->amountRead
+= len
;
99 if (err
== ioErr
&& ctx
->amountRead
== 0) /* If the session closes on a record boundary, it's graceful */
100 err
= errSSLClosedGraceful
;
103 ctx
->amountRead
+= len
;
106 rec
.contentType
= SSL_RecordTypeV2_0
;
107 rec
.protocolVersion
= SSL_Version_2_0
;
108 charPtr
= ctx
->partialReadBuffer
.data
;
110 if (((*charPtr
) & 0x80) != 0) /* High bit on -> specifies 2-byte header */
112 contentLen
= ((charPtr
[0] & 0x7F) << 8) | charPtr
[1];
115 else if (((*charPtr
) & 0x40) != 0) /* Bit 6 on -> specifies security escape */
116 { return errSSLProtocol
; /* No security escapes are defined */
118 else /* 3-byte header */
120 contentLen
= ((charPtr
[0] & 0x3F) << 8) | charPtr
[1];
121 padding
= charPtr
[2];
125 * FIXME - what's the max record size?
126 * and why doesn't SSLReadRecord parse the 2 or 3 byte header?
127 * Note: I see contentLen of 0 coming back from www.cduniverse.com when
128 * it's only been given SSL_RSA_EXPORT_WITH_DES40_CBC_SHA.
130 if((contentLen
== 0) || (contentLen
> 0xffff)) {
131 return errSSLProtocol
;
134 charPtr
+= headerSize
;
136 if (ctx
->partialReadBuffer
.length
< headerSize
+ contentLen
)
137 { if ((err
= SSLReallocBuffer(ctx
->partialReadBuffer
, 5 + contentLen
, ctx
)) != 0)
141 if (ctx
->amountRead
< headerSize
+ contentLen
)
142 { readData
.length
= headerSize
+ contentLen
- ctx
->amountRead
;
143 readData
.data
= ctx
->partialReadBuffer
.data
+ ctx
->amountRead
;
144 len
= readData
.length
;
145 err
= sslIoRead(readData
, &len
, ctx
);
147 { if (err
== errSSLWouldBlock
)
148 ctx
->amountRead
+= len
;
151 ctx
->amountRead
+= len
;
154 cipherFragment
.data
= ctx
->partialReadBuffer
.data
+ headerSize
;
155 cipherFragment
.length
= contentLen
;
156 if ((err
= SSL2DecryptRecord(cipherFragment
, ctx
)) != 0)
159 cipherFragment
.length
-= padding
; /* Remove padding; MAC was removed
160 * by SSL2DecryptRecord */
162 IncrementUInt64(&ctx
->readCipher
.sequenceNum
);
164 /* Allocate a buffer to return the plaintext in and return it */
165 if ((err
= SSLAllocBuffer(rec
.contents
, cipherFragment
.length
, ctx
)) != 0)
167 memcpy(rec
.contents
.data
, cipherFragment
.data
, cipherFragment
.length
);
169 ctx
->amountRead
= 0; /* We've used all the data in the cache */
175 SSL2WriteRecord(SSLRecord
&rec
, SSLContext
*ctx
)
177 int padding
= 0, i
, headerSize
;
178 WaitingRecord
*out
, *queue
;
179 SSLBuffer buf
, content
, payload
, secret
, mac
;
181 UInt16 payloadSize
, blockSize
;
183 assert(rec
.contents
.length
< 16384);
186 /* Allocate a WaitingRecord to store our ready-to-send record in */
187 if ((err
= SSLAllocBuffer(buf
, sizeof(WaitingRecord
), ctx
)) != 0)
189 out
= (WaitingRecord
*)buf
.data
;
193 payloadSize
= (UInt16
)
194 (rec
.contents
.length
+ ctx
->writeCipher
.macRef
->hash
->digestSize
);
195 blockSize
= ctx
->writeCipher
.symCipher
->blockSize
;
198 padding
= blockSize
- (payloadSize
% blockSize
);
199 if (padding
== blockSize
)
201 payloadSize
+= padding
;
209 if ((err
= SSLAllocBuffer(out
->data
, headerSize
+ payloadSize
, ctx
)) != 0)
211 charPtr
= out
->data
.data
;
214 charPtr
= SSLEncodeInt(charPtr
, payloadSize
| 0x8000, 2);
216 { charPtr
= SSLEncodeInt(charPtr
, payloadSize
, 2);
217 *charPtr
++ = padding
;
220 payload
.data
= charPtr
;
221 payload
.length
= payloadSize
;
224 mac
.length
= ctx
->writeCipher
.macRef
->hash
->digestSize
;
225 charPtr
+= mac
.length
;
227 content
.data
= charPtr
;
228 content
.length
= rec
.contents
.length
+ padding
;
229 memcpy(charPtr
, rec
.contents
.data
, rec
.contents
.length
);
230 charPtr
+= rec
.contents
.length
;
233 *charPtr
++ = padding
;
235 assert(charPtr
== out
->data
.data
+ out
->data
.length
);
237 secret
.data
= ctx
->writeCipher
.macSecret
;
238 secret
.length
= ctx
->writeCipher
.symCipher
->keySize
;
240 if ((err
= SSL2CalculateMAC(secret
, content
,
241 ctx
->writeCipher
.sequenceNum
.low
,
242 *ctx
->writeCipher
.macRef
->hash
, mac
, ctx
)) != 0)
245 if ((err
= ctx
->writeCipher
.symCipher
->encrypt(payload
,
251 /* Enqueue the record to be written from the idle loop */
252 if (ctx
->recordWriteQueue
== 0)
253 ctx
->recordWriteQueue
= out
;
255 { queue
= ctx
->recordWriteQueue
;
256 while (queue
->next
!= 0)
261 /* Increment the sequence number */
262 IncrementUInt64(&ctx
->writeCipher
.sequenceNum
);
268 * Only for if we fail between when the WaitingRecord is allocated and
271 SSLFreeBuffer(out
->data
, 0);
272 buf
.data
= (UInt8
*)out
;
273 buf
.length
= sizeof(WaitingRecord
);
274 SSLFreeBuffer(buf
, ctx
);
279 SSL2DecryptRecord(SSLBuffer
&payload
, SSLContext
*ctx
)
283 if (ctx
->readCipher
.symCipher
->blockSize
> 0)
284 if (payload
.length
% ctx
->readCipher
.symCipher
->blockSize
!= 0)
285 return errSSLProtocol
;
287 /* Decrypt in place */
288 if ((err
= ctx
->readCipher
.symCipher
->decrypt(payload
,
294 if (ctx
->readCipher
.macRef
->hash
->digestSize
> 0)
295 /* Optimize away MAC for null case */
296 { content
.data
= payload
.data
+ ctx
->readCipher
.macRef
->hash
->digestSize
; /* Data is after MAC */
297 content
.length
= payload
.length
- ctx
->readCipher
.macRef
->hash
->digestSize
;
298 if ((err
= SSL2VerifyMAC(content
, payload
.data
, ctx
)) != 0)
300 /* Adjust payload to remove MAC; caller is still responsible
301 * for removing padding [if any] */
308 #define IGNORE_MAC_FAILURE 0
311 SSL2VerifyMAC(SSLBuffer
&content
, UInt8
*compareMAC
, SSLContext
*ctx
)
313 UInt8 calculatedMAC
[SSL_MAX_DIGEST_LEN
];
314 SSLBuffer secret
, mac
;
316 secret
.data
= ctx
->readCipher
.macSecret
;
317 secret
.length
= ctx
->readCipher
.symCipher
->keySize
;
318 mac
.data
= calculatedMAC
;
319 mac
.length
= ctx
->readCipher
.macRef
->hash
->digestSize
;
320 if ((err
= SSL2CalculateMAC(secret
, content
, ctx
->readCipher
.sequenceNum
.low
,
321 *ctx
->readCipher
.macRef
->hash
, mac
, ctx
)) != 0)
323 if (memcmp(mac
.data
, compareMAC
, mac
.length
) != 0) {
324 #if IGNORE_MAC_FAILURE
325 sslErrorLog("SSL2VerifyMAC: Mac verify failure\n");
328 sslErrorLog("SSL2VerifyMAC: Mac verify failure\n");
329 return errSSLProtocol
;
335 #define LOG_MAC_DATA 0
337 static void logMacData(
343 printf("%s: ", field
);
344 for(i
=0; i
<data
->length
; i
++) {
345 printf("%02X", data
->data
[i
]);
352 #else /* LOG_MAC_DATA */
353 #define logMacData(f, d)
354 #endif /* LOG_MAC_DATA */
356 /* For SSL 2, the MAC is hash ( secret || content || sequence# )
357 * where secret is the decryption key for the message, content is
358 * the record data plus any padding used to round out the record
359 * size to an even multiple of the block size and sequence# is
360 * a monotonically increasing 32-bit unsigned integer.
367 const HashReference
&hash
,
371 UInt8 sequenceNum
[4];
372 SSLBuffer seqData
, hashContext
;
374 SSLEncodeInt(sequenceNum
, seqNo
, 4);
375 seqData
.data
= sequenceNum
;
378 hashContext
.data
= 0;
379 if ((err
= ReadyHash(hash
, hashContext
, ctx
)) != 0)
381 if ((err
= hash
.update(hashContext
, secret
)) != 0)
383 if ((err
= hash
.update(hashContext
, content
)) != 0)
385 if ((err
= hash
.update(hashContext
, seqData
)) != 0)
387 if ((err
= hash
.final(hashContext
, mac
)) != 0)
390 logMacData("secret ", &secret
);
391 logMacData("seqData", &seqData
);
392 logMacData("mac ", &mac
);
396 SSLFreeBuffer(hashContext
, ctx
);
401 SSL2SendError(SSL2ErrorCode error
, SSLContext
*ctx
)
406 rec
.contentType
= SSL_RecordTypeV2_0
;
407 rec
.protocolVersion
= SSL_Version_2_0
;
408 rec
.contents
.data
= errorData
;
409 rec
.contents
.length
= 3;
410 errorData
[0] = SSL2_MsgError
;
411 SSLEncodeInt(errorData
+ 1, error
, 2);
413 err
= SSL2WriteRecord(rec
, ctx
);