#include "sslMemory.h"
#include "tls_ssl.h"
#include "sslUtils.h"
#include "sslDigests.h"
#include "ssl2.h"
#include "sslDebug.h"
#include "sslAlertMessage.h"
#include <assert.h>
#include <strings.h>
OSStatus ssl3WriteRecord(
SSLRecord rec,
SSLContext *ctx)
{
OSStatus err;
int padding = 0, i;
WaitingRecord *out, *queue;
SSLBuffer buf, payload, mac;
UInt8 *charPtr;
UInt16 payloadSize,blockSize;
switch(rec.protocolVersion) {
case SSL_Version_2_0:
return SSL2WriteRecord(rec, ctx);
case SSL_Version_3_0:
case TLS_Version_1_0:
break;
default:
assert(0);
return errSSLInternal;
}
assert(rec.contents.length <= 16384);
out = 0;
if ((err = SSLAllocBuffer(buf, sizeof(WaitingRecord), ctx)) != 0)
return err;
out = (WaitingRecord*)buf.data;
out->next = 0;
out->sent = 0;
payloadSize = (UInt16) (rec.contents.length + ctx->writeCipher.macRef->hash->digestSize);
blockSize = ctx->writeCipher.symCipher->blockSize;
if (blockSize > 0)
{ padding = blockSize - (payloadSize % blockSize) - 1;
payloadSize += padding + 1;
}
out->data.data = 0;
if ((err = SSLAllocBuffer(out->data, 5 + payloadSize, ctx)) != 0)
goto fail;
charPtr = out->data.data;
*(charPtr++) = rec.contentType;
charPtr = SSLEncodeInt(charPtr, rec.protocolVersion, 2);
charPtr = SSLEncodeInt(charPtr, payloadSize, 2);
memcpy(charPtr, rec.contents.data, rec.contents.length);
payload.data = charPtr;
payload.length = rec.contents.length;
charPtr += rec.contents.length;
mac.data = charPtr;
mac.length = ctx->writeCipher.macRef->hash->digestSize;
charPtr += mac.length;
if (mac.length > 0)
{
assert(ctx->sslTslCalls != NULL);
if ((err = ctx->sslTslCalls->computeMac(rec.contentType,
payload,
mac,
&ctx->writeCipher,
ctx->writeCipher.sequenceNum,
ctx)) != 0)
goto fail;
}
payload.length = payloadSize;
if (ctx->writeCipher.symCipher->blockSize > 0)
for (i = 1; i <= padding + 1; ++i)
payload.data[payload.length - i] = padding;
if ((err = ctx->writeCipher.symCipher->encrypt(payload,
payload,
&ctx->writeCipher,
ctx)) != 0)
goto fail;
if (ctx->recordWriteQueue == 0)
ctx->recordWriteQueue = out;
else
{ queue = ctx->recordWriteQueue;
while (queue->next != 0)
queue = queue->next;
queue->next = out;
}
IncrementUInt64(&ctx->writeCipher.sequenceNum);
return noErr;
fail:
SSLFreeBuffer(out->data, ctx);
buf.data = (UInt8*)out;
buf.length = sizeof(WaitingRecord);
SSLFreeBuffer(buf, ctx);
return err;
}
static OSStatus ssl3DecryptRecord(
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;
}
if ((err = ctx->readCipher.symCipher->decrypt(*payload,
*payload,
&ctx->readCipher,
ctx)) != 0)
{ SSLFatalSessionAlert(SSL_AlertDecryptionFail, ctx);
return err;
}
content.data = payload->data;
content.length = payload->length - ctx->readCipher.macRef->hash->digestSize;
if (ctx->readCipher.symCipher->blockSize > 0)
{
if (payload->data[payload->length - 1] >= ctx->readCipher.symCipher->blockSize)
{ SSLFatalSessionAlert(SSL_AlertDecryptionFail, ctx);
sslErrorLog("DecryptSSLRecord: bad padding length (%d)\n",
(unsigned)payload->data[payload->length - 1]);
return errSSLDecryptionFail;
}
content.length -= 1 + payload->data[payload->length - 1];
}
if (ctx->readCipher.macRef->hash->digestSize > 0)
if ((err = SSLVerifyMac(type, content,
payload->data + content.length, ctx)) != 0)
{ SSLFatalSessionAlert(SSL_AlertBadRecordMac, ctx);
return errSSLBadRecordMac;
}
*payload = content;
return noErr;
}
static OSStatus ssl3InitMac (
CipherContext *cipherCtx, SSLContext *ctx)
{
const HashReference *hash;
SSLBuffer *hashCtx;
OSStatus serr;
assert(cipherCtx->macRef != NULL);
hash = cipherCtx->macRef->hash;
assert(hash != NULL);
hashCtx = &cipherCtx->macCtx.hashCtx;
if(hashCtx->data != NULL) {
SSLFreeBuffer(*hashCtx, ctx);
}
serr = SSLAllocBuffer(*hashCtx, hash->contextSize, ctx);
if(serr) {
return serr;
}
return noErr;
}
static OSStatus ssl3FreeMac (
CipherContext *cipherCtx)
{
SSLBuffer *hashCtx;
assert(cipherCtx != NULL);
if(cipherCtx->macRef == NULL) {
return noErr;
}
hashCtx = &cipherCtx->macCtx.hashCtx;
if(hashCtx->data != NULL) {
sslFree(hashCtx->data);
hashCtx->data = NULL;
}
hashCtx->length = 0;
return noErr;
}
static OSStatus ssl3ComputeMac (
UInt8 type,
SSLBuffer data,
SSLBuffer mac, CipherContext *cipherCtx, sslUint64 seqNo,
SSLContext *ctx)
{
OSStatus err;
UInt8 innerDigestData[SSL_MAX_DIGEST_LEN];
UInt8 scratchData[11], *charPtr;
SSLBuffer digest, digestCtx, scratch;
SSLBuffer secret;
const HashReference *hash;
assert(cipherCtx != NULL);
assert(cipherCtx->macRef != NULL);
hash = cipherCtx->macRef->hash;
assert(hash != NULL);
assert(hash->macPadSize <= MAX_MAC_PADDING);
assert(hash->digestSize <= SSL_MAX_DIGEST_LEN);
digestCtx = cipherCtx->macCtx.hashCtx; secret.data = cipherCtx->macSecret;
secret.length = hash->digestSize;
assert(SSLMACPad1[0] == 0x36 && SSLMACPad2[0] == 0x5C);
if ((err = hash->init(digestCtx, ctx)) != 0)
goto exit;
if ((err = hash->update(digestCtx, secret)) != 0)
goto exit;
scratch.data = (UInt8 *)SSLMACPad1;
scratch.length = hash->macPadSize;
if ((err = hash->update(digestCtx, scratch)) != 0)
goto exit;
charPtr = scratchData;
charPtr = SSLEncodeUInt64(charPtr, seqNo);
*charPtr++ = type;
charPtr = SSLEncodeInt(charPtr, data.length, 2);
scratch.data = scratchData;
scratch.length = 11;
assert(charPtr = scratchData+11);
if ((err = hash->update(digestCtx, scratch)) != 0)
goto exit;
if ((err = hash->update(digestCtx, data)) != 0)
goto exit;
digest.data = innerDigestData;
digest.length = hash->digestSize;
if ((err = hash->final(digestCtx, digest)) != 0)
goto exit;
if ((err = hash->init(digestCtx, ctx)) != 0)
goto exit;
if ((err = hash->update(digestCtx, secret)) != 0)
goto exit;
scratch.data = (UInt8 *)SSLMACPad2;
scratch.length = hash->macPadSize;
if ((err = hash->update(digestCtx, scratch)) != 0)
goto exit;
if ((err = hash->update(digestCtx, digest)) != 0)
goto exit;
if ((err = hash->final(digestCtx, mac)) != 0)
goto exit;
err = noErr;
exit:
return err;
}
#define LOG_GEN_KEY 0
static OSStatus ssl3GenerateKeyMaterial (
SSLBuffer key, SSLContext *ctx)
{
OSStatus err;
UInt8 leaderData[10];
UInt8 shaHashData[20], md5HashData[16];
SSLBuffer shaContext, md5Context;
UInt8 *keyProgress;
int i,j,remaining, satisfied;
SSLBuffer leader, masterSecret, serverRandom, clientRandom, shaHash, md5Hash;
#if LOG_GEN_KEY
printf("GenerateKey: master ");
for(i=0; i<SSL_MASTER_SECRET_SIZE; i++) {
printf("%02X ", ctx->masterSecret[i]);
}
printf("\n");
#endif
assert(key.length <= 16 * sizeof(leaderData));
leader.data = leaderData;
masterSecret.data = ctx->masterSecret;
masterSecret.length = SSL_MASTER_SECRET_SIZE;
serverRandom.data = ctx->serverRandom;
serverRandom.length = SSL_CLIENT_SRVR_RAND_SIZE;
clientRandom.data = ctx->clientRandom;
clientRandom.length = SSL_CLIENT_SRVR_RAND_SIZE;
shaHash.data = shaHashData;
shaHash.length = 20;
md5Hash.data = md5HashData;
md5Hash.length = 16;
md5Context.data = 0;
shaContext.data = 0;
if ((err = ReadyHash(SSLHashMD5, md5Context, ctx)) != 0)
goto fail;
if ((err = ReadyHash(SSLHashSHA1, shaContext, ctx)) != 0)
goto fail;
keyProgress = key.data;
remaining = key.length;
for (i = 0; remaining > 0; ++i)
{ for (j = 0; j <= i; j++)
leaderData[j] = 0x41 + i;
leader.length = i+1;
if ((err = SSLHashSHA1.update(shaContext, leader)) != 0)
goto fail;
if ((err = SSLHashSHA1.update(shaContext, masterSecret)) != 0)
goto fail;
if ((err = SSLHashSHA1.update(shaContext, serverRandom)) != 0)
goto fail;
if ((err = SSLHashSHA1.update(shaContext, clientRandom)) != 0)
goto fail;
if ((err = SSLHashSHA1.final(shaContext, shaHash)) != 0)
goto fail;
if ((err = SSLHashMD5.update(md5Context, masterSecret)) != 0)
goto fail;
if ((err = SSLHashMD5.update(md5Context, shaHash)) != 0)
goto fail;
if ((err = SSLHashMD5.final(md5Context, md5Hash)) != 0)
goto fail;
satisfied = 16;
if (remaining < 16)
satisfied = remaining;
memcpy(keyProgress, md5HashData, satisfied);
remaining -= satisfied;
keyProgress += satisfied;
if(remaining > 0) {
if ((err = SSLHashMD5.init(md5Context, ctx)) != 0)
goto fail;
if ((err = SSLHashSHA1.init(shaContext, ctx)) != 0)
goto fail;
}
}
assert(remaining == 0 && keyProgress == (key.data + key.length));
err = noErr;
fail:
SSLFreeBuffer(md5Context, ctx);
SSLFreeBuffer(shaContext, ctx);
#if LOG_GEN_KEY
printf("GenerateKey: DONE\n");
#endif
return err;
}
static OSStatus ssl3GenerateExportKeyAndIv (
SSLContext *ctx, const SSLBuffer clientWriteKey,
const SSLBuffer serverWriteKey,
SSLBuffer finalClientWriteKey, SSLBuffer finalServerWriteKey, SSLBuffer finalClientIV, SSLBuffer finalServerIV) {
OSStatus err;
SSLBuffer hashCtx, serverRandom, clientRandom;
serverRandom.data = ctx->serverRandom;
serverRandom.length = SSL_CLIENT_SRVR_RAND_SIZE;
clientRandom.data = ctx->clientRandom;
clientRandom.length = SSL_CLIENT_SRVR_RAND_SIZE;
if ((err = SSLAllocBuffer(hashCtx, SSLHashMD5.contextSize, ctx)) != 0)
return err;
if ((err = SSLHashMD5.init(hashCtx, ctx)) != 0)
goto fail;
if ((err = SSLHashMD5.update(hashCtx, clientWriteKey)) != 0)
goto fail;
if ((err = SSLHashMD5.update(hashCtx, clientRandom)) != 0)
goto fail;
if ((err = SSLHashMD5.update(hashCtx, serverRandom)) != 0)
goto fail;
finalClientWriteKey.length = 16;
if ((err = SSLHashMD5.final(hashCtx, finalClientWriteKey)) != 0)
goto fail;
if (ctx->selectedCipherSpec->cipher->ivSize > 0)
{ if ((err = SSLHashMD5.init(hashCtx, ctx)) != 0)
goto fail;
if ((err = SSLHashMD5.update(hashCtx, clientRandom)) != 0)
goto fail;
if ((err = SSLHashMD5.update(hashCtx, serverRandom)) != 0)
goto fail;
finalClientIV.length = 16;
if ((err = SSLHashMD5.final(hashCtx, finalClientIV)) != 0)
goto fail;
}
if ((err = SSLHashMD5.init(hashCtx, ctx)) != 0)
goto fail;
if ((err = SSLHashMD5.update(hashCtx, serverWriteKey)) != 0)
goto fail;
if ((err = SSLHashMD5.update(hashCtx, serverRandom)) != 0)
goto fail;
if ((err = SSLHashMD5.update(hashCtx, clientRandom)) != 0)
goto fail;
finalServerWriteKey.length = 16;
if ((err = SSLHashMD5.final(hashCtx, finalServerWriteKey)) != 0)
goto fail;
if (ctx->selectedCipherSpec->cipher->ivSize > 0)
{ if ((err = SSLHashMD5.init(hashCtx, ctx)) != 0)
goto fail;
if ((err = SSLHashMD5.update(hashCtx, serverRandom)) != 0)
goto fail;
if ((err = SSLHashMD5.update(hashCtx, clientRandom)) != 0)
goto fail;
finalServerIV.length = 16;
if ((err = SSLHashMD5.final(hashCtx, finalServerIV)) != 0)
goto fail;
}
err = noErr;
fail:
SSLFreeBuffer(hashCtx, ctx);
return err;
}
static OSStatus ssl3GenerateMasterSecret (
SSLContext *ctx)
{
OSStatus err;
SSLBuffer shaState, md5State, clientRandom,
serverRandom, shaHash, md5Hash, leader;
UInt8 *masterProgress, shaHashData[20], leaderData[3];
int i;
md5State.data = shaState.data = 0;
if ((err = SSLAllocBuffer(md5State, SSLHashMD5.contextSize, ctx)) != 0)
goto fail;
if ((err = SSLAllocBuffer(shaState, SSLHashSHA1.contextSize, ctx)) != 0)
goto fail;
clientRandom.data = ctx->clientRandom;
clientRandom.length = SSL_CLIENT_SRVR_RAND_SIZE;
serverRandom.data = ctx->serverRandom;
serverRandom.length = SSL_CLIENT_SRVR_RAND_SIZE;
shaHash.data = shaHashData;
shaHash.length = 20;
masterProgress = ctx->masterSecret;
for (i = 1; i <= 3; i++)
{ if ((err = SSLHashMD5.init(md5State, ctx)) != 0)
goto fail;
if ((err = SSLHashSHA1.init(shaState, ctx)) != 0)
goto fail;
leaderData[0] = leaderData[1] = leaderData[2] = 0x40 + i;
leader.data = leaderData;
leader.length = i;
if ((err = SSLHashSHA1.update(shaState, leader)) != 0)
goto fail;
if ((err = SSLHashSHA1.update(shaState, ctx->preMasterSecret)) != 0)
goto fail;
if ((err = SSLHashSHA1.update(shaState, clientRandom)) != 0)
goto fail;
if ((err = SSLHashSHA1.update(shaState, serverRandom)) != 0)
goto fail;
if ((err = SSLHashSHA1.final(shaState, shaHash)) != 0)
goto fail;
if ((err = SSLHashMD5.update(md5State, ctx->preMasterSecret)) != 0)
goto fail;
if ((err = SSLHashMD5.update(md5State, shaHash)) != 0)
goto fail;
md5Hash.data = masterProgress;
md5Hash.length = 16;
if ((err = SSLHashMD5.final(md5State, md5Hash)) != 0)
goto fail;
masterProgress += 16;
}
err = noErr;
fail:
SSLFreeBuffer(shaState, ctx);
SSLFreeBuffer(md5State, ctx);
return err;
}
static OSStatus
ssl3CalculateFinishedMessage(
SSLContext *ctx,
SSLBuffer finished, SSLBuffer shaMsgState, SSLBuffer md5MsgState, UInt32 senderID) {
OSStatus err;
SSLBuffer hash, input;
UInt8 sender[4], md5Inner[16], shaInner[20];
if (senderID != 0) {
SSLEncodeInt(sender, senderID, 4);
input.data = sender;
input.length = 4;
if ((err = SSLHashMD5.update(md5MsgState, input)) != 0)
return err;
if ((err = SSLHashSHA1.update(shaMsgState, input)) != 0)
return err;
}
input.data = ctx->masterSecret;
input.length = SSL_MASTER_SECRET_SIZE;
if ((err = SSLHashMD5.update(md5MsgState, input)) != 0)
return err;
if ((err = SSLHashSHA1.update(shaMsgState, input)) != 0)
return err;
input.data = (UInt8 *)SSLMACPad1;
input.length = SSLHashMD5.macPadSize;
if ((err = SSLHashMD5.update(md5MsgState, input)) != 0)
return err;
input.length = SSLHashSHA1.macPadSize;
if ((err = SSLHashSHA1.update(shaMsgState, input)) != 0)
return err;
hash.data = md5Inner;
hash.length = 16;
if ((err = SSLHashMD5.final(md5MsgState, hash)) != 0)
return err;
hash.data = shaInner;
hash.length = 20;
if ((err = SSLHashSHA1.final(shaMsgState, hash)) != 0)
return err;
if ((err = SSLHashMD5.init(md5MsgState, ctx)) != 0)
return err;
if ((err = SSLHashSHA1.init(shaMsgState, ctx)) != 0)
return err;
input.data = ctx->masterSecret;
input.length = SSL_MASTER_SECRET_SIZE;
if ((err = SSLHashMD5.update(md5MsgState, input)) != 0)
return err;
if ((err = SSLHashSHA1.update(shaMsgState, input)) != 0)
return err;
input.data = (UInt8 *)SSLMACPad2;
input.length = SSLHashMD5.macPadSize;
if ((err = SSLHashMD5.update(md5MsgState, input)) != 0)
return err;
input.length = SSLHashSHA1.macPadSize;
if ((err = SSLHashSHA1.update(shaMsgState, input)) != 0)
return err;
input.data = md5Inner;
input.length = 16;
if ((err = SSLHashMD5.update(md5MsgState, input)) != 0)
return err;
hash.data = finished.data;
hash.length = 16;
if ((err = SSLHashMD5.final(md5MsgState, hash)) != 0)
return err;
input.data = shaInner;
input.length = 20;
if ((err = SSLHashSHA1.update(shaMsgState, input)) != 0)
return err;
hash.data = finished.data + 16;
hash.length = 20;
if ((err = SSLHashSHA1.final(shaMsgState, hash)) != 0)
return err;
return noErr;
}
static OSStatus ssl3ComputeFinishedMac (
SSLContext *ctx,
SSLBuffer finished, SSLBuffer shaMsgState, SSLBuffer md5MsgState, Boolean isServer) {
return ssl3CalculateFinishedMessage(ctx, finished, shaMsgState, md5MsgState,
isServer ? SSL_Finished_Sender_Server : SSL_Finished_Sender_Client);
}
static OSStatus ssl3ComputeCertVfyMac (
SSLContext *ctx,
SSLBuffer finished, SSLBuffer shaMsgState, SSLBuffer md5MsgState) {
return ssl3CalculateFinishedMessage(ctx, finished, shaMsgState, md5MsgState, 0);
}
const SslTlsCallouts Ssl3Callouts = {
ssl3DecryptRecord,
ssl3WriteRecord,
ssl3InitMac,
ssl3FreeMac,
ssl3ComputeMac,
ssl3GenerateKeyMaterial,
ssl3GenerateExportKeyAndIv,
ssl3GenerateMasterSecret,
ssl3ComputeFinishedMac,
ssl3ComputeCertVfyMac
};