--- /dev/null
+/*
+ * Copyright (c) 2002 Apple Computer, Inc. All Rights Reserved.
+ *
+ * The contents of this file constitute Original Code as defined in and are
+ * subject to the Apple Public Source License Version 1.2 (the 'License').
+ * You may not use this file except in compliance with the License. Please obtain
+ * a copy of the License at http://www.apple.com/publicsource and read it before
+ * using this file.
+ *
+ * This Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS
+ * OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, INCLUDING WITHOUT
+ * LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
+ * PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. Please see the License for the
+ * specific language governing rights and limitations under the License.
+ */
+
+
+/*
+ File: tls1Callouts.c
+
+ Contains: TLSv1-specific routines for SslTlsCallouts.
+
+ Written by: Doug Mitchell
+*/
+
+#include "tls_ssl.h"
+#include "sslMemory.h"
+#include "sslUtils.h"
+#include "sslDigests.h"
+#include "sslAlertMessage.h"
+#include "sslDebug.h"
+#include <assert.h>
+#include <strings.h>
+
+#define TLS_ENC_DEBUG 0
+#if TLS_ENC_DEBUG
+#define tlsDebug(format, args...) printf(format , ## args)
+static void tlsDump(const char *name, void *b, unsigned len)
+{
+ unsigned char *cp = (unsigned char *)b;
+ unsigned i, dex;
+
+ printf("%s\n", name);
+ for(dex=0; dex<len; dex++) {
+ i = cp[dex];
+ printf("%02X ", i);
+ if((dex % 16) == 15) {
+ printf("\n");
+ }
+ }
+ printf("\n");
+}
+
+#else
+#define tlsDebug(s, ...)
+#define tlsDump(name, b, len)
+#endif /* TLS_ENC_DEBUG */
+
+#pragma *** PRF label strings ***
+/*
+ * Note we could optimize away a bunch of mallocs and frees if we, like openSSL,
+ * just mallocd buffers for inputs to SSLInternal_PRF() on the stack,
+ * with "known" max values for all of the inputs.
+ *
+ * At least we hard-code string lengths here instead of calling strlen at runtime...
+ */
+#define PLS_MASTER_SECRET "master secret"
+#define PLS_MASTER_SECRET_LEN 13
+#define PLS_KEY_EXPAND "key expansion"
+#define PLS_KEY_EXPAND_LEN 13
+#define PLS_CLIENT_FINISH "client finished"
+#define PLS_CLIENT_FINISH_LEN 15
+#define PLS_SERVER_FINISH "server finished"
+#define PLS_SERVER_FINISH_LEN 15
+#define PLS_EXPORT_CLIENT_WRITE "client write key"
+#define PLS_EXPORT_CLIENT_WRITE_LEN 16
+#define PLS_EXPORT_SERVER_WRITE "server write key"
+#define PLS_EXPORT_SERVER_WRITE_LEN 16
+#define PLS_EXPORT_IV_BLOCK "IV block"
+#define PLS_EXPORT_IV_BLOCK_LEN 8
+
+#pragma mark *** private functions ***
+
+/*
+ * P_Hash function defined in RFC2246, section 5.
+ */
+static OSStatus tlsPHash(
+ SSLContext *ctx,
+ const HMACReference *hmac, // &TlsHmacSHA1, TlsHmacMD5
+ const unsigned char *secret,
+ unsigned secretLen,
+ unsigned char *seed,
+ unsigned seedLen,
+ unsigned char *out, // mallocd by caller, size >= outLen
+ unsigned outLen) // desired output size
+{
+ unsigned char aSubI[TLS_HMAC_MAX_SIZE]; /* A(i) */
+ unsigned char digest[TLS_HMAC_MAX_SIZE];
+ HMACContextRef hmacCtx;
+ OSStatus serr;
+ unsigned digestLen = hmac->macSize;
+
+ serr = hmac->alloc(hmac, ctx, secret, secretLen, &hmacCtx);
+ if(serr) {
+ return serr;
+ }
+
+ /* A(0) = seed */
+ /* A(1) := HMAC_hash(secret, seed) */
+ serr = hmac->hmac(hmacCtx, seed, seedLen, aSubI, &digestLen);
+ if(serr) {
+ goto fail;
+ }
+ assert(digestLen = hmac->macSize);
+
+ /* starting at loopNum 1... */
+ for (;;) {
+ /*
+ * This loop's chunk = HMAC_hash(secret, A(loopNum) + seed))
+ */
+ serr = hmac->init(hmacCtx);
+ if(serr) {
+ break;
+ }
+ serr = hmac->update(hmacCtx, aSubI, digestLen);
+ if(serr) {
+ break;
+ }
+ serr = hmac->update(hmacCtx, seed, seedLen);
+ if(serr) {
+ break;
+ }
+ serr = hmac->final(hmacCtx, digest, &digestLen);
+ if(serr) {
+ break;
+ }
+ assert(digestLen = hmac->macSize);
+
+ if(outLen <= digestLen) {
+ /* last time, possible partial digest */
+ memmove(out, digest, outLen);
+ break;
+ }
+
+ memmove(out, digest, digestLen);
+ out += digestLen;
+ outLen -= digestLen;
+
+ /*
+ * A(i) = HMAC_hash(secret, A(i-1))
+ * Note there is a possible optimization involving obtaining this
+ * hmac by cloning the state of hmacCtx above after updating with
+ * aSubI, and getting the final version of that here. However CDSA
+ * does not support cloning of a MAC context (only for digest contexts).
+ */
+ serr = hmac->hmac(hmacCtx, aSubI, digestLen,
+ aSubI, &digestLen);
+ if(serr) {
+ break;
+ }
+ assert(digestLen = hmac->macSize);
+ }
+fail:
+ hmac->free(hmacCtx);
+ memset(aSubI, 0, TLS_HMAC_MAX_SIZE);
+ memset(digest, 0, TLS_HMAC_MAX_SIZE);
+ return serr;
+}
+
+/*
+ * The TLS pseudorandom function, defined in RFC2246, section 5.
+ * This takes as its input a secret block, a label, and a seed, and produces
+ * a caller-specified length of pseudorandom data.
+ *
+ * Optimization TBD: make label optional, avoid malloc and two copies if it's
+ * not there, so callers can take advantage of fixed-size seeds.
+ */
+OSStatus SSLInternal_PRF(
+ SSLContext *ctx,
+ const void *vsecret,
+ size_t secretLen,
+ const void *label, // optional, NULL implies that seed contains
+ // the label
+ size_t labelLen,
+ const void *seed,
+ size_t seedLen,
+ void *vout, // mallocd by caller, length >= outLen
+ size_t outLen)
+{
+ OSStatus serr = errSSLInternal;
+ const unsigned char *S1, *S2; // the two seeds
+ unsigned sLen; // effective length of each seed
+ unsigned char *labelSeed = NULL; // label + seed, passed to tlsPHash
+ unsigned labelSeedLen;
+ unsigned char *tmpOut = NULL; // output of P_SHA1
+ unsigned i;
+ const unsigned char *secret = (const unsigned char *)vsecret;
+
+ /* two seeds for tlsPHash */
+ sLen = secretLen / 2; // for partitioning
+ S1 = secret;
+ S2 = &secret[sLen];
+ sLen += (secretLen & 1); // secret length odd, increment effective size
+
+ if(label != NULL) {
+ /* concatenate label and seed */
+ labelSeedLen = labelLen + seedLen;
+ labelSeed = (unsigned char *)sslMalloc(labelSeedLen);
+ if(labelSeed == NULL) {
+ return memFullErr;
+ }
+ memmove(labelSeed, label, labelLen);
+ memmove(labelSeed + labelLen, seed, seedLen);
+ }
+ else {
+ /* fast track - just use seed as is */
+ labelSeed = (unsigned char *)seed;
+ labelSeedLen = seedLen;
+ }
+
+ /* temporary output for SHA1, to be XORd with MD5 */
+ unsigned char *out = (unsigned char *)vout;
+ tmpOut = (unsigned char *)sslMalloc(outLen);
+ if(tmpOut == NULL) {
+ serr = memFullErr;
+ goto fail;
+ }
+ serr = tlsPHash(ctx, &TlsHmacMD5, S1, sLen, labelSeed, labelSeedLen,
+ out, outLen);
+ if(serr) {
+ goto fail;
+ }
+ serr = tlsPHash(ctx, &TlsHmacSHA1, S2, sLen, labelSeed, labelSeedLen,
+ tmpOut, outLen);
+ if(serr) {
+ goto fail;
+ }
+
+ /* XOR together to get final result */
+ for(i=0; i<outLen; i++) {
+ out[i] ^= tmpOut[i];
+ }
+ serr = noErr;
+
+fail:
+ if((labelSeed != NULL) && (label != NULL)) {
+ sslFree(labelSeed);
+ }
+ if(tmpOut != NULL) {
+ sslFree(tmpOut);
+ }
+ return serr;
+}
+
+/* not needed; encrypt/encode is the same for both protocols as long as
+ * we don't use the "variable length padding" feature. */
+#if 0
+static OSStatus tls1WriteRecord(
+ SSLRecord rec,
+ SSLContext *ctx)
+{
+ assert(0);
+ return unimpErr;
+}
+#endif
+
+static OSStatus tls1DecryptRecord(
+ UInt8 type,
+ SSLBuffer *payload,
+ SSLContext *ctx)
+{
+ OSStatus err;
+ SSLBuffer content;
+
+ if ((ctx->readCipher.symCipher->blockSize > 0) &&
+ ((payload->length % ctx->readCipher.symCipher->blockSize) != 0)) {
+ SSLFatalSessionAlert(SSL_AlertUnexpectedMsg, ctx);
+ return errSSLProtocol;
+ }
+
+ /* Decrypt in place */
+ if ((err = ctx->readCipher.symCipher->decrypt(*payload,
+ *payload,
+ &ctx->readCipher,
+ ctx)) != 0)
+ { SSLFatalSessionAlert(SSL_AlertCloseNotify, ctx);
+ return err;
+ }
+
+ /* Locate content within decrypted payload */
+ content.data = payload->data;
+ content.length = payload->length - ctx->readCipher.macRef->hash->digestSize;
+ if (ctx->readCipher.symCipher->blockSize > 0) {
+ /* for TLSv1, padding can be anywhere from 0 to 255 bytes */
+ UInt8 padSize = payload->data[payload->length - 1];
+ UInt8 *padChars;
+
+ /* verify that all padding bytes are equal - WARNING - OpenSSL code
+ * has a special case here dealing with some kind of bug related to
+ * even size packets...beware... */
+ if(padSize > payload->length) {
+ SSLFatalSessionAlert(SSL_AlertUnexpectedMsg, ctx);
+ sslErrorLog("tls1DecryptRecord: bad padding length (%d)\n",
+ (unsigned)payload->data[payload->length - 1]);
+ return errSSLProtocol;
+ }
+ padChars = payload->data + payload->length - padSize;
+ while(padChars < (payload->data + payload->length)) {
+ if(*padChars++ != padSize) {
+ SSLFatalSessionAlert(SSL_AlertUnexpectedMsg, ctx);
+ sslErrorLog("tls1DecryptRecord: bad padding value\n");
+ return errSSLProtocol;
+ }
+ }
+ /* Remove block size padding and its one-byte length */
+ content.length -= (1 + padSize);
+ }
+
+ /* Verify MAC on payload */
+ if (ctx->readCipher.macRef->hash->digestSize > 0)
+ /* Optimize away MAC for null case */
+ if ((err = SSLVerifyMac(type, content,
+ payload->data + content.length, ctx)) != 0)
+ { SSLFatalSessionAlert(SSL_AlertBadRecordMac, ctx);
+ return err;
+ }
+
+ *payload = content; /* Modify payload buffer to indicate content length */
+
+ return noErr;
+}
+
+/* initialize a per-CipherContext HashHmacContext for use in MACing each record */
+static OSStatus tls1InitMac (
+ CipherContext *cipherCtx, // macRef, macSecret valid on entry
+ // macCtx valid on return
+ SSLContext *ctx)
+{
+ const HMACReference *hmac;
+ OSStatus serr;
+
+ assert(cipherCtx->macRef != NULL);
+ hmac = cipherCtx->macRef->hmac;
+ assert(hmac != NULL);
+
+ if(cipherCtx->macCtx.hmacCtx != NULL) {
+ hmac->free(cipherCtx->macCtx.hmacCtx);
+ cipherCtx->macCtx.hmacCtx = NULL;
+ }
+ serr = hmac->alloc(hmac, ctx, cipherCtx->macSecret,
+ cipherCtx->macRef->hmac->macSize, &cipherCtx->macCtx.hmacCtx);
+
+ /* mac secret now stored in macCtx.hmacCtx, delete it from cipherCtx */
+ memset(cipherCtx->macSecret, 0, sizeof(cipherCtx->macSecret));
+ return serr;
+}
+
+static OSStatus tls1FreeMac (
+ CipherContext *cipherCtx)
+{
+ /* this can be called on a completely zeroed out CipherContext... */
+ if(cipherCtx->macRef == NULL) {
+ return noErr;
+ }
+ assert(cipherCtx->macRef->hmac != NULL);
+
+ if(cipherCtx->macCtx.hmacCtx != NULL) {
+ cipherCtx->macRef->hmac->free(cipherCtx->macCtx.hmacCtx);
+ cipherCtx->macCtx.hmacCtx = NULL;
+ }
+ return noErr;
+}
+
+/*
+ * mac = HMAC_hash(MAC_write_secret, seq_num + TLSCompressed.type +
+ * TLSCompressed.version + TLSCompressed.length +
+ * TLSCompressed.fragment));
+ */
+
+/* sequence, type, version, length */
+#define HDR_LENGTH (8 + 1 + 2 + 2)
+OSStatus tls1ComputeMac (
+ UInt8 type,
+ SSLBuffer data,
+ SSLBuffer mac, // caller mallocs data
+ CipherContext *cipherCtx, // assumes macCtx, macRef
+ sslUint64 seqNo,
+ SSLContext *ctx)
+{
+ unsigned char hdr[HDR_LENGTH];
+ unsigned char *p;
+ HMACContextRef hmacCtx;
+ OSStatus serr;
+ const HMACReference *hmac;
+ unsigned macLength;
+
+ assert(cipherCtx != NULL);
+ assert(cipherCtx->macRef != NULL);
+ hmac = cipherCtx->macRef->hmac;
+ assert(hmac != NULL);
+ hmacCtx = cipherCtx->macCtx.hmacCtx; // may be NULL, for null cipher
+
+ serr = hmac->init(hmacCtx);
+ if(serr) {
+ goto fail;
+ }
+ p = SSLEncodeUInt64(hdr, seqNo);
+ *p++ = type;
+ *p++ = TLS_Version_1_0 >> 8;
+ *p++ = TLS_Version_1_0 & 0xff;
+ *p++ = data.length >> 8;
+ *p = data.length & 0xff;
+ serr = hmac->update(hmacCtx, hdr, HDR_LENGTH);
+ if(serr) {
+ goto fail;
+ }
+ serr = hmac->update(hmacCtx, data.data, data.length);
+ if(serr) {
+ goto fail;
+ }
+ macLength = mac.length;
+ serr = hmac->final(hmacCtx, mac.data, &macLength);
+ if(serr) {
+ goto fail;
+ }
+ mac.length = macLength;
+fail:
+ return serr;
+}
+
+/*
+ * On input, the following are valid:
+ * MasterSecret[48]
+ * ClientHello.random[32]
+ * ServerHello.random[32]
+ *
+ * key_block = PRF(SecurityParameters.master_secret,
+ * "key expansion",
+ * SecurityParameters.server_random +
+ * SecurityParameters.client_random);
+ */
+
+#define GKM_SEED_LEN (PLS_KEY_EXPAND_LEN + (2 * SSL_CLIENT_SRVR_RAND_SIZE))
+
+OSStatus tls1GenerateKeyMaterial (
+ SSLBuffer key, // caller mallocs and specifies length of
+ // required key material here
+ SSLContext *ctx)
+{
+ unsigned char seedBuf[GKM_SEED_LEN];
+ OSStatus serr;
+
+ /* use optimized label-less PRF */
+ memmove(seedBuf, PLS_KEY_EXPAND, PLS_KEY_EXPAND_LEN);
+ memmove(seedBuf + PLS_KEY_EXPAND_LEN, ctx->serverRandom,
+ SSL_CLIENT_SRVR_RAND_SIZE);
+ memmove(seedBuf + PLS_KEY_EXPAND_LEN + SSL_CLIENT_SRVR_RAND_SIZE,
+ ctx->clientRandom, SSL_CLIENT_SRVR_RAND_SIZE);
+ serr = SSLInternal_PRF(ctx,
+ ctx->masterSecret,
+ SSL_MASTER_SECRET_SIZE,
+ NULL, // no label
+ 0,
+ seedBuf,
+ GKM_SEED_LEN,
+ key.data, // destination
+ key.length);
+ tlsDump("key expansion", key.data, key.length);
+ return serr;
+}
+
+/*
+ * final_client_write_key =
+ * PRF(SecurityParameters.client_write_key,
+ * "client write key",
+ * SecurityParameters.client_random +
+ * SecurityParameters.server_random);
+ * final_server_write_key =
+ * PRF(SecurityParameters.server_write_key,
+ * "server write key",
+ * SecurityParameters.client_random +
+ * SecurityParameters.server_random);
+ *
+ * iv_block = PRF("", "IV block", SecurityParameters.client_random +
+ * SecurityParameters.server_random);
+ *
+ * iv_block is broken up into:
+ *
+ * client_write_IV[SecurityParameters.IV_size]
+ * server_write_IV[SecurityParameters.IV_size]
+ */
+OSStatus tls1GenerateExportKeyAndIv (
+ SSLContext *ctx, // clientRandom, serverRandom valid
+ const SSLBuffer clientWriteKey,
+ const SSLBuffer serverWriteKey,
+ SSLBuffer finalClientWriteKey, // RETURNED, mallocd by caller
+ SSLBuffer finalServerWriteKey, // RETURNED, mallocd by caller
+ SSLBuffer finalClientIV, // RETURNED, mallocd by caller
+ SSLBuffer finalServerIV) // RETURNED, mallocd by caller
+{
+ unsigned char randBuf[2 * SSL_CLIENT_SRVR_RAND_SIZE];
+ OSStatus serr;
+ unsigned char *ivBlock;
+ char *nullKey = "";
+
+ /* all three PRF calls use the same seed */
+ memmove(randBuf, ctx->clientRandom, SSL_CLIENT_SRVR_RAND_SIZE);
+ memmove(randBuf + SSL_CLIENT_SRVR_RAND_SIZE,
+ ctx->serverRandom, SSL_CLIENT_SRVR_RAND_SIZE);
+
+ serr = SSLInternal_PRF(ctx,
+ clientWriteKey.data,
+ clientWriteKey.length,
+ (const unsigned char *)PLS_EXPORT_CLIENT_WRITE,
+ PLS_EXPORT_CLIENT_WRITE_LEN,
+ randBuf,
+ 2 * SSL_CLIENT_SRVR_RAND_SIZE,
+ finalClientWriteKey.data, // destination
+ finalClientWriteKey.length);
+ if(serr) {
+ return serr;
+ }
+ serr = SSLInternal_PRF(ctx,
+ serverWriteKey.data,
+ serverWriteKey.length,
+ (const unsigned char *)PLS_EXPORT_SERVER_WRITE,
+ PLS_EXPORT_SERVER_WRITE_LEN,
+ randBuf,
+ 2 * SSL_CLIENT_SRVR_RAND_SIZE,
+ finalServerWriteKey.data, // destination
+ finalServerWriteKey.length);
+ if(serr) {
+ return serr;
+ }
+ if((finalClientIV.length == 0) && (finalServerIV.length == 0)) {
+ /* skip remainder as optimization */
+ return noErr;
+ }
+ ivBlock = (unsigned char *)sslMalloc(finalClientIV.length + finalServerIV.length);
+ if(ivBlock == NULL) {
+ return memFullErr;
+ }
+ serr = SSLInternal_PRF(ctx,
+ (const unsigned char *)nullKey,
+ 0,
+ (const unsigned char *)PLS_EXPORT_IV_BLOCK,
+ PLS_EXPORT_IV_BLOCK_LEN,
+ randBuf,
+ 2 * SSL_CLIENT_SRVR_RAND_SIZE,
+ ivBlock, // destination
+ finalClientIV.length + finalServerIV.length);
+ if(serr) {
+ goto done;
+ }
+ memmove(finalClientIV.data, ivBlock, finalClientIV.length);
+ memmove(finalServerIV.data, ivBlock + finalClientIV.length, finalServerIV.length);
+done:
+ sslFree(ivBlock);
+ return serr;
+}
+
+/*
+ * On entry: clientRandom, serverRandom, preMasterSecret valid
+ * On return: masterSecret valid
+ *
+ * master_secret = PRF(pre_master_secret, "master secret",
+ * ClientHello.random + ServerHello.random)
+ * [0..47];
+ */
+
+OSStatus tls1GenerateMasterSecret (
+ SSLContext *ctx)
+{
+ unsigned char randBuf[2 * SSL_CLIENT_SRVR_RAND_SIZE];
+ OSStatus serr;
+
+ memmove(randBuf, ctx->clientRandom, SSL_CLIENT_SRVR_RAND_SIZE);
+ memmove(randBuf + SSL_CLIENT_SRVR_RAND_SIZE,
+ ctx->serverRandom, SSL_CLIENT_SRVR_RAND_SIZE);
+ serr = SSLInternal_PRF(ctx,
+ ctx->preMasterSecret.data,
+ ctx->preMasterSecret.length,
+ (const unsigned char *)PLS_MASTER_SECRET,
+ PLS_MASTER_SECRET_LEN,
+ randBuf,
+ 2 * SSL_CLIENT_SRVR_RAND_SIZE,
+ ctx->masterSecret, // destination
+ SSL_MASTER_SECRET_SIZE);
+ tlsDump("master secret", ctx->masterSecret, SSL_MASTER_SECRET_SIZE);
+ return serr;
+}
+
+/*
+ * Given digests contexts representing the running total of all handshake messages,
+ * calculate mac for "finished" message.
+ *
+ * verify_data = 12 bytes =
+ * PRF(master_secret, finished_label, MD5(handshake_messages) +
+ * SHA-1(handshake_messages)) [0..11];
+ */
+OSStatus tls1ComputeFinishedMac (
+ SSLContext *ctx,
+ SSLBuffer finished, // output - mallocd by caller
+ SSLBuffer shaMsgState, // clone of running digest of all handshake msgs
+ SSLBuffer md5MsgState, // ditto
+ Boolean isServer)
+{
+ unsigned char digests[SSL_MD5_DIGEST_LEN + SSL_SHA1_DIGEST_LEN];
+ SSLBuffer digBuf;
+ char *finLabel;
+ unsigned finLabelLen;
+ OSStatus serr;
+
+ if(isServer) {
+ finLabel = PLS_SERVER_FINISH;
+ finLabelLen = PLS_SERVER_FINISH_LEN;
+ }
+ else {
+ finLabel = PLS_CLIENT_FINISH;
+ finLabelLen = PLS_CLIENT_FINISH_LEN;
+ }
+
+ /* concatenate two digest results */
+ digBuf.data = digests;
+ digBuf.length = SSL_MD5_DIGEST_LEN;
+ serr = SSLHashMD5.final(md5MsgState, digBuf);
+ if(serr) {
+ return serr;
+ }
+ digBuf.data += SSL_MD5_DIGEST_LEN;
+ digBuf.length = SSL_SHA1_DIGEST_LEN;
+ serr = SSLHashSHA1.final(shaMsgState, digBuf);
+ if(serr) {
+ return serr;
+ }
+ return SSLInternal_PRF(ctx,
+ ctx->masterSecret,
+ SSL_MASTER_SECRET_SIZE,
+ (const unsigned char *)finLabel,
+ finLabelLen,
+ digests,
+ SSL_MD5_DIGEST_LEN + SSL_SHA1_DIGEST_LEN,
+ finished.data, // destination
+ finished.length);
+}
+
+/*
+ * This one is trivial.
+ *
+ * mac := MD5(handshake_messages) + SHA(handshake_messages);
+ *
+ * I don't know why this one doesn't use an HMAC or the master secret (as SSLv3
+ * does).
+ */
+OSStatus tls1ComputeCertVfyMac (
+ SSLContext *ctx,
+ SSLBuffer finished, // output - mallocd by caller
+ SSLBuffer shaMsgState, // clone of running digest of all handshake msgs
+ SSLBuffer md5MsgState) // ditto
+{
+ SSLBuffer digBuf;
+ OSStatus serr;
+
+ assert(finished.length == (SSL_MD5_DIGEST_LEN + SSL_SHA1_DIGEST_LEN));
+ digBuf.data = finished.data;
+ digBuf.length = SSL_MD5_DIGEST_LEN;
+ serr = SSLHashMD5.final(md5MsgState, digBuf);
+ if(serr) {
+ return serr;
+ }
+ digBuf.data = finished.data + SSL_MD5_DIGEST_LEN;
+ digBuf.length = SSL_SHA1_DIGEST_LEN;
+ return SSLHashSHA1.final(shaMsgState, digBuf);
+}
+
+const SslTlsCallouts Tls1Callouts = {
+ tls1DecryptRecord,
+ ssl3WriteRecord,
+ tls1InitMac,
+ tls1FreeMac,
+ tls1ComputeMac,
+ tls1GenerateKeyMaterial,
+ tls1GenerateExportKeyAndIv,
+ tls1GenerateMasterSecret,
+ tls1ComputeFinishedMac,
+ tls1ComputeCertVfyMac
+};