+++ /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_AlertRecordOverflow, ctx);
- return errSSLRecordOverflow;
- }
-
- /* Decrypt in place */
- if ((err = ctx->readCipher.symCipher->decrypt(*payload,
- *payload,
- &ctx->readCipher,
- ctx)) != 0)
- { SSLFatalSessionAlert(SSL_AlertDecryptError, ctx);
- return errSSLDecryptionFail;
- }
-
- /* 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_AlertDecodeError, ctx);
- sslErrorLog("tls1DecryptRecord: bad padding length (%d)\n",
- (unsigned)payload->data[payload->length - 1]);
- return errSSLDecryptionFail;
- }
- padChars = payload->data + payload->length - padSize;
- while(padChars < (payload->data + payload->length)) {
- if(*padChars++ != padSize) {
- SSLFatalSessionAlert(SSL_AlertDecodeError, ctx);
- sslErrorLog("tls1DecryptRecord: bad padding value\n");
- return errSSLDecryptionFail;
- }
- }
- /* 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 errSSLBadRecordMac;
- }
-
- *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
-};