X-Git-Url: https://git.saurik.com/apple/security.git/blobdiff_plain/c38e3ce98599a410a47dc10253faa4d5830f13b2..427c49bcad63d042b29ada2ac27e3dfc4845c779:/libsecurity_ssl/lib/tls1RecordCallouts.c?ds=inline diff --git a/libsecurity_ssl/lib/tls1RecordCallouts.c b/libsecurity_ssl/lib/tls1RecordCallouts.c new file mode 100644 index 00000000..a3e2da9d --- /dev/null +++ b/libsecurity_ssl/lib/tls1RecordCallouts.c @@ -0,0 +1,234 @@ +/* + * Copyright (c) 2002,2005-2007,2010-2011 Apple Inc. All Rights Reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The 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. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +/* + * tls1RecordCallouts.c - TLSv1-specific routines for SslTlsCallouts. + */ + +/* THIS FILE CONTAINS KERNEL CODE */ + +#include "tls_record.h" +#include "sslMemory.h" +#include "sslDebug.h" +#include "sslUtils.h" + +#include +#include + +/* 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 int tls1WriteRecord( + SSLRecord rec, + SSLContext *ctx) +{ + check(0); + return errSecUnimplemented; +} +#endif + +static int tls1DecryptRecord( + uint8_t type, + SSLBuffer *payload, + struct SSLRecordInternalContext *ctx) +{ + int err; + SSLBuffer content; + + if ((ctx->readCipher.symCipher->params->blockSize > 0) && + ((payload->length % ctx->readCipher.symCipher->params->blockSize) != 0)) { + return errSSLRecordRecordOverflow; + } + + /* Decrypt in place */ + if ((err = ctx->readCipher.symCipher->c.cipher.decrypt(payload->data, + payload->data, payload->length, + ctx->readCipher.cipherCtx)) != 0) + { + return errSSLRecordDecryptionFail; + } + + /* Locate content within decrypted payload */ + + /* TLS 1.1 and DTLS 1.0 block ciphers */ + if((ctx->negProtocolVersion>=TLS_Version_1_1) && (ctx->readCipher.symCipher->params->blockSize>0)) + { + content.data = payload->data + ctx->readCipher.symCipher->params->blockSize; + content.length = payload->length - (ctx->readCipher.macRef->hash->digestSize + ctx->readCipher.symCipher->params->blockSize); + } else { + content.data = payload->data; + content.length = payload->length - ctx->readCipher.macRef->hash->digestSize; + } + + /* Test for underflow - if the record size is smaller than required */ + if(content.length > payload->length) { + return errSSLRecordClosedAbort; + } + + err = 0; + + if (ctx->readCipher.symCipher->params->blockSize > 0) { + /* for TLSv1, padding can be anywhere from 0 to 255 bytes */ + uint8_t padSize = payload->data[payload->length - 1]; + + /* Padding check sequence: + 1. Check that the padding size (last byte in padding) is within bound + 2. Adjust content.length accordingly + 3. Check every padding byte (except last, already checked) + Do not return, just set the error value on failure, + to avoid creating a padding oracle with timing. + */ + if(padSize+1<=content.length) { + uint8_t *padChars; + content.length -= (1 + padSize); + padChars = payload->data + payload->length - (padSize+1); + while(padChars < (payload->data + payload->length - 1)) { + if(*padChars++ != padSize) { + err = errSSLRecordBadRecordMac; + } + } + } else { + err = errSSLRecordBadRecordMac; + } + } + + /* Verify MAC on payload */ + if (ctx->readCipher.macRef->hash->digestSize > 0) + /* Optimize away MAC for null case */ + if (SSLVerifyMac(type, &content, + content.data + content.length, ctx) != 0) + { + err = errSSLRecordBadRecordMac; + } + + *payload = content; /* Modify payload buffer to indicate content length */ + + return err; +} + +/* initialize a per-CipherContext HashHmacContext for use in MACing each record */ +static int tls1InitMac ( + CipherContext *cipherCtx) // macRef, macSecret valid on entry + // macCtx valid on return +{ + const HMACReference *hmac; + int serr; + + check(cipherCtx); + check(cipherCtx->macRef != NULL); + hmac = cipherCtx->macRef->hmac; + check(hmac != NULL); + + if(cipherCtx->macCtx.hmacCtx != NULL) { + hmac->free(cipherCtx->macCtx.hmacCtx); + cipherCtx->macCtx.hmacCtx = NULL; + } + serr = hmac->alloc(hmac, 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 int tls1FreeMac ( + CipherContext *cipherCtx) +{ + /* this can be called on a completely zeroed out CipherContext... */ + if(cipherCtx->macRef == NULL) { + return 0; + } + check(cipherCtx->macRef->hmac != NULL); + + if(cipherCtx->macCtx.hmacCtx != NULL) { + cipherCtx->macRef->hmac->free(cipherCtx->macCtx.hmacCtx); + cipherCtx->macCtx.hmacCtx = NULL; + } + return 0; +} + +/* + * 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) +static int tls1ComputeMac ( + uint8_t type, + SSLBuffer data, + SSLBuffer mac, // caller mallocs data + CipherContext *cipherCtx, // assumes macCtx, macRef + sslUint64 seqNo, + struct SSLRecordInternalContext *ctx) +{ + uint8_t hdr[HDR_LENGTH]; + uint8_t *p; + HMACContextRef hmacCtx; + int serr; + const HMACReference *hmac; + size_t macLength; + + check(cipherCtx != NULL); + check(cipherCtx->macRef != NULL); + hmac = cipherCtx->macRef->hmac; + check(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++ = 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; +} + +const SslRecordCallouts Tls1RecordCallouts = { + tls1DecryptRecord, + ssl3WriteRecord, + tls1InitMac, + tls1FreeMac, + tls1ComputeMac, +}; +