+/*
+ * Copyright (c) 2002,2005-2008,2010-2012,2014 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@
+ */
+
+/*
+ * tls_hmac.c - HMAC routines used by TLS
+ */
+
+/* THIS FILE CONTAINS KERNEL CODE */
+
+#include "tls_hmac.h"
+#include "tls_digest.h"
+#include "sslMemory.h"
+#include "sslDebug.h"
+#include <string.h>
+#include <AssertMacros.h>
+
+
+/* Per-session state, opaque to callers; all fields set at alloc time */
+struct HMACContext {
+ const HashReference *digest;
+ SSLBuffer outerHashCtx;
+ SSLBuffer innerHashCtx;
+ SSLBuffer currentHashCtx;
+};
+
+// MARK: -
+// MARK: Common HMAC routines
+
+/* Create an HMAC session */
+static int HMAC_Alloc(
+ const struct HMACReference *hmac,
+ const void *keyPtr,
+ size_t keyLen,
+ HMACContextRef *hmacCtx) // RETURNED
+{
+ const HashReference *digest;
+ HMACContextRef href;
+ size_t ix;
+ uint8_t *context;
+ const uint8_t *key;
+ size_t digest_block_size;
+
+ switch(hmac->alg) {
+ case HA_SHA384:
+ digest = &SSLHashSHA384;
+ digest_block_size = 128;
+ break;
+ case HA_SHA256:
+ digest = &SSLHashSHA256;
+ digest_block_size = 64;
+ break;
+ case HA_SHA1:
+ digest = &SSLHashSHA1;
+ digest_block_size = 64;
+ break;
+ case HA_MD5:
+ digest = &SSLHashMD5;
+ digest_block_size = 64;
+ break;
+ default:
+ check(0);
+ return -1;
+ }
+
+ context = (uint8_t *)sslMalloc(sizeof(struct HMACContext) +
+ 3 * digest->contextSize);
+ if(context == NULL) {
+ check(0);
+ return -1;
+ }
+ href = (HMACContextRef)context;
+ href->digest = digest;
+ href->outerHashCtx.data = context + sizeof(*href);
+ href->outerHashCtx.length = digest->contextSize;
+ href->innerHashCtx.data = href->outerHashCtx.data + digest->contextSize;
+ href->innerHashCtx.length = digest->contextSize;
+ href->currentHashCtx.data = href->innerHashCtx.data + digest->contextSize;
+ href->currentHashCtx.length = digest->contextSize;
+
+ digest->init(&href->outerHashCtx);
+ digest->init(&href->innerHashCtx);
+
+ uint8_t tmpkey[digest->digestSize];
+ uint8_t pad[digest_block_size];
+ SSLBuffer kpad = { digest_block_size, pad };
+
+ /* If the key is longer than digest_block_size, reset it to key=digest(key) */
+ if (keyLen <= digest_block_size) {
+ key = (const uint8_t *)keyPtr;
+ } else {
+ SSLBuffer keyBuffer = { keyLen, (uint8_t *)keyPtr };
+ SSLBuffer outBuffer = { digest->digestSize, tmpkey };
+ digest->update(&href->innerHashCtx, &keyBuffer);
+ digest->final(&href->innerHashCtx, &outBuffer);
+ key = outBuffer.data;
+ keyLen = outBuffer.length;
+ /* Re-initialize the inner context. */
+ digest->init(&href->innerHashCtx);
+ }
+
+ /* Copy the key into k_opad while doing the XOR. */
+ for (ix = 0; ix < keyLen; ++ix)
+ pad[ix] = key[ix] ^ 0x5c;
+ memset(pad + keyLen, 0x5c, digest_block_size - keyLen);
+ digest->update(&href->outerHashCtx, &kpad);
+
+ /* Copy the key into k_ipad while doing the XOR. */
+ for (ix = 0; ix < keyLen; ++ix)
+ pad[ix] = key[ix] ^ 0x36;
+ memset(pad + keyLen, 0x36, digest_block_size - keyLen);
+ digest->update(&href->innerHashCtx, &kpad);
+
+ /* Clear out the key bits in pad. */
+ bzero(pad, keyLen);
+
+ /* Now clone the inner digest so we are ready to receive an update. */
+ /* @@@ If init is always called before update we could skip this step. */
+ digest->clone(&href->innerHashCtx, &href->currentHashCtx);
+
+ /* success */
+ *hmacCtx = href;
+ return 0;
+}
+
+/* free a session */
+static int HMAC_Free(
+ HMACContextRef hmacCtx)
+{
+ if(hmacCtx != NULL) {
+ hmacCtx->digest->close(&hmacCtx->outerHashCtx);
+ hmacCtx->digest->close(&hmacCtx->innerHashCtx);
+ hmacCtx->digest->close(&hmacCtx->currentHashCtx);
+
+ /* Clear out any key material left in the digest contexts. */
+ bzero(hmacCtx->outerHashCtx.data, hmacCtx->outerHashCtx.length);
+ bzero(hmacCtx->innerHashCtx.data, hmacCtx->innerHashCtx.length);
+ bzero(hmacCtx->currentHashCtx.data, hmacCtx->currentHashCtx.length);
+
+ sslFree(hmacCtx);
+ }
+ return 0;
+}
+
+/* Reusable init */
+static int HMAC_Init(
+ HMACContextRef hmacCtx)
+{
+ if(hmacCtx == NULL) {
+ check(0);
+ return -1;
+ }
+
+ check(hmacCtx->digest != NULL);
+
+ hmacCtx->digest->close(&hmacCtx->currentHashCtx);
+ hmacCtx->digest->clone(&hmacCtx->innerHashCtx, &hmacCtx->currentHashCtx);
+
+ return 0;
+}
+
+/* normal crypt ops */
+static int HMAC_Update(
+ HMACContextRef hmacCtx,
+ const void *data,
+ size_t dataLen)
+{
+ SSLBuffer cdata = { dataLen, (uint8_t *)data };
+ if(hmacCtx == NULL) {
+ check(0);
+ return -1;
+ }
+
+ check(hmacCtx->digest != NULL);
+
+ hmacCtx->digest->update(&hmacCtx->currentHashCtx, &cdata);
+
+ return 0;
+}
+
+static int HMAC_Final(
+ HMACContextRef hmacCtx,
+ void *hmac, // mallocd by caller
+ size_t *hmacLen) // IN/OUT
+{
+ uint8_t bytes[TLS_HMAC_MAX_SIZE];
+ SSLBuffer digest = { TLS_HMAC_MAX_SIZE, bytes };
+ SSLBuffer cdata;
+
+ if(hmacCtx == NULL) {
+ check(0);
+ return -1;
+ }
+ if((hmac == NULL) || (hmacLen == NULL)) {
+ check(0);
+ return -1;
+ }
+ check(hmacCtx->digest != NULL);
+ check(*hmacLen >= hmacCtx->digest->digestSize);
+
+ cdata.length = *hmacLen;
+ cdata.data = (uint8_t *)hmac;
+
+ hmacCtx->digest->final(&hmacCtx->currentHashCtx, &digest);
+ hmacCtx->digest->clone(&hmacCtx->outerHashCtx, &hmacCtx->currentHashCtx);
+ hmacCtx->digest->update(&hmacCtx->currentHashCtx, &digest);
+ bzero(bytes, hmacCtx->digest->digestSize);
+ hmacCtx->digest->final(&hmacCtx->currentHashCtx, &cdata);
+ *hmacLen = hmacCtx->digest->digestSize;
+
+ return 0;
+}
+
+/* one-shot */
+static int HMAC_Hmac (
+ HMACContextRef hmacCtx,
+ const void *data,
+ size_t dataLen,
+ void *hmac, // mallocd by caller
+ size_t *hmacLen) // IN/OUT
+{
+ int serr;
+
+ if(hmacCtx == NULL) {
+ check(0);
+ return -1;
+ }
+ serr = HMAC_Init(hmacCtx);
+ if(serr) {
+ return serr;
+ }
+ serr = HMAC_Update(hmacCtx, data, dataLen);
+ if(serr) {
+ return serr;
+ }
+ return HMAC_Final(hmacCtx, hmac, hmacLen);
+}
+
+
+// MARK: -
+// MARK: Null HMAC
+
+static int HMAC_AllocNull(
+ const struct HMACReference *hmac,
+ const void *keyPtr,
+ size_t keyLen,
+ HMACContextRef *hmacCtx) // RETURNED
+{
+ *hmacCtx = NULL;
+ return 0;
+}
+
+static int HMAC_FreeNull(
+ HMACContextRef hmacCtx)
+{
+ return 0;
+}
+
+static int HMAC_InitNull(
+ HMACContextRef hmacCtx)
+ {
+ return 0;
+}
+
+static int HMAC_UpdateNull(
+ HMACContextRef hmacCtx,
+ const void *data,
+ size_t dataLen)
+{
+ return 0;
+}
+
+static int HMAC_FinalNull(
+ HMACContextRef hmacCtx,
+ void *hmac, // mallocd by caller
+ size_t *hmacLen) // IN/OUT
+{
+ return 0;
+}
+
+static int HMAC_HmacNull (
+ HMACContextRef hmacCtx,
+ const void *data,
+ size_t dataLen,
+ void *hmac, // mallocd by caller
+ size_t *hmacLen)
+{
+ return 0;
+}
+
+const HMACReference TlsHmacNull = {
+ 0,
+ HA_Null,
+ HMAC_AllocNull,
+ HMAC_FreeNull,
+ HMAC_InitNull,
+ HMAC_UpdateNull,
+ HMAC_FinalNull,
+ HMAC_HmacNull
+};
+
+const HMACReference TlsHmacMD5 = {
+ 16,
+ HA_MD5,
+ HMAC_Alloc,
+ HMAC_Free,
+ HMAC_Init,
+ HMAC_Update,
+ HMAC_Final,
+ HMAC_Hmac
+};
+
+const HMACReference TlsHmacSHA1 = {
+ 20,
+ HA_SHA1,
+ HMAC_Alloc,
+ HMAC_Free,
+ HMAC_Init,
+ HMAC_Update,
+ HMAC_Final,
+ HMAC_Hmac
+};
+
+const HMACReference TlsHmacSHA256 = {
+ 32,
+ HA_SHA256,
+ HMAC_Alloc,
+ HMAC_Free,
+ HMAC_Init,
+ HMAC_Update,
+ HMAC_Final,
+ HMAC_Hmac
+};
+
+const HMACReference TlsHmacSHA384 = {
+ 48,
+ HA_SHA384,
+ HMAC_Alloc,
+ HMAC_Free,
+ HMAC_Init,
+ HMAC_Update,
+ HMAC_Final,
+ HMAC_Hmac
+};