2 * Copyright (c) 2002 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: SSLv3-specific routines for SslTlsCallouts.
24 Written by: Doug Mitchell
38 * ssl3WriteRecord does not send alerts on failure, out of the assumption/fear
39 * that this might result in a loop (since sending an alert causes ssl3WriteRecord
42 * As far as I can tell, we can use this same routine for SSLv3 and TLSv1, as long
43 * as we're not trying to use the "variable length padding" feature of TLSv1.
44 * OpenSSL doesn't use that feature; for now, neither will we. Thus this routine
45 * is used for the SslTlsCallouts.writeRecord function for both protocols.
47 SSLErr
ssl3WriteRecord(
53 WaitingRecord
*out
, *queue
;
54 SSLBuffer buf
, payload
, mac
;
56 UInt16 payloadSize
,blockSize
;
58 switch(rec
.protocolVersion
) {
60 return SSL2WriteRecord(rec
, ctx
);
66 return SSLInternalError
;
68 assert(rec
.contents
.length
<= 16384);
71 /* Allocate a WaitingRecord to store our ready-to-send record in */
72 if ((err
= SSLAllocBuffer(&buf
, sizeof(WaitingRecord
), &ctx
->sysCtx
)) != 0)
74 out
= (WaitingRecord
*)buf
.data
;
77 /* Allocate enough room for the transmitted record, which will be:
79 * encrypted contents +
81 * padding [block ciphers only] +
82 * padding length field (1 byte) [block ciphers only]
84 payloadSize
= (UInt16
) (rec
.contents
.length
+ ctx
->writeCipher
.macRef
->hash
->digestSize
);
85 blockSize
= ctx
->writeCipher
.symCipher
->blockSize
;
87 { padding
= blockSize
- (payloadSize
% blockSize
) - 1;
88 payloadSize
+= padding
+ 1;
91 if ((err
= SSLAllocBuffer(&out
->data
, 5 + payloadSize
, &ctx
->sysCtx
)) != 0)
94 progress
= out
->data
.data
;
95 *(progress
++) = rec
.contentType
;
96 progress
= SSLEncodeInt(progress
, rec
.protocolVersion
, 2);
97 progress
= SSLEncodeInt(progress
, payloadSize
, 2);
99 /* Copy the contents into the output buffer */
100 memcpy(progress
, rec
.contents
.data
, rec
.contents
.length
);
101 payload
.data
= progress
;
102 payload
.length
= rec
.contents
.length
;
104 progress
+= rec
.contents
.length
;
105 /* MAC immediately follows data */
107 mac
.length
= ctx
->writeCipher
.macRef
->hash
->digestSize
;
108 progress
+= mac
.length
;
111 if (mac
.length
> 0) /* Optimize away null case */
113 assert(ctx
->sslTslCalls
!= NULL
);
114 if ((err
= ctx
->sslTslCalls
->computeMac(rec
.contentType
,
118 ctx
->writeCipher
.sequenceNum
,
123 /* Update payload to reflect encrypted data: contents, mac & padding */
124 payload
.length
= payloadSize
;
126 /* Fill in the padding bytes & padding length field with the padding value; the
127 * protocol only requires the last byte,
128 * but filling them all in avoids leaking data
130 if (ctx
->writeCipher
.symCipher
->blockSize
> 0)
131 for (i
= 1; i
<= padding
+ 1; ++i
)
132 payload
.data
[payload
.length
- i
] = padding
;
134 /* Encrypt the data */
135 if ((err
= ctx
->writeCipher
.symCipher
->encrypt(payload
,
140 DUMP_BUFFER_NAME("encrypted data", payload
);
142 /* Enqueue the record to be written from the idle loop */
143 if (ctx
->recordWriteQueue
== 0)
144 ctx
->recordWriteQueue
= out
;
146 { queue
= ctx
->recordWriteQueue
;
147 while (queue
->next
!= 0)
152 /* Increment the sequence number */
153 IncrementUInt64(&ctx
->writeCipher
.sequenceNum
);
159 * Only for if we fail between when the WaitingRecord is allocated and when
162 SSLFreeBuffer(&out
->data
, &ctx
->sysCtx
);
163 buf
.data
= (UInt8
*)out
;
164 buf
.length
= sizeof(WaitingRecord
);
165 SSLFreeBuffer(&buf
, &ctx
->sysCtx
);
169 static SSLErr
ssl3DecryptRecord(
177 if ((ctx
->readCipher
.symCipher
->blockSize
> 0) &&
178 ((payload
->length
% ctx
->readCipher
.symCipher
->blockSize
) != 0))
179 { SSLFatalSessionAlert(alert_unexpected_message
, ctx
);
180 return ERR(SSLProtocolErr
);
183 /* Decrypt in place */
184 DUMP_BUFFER_NAME("encrypted data", (*payload
));
185 if ((err
= ctx
->readCipher
.symCipher
->decrypt(*payload
,
189 { SSLFatalSessionAlert(alert_close_notify
, ctx
);
192 DUMP_BUFFER_NAME("decrypted data", (*payload
));
194 /* Locate content within decrypted payload */
195 content
.data
= payload
->data
;
196 content
.length
= payload
->length
- ctx
->readCipher
.macRef
->hash
->digestSize
;
197 if (ctx
->readCipher
.symCipher
->blockSize
> 0)
198 { /* padding can't be equal to or more than a block */
199 if (payload
->data
[payload
->length
- 1] >= ctx
->readCipher
.symCipher
->blockSize
)
200 { SSLFatalSessionAlert(alert_unexpected_message
, ctx
);
201 errorLog1("DecryptSSLRecord: bad padding length (%d)\n",
202 (unsigned)payload
->data
[payload
->length
- 1]);
203 return ERR(SSLProtocolErr
);
205 content
.length
-= 1 + payload
->data
[payload
->length
- 1];
206 /* Remove block size padding */
209 /* Verify MAC on payload */
210 if (ctx
->readCipher
.macRef
->hash
->digestSize
> 0)
211 /* Optimize away MAC for null case */
212 if ((err
= SSLVerifyMac(type
, content
,
213 payload
->data
+ content
.length
, ctx
)) != 0)
214 { SSLFatalSessionAlert(alert_bad_record_mac
, ctx
);
218 *payload
= content
; /* Modify payload buffer to indicate content length */
223 /* initialize a per-CipherContext HashHmacContext for use in MACing each record */
224 static SSLErr
ssl3InitMac (
225 CipherContext
*cipherCtx
, // macRef, macSecret valid on entry
226 // macCtx valid on return
229 const HashReference
*hash
;
233 assert(cipherCtx
->macRef
!= NULL
);
234 hash
= cipherCtx
->macRef
->hash
;
235 assert(hash
!= NULL
);
237 hashCtx
= &cipherCtx
->macCtx
.hashCtx
;
238 if(hashCtx
->data
!= NULL
) {
239 SSLFreeBuffer(hashCtx
, &ctx
->sysCtx
);
241 serr
= SSLAllocBuffer(hashCtx
, hash
->contextSize
, &ctx
->sysCtx
);
248 static SSLErr
ssl3FreeMac (
249 CipherContext
*cipherCtx
)
253 assert(cipherCtx
!= NULL
);
254 /* this can be called on a completely zeroed out CipherContext... */
255 if(cipherCtx
->macRef
== NULL
) {
258 hashCtx
= &cipherCtx
->macCtx
.hashCtx
;
259 if(hashCtx
->data
!= NULL
) {
260 sslFree(hashCtx
->data
);
261 hashCtx
->data
= NULL
;
267 static SSLErr
ssl3ComputeMac (
270 SSLBuffer mac
, // caller mallocs data
271 CipherContext
*cipherCtx
, // assumes macCtx, macRef
276 UInt8 innerDigestData
[MAX_DIGEST_SIZE
];
277 UInt8 scratchData
[11], *progress
;
278 SSLBuffer digest
, digestCtx
, scratch
;
281 const HashReference
*hash
;
283 assert(cipherCtx
!= NULL
);
284 assert(cipherCtx
->macRef
!= NULL
);
285 hash
= cipherCtx
->macRef
->hash
;
286 assert(hash
!= NULL
);
287 assert(hash
->macPadSize
<= MAX_MAC_PADDING
);
288 assert(hash
->digestSize
<= MAX_DIGEST_SIZE
);
289 digestCtx
= cipherCtx
->macCtx
.hashCtx
; // may be NULL, for null cipher
290 secret
.data
= cipherCtx
->macSecret
;
291 secret
.length
= hash
->digestSize
;
293 /* init'd early in SSLNewContext() */
294 assert(SSLMACPad1
[0] == 0x36 && SSLMACPad2
[0] == 0x5C);
297 * MAC = hash( MAC_write_secret + pad_2 +
298 * hash( MAC_write_secret + pad_1 + seq_num + type +
302 if ((err
= hash
->init(digestCtx
, ctx
)) != 0)
304 if ((err
= hash
->update(digestCtx
, secret
)) != 0) /* MAC secret */
306 scratch
.data
= SSLMACPad1
;
307 scratch
.length
= hash
->macPadSize
;
308 if ((err
= hash
->update(digestCtx
, scratch
)) != 0) /* pad1 */
310 progress
= scratchData
;
311 progress
= SSLEncodeUInt64(progress
, seqNo
);
313 progress
= SSLEncodeInt(progress
, data
.length
, 2);
314 scratch
.data
= scratchData
;
316 assert(progress
= scratchData
+11);
317 if ((err
= hash
->update(digestCtx
, scratch
)) != 0)
318 /* sequenceNo, type & length */
320 if ((err
= hash
->update(digestCtx
, data
)) != 0) /* content */
322 digest
.data
= innerDigestData
;
323 digest
.length
= hash
->digestSize
;
324 if ((err
= hash
->final(digestCtx
, digest
)) != 0) /* figure inner digest */
327 if ((err
= hash
->init(digestCtx
, ctx
)) != 0)
329 if ((err
= hash
->update(digestCtx
, secret
)) != 0) /* MAC secret */
331 scratch
.data
= SSLMACPad2
;
332 scratch
.length
= hash
->macPadSize
;
333 if ((err
= hash
->update(digestCtx
, scratch
)) != 0) /* pad2 */
335 if ((err
= hash
->update(digestCtx
, digest
)) != 0) /* inner digest */
337 if ((err
= hash
->final(digestCtx
, mac
)) != 0) /* figure the mac */
340 err
= SSLNoErr
; /* redundant, I know */
346 #define LOG_GEN_KEY 0
349 * On input, the following are valid:
351 * ClientHello.random[32]
352 * ServerHello.random[32]
355 * MD5(master_secret + SHA(`A' + master_secret +
356 * ServerHello.random +
357 * ClientHello.random)) +
358 * MD5(master_secret + SHA(`BB' + master_secret +
359 * ServerHello.random +
360 * ClientHello.random)) +
361 * MD5(master_secret + SHA(`CCC' + master_secret +
362 * ServerHello.random +
363 * ClientHello.random)) + [...];
365 static SSLErr
ssl3GenerateKeyMaterial (
366 SSLBuffer key
, // caller mallocs and specifies length of
367 // required key material here
371 UInt8 leaderData
[10]; /* Max of 10 hashes
372 * (* 16 bytes/hash = 160 bytes of key) */
373 UInt8 shaHashData
[20], md5HashData
[16];
374 SSLBuffer shaContext
, md5Context
;
376 int i
,j
,remaining
, satisfied
;
377 SSLBuffer leader
, masterSecret
, serverRandom
, clientRandom
, shaHash
, md5Hash
;
380 printf("GenerateKey: master ");
381 for(i
=0; i
<SSL_MASTER_SECRET_SIZE
; i
++) {
382 printf("%02X ", ctx
->masterSecret
[i
]);
387 assert(key
.length
<= 16 * sizeof(leaderData
));
389 leader
.data
= leaderData
;
390 masterSecret
.data
= ctx
->masterSecret
;
391 masterSecret
.length
= SSL_MASTER_SECRET_SIZE
;
392 serverRandom
.data
= ctx
->serverRandom
;
393 serverRandom
.length
= SSL_CLIENT_SRVR_RAND_SIZE
;
394 clientRandom
.data
= ctx
->clientRandom
;
395 clientRandom
.length
= SSL_CLIENT_SRVR_RAND_SIZE
;
396 shaHash
.data
= shaHashData
;
398 md5Hash
.data
= md5HashData
;
403 if ((err
= ReadyHash(&SSLHashMD5
, &md5Context
, ctx
)) != 0)
405 if ((err
= ReadyHash(&SSLHashSHA1
, &shaContext
, ctx
)) != 0)
408 keyProgress
= key
.data
;
409 remaining
= key
.length
;
411 for (i
= 0; remaining
> 0; ++i
)
412 { for (j
= 0; j
<= i
; j
++)
413 leaderData
[j
] = 0x41 + i
; /* 'A', 'BB', 'CCC', etc. */
416 if ((err
= SSLHashSHA1
.update(shaContext
, leader
)) != 0)
418 if ((err
= SSLHashSHA1
.update(shaContext
, masterSecret
)) != 0)
420 if ((err
= SSLHashSHA1
.update(shaContext
, serverRandom
)) != 0)
422 if ((err
= SSLHashSHA1
.update(shaContext
, clientRandom
)) != 0)
424 if ((err
= SSLHashSHA1
.final(shaContext
, shaHash
)) != 0)
426 if ((err
= SSLHashMD5
.update(md5Context
, masterSecret
)) != 0)
428 if ((err
= SSLHashMD5
.update(md5Context
, shaHash
)) != 0)
430 if ((err
= SSLHashMD5
.final(md5Context
, md5Hash
)) != 0)
435 satisfied
= remaining
;
436 memcpy(keyProgress
, md5HashData
, satisfied
);
437 remaining
-= satisfied
;
438 keyProgress
+= satisfied
;
441 /* at top of loop, this was done in ReadyHash() */
442 if ((err
= SSLHashMD5
.init(md5Context
, ctx
)) != 0)
444 if ((err
= SSLHashSHA1
.init(shaContext
, ctx
)) != 0)
449 assert(remaining
== 0 && keyProgress
== (key
.data
+ key
.length
));
452 SSLFreeBuffer(&md5Context
, &ctx
->sysCtx
);
453 SSLFreeBuffer(&shaContext
, &ctx
->sysCtx
);
456 printf("GenerateKey: DONE\n");
461 static SSLErr
ssl3GenerateExportKeyAndIv (
462 SSLContext
*ctx
, // clientRandom, serverRandom valid
463 const SSLBuffer clientWriteKey
,
464 const SSLBuffer serverWriteKey
,
465 SSLBuffer finalClientWriteKey
, // RETURNED, mallocd by caller
466 SSLBuffer finalServerWriteKey
, // RETURNED, mallocd by caller
467 SSLBuffer finalClientIV
, // RETURNED, mallocd by caller
468 SSLBuffer finalServerIV
) // RETURNED, mallocd by caller
471 SSLBuffer hashCtx
, serverRandom
, clientRandom
;
473 /* random blobs are 32 bytes */
474 serverRandom
.data
= ctx
->serverRandom
;
475 serverRandom
.length
= SSL_CLIENT_SRVR_RAND_SIZE
;
476 clientRandom
.data
= ctx
->clientRandom
;
477 clientRandom
.length
= SSL_CLIENT_SRVR_RAND_SIZE
;
479 if ((err
= SSLAllocBuffer(&hashCtx
, SSLHashMD5
.contextSize
, &ctx
->sysCtx
)) != 0)
481 /* client write key */
482 if ((err
= SSLHashMD5
.init(hashCtx
, ctx
)) != 0)
484 if ((err
= SSLHashMD5
.update(hashCtx
, clientWriteKey
)) != 0)
486 if ((err
= SSLHashMD5
.update(hashCtx
, clientRandom
)) != 0)
488 if ((err
= SSLHashMD5
.update(hashCtx
, serverRandom
)) != 0)
490 finalClientWriteKey
.length
= 16;
491 if ((err
= SSLHashMD5
.final(hashCtx
, finalClientWriteKey
)) != 0)
494 /* optional client IV */
495 if (ctx
->selectedCipherSpec
->cipher
->ivSize
> 0)
496 { if ((err
= SSLHashMD5
.init(hashCtx
, ctx
)) != 0)
498 if ((err
= SSLHashMD5
.update(hashCtx
, clientRandom
)) != 0)
500 if ((err
= SSLHashMD5
.update(hashCtx
, serverRandom
)) != 0)
502 finalClientIV
.length
= 16;
503 if ((err
= SSLHashMD5
.final(hashCtx
, finalClientIV
)) != 0)
507 /* server write key */
508 if ((err
= SSLHashMD5
.init(hashCtx
, ctx
)) != 0)
510 if ((err
= SSLHashMD5
.update(hashCtx
, serverWriteKey
)) != 0)
512 if ((err
= SSLHashMD5
.update(hashCtx
, serverRandom
)) != 0)
514 if ((err
= SSLHashMD5
.update(hashCtx
, clientRandom
)) != 0)
516 finalServerWriteKey
.length
= 16;
517 if ((err
= SSLHashMD5
.final(hashCtx
, finalServerWriteKey
)) != 0)
520 /* optional server IV */
521 if (ctx
->selectedCipherSpec
->cipher
->ivSize
> 0)
522 { if ((err
= SSLHashMD5
.init(hashCtx
, ctx
)) != 0)
524 if ((err
= SSLHashMD5
.update(hashCtx
, serverRandom
)) != 0)
526 if ((err
= SSLHashMD5
.update(hashCtx
, clientRandom
)) != 0)
528 finalServerIV
.length
= 16;
529 if ((err
= SSLHashMD5
.final(hashCtx
, finalServerIV
)) != 0)
535 SSLFreeBuffer(&hashCtx
, &ctx
->sysCtx
);
540 * On entry: clientRandom, serverRandom, preMasterSecret valid
541 * On return: masterSecret valid
543 static SSLErr
ssl3GenerateMasterSecret (
547 SSLBuffer shaState
, md5State
, clientRandom
,
548 serverRandom
, shaHash
, md5Hash
, leader
;
549 UInt8
*masterProgress
, shaHashData
[20], leaderData
[3];
552 md5State
.data
= shaState
.data
= 0;
553 if ((err
= SSLAllocBuffer(&md5State
, SSLHashMD5
.contextSize
, &ctx
->sysCtx
)) != 0)
555 if ((err
= SSLAllocBuffer(&shaState
, SSLHashSHA1
.contextSize
, &ctx
->sysCtx
)) != 0)
558 clientRandom
.data
= ctx
->clientRandom
;
559 clientRandom
.length
= SSL_CLIENT_SRVR_RAND_SIZE
;
560 serverRandom
.data
= ctx
->serverRandom
;
561 serverRandom
.length
= SSL_CLIENT_SRVR_RAND_SIZE
;
562 shaHash
.data
= shaHashData
;
565 masterProgress
= ctx
->masterSecret
;
567 for (i
= 1; i
<= 3; i
++)
568 { if ((err
= SSLHashMD5
.init(md5State
, ctx
)) != 0)
570 if ((err
= SSLHashSHA1
.init(shaState
, ctx
)) != 0)
573 leaderData
[0] = leaderData
[1] = leaderData
[2] = 0x40 + i
; /* 'A', 'B', etc. */
574 leader
.data
= leaderData
;
577 if ((err
= SSLHashSHA1
.update(shaState
, leader
)) != 0)
579 if ((err
= SSLHashSHA1
.update(shaState
, ctx
->preMasterSecret
)) != 0)
581 if ((err
= SSLHashSHA1
.update(shaState
, clientRandom
)) != 0)
583 if ((err
= SSLHashSHA1
.update(shaState
, serverRandom
)) != 0)
585 if ((err
= SSLHashSHA1
.final(shaState
, shaHash
)) != 0)
587 if ((err
= SSLHashMD5
.update(md5State
, ctx
->preMasterSecret
)) != 0)
589 if ((err
= SSLHashMD5
.update(md5State
, shaHash
)) != 0)
591 md5Hash
.data
= masterProgress
;
593 if ((err
= SSLHashMD5
.final(md5State
, md5Hash
)) != 0)
595 masterProgress
+= 16;
600 SSLFreeBuffer(&shaState
, &ctx
->sysCtx
);
601 SSLFreeBuffer(&md5State
, &ctx
->sysCtx
);
605 /* common routine to compute a Mac for finished message and cert verify message */
607 ssl3CalculateFinishedMessage(
609 SSLBuffer finished
, // mallocd by caller
610 SSLBuffer shaMsgState
, // running total
611 SSLBuffer md5MsgState
, // ditto
612 UInt32 senderID
) // optional, nonzero for finished message
615 SSLBuffer hash
, input
;
616 UInt8 sender
[4], md5Inner
[16], shaInner
[20];
618 // assert(finished.length == 36);
621 SSLEncodeInt(sender
, senderID
, 4);
624 if ((err
= SSLHashMD5
.update(md5MsgState
, input
)) != 0)
626 if ((err
= SSLHashSHA1
.update(shaMsgState
, input
)) != 0)
629 input
.data
= ctx
->masterSecret
;
630 input
.length
= SSL_MASTER_SECRET_SIZE
;
631 if ((err
= SSLHashMD5
.update(md5MsgState
, input
)) != 0)
633 if ((err
= SSLHashSHA1
.update(shaMsgState
, input
)) != 0)
635 input
.data
= SSLMACPad1
;
636 input
.length
= SSLHashMD5
.macPadSize
;
637 if ((err
= SSLHashMD5
.update(md5MsgState
, input
)) != 0)
639 input
.length
= SSLHashSHA1
.macPadSize
;
640 if ((err
= SSLHashSHA1
.update(shaMsgState
, input
)) != 0)
642 hash
.data
= md5Inner
;
644 if ((err
= SSLHashMD5
.final(md5MsgState
, hash
)) != 0)
646 hash
.data
= shaInner
;
648 if ((err
= SSLHashSHA1
.final(shaMsgState
, hash
)) != 0)
650 if ((err
= SSLHashMD5
.init(md5MsgState
, ctx
)) != 0)
652 if ((err
= SSLHashSHA1
.init(shaMsgState
, ctx
)) != 0)
654 input
.data
= ctx
->masterSecret
;
655 input
.length
= SSL_MASTER_SECRET_SIZE
;
656 if ((err
= SSLHashMD5
.update(md5MsgState
, input
)) != 0)
658 if ((err
= SSLHashSHA1
.update(shaMsgState
, input
)) != 0)
660 input
.data
= SSLMACPad2
;
661 input
.length
= SSLHashMD5
.macPadSize
;
662 if ((err
= SSLHashMD5
.update(md5MsgState
, input
)) != 0)
664 input
.length
= SSLHashSHA1
.macPadSize
;
665 if ((err
= SSLHashSHA1
.update(shaMsgState
, input
)) != 0)
667 input
.data
= md5Inner
;
669 if ((err
= SSLHashMD5
.update(md5MsgState
, input
)) != 0)
671 hash
.data
= finished
.data
;
673 if ((err
= SSLHashMD5
.final(md5MsgState
, hash
)) != 0)
675 input
.data
= shaInner
;
677 if ((err
= SSLHashSHA1
.update(shaMsgState
, input
)) != 0)
679 hash
.data
= finished
.data
+ 16;
681 if ((err
= SSLHashSHA1
.final(shaMsgState
, hash
)) != 0)
687 static SSLErr
ssl3ComputeFinishedMac (
689 SSLBuffer finished
, // output - mallocd by caller
690 SSLBuffer shaMsgState
, // clone of running digest of all handshake msgs
691 SSLBuffer md5MsgState
, // ditto
692 Boolean isServer
) // refers to message, not us
694 return ssl3CalculateFinishedMessage(ctx
, finished
, shaMsgState
, md5MsgState
,
695 isServer
? SSL_Finished_Sender_Server
: SSL_Finished_Sender_Client
);
698 static SSLErr
ssl3ComputeCertVfyMac (
700 SSLBuffer finished
, // output - mallocd by caller
701 SSLBuffer shaMsgState
, // clone of running digest of all handshake msgs
702 SSLBuffer md5MsgState
) // ditto
704 return ssl3CalculateFinishedMessage(ctx
, finished
, shaMsgState
, md5MsgState
, 0);
707 const SslTlsCallouts Ssl3Callouts
= {
713 ssl3GenerateKeyMaterial
,
714 ssl3GenerateExportKeyAndIv
,
715 ssl3GenerateMasterSecret
,
716 ssl3ComputeFinishedMac
,
717 ssl3ComputeCertVfyMac