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.
20 File: ssl3Callouts.cpp
22 Contains: SSLv3-specific routines for SslTlsCallouts.
24 Written by: Doug Mitchell
27 #include "sslMemory.h"
30 #include "sslDigests.h"
33 #include "sslAlertMessage.h"
39 * ssl3WriteRecord does not send alerts on failure, out of the assumption/fear
40 * that this might result in a loop (since sending an alert causes ssl3WriteRecord
43 * As far as I can tell, we can use this same routine for SSLv3 and TLSv1, as long
44 * as we're not trying to use the "variable length padding" feature of TLSv1.
45 * OpenSSL doesn't use that feature; for now, neither will we. Thus this routine
46 * is used for the SslTlsCallouts.writeRecord function for both protocols.
48 OSStatus
ssl3WriteRecord(
54 WaitingRecord
*out
, *queue
;
55 SSLBuffer buf
, payload
, mac
;
57 UInt16 payloadSize
,blockSize
;
59 switch(rec
.protocolVersion
) {
61 return SSL2WriteRecord(rec
, ctx
);
67 return errSSLInternal
;
69 assert(rec
.contents
.length
<= 16384);
72 /* Allocate a WaitingRecord to store our ready-to-send record in */
73 if ((err
= SSLAllocBuffer(buf
, sizeof(WaitingRecord
), ctx
)) != 0)
75 out
= (WaitingRecord
*)buf
.data
;
78 /* Allocate enough room for the transmitted record, which will be:
80 * encrypted contents +
82 * padding [block ciphers only] +
83 * padding length field (1 byte) [block ciphers only]
85 payloadSize
= (UInt16
) (rec
.contents
.length
+ ctx
->writeCipher
.macRef
->hash
->digestSize
);
86 blockSize
= ctx
->writeCipher
.symCipher
->blockSize
;
88 { padding
= blockSize
- (payloadSize
% blockSize
) - 1;
89 payloadSize
+= padding
+ 1;
92 if ((err
= SSLAllocBuffer(out
->data
, 5 + payloadSize
, ctx
)) != 0)
95 charPtr
= out
->data
.data
;
96 *(charPtr
++) = rec
.contentType
;
97 charPtr
= SSLEncodeInt(charPtr
, rec
.protocolVersion
, 2);
98 charPtr
= SSLEncodeInt(charPtr
, payloadSize
, 2);
100 /* Copy the contents into the output buffer */
101 memcpy(charPtr
, rec
.contents
.data
, rec
.contents
.length
);
102 payload
.data
= charPtr
;
103 payload
.length
= rec
.contents
.length
;
105 charPtr
+= rec
.contents
.length
;
106 /* MAC immediately follows data */
108 mac
.length
= ctx
->writeCipher
.macRef
->hash
->digestSize
;
109 charPtr
+= mac
.length
;
112 if (mac
.length
> 0) /* Optimize away null case */
114 assert(ctx
->sslTslCalls
!= NULL
);
115 if ((err
= ctx
->sslTslCalls
->computeMac(rec
.contentType
,
119 ctx
->writeCipher
.sequenceNum
,
124 /* Update payload to reflect encrypted data: contents, mac & padding */
125 payload
.length
= payloadSize
;
127 /* Fill in the padding bytes & padding length field with the padding value; the
128 * protocol only requires the last byte,
129 * but filling them all in avoids leaking data
131 if (ctx
->writeCipher
.symCipher
->blockSize
> 0)
132 for (i
= 1; i
<= padding
+ 1; ++i
)
133 payload
.data
[payload
.length
- i
] = padding
;
135 /* Encrypt the data */
136 if ((err
= ctx
->writeCipher
.symCipher
->encrypt(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
);
163 buf
.data
= (UInt8
*)out
;
164 buf
.length
= sizeof(WaitingRecord
);
165 SSLFreeBuffer(buf
, ctx
);
169 static OSStatus
ssl3DecryptRecord(
177 if ((ctx
->readCipher
.symCipher
->blockSize
> 0) &&
178 ((payload
->length
% ctx
->readCipher
.symCipher
->blockSize
) != 0))
179 { SSLFatalSessionAlert(SSL_AlertUnexpectedMsg
, ctx
);
180 return errSSLProtocol
;
183 /* Decrypt in place */
184 if ((err
= ctx
->readCipher
.symCipher
->decrypt(*payload
,
188 { SSLFatalSessionAlert(SSL_AlertDecryptionFail
, ctx
);
192 /* Locate content within decrypted payload */
193 content
.data
= payload
->data
;
194 content
.length
= payload
->length
- ctx
->readCipher
.macRef
->hash
->digestSize
;
195 if (ctx
->readCipher
.symCipher
->blockSize
> 0)
196 { /* padding can't be equal to or more than a block */
197 if (payload
->data
[payload
->length
- 1] >= ctx
->readCipher
.symCipher
->blockSize
)
198 { SSLFatalSessionAlert(SSL_AlertDecryptionFail
, ctx
);
199 sslErrorLog("DecryptSSLRecord: bad padding length (%d)\n",
200 (unsigned)payload
->data
[payload
->length
- 1]);
201 return errSSLDecryptionFail
;
203 content
.length
-= 1 + payload
->data
[payload
->length
- 1];
204 /* Remove block size padding */
207 /* Verify MAC on payload */
208 if (ctx
->readCipher
.macRef
->hash
->digestSize
> 0)
209 /* Optimize away MAC for null case */
210 if ((err
= SSLVerifyMac(type
, content
,
211 payload
->data
+ content
.length
, ctx
)) != 0)
212 { SSLFatalSessionAlert(SSL_AlertBadRecordMac
, ctx
);
213 return errSSLBadRecordMac
;
216 *payload
= content
; /* Modify payload buffer to indicate content length */
221 /* initialize a per-CipherContext HashHmacContext for use in MACing each record */
222 static OSStatus
ssl3InitMac (
223 CipherContext
*cipherCtx
, // macRef, macSecret valid on entry
224 // macCtx valid on return
227 const HashReference
*hash
;
231 assert(cipherCtx
->macRef
!= NULL
);
232 hash
= cipherCtx
->macRef
->hash
;
233 assert(hash
!= NULL
);
235 hashCtx
= &cipherCtx
->macCtx
.hashCtx
;
236 if(hashCtx
->data
!= NULL
) {
237 SSLFreeBuffer(*hashCtx
, ctx
);
239 serr
= SSLAllocBuffer(*hashCtx
, hash
->contextSize
, ctx
);
246 static OSStatus
ssl3FreeMac (
247 CipherContext
*cipherCtx
)
251 assert(cipherCtx
!= NULL
);
252 /* this can be called on a completely zeroed out CipherContext... */
253 if(cipherCtx
->macRef
== NULL
) {
256 hashCtx
= &cipherCtx
->macCtx
.hashCtx
;
257 if(hashCtx
->data
!= NULL
) {
258 sslFree(hashCtx
->data
);
259 hashCtx
->data
= NULL
;
265 static OSStatus
ssl3ComputeMac (
268 SSLBuffer mac
, // caller mallocs data
269 CipherContext
*cipherCtx
, // assumes macCtx, macRef
274 UInt8 innerDigestData
[SSL_MAX_DIGEST_LEN
];
275 UInt8 scratchData
[11], *charPtr
;
276 SSLBuffer digest
, digestCtx
, scratch
;
279 const HashReference
*hash
;
281 assert(cipherCtx
!= NULL
);
282 assert(cipherCtx
->macRef
!= NULL
);
283 hash
= cipherCtx
->macRef
->hash
;
284 assert(hash
!= NULL
);
285 assert(hash
->macPadSize
<= MAX_MAC_PADDING
);
286 assert(hash
->digestSize
<= SSL_MAX_DIGEST_LEN
);
287 digestCtx
= cipherCtx
->macCtx
.hashCtx
; // may be NULL, for null cipher
288 secret
.data
= cipherCtx
->macSecret
;
289 secret
.length
= hash
->digestSize
;
291 /* init'd early in SSLNewContext() */
292 assert(SSLMACPad1
[0] == 0x36 && SSLMACPad2
[0] == 0x5C);
295 * MAC = hash( MAC_write_secret + pad_2 +
296 * hash( MAC_write_secret + pad_1 + seq_num + type +
300 if ((err
= hash
->init(digestCtx
, ctx
)) != 0)
302 if ((err
= hash
->update(digestCtx
, secret
)) != 0) /* MAC secret */
304 scratch
.data
= (UInt8
*)SSLMACPad1
;
305 scratch
.length
= hash
->macPadSize
;
306 if ((err
= hash
->update(digestCtx
, scratch
)) != 0) /* pad1 */
308 charPtr
= scratchData
;
309 charPtr
= SSLEncodeUInt64(charPtr
, seqNo
);
311 charPtr
= SSLEncodeInt(charPtr
, data
.length
, 2);
312 scratch
.data
= scratchData
;
314 assert(charPtr
= scratchData
+11);
315 if ((err
= hash
->update(digestCtx
, scratch
)) != 0)
316 /* sequenceNo, type & length */
318 if ((err
= hash
->update(digestCtx
, data
)) != 0) /* content */
320 digest
.data
= innerDigestData
;
321 digest
.length
= hash
->digestSize
;
322 if ((err
= hash
->final(digestCtx
, digest
)) != 0) /* figure inner digest */
325 if ((err
= hash
->init(digestCtx
, ctx
)) != 0)
327 if ((err
= hash
->update(digestCtx
, secret
)) != 0) /* MAC secret */
329 scratch
.data
= (UInt8
*)SSLMACPad2
;
330 scratch
.length
= hash
->macPadSize
;
331 if ((err
= hash
->update(digestCtx
, scratch
)) != 0) /* pad2 */
333 if ((err
= hash
->update(digestCtx
, digest
)) != 0) /* inner digest */
335 if ((err
= hash
->final(digestCtx
, mac
)) != 0) /* figure the mac */
338 err
= noErr
; /* redundant, I know */
344 #define LOG_GEN_KEY 0
347 * On input, the following are valid:
349 * ClientHello.random[32]
350 * ServerHello.random[32]
353 * MD5(master_secret + SHA(`A' + master_secret +
354 * ServerHello.random +
355 * ClientHello.random)) +
356 * MD5(master_secret + SHA(`BB' + master_secret +
357 * ServerHello.random +
358 * ClientHello.random)) +
359 * MD5(master_secret + SHA(`CCC' + master_secret +
360 * ServerHello.random +
361 * ClientHello.random)) + [...];
363 static OSStatus
ssl3GenerateKeyMaterial (
364 SSLBuffer key
, // caller mallocs and specifies length of
365 // required key material here
369 UInt8 leaderData
[10]; /* Max of 10 hashes
370 * (* 16 bytes/hash = 160 bytes of key) */
371 UInt8 shaHashData
[20], md5HashData
[16];
372 SSLBuffer shaContext
, md5Context
;
374 int i
,j
,remaining
, satisfied
;
375 SSLBuffer leader
, masterSecret
, serverRandom
, clientRandom
, shaHash
, md5Hash
;
378 printf("GenerateKey: master ");
379 for(i
=0; i
<SSL_MASTER_SECRET_SIZE
; i
++) {
380 printf("%02X ", ctx
->masterSecret
[i
]);
385 assert(key
.length
<= 16 * sizeof(leaderData
));
387 leader
.data
= leaderData
;
388 masterSecret
.data
= ctx
->masterSecret
;
389 masterSecret
.length
= SSL_MASTER_SECRET_SIZE
;
390 serverRandom
.data
= ctx
->serverRandom
;
391 serverRandom
.length
= SSL_CLIENT_SRVR_RAND_SIZE
;
392 clientRandom
.data
= ctx
->clientRandom
;
393 clientRandom
.length
= SSL_CLIENT_SRVR_RAND_SIZE
;
394 shaHash
.data
= shaHashData
;
396 md5Hash
.data
= md5HashData
;
401 if ((err
= ReadyHash(SSLHashMD5
, md5Context
, ctx
)) != 0)
403 if ((err
= ReadyHash(SSLHashSHA1
, shaContext
, ctx
)) != 0)
406 keyProgress
= key
.data
;
407 remaining
= key
.length
;
409 for (i
= 0; remaining
> 0; ++i
)
410 { for (j
= 0; j
<= i
; j
++)
411 leaderData
[j
] = 0x41 + i
; /* 'A', 'BB', 'CCC', etc. */
414 if ((err
= SSLHashSHA1
.update(shaContext
, leader
)) != 0)
416 if ((err
= SSLHashSHA1
.update(shaContext
, masterSecret
)) != 0)
418 if ((err
= SSLHashSHA1
.update(shaContext
, serverRandom
)) != 0)
420 if ((err
= SSLHashSHA1
.update(shaContext
, clientRandom
)) != 0)
422 if ((err
= SSLHashSHA1
.final(shaContext
, shaHash
)) != 0)
424 if ((err
= SSLHashMD5
.update(md5Context
, masterSecret
)) != 0)
426 if ((err
= SSLHashMD5
.update(md5Context
, shaHash
)) != 0)
428 if ((err
= SSLHashMD5
.final(md5Context
, md5Hash
)) != 0)
433 satisfied
= remaining
;
434 memcpy(keyProgress
, md5HashData
, satisfied
);
435 remaining
-= satisfied
;
436 keyProgress
+= satisfied
;
439 /* at top of loop, this was done in ReadyHash() */
440 if ((err
= SSLHashMD5
.init(md5Context
, ctx
)) != 0)
442 if ((err
= SSLHashSHA1
.init(shaContext
, ctx
)) != 0)
447 assert(remaining
== 0 && keyProgress
== (key
.data
+ key
.length
));
450 SSLFreeBuffer(md5Context
, ctx
);
451 SSLFreeBuffer(shaContext
, ctx
);
454 printf("GenerateKey: DONE\n");
459 static OSStatus
ssl3GenerateExportKeyAndIv (
460 SSLContext
*ctx
, // clientRandom, serverRandom valid
461 const SSLBuffer clientWriteKey
,
462 const SSLBuffer serverWriteKey
,
463 SSLBuffer finalClientWriteKey
, // RETURNED, mallocd by caller
464 SSLBuffer finalServerWriteKey
, // RETURNED, mallocd by caller
465 SSLBuffer finalClientIV
, // RETURNED, mallocd by caller
466 SSLBuffer finalServerIV
) // RETURNED, mallocd by caller
469 SSLBuffer hashCtx
, serverRandom
, clientRandom
;
471 /* random blobs are 32 bytes */
472 serverRandom
.data
= ctx
->serverRandom
;
473 serverRandom
.length
= SSL_CLIENT_SRVR_RAND_SIZE
;
474 clientRandom
.data
= ctx
->clientRandom
;
475 clientRandom
.length
= SSL_CLIENT_SRVR_RAND_SIZE
;
477 if ((err
= SSLAllocBuffer(hashCtx
, SSLHashMD5
.contextSize
, ctx
)) != 0)
479 /* client write key */
480 if ((err
= SSLHashMD5
.init(hashCtx
, ctx
)) != 0)
482 if ((err
= SSLHashMD5
.update(hashCtx
, clientWriteKey
)) != 0)
484 if ((err
= SSLHashMD5
.update(hashCtx
, clientRandom
)) != 0)
486 if ((err
= SSLHashMD5
.update(hashCtx
, serverRandom
)) != 0)
488 finalClientWriteKey
.length
= 16;
489 if ((err
= SSLHashMD5
.final(hashCtx
, finalClientWriteKey
)) != 0)
492 /* optional client IV */
493 if (ctx
->selectedCipherSpec
->cipher
->ivSize
> 0)
494 { if ((err
= SSLHashMD5
.init(hashCtx
, ctx
)) != 0)
496 if ((err
= SSLHashMD5
.update(hashCtx
, clientRandom
)) != 0)
498 if ((err
= SSLHashMD5
.update(hashCtx
, serverRandom
)) != 0)
500 finalClientIV
.length
= 16;
501 if ((err
= SSLHashMD5
.final(hashCtx
, finalClientIV
)) != 0)
505 /* server write key */
506 if ((err
= SSLHashMD5
.init(hashCtx
, ctx
)) != 0)
508 if ((err
= SSLHashMD5
.update(hashCtx
, serverWriteKey
)) != 0)
510 if ((err
= SSLHashMD5
.update(hashCtx
, serverRandom
)) != 0)
512 if ((err
= SSLHashMD5
.update(hashCtx
, clientRandom
)) != 0)
514 finalServerWriteKey
.length
= 16;
515 if ((err
= SSLHashMD5
.final(hashCtx
, finalServerWriteKey
)) != 0)
518 /* optional server IV */
519 if (ctx
->selectedCipherSpec
->cipher
->ivSize
> 0)
520 { if ((err
= SSLHashMD5
.init(hashCtx
, ctx
)) != 0)
522 if ((err
= SSLHashMD5
.update(hashCtx
, serverRandom
)) != 0)
524 if ((err
= SSLHashMD5
.update(hashCtx
, clientRandom
)) != 0)
526 finalServerIV
.length
= 16;
527 if ((err
= SSLHashMD5
.final(hashCtx
, finalServerIV
)) != 0)
533 SSLFreeBuffer(hashCtx
, ctx
);
538 * On entry: clientRandom, serverRandom, preMasterSecret valid
539 * On return: masterSecret valid
541 static OSStatus
ssl3GenerateMasterSecret (
545 SSLBuffer shaState
, md5State
, clientRandom
,
546 serverRandom
, shaHash
, md5Hash
, leader
;
547 UInt8
*masterProgress
, shaHashData
[20], leaderData
[3];
550 md5State
.data
= shaState
.data
= 0;
551 if ((err
= SSLAllocBuffer(md5State
, SSLHashMD5
.contextSize
, ctx
)) != 0)
553 if ((err
= SSLAllocBuffer(shaState
, SSLHashSHA1
.contextSize
, ctx
)) != 0)
556 clientRandom
.data
= ctx
->clientRandom
;
557 clientRandom
.length
= SSL_CLIENT_SRVR_RAND_SIZE
;
558 serverRandom
.data
= ctx
->serverRandom
;
559 serverRandom
.length
= SSL_CLIENT_SRVR_RAND_SIZE
;
560 shaHash
.data
= shaHashData
;
563 masterProgress
= ctx
->masterSecret
;
565 for (i
= 1; i
<= 3; i
++)
566 { if ((err
= SSLHashMD5
.init(md5State
, ctx
)) != 0)
568 if ((err
= SSLHashSHA1
.init(shaState
, ctx
)) != 0)
571 leaderData
[0] = leaderData
[1] = leaderData
[2] = 0x40 + i
; /* 'A', 'B', etc. */
572 leader
.data
= leaderData
;
575 if ((err
= SSLHashSHA1
.update(shaState
, leader
)) != 0)
577 if ((err
= SSLHashSHA1
.update(shaState
, ctx
->preMasterSecret
)) != 0)
579 if ((err
= SSLHashSHA1
.update(shaState
, clientRandom
)) != 0)
581 if ((err
= SSLHashSHA1
.update(shaState
, serverRandom
)) != 0)
583 if ((err
= SSLHashSHA1
.final(shaState
, shaHash
)) != 0)
585 if ((err
= SSLHashMD5
.update(md5State
, ctx
->preMasterSecret
)) != 0)
587 if ((err
= SSLHashMD5
.update(md5State
, shaHash
)) != 0)
589 md5Hash
.data
= masterProgress
;
591 if ((err
= SSLHashMD5
.final(md5State
, md5Hash
)) != 0)
593 masterProgress
+= 16;
598 SSLFreeBuffer(shaState
, ctx
);
599 SSLFreeBuffer(md5State
, ctx
);
603 /* common routine to compute a Mac for finished message and cert verify message */
605 ssl3CalculateFinishedMessage(
607 SSLBuffer finished
, // mallocd by caller
608 SSLBuffer shaMsgState
, // running total
609 SSLBuffer md5MsgState
, // ditto
610 UInt32 senderID
) // optional, nonzero for finished message
613 SSLBuffer hash
, input
;
614 UInt8 sender
[4], md5Inner
[16], shaInner
[20];
616 // assert(finished.length == 36);
619 SSLEncodeInt(sender
, senderID
, 4);
622 if ((err
= SSLHashMD5
.update(md5MsgState
, input
)) != 0)
624 if ((err
= SSLHashSHA1
.update(shaMsgState
, input
)) != 0)
627 input
.data
= ctx
->masterSecret
;
628 input
.length
= SSL_MASTER_SECRET_SIZE
;
629 if ((err
= SSLHashMD5
.update(md5MsgState
, input
)) != 0)
631 if ((err
= SSLHashSHA1
.update(shaMsgState
, input
)) != 0)
633 input
.data
= (UInt8
*)SSLMACPad1
;
634 input
.length
= SSLHashMD5
.macPadSize
;
635 if ((err
= SSLHashMD5
.update(md5MsgState
, input
)) != 0)
637 input
.length
= SSLHashSHA1
.macPadSize
;
638 if ((err
= SSLHashSHA1
.update(shaMsgState
, input
)) != 0)
640 hash
.data
= md5Inner
;
642 if ((err
= SSLHashMD5
.final(md5MsgState
, hash
)) != 0)
644 hash
.data
= shaInner
;
646 if ((err
= SSLHashSHA1
.final(shaMsgState
, hash
)) != 0)
648 if ((err
= SSLHashMD5
.init(md5MsgState
, ctx
)) != 0)
650 if ((err
= SSLHashSHA1
.init(shaMsgState
, ctx
)) != 0)
652 input
.data
= ctx
->masterSecret
;
653 input
.length
= SSL_MASTER_SECRET_SIZE
;
654 if ((err
= SSLHashMD5
.update(md5MsgState
, input
)) != 0)
656 if ((err
= SSLHashSHA1
.update(shaMsgState
, input
)) != 0)
658 input
.data
= (UInt8
*)SSLMACPad2
;
659 input
.length
= SSLHashMD5
.macPadSize
;
660 if ((err
= SSLHashMD5
.update(md5MsgState
, input
)) != 0)
662 input
.length
= SSLHashSHA1
.macPadSize
;
663 if ((err
= SSLHashSHA1
.update(shaMsgState
, input
)) != 0)
665 input
.data
= md5Inner
;
667 if ((err
= SSLHashMD5
.update(md5MsgState
, input
)) != 0)
669 hash
.data
= finished
.data
;
671 if ((err
= SSLHashMD5
.final(md5MsgState
, hash
)) != 0)
673 input
.data
= shaInner
;
675 if ((err
= SSLHashSHA1
.update(shaMsgState
, input
)) != 0)
677 hash
.data
= finished
.data
+ 16;
679 if ((err
= SSLHashSHA1
.final(shaMsgState
, hash
)) != 0)
685 static OSStatus
ssl3ComputeFinishedMac (
687 SSLBuffer finished
, // output - mallocd by caller
688 SSLBuffer shaMsgState
, // clone of running digest of all handshake msgs
689 SSLBuffer md5MsgState
, // ditto
690 Boolean isServer
) // refers to message, not us
692 return ssl3CalculateFinishedMessage(ctx
, finished
, shaMsgState
, md5MsgState
,
693 isServer
? SSL_Finished_Sender_Server
: SSL_Finished_Sender_Client
);
696 static OSStatus
ssl3ComputeCertVfyMac (
698 SSLBuffer finished
, // output - mallocd by caller
699 SSLBuffer shaMsgState
, // clone of running digest of all handshake msgs
700 SSLBuffer md5MsgState
) // ditto
702 return ssl3CalculateFinishedMessage(ctx
, finished
, shaMsgState
, md5MsgState
, 0);
705 const SslTlsCallouts Ssl3Callouts
= {
711 ssl3GenerateKeyMaterial
,
712 ssl3GenerateExportKeyAndIv
,
713 ssl3GenerateMasterSecret
,
714 ssl3ComputeFinishedMac
,
715 ssl3ComputeCertVfyMac