]> git.saurik.com Git - apple/security.git/blob - SecureTransport/ssl2Record.cpp
40ea26a8102b8f34d57563f72ae7d3054e89105c
[apple/security.git] / SecureTransport / ssl2Record.cpp
1 /*
2 * Copyright (c) 2000-2001 Apple Computer, Inc. All Rights Reserved.
3 *
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
8 * using this file.
9 *
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.
16 */
17
18
19 /*
20 File: ssl2Record.cpp
21
22 Contains: Record encrypting/decrypting/MACing for SSL 2
23
24 Written by: Doug Mitchell
25
26 Copyright: (c) 1999 by Apple Computer, Inc., all rights reserved.
27
28 */
29
30 #include "ssl2.h"
31 #include "sslRecord.h"
32 #include "sslMemory.h"
33 #include "sslContext.h"
34 #include "sslAlertMessage.h"
35 #include "sslDebug.h"
36 #include "sslUtils.h"
37 #include "sslDigests.h"
38
39 #include <string.h>
40
41 static OSStatus SSL2DecryptRecord(
42 SSLBuffer &payload,
43 SSLContext *ctx);
44 static OSStatus SSL2VerifyMAC(
45 SSLBuffer &content,
46 UInt8 *compareMAC,
47 SSLContext *ctx);
48 static OSStatus SSL2CalculateMAC(
49 SSLBuffer &secret,
50 SSLBuffer &content,
51 UInt32 seqNo,
52 const HashReference &hash,
53 SSLBuffer &mac,
54 SSLContext *ctx);
55
56
57 OSStatus
58 SSL2ReadRecord(SSLRecord &rec, SSLContext *ctx)
59 { OSStatus err;
60 UInt32 len, contentLen;
61 int padding, headerSize;
62 UInt8 *charPtr;
63 SSLBuffer readData, cipherFragment;
64
65 switch (ctx->negProtocolVersion)
66 { case SSL_Version_Undetermined:
67 case SSL_Version_2_0:
68 break;
69 case SSL_Version_3_0: /* We've negotiated a 3.0 session;
70 * we can send an alert */
71 case TLS_Version_1_0:
72 SSLFatalSessionAlert(SSL_AlertUnexpectedMsg, ctx);
73 return errSSLProtocol;
74 default:
75 sslErrorLog("bad protocolVersion in ctx->protocolVersion");
76 return errSSLInternal;
77 }
78
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);
83 return err;
84 }
85 if ((err = SSLAllocBuffer(ctx->partialReadBuffer, DEFAULT_BUFFER_SIZE, ctx)) != 0)
86 { SSL2SendError(SSL2_ErrNoCipher, ctx);
87 return err;
88 }
89 }
90
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);
96 if(err != 0)
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;
101 return err;
102 }
103 ctx->amountRead += len;
104 }
105
106 rec.contentType = SSL_RecordTypeV2_0;
107 rec.protocolVersion = SSL_Version_2_0;
108 charPtr = ctx->partialReadBuffer.data;
109
110 if (((*charPtr) & 0x80) != 0) /* High bit on -> specifies 2-byte header */
111 { headerSize = 2;
112 contentLen = ((charPtr[0] & 0x7F) << 8) | charPtr[1];
113 padding = 0;
114 }
115 else if (((*charPtr) & 0x40) != 0) /* Bit 6 on -> specifies security escape */
116 { return errSSLProtocol; /* No security escapes are defined */
117 }
118 else /* 3-byte header */
119 { headerSize = 3;
120 contentLen = ((charPtr[0] & 0x3F) << 8) | charPtr[1];
121 padding = charPtr[2];
122 }
123
124 /*
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.
129 */
130 if((contentLen == 0) || (contentLen > 0xffff)) {
131 return errSSLProtocol;
132 }
133
134 charPtr += headerSize;
135
136 if (ctx->partialReadBuffer.length < headerSize + contentLen)
137 { if ((err = SSLReallocBuffer(ctx->partialReadBuffer, 5 + contentLen, ctx)) != 0)
138 return err;
139 }
140
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);
146 if(err != 0)
147 { if (err == errSSLWouldBlock)
148 ctx->amountRead += len;
149 return err;
150 }
151 ctx->amountRead += len;
152 }
153
154 cipherFragment.data = ctx->partialReadBuffer.data + headerSize;
155 cipherFragment.length = contentLen;
156 if ((err = SSL2DecryptRecord(cipherFragment, ctx)) != 0)
157 return err;
158
159 cipherFragment.length -= padding; /* Remove padding; MAC was removed
160 * by SSL2DecryptRecord */
161
162 IncrementUInt64(&ctx->readCipher.sequenceNum);
163
164 /* Allocate a buffer to return the plaintext in and return it */
165 if ((err = SSLAllocBuffer(rec.contents, cipherFragment.length, ctx)) != 0)
166 return err;
167 memcpy(rec.contents.data, cipherFragment.data, cipherFragment.length);
168
169 ctx->amountRead = 0; /* We've used all the data in the cache */
170
171 return noErr;
172 }
173
174 OSStatus
175 SSL2WriteRecord(SSLRecord &rec, SSLContext *ctx)
176 { OSStatus err;
177 int padding = 0, i, headerSize;
178 WaitingRecord *out, *queue;
179 SSLBuffer buf, content, payload, secret, mac;
180 UInt8 *charPtr;
181 UInt16 payloadSize, blockSize;
182
183 assert(rec.contents.length < 16384);
184
185 out = 0;
186 /* Allocate a WaitingRecord to store our ready-to-send record in */
187 if ((err = SSLAllocBuffer(buf, sizeof(WaitingRecord), ctx)) != 0)
188 return err;
189 out = (WaitingRecord*)buf.data;
190 out->next = 0;
191 out->sent = 0;
192
193 payloadSize = (UInt16)
194 (rec.contents.length + ctx->writeCipher.macRef->hash->digestSize);
195 blockSize = ctx->writeCipher.symCipher->blockSize;
196 if (blockSize > 0)
197 {
198 padding = blockSize - (payloadSize % blockSize);
199 if (padding == blockSize)
200 padding = 0;
201 payloadSize += padding;
202 headerSize = 3;
203 }
204 else
205 { padding = 0;
206 headerSize = 2;
207 }
208 out->data.data = 0;
209 if ((err = SSLAllocBuffer(out->data, headerSize + payloadSize, ctx)) != 0)
210 goto fail;
211 charPtr = out->data.data;
212
213 if (headerSize == 2)
214 charPtr = SSLEncodeInt(charPtr, payloadSize | 0x8000, 2);
215 else
216 { charPtr = SSLEncodeInt(charPtr, payloadSize, 2);
217 *charPtr++ = padding;
218 }
219
220 payload.data = charPtr;
221 payload.length = payloadSize;
222
223 mac.data = charPtr;
224 mac.length = ctx->writeCipher.macRef->hash->digestSize;
225 charPtr += mac.length;
226
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;
231 i = padding;
232 while (i--)
233 *charPtr++ = padding;
234
235 assert(charPtr == out->data.data + out->data.length);
236
237 secret.data = ctx->writeCipher.macSecret;
238 secret.length = ctx->writeCipher.symCipher->keySize;
239 if (mac.length > 0)
240 if ((err = SSL2CalculateMAC(secret, content,
241 ctx->writeCipher.sequenceNum.low,
242 *ctx->writeCipher.macRef->hash, mac, ctx)) != 0)
243 goto fail;
244
245 if ((err = ctx->writeCipher.symCipher->encrypt(payload,
246 payload,
247 &ctx->writeCipher,
248 ctx)) != 0)
249 goto fail;
250
251 /* Enqueue the record to be written from the idle loop */
252 if (ctx->recordWriteQueue == 0)
253 ctx->recordWriteQueue = out;
254 else
255 { queue = ctx->recordWriteQueue;
256 while (queue->next != 0)
257 queue = queue->next;
258 queue->next = out;
259 }
260
261 /* Increment the sequence number */
262 IncrementUInt64(&ctx->writeCipher.sequenceNum);
263
264 return noErr;
265
266 fail:
267 /*
268 * Only for if we fail between when the WaitingRecord is allocated and
269 * when it is queued
270 */
271 SSLFreeBuffer(out->data, 0);
272 buf.data = (UInt8*)out;
273 buf.length = sizeof(WaitingRecord);
274 SSLFreeBuffer(buf, ctx);
275 return err;
276 }
277
278 static OSStatus
279 SSL2DecryptRecord(SSLBuffer &payload, SSLContext *ctx)
280 { OSStatus err;
281 SSLBuffer content;
282
283 if (ctx->readCipher.symCipher->blockSize > 0)
284 if (payload.length % ctx->readCipher.symCipher->blockSize != 0)
285 return errSSLProtocol;
286
287 /* Decrypt in place */
288 if ((err = ctx->readCipher.symCipher->decrypt(payload,
289 payload,
290 &ctx->readCipher,
291 ctx)) != 0)
292 return err;
293
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)
299 return err;
300 /* Adjust payload to remove MAC; caller is still responsible
301 * for removing padding [if any] */
302 payload = content;
303 }
304
305 return noErr;
306 }
307
308 #define IGNORE_MAC_FAILURE 0
309
310 static OSStatus
311 SSL2VerifyMAC(SSLBuffer &content, UInt8 *compareMAC, SSLContext *ctx)
312 { OSStatus err;
313 UInt8 calculatedMAC[SSL_MAX_DIGEST_LEN];
314 SSLBuffer secret, mac;
315
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)
322 return err;
323 if (memcmp(mac.data, compareMAC, mac.length) != 0) {
324 #if IGNORE_MAC_FAILURE
325 sslErrorLog("SSL2VerifyMAC: Mac verify failure\n");
326 return noErr;
327 #else
328 sslErrorLog("SSL2VerifyMAC: Mac verify failure\n");
329 return errSSLProtocol;
330 #endif
331 }
332 return noErr;
333 }
334
335 #define LOG_MAC_DATA 0
336 #if LOG_MAC_DATA
337 static void logMacData(
338 char *field,
339 SSLBuffer *data)
340 {
341 int i;
342
343 printf("%s: ", field);
344 for(i=0; i<data->length; i++) {
345 printf("%02X", data->data[i]);
346 if((i % 4) == 3) {
347 printf(" ");
348 }
349 }
350 printf("\n");
351 }
352 #else /* LOG_MAC_DATA */
353 #define logMacData(f, d)
354 #endif /* LOG_MAC_DATA */
355
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.
361 */
362 static OSStatus
363 SSL2CalculateMAC(
364 SSLBuffer &secret,
365 SSLBuffer &content,
366 UInt32 seqNo,
367 const HashReference &hash,
368 SSLBuffer &mac,
369 SSLContext *ctx)
370 { OSStatus err;
371 UInt8 sequenceNum[4];
372 SSLBuffer seqData, hashContext;
373
374 SSLEncodeInt(sequenceNum, seqNo, 4);
375 seqData.data = sequenceNum;
376 seqData.length = 4;
377
378 hashContext.data = 0;
379 if ((err = ReadyHash(hash, hashContext, ctx)) != 0)
380 return err;
381 if ((err = hash.update(hashContext, secret)) != 0)
382 goto fail;
383 if ((err = hash.update(hashContext, content)) != 0)
384 goto fail;
385 if ((err = hash.update(hashContext, seqData)) != 0)
386 goto fail;
387 if ((err = hash.final(hashContext, mac)) != 0)
388 goto fail;
389
390 logMacData("secret ", &secret);
391 logMacData("seqData", &seqData);
392 logMacData("mac ", &mac);
393
394 err = noErr;
395 fail:
396 SSLFreeBuffer(hashContext, ctx);
397 return err;
398 }
399
400 OSStatus
401 SSL2SendError(SSL2ErrorCode error, SSLContext *ctx)
402 { OSStatus err;
403 SSLRecord rec;
404 UInt8 errorData[3];
405
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);
412
413 err = SSL2WriteRecord(rec, ctx);
414 return err;
415 }