#include "tls_ssl.h"
#include "sslMemory.h"
#include "sslUtils.h"
#include "sslDigests.h"
#include "sslAlertMessage.h"
#include "sslCrypto.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
#pragma mark -
#pragma mark PRF label strings
#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 -
#pragma mark private functions
static OSStatus tlsPHash(
SSLContext *ctx,
const HMACReference *hmac, const uint8_t *secret,
size_t secretLen,
const uint8_t *seed,
size_t seedLen,
uint8_t *out, size_t outLen) {
unsigned char aSubI[TLS_HMAC_MAX_SIZE];
unsigned char digest[TLS_HMAC_MAX_SIZE];
HMACContextRef hmacCtx;
OSStatus serr;
size_t digestLen = hmac->macSize;
serr = hmac->alloc(hmac, ctx, secret, secretLen, &hmacCtx);
if(serr) {
return serr;
}
serr = hmac->hmac(hmacCtx, seed, seedLen, aSubI, &digestLen);
if(serr) {
goto fail;
}
assert(digestLen = hmac->macSize);
for (;;) {
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) {
memmove(out, digest, outLen);
break;
}
memmove(out, digest, digestLen);
out += digestLen;
outLen -= digestLen;
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;
}
OSStatus SSLInternal_PRF(
SSLContext *ctx,
const void *vsecret,
size_t secretLen,
const void *label, size_t labelLen,
const void *seed,
size_t seedLen,
void *vout, size_t outLen)
{
OSStatus serr = errSSLInternal;
const unsigned char *S1, *S2; size_t sLen; unsigned char *labelSeed = NULL; size_t labelSeedLen;
unsigned char *tmpOut = NULL; size_t i;
const unsigned char *secret = (const unsigned char *)vsecret;
if(label != NULL) {
labelSeedLen = labelLen + seedLen;
labelSeed = (unsigned char *)sslMalloc(labelSeedLen);
if(labelSeed == NULL) {
return memFullErr;
}
memmove(labelSeed, label, labelLen);
memmove(labelSeed + labelLen, seed, seedLen);
}
else {
labelSeed = (unsigned char *)seed;
labelSeedLen = seedLen;
}
unsigned char *out = (unsigned char *)vout;
if(sslVersionIsLikeTls12(ctx)) {
const HMACReference *mac = &TlsHmacSHA256;
if (ctx->selectedCipherSpec.macAlgorithm->hmac->alg == HA_SHA384) {
mac = ctx->selectedCipherSpec.macAlgorithm->hmac;
}
serr = tlsPHash(ctx, mac, secret, secretLen, labelSeed, labelSeedLen,
out, outLen);
if(serr) {
goto fail;
}
} else {
sLen = secretLen / 2; S1 = secret;
S2 = &secret[sLen];
sLen += (secretLen & 1);
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;
}
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;
}
#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;
bool decryption_failed_or_bad_record_mac = false;
if ((ctx->readCipher.symCipher->blockSize > 0) &&
((payload->length % ctx->readCipher.symCipher->blockSize) != 0)) {
SSLFatalSessionAlert(SSL_AlertRecordOverflow, ctx);
return errSSLRecordOverflow;
}
if ((err = ctx->readCipher.symCipher->decrypt(payload->data,
payload->data, payload->length,
&ctx->readCipher,
ctx)) != 0)
{
decryption_failed_or_bad_record_mac = true;
}
if((ctx->negProtocolVersion>=TLS_Version_1_1) && (ctx->readCipher.symCipher->blockSize>0))
{
content.data = payload->data + ctx->readCipher.symCipher->blockSize;
content.length = payload->length - (ctx->readCipher.macRef->hash->digestSize + ctx->readCipher.symCipher->blockSize);
} else {
content.data = payload->data;
content.length = payload->length - ctx->readCipher.macRef->hash->digestSize;
}
if (ctx->readCipher.symCipher->blockSize > 0) {
UInt8 padSize = payload->data[payload->length - 1];
UInt8 *padChars;
if(padSize > payload->length) {
sslErrorLog("tls1DecryptRecord: bad padding length (%d)\n",
(unsigned)payload->data[payload->length - 1]);
decryption_failed_or_bad_record_mac = true;
}
padChars = payload->data + payload->length - padSize;
while(padChars < (payload->data + payload->length)) {
if(*padChars++ != padSize) {
sslErrorLog("tls1DecryptRecord: bad padding value\n");
decryption_failed_or_bad_record_mac = true;
}
}
content.length -= (1 + padSize);
}
if (ctx->readCipher.macRef->hash->digestSize > 0)
if ((err = SSLVerifyMac(type, &content,
content.data + content.length, ctx)) != 0)
{
decryption_failed_or_bad_record_mac = true;
}
if (decryption_failed_or_bad_record_mac) {
SSLFatalSessionAlert(SSL_AlertBadRecordMac, ctx);
return errSSLDecryptionFail;
}
*payload = content;
return noErr;
}
static OSStatus tls1InitMac (
CipherContext *cipherCtx, 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);
memset(cipherCtx->macSecret, 0, sizeof(cipherCtx->macSecret));
return serr;
}
static OSStatus tls1FreeMac (
CipherContext *cipherCtx)
{
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;
}
#define HDR_LENGTH (8 + 1 + 2 + 2)
static OSStatus tls1ComputeMac (
UInt8 type,
SSLBuffer data,
SSLBuffer mac, CipherContext *cipherCtx, sslUint64 seqNo,
SSLContext *ctx)
{
uint8_t hdr[HDR_LENGTH];
uint8_t *p;
HMACContextRef hmacCtx;
OSStatus serr;
const HMACReference *hmac;
size_t macLength;
assert(cipherCtx != NULL);
assert(cipherCtx->macRef != NULL);
hmac = cipherCtx->macRef->hmac;
assert(hmac != NULL);
hmacCtx = cipherCtx->macCtx.hmacCtx;
serr = hmac->init(hmacCtx);
if(serr) {
goto fail;
}
p = SSLEncodeUInt64(hdr, seqNo);
*p++ = type;
*p++ = ctx->negProtocolVersion >> 8;
*p++ = ctx->negProtocolVersion & 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;
}
#define GKM_SEED_LEN (PLS_KEY_EXPAND_LEN + (2 * SSL_CLIENT_SRVR_RAND_SIZE))
static OSStatus tls1GenerateKeyMaterial (
SSLBuffer key, SSLContext *ctx)
{
unsigned char seedBuf[GKM_SEED_LEN];
OSStatus serr;
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, 0,
seedBuf,
GKM_SEED_LEN,
key.data, key.length);
tlsDump("key expansion", key.data, key.length);
return serr;
}
static OSStatus tls1GenerateExportKeyAndIv (
SSLContext *ctx, const SSLBuffer clientWriteKey,
const SSLBuffer serverWriteKey,
SSLBuffer finalClientWriteKey, SSLBuffer finalServerWriteKey, SSLBuffer finalClientIV, SSLBuffer finalServerIV) {
unsigned char randBuf[2 * SSL_CLIENT_SRVR_RAND_SIZE];
OSStatus serr;
unsigned char *ivBlock;
char *nullKey = "";
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, 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, finalServerWriteKey.length);
if(serr) {
return serr;
}
if((finalClientIV.length == 0) && (finalServerIV.length == 0)) {
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, 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;
}
static 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, SSL_MASTER_SECRET_SIZE);
tlsDump("master secret", ctx->masterSecret, SSL_MASTER_SECRET_SIZE);
return serr;
}
static OSStatus tls1ComputeFinishedMac (
SSLContext *ctx,
SSLBuffer finished, Boolean isServer)
{
unsigned char digests[SSL_MD5_DIGEST_LEN + SSL_SHA1_DIGEST_LEN];
SSLBuffer digBuf;
char *finLabel;
unsigned finLabelLen;
OSStatus serr;
SSLBuffer shaMsgState, md5MsgState;
shaMsgState.data = 0;
md5MsgState.data = 0;
if ((serr = CloneHashState(&SSLHashSHA1, &ctx->shaState, &shaMsgState, ctx)) != 0)
goto fail;
if ((serr = CloneHashState(&SSLHashMD5, &ctx->md5State, &md5MsgState, ctx)) != 0)
goto fail;
if(isServer) {
finLabel = PLS_SERVER_FINISH;
finLabelLen = PLS_SERVER_FINISH_LEN;
}
else {
finLabel = PLS_CLIENT_FINISH;
finLabelLen = PLS_CLIENT_FINISH_LEN;
}
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;
}
serr = 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, finished.length);
fail:
SSLFreeBuffer(&shaMsgState, ctx);
SSLFreeBuffer(&md5MsgState, ctx);
return serr;
}
static OSStatus tls12ComputeFinishedMac (
SSLContext *ctx,
SSLBuffer finished, Boolean isServer)
{
unsigned char digest[SSL_MAX_DIGEST_LEN];
SSLBuffer digBuf;
char *finLabel;
unsigned finLabelLen;
OSStatus serr;
SSLBuffer hashState;
const HashReference *hashRef;
const SSLBuffer *ctxHashState;
if (ctx->selectedCipherSpec.macAlgorithm->hmac->alg == HA_SHA384) {
hashRef = &SSLHashSHA384;
ctxHashState = &ctx->sha512State;
} else {
hashRef = &SSLHashSHA256;
ctxHashState = &ctx->sha256State;
}
hashState.data = 0;
if ((serr = CloneHashState(hashRef, ctxHashState, &hashState, ctx)) != 0)
goto fail;
if(isServer) {
finLabel = PLS_SERVER_FINISH;
finLabelLen = PLS_SERVER_FINISH_LEN;
}
else {
finLabel = PLS_CLIENT_FINISH;
finLabelLen = PLS_CLIENT_FINISH_LEN;
}
digBuf.data = digest;
digBuf.length = hashRef->digestSize;
if ((serr = hashRef->final(&hashState, &digBuf)) != 0)
goto fail;
serr = SSLInternal_PRF(ctx,
ctx->masterSecret,
SSL_MASTER_SECRET_SIZE,
(const unsigned char *)finLabel,
finLabelLen,
digBuf.data,
digBuf.length,
finished.data, finished.length);
fail:
SSLFreeBuffer(&hashState, ctx);
return serr;
}
static OSStatus tls1ComputeCertVfyMac (
SSLContext *ctx,
SSLBuffer *finished, SSL_HashAlgorithm hash) {
SSLBuffer digBuf, shaMsgState, md5MsgState;
OSStatus serr;
shaMsgState.data = 0;
md5MsgState.data = 0;
if ((serr = CloneHashState(&SSLHashSHA1, &ctx->shaState, &shaMsgState, ctx)) != 0)
goto fail;
if ((serr = CloneHashState(&SSLHashMD5, &ctx->md5State, &md5MsgState, ctx)) != 0)
goto fail;
if ((ctx->protocolSide == kSSLServerSide && sslPubKeyGetAlgorithmID(ctx->peerPubKey) == kSecECDSAAlgorithmID) ||
(ctx->protocolSide == kSSLClientSide && ctx->negAuthType == SSLClientAuth_ECDSASign)) {
assert(finished->length >= SSL_SHA1_DIGEST_LEN);
digBuf.data = finished->data;
finished->length = SSL_SHA1_DIGEST_LEN;
} else {
assert(finished->length >= (SSL_MD5_DIGEST_LEN + SSL_SHA1_DIGEST_LEN));
digBuf.data = finished->data;
digBuf.length = SSL_MD5_DIGEST_LEN;
if ((serr = SSLHashMD5.final(&md5MsgState, &digBuf)) != 0)
goto fail;
digBuf.data = finished->data + SSL_MD5_DIGEST_LEN;
finished->length = SSL_MD5_DIGEST_LEN + SSL_SHA1_DIGEST_LEN;
}
digBuf.length = SSL_SHA1_DIGEST_LEN;
serr = SSLHashSHA1.final(&shaMsgState, &digBuf);
fail:
SSLFreeBuffer(&shaMsgState, ctx);
SSLFreeBuffer(&md5MsgState, ctx);
return serr;
}
static OSStatus tls12ComputeCertVfyMac (
SSLContext *ctx,
SSLBuffer *finished, SSL_HashAlgorithm hash)
{
const SSLBuffer *ctxHashState;
const HashReference *hashRef;
SSLBuffer hashState;
OSStatus serr;
hashState.data = 0;
switch (hash) {
case SSL_HashAlgorithmSHA1:
hashRef = &SSLHashSHA1;
ctxHashState = &ctx->shaState;
break;
case SSL_HashAlgorithmSHA256:
hashRef = &SSLHashSHA256;
ctxHashState = &ctx->sha256State;
break;
case SSL_HashAlgorithmSHA384:
hashRef = &SSLHashSHA384;
ctxHashState = &ctx->sha512State;
break;
default:
break;
}
if ((serr = CloneHashState(hashRef, ctxHashState, &hashState, ctx)) != 0)
goto fail;
assert(finished->length >= (hashRef->digestSize));
finished->length = hashRef->digestSize;
serr = hashRef->final(&hashState, finished);
fail:
SSLFreeBuffer(&hashState, ctx);
return serr;
}
const SslTlsCallouts Tls1Callouts = {
tls1DecryptRecord,
ssl3WriteRecord,
tls1InitMac,
tls1FreeMac,
tls1ComputeMac,
tls1GenerateKeyMaterial,
tls1GenerateExportKeyAndIv,
tls1GenerateMasterSecret,
tls1ComputeFinishedMac,
tls1ComputeCertVfyMac
};
const SslTlsCallouts Tls12Callouts = {
tls1DecryptRecord,
ssl3WriteRecord,
tls1InitMac,
tls1FreeMac,
tls1ComputeMac,
tls1GenerateKeyMaterial,
tls1GenerateExportKeyAndIv,
tls1GenerateMasterSecret,
tls12ComputeFinishedMac,
tls12ComputeCertVfyMac
};