+/*
+ * Copyright (c) 1999-2001,2005-2008,2010-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@
+ */
+
+/*
+ * symCipher.c - CommonCrypto-based symmetric cipher module
+ */
+
+/* THIS FILE CONTAINS KERNEL CODE */
+
+#include "sslBuildFlags.h"
+#include "sslDebug.h"
+#include "sslMemory.h"
+#include "symCipher.h"
+#include "cipherSpecs.h"
+#include "SSLRecordInternal.h"
+
+#ifdef KERNEL
+
+#include <corecrypto/ccaes.h>
+#include <corecrypto/ccdes.h>
+#include <corecrypto/ccmode.h>
+
+#include <AssertMacros.h>
+
+struct SymCipherContext {
+ const struct ccmode_cbc *cbc;
+ cccbc_ctx u[]; /* this will have the key and iv */
+};
+
+/* Macros for accessing the content of a SymCipherContext */
+
+/*
+ SymCipherContext looks like this in memory:
+
+ {
+ const struct ccmode_cbc *cbc;
+ cccbc_ctx key[n];
+ cccbc_iv iv[m];
+ }
+
+ cccbc_ctx and cccbc_iv are typedef-ined as aligned opaque struct, the actual contexts are arrays
+ of those types normally declared with a cc_ctx_decl macro.
+ The cc_ctx_n macros gives the number of elements in those arrays that are needed to store the
+ contexts.
+ The size of the context depends on the actual cbc implementation used.
+*/
+
+
+/* CTX_SIZE: Total size of the SymCipherContext struct for a cbc implementation */
+static inline
+size_t CTX_SIZE(const struct ccmode_cbc *cbc)
+{
+#ifdef __CC_HAS_FIX_FOR_11468135__
+ return (sizeof(SymCipherContext) + (sizeof(cccbc_ctx) * (cc_ctx_n(cccbc_ctx, cbc->size) + cc_ctx_n(cccbc_iv, cbc->block_size))));
+#else
+ /* This is approximate, but will work in that case, this code will go away after we transition */
+ return (sizeof(SymCipherContext) + sizeof(cccbc_ctx) + cbc->size);
+#endif
+}
+
+/* CTX_KEY: Address of the key context in the SymCipherContext struct */
+static inline
+cccbc_ctx *CTX_KEY(struct SymCipherContext *ctx)
+{
+ return &ctx->u[0];
+}
+
+
+/* CTX_IV: Address of the iv context in the SymCipherContext struct */
+#ifdef __CC_HAS_FIX_FOR_11468135__
+static inline
+cccbc_iv *CTX_IV(struct SymCipherContext *ctx)
+{
+ return (cccbc_iv *)&ctx->u[cc_ctx_n(cccbc_ctx, ctx->cbc->size)];
+}
+#endif
+
+static
+const void *ccmode(SSL_CipherAlgorithm alg, int enc)
+{
+ switch(alg) {
+ case SSL_CipherAlgorithmAES_128_CBC:
+ case SSL_CipherAlgorithmAES_256_CBC:
+ return enc?ccaes_cbc_encrypt_mode():ccaes_cbc_decrypt_mode();
+ case SSL_CipherAlgorithm3DES_CBC:
+ return enc?ccdes3_cbc_encrypt_mode():ccdes3_cbc_decrypt_mode();
+ case SSL_CipherAlgorithmAES_128_GCM:
+ case SSL_CipherAlgorithmAES_256_GCM:
+ case SSL_CipherAlgorithmRC4_128:
+ /* TODO: we should do RC4 for TLS, but we dont need it for DTLS */
+ default:
+ check(0);
+ return NULL; /* This will cause CCCryptorCreate to return an error */
+ }
+}
+
+static
+int CCSymmInit(
+ const SSLSymmetricCipherParams *params,
+ int encrypting,
+ uint8_t *key,
+ uint8_t* iv,
+ SymCipherContext *cipherCtx)
+{
+ check(cipherCtx!=NULL);
+ check(params);
+ SymCipherContext ctx = *cipherCtx;
+
+ /*
+ * Cook up a cccbx_ctx object. Assumes:
+ * cipherCtx->symCipher.keyAlg
+ * cipherCtx->encrypting
+ * key (raw key bytes)
+ * iv (raw bytes)
+ * On successful exit:
+ * Resulting ccmode --> cipherCtx
+ */
+
+ /* FIXME: this should not be needed as long as CCSymFinish is called */
+ if(ctx) {
+ sslFree(ctx);
+ ctx = NULL;
+ }
+
+ const struct ccmode_cbc *cbc = ccmode(params->keyAlg, encrypting);
+
+ ctx = sslMalloc(CTX_SIZE(cbc));
+
+ if(ctx==NULL) {
+ sslErrorLog("CCSymmInit: Can't allocate context\n");
+ return errSSLRecordInternal;
+ }
+
+ ctx->cbc = cbc;
+
+#ifdef __CC_HAS_FIX_FOR_11468135__
+ cccbc_init(cbc, CTX_KEY(ctx), params->keySize, key);
+ cccbc_set_iv(cbc, CTX_IV(ctx), iv);
+#else
+ cccbc_init(cbc, CTX_KEY(ctx), params->keySize, key, iv);
+#endif
+
+ *cipherCtx = ctx;
+ return 0;
+}
+
+/* same for en/decrypt */
+static
+int CCSymmEncryptDecrypt(
+ const uint8_t *src,
+ uint8_t *dest,
+ size_t len,
+ SymCipherContext cipherCtx)
+{
+
+ ASSERT(cipherCtx != NULL);
+ ASSERT(cipherCtx->cbc != NULL);
+
+ if(cipherCtx == NULL || cipherCtx->cbc == NULL) {
+ sslErrorLog("CCSymmEncryptDecrypt: NULL cipherCtx\n");
+ return errSSLRecordInternal;
+ }
+
+ ASSERT((len%cipherCtx->cbc->block_size)==0);
+
+ if(len%cipherCtx->cbc->block_size) {
+ sslErrorLog("CCSymmEncryptDecrypt: Invalid size\n");
+ return errSSLRecordInternal;
+ }
+
+ unsigned long nblocks = len/cipherCtx->cbc->block_size;
+
+#ifdef __CC_HAS_FIX_FOR_11468135__
+ cccbc_update(cipherCtx->cbc, CTX_KEY(cipherCtx), CTX_IV(cipherCtx), nblocks, src, dest);
+#else
+ cipherCtx->cbc->cbc(CTX_KEY(cipherCtx), nblocks, src, dest);
+#endif
+ return 0;
+}
+
+static
+int CCSymmFinish(
+ SymCipherContext cipherCtx)
+{
+ if(cipherCtx) {
+ sslFree(cipherCtx);
+ }
+ return 0;
+}
+
+
+#else
+
+#define ENABLE_RC4 1
+#define ENABLE_3DES 1
+#define ENABLE_AES 1
+#define ENABLE_AES256 1
+
+/*
+ * CommonCrypto-based symmetric cipher callouts
+ */
+#include <CommonCrypto/CommonCryptor.h>
+#include <CommonCrypto/CommonCryptorSPI.h>
+#include <assert.h>
+
+static
+CCAlgorithm CCAlg(SSL_CipherAlgorithm alg)
+{
+ switch(alg) {
+ case SSL_CipherAlgorithmAES_128_CBC:
+ case SSL_CipherAlgorithmAES_256_CBC:
+ case SSL_CipherAlgorithmAES_128_GCM:
+ case SSL_CipherAlgorithmAES_256_GCM:
+ return kCCAlgorithmAES128; /* AES128 here means 128bit block size, not key size */
+ case SSL_CipherAlgorithm3DES_CBC:
+ return kCCAlgorithm3DES;
+ case SSL_CipherAlgorithmDES_CBC:
+ return kCCAlgorithmDES;
+ case SSL_CipherAlgorithmRC4_128:
+ return kCCAlgorithmRC4;
+ case SSL_CipherAlgorithmRC2_128:
+ return kCCAlgorithmRC2;
+ default:
+ assert(0);
+ return (CCAlgorithm)(-1); /* This will cause CCCryptorCreate to return an error */
+ }
+}
+
+static CCOptions CCOpt(CipherType cipherType)
+{
+#if 0
+ if(cipherType==aeadCipherType) return kCCModeGCM;
+#endif
+ return 0;
+}
+
+static
+int CCSymmInit(
+ const SSLSymmetricCipherParams *params,
+ int encrypting,
+ uint8_t *key,
+ uint8_t* iv,
+ SymCipherContext *cipherCtx)
+{
+ assert(cipherCtx!=NULL);
+
+ /*
+ * Cook up a CCCryptorRef. Assumes:
+ * cipherCtx->symCipher.keyAlg
+ * cipherCtx->encrypting
+ * key (raw key bytes)
+ * iv (raw bytes)
+ * On successful exit:
+ * Resulting CCCryptorRef --> cipherCtx->cryptorRef
+ */
+ CCCryptorStatus ccrtn;
+ CCOperation op = encrypting ? kCCEncrypt : kCCDecrypt;
+ CCCryptorRef cryptorRef = (CCCryptorRef)*cipherCtx;
+
+ /* FIXME: this should not be needed as long as CCSymFinish is called */
+ if(cryptorRef) {
+ CCCryptorRelease(cryptorRef);
+ cryptorRef = NULL;
+ }
+
+ ccrtn = CCCryptorCreate(op, CCAlg(params->keyAlg),
+ /* options - gcm or no padding, default CBC */
+ CCOpt(params->cipherType),
+ key, params->keySize,
+ iv,
+ &cryptorRef);
+ if(ccrtn) {
+ sslErrorLog("CCCryptorCreate returned %d\n", (int)ccrtn);
+ return errSSLRecordInternal;
+ }
+ *cipherCtx = (SymCipherContext)cryptorRef;
+ return 0;
+}
+
+/* same for en/decrypt */
+static
+int CCSymmEncryptDecrypt(
+ const uint8_t *src,
+ uint8_t *dest,
+ size_t len,
+ SymCipherContext cipherCtx)
+{
+ CCCryptorStatus ccrtn;
+ CCCryptorRef cryptorRef = (CCCryptorRef)cipherCtx;
+ ASSERT(cryptorRef != NULL);
+ if(cryptorRef == NULL) {
+ sslErrorLog("CCSymmEncryptDecrypt: NULL cryptorRef\n");
+ return errSSLRecordInternal;
+ }
+ size_t data_moved;
+ ccrtn = CCCryptorUpdate(cryptorRef, src, len,
+ dest, len, &data_moved);
+ assert(data_moved == len);
+#if SSL_DEBUG
+ if(ccrtn) {
+ sslErrorLog("CCSymmEncryptDecrypt: returned %d\n", (int)ccrtn);
+ return errSSLRecordInternal;
+ }
+#endif
+ return 0;
+}
+
+#if ENABLE_AES_GCM
+
+/* same for en/decrypt */
+static
+int CCSymmAEADSetIV(
+ const uint8_t *iv,
+ size_t len,
+ SymCipherContext cipherCtx)
+{
+ CCCryptorStatus ccrtn;
+ CCCryptorRef cryptorRef = (CCCryptorRef)cipherCtx;
+
+ ASSERT(cryptorRef != NULL);
+ if(cryptorRef == NULL) {
+ sslErrorLog("CCSymmAEADAddIV: NULL cryptorRef\n");
+ return errSecInternalComponent;
+ }
+ ccrtn = CCCryptorGCMAddIV(cryptorRef, iv, len);
+#if SSL_DEBUG
+ if(ccrtn) {
+ sslErrorLog("CCSymmAEADAddIV: returned %d\n", (int)ccrtn);
+ return errSSLRecordInternal;
+ }
+#endif
+ return 0;
+}
+
+/* same for en/decrypt */
+static
+int CCSymmAddADD(
+ const uint8_t *src,
+ size_t len,
+ SymCipherContext cipherCtx)
+{
+ CCCryptorStatus ccrtn;
+ CCCryptorRef cryptorRef = (CCCryptorRef)cipherCtx;
+
+ ASSERT(cryptorRef != NULL);
+ if(cryptorRef == NULL) {
+ sslErrorLog("CCSymmAddADD: NULL cryptorRef\n");
+ return errSSLRecordInternal;
+ }
+ ccrtn = CCCryptorGCMAddADD(cryptorRef, src, len);
+#if SSL_DEBUG
+ if(ccrtn) {
+ sslErrorLog("CCSymmAddADD: returned %d\n", (int)ccrtn);
+ return errSSLRecordInternal;
+ }
+#endif
+ return 0;
+}
+
+static
+int CCSymmAEADEncrypt(
+ const uint8_t *src,
+ uint8_t *dest,
+ size_t len,
+ SymCipherContext cipherCtx)
+{
+ CCCryptorStatus ccrtn;
+ CCCryptorRef cryptorRef = (CCCryptorRef)cipherCtx;
+
+ ASSERT(cryptorRef != NULL);
+ if(cryptorRef == NULL) {
+ sslErrorLog("CCSymmAEADEncrypt: NULL cryptorRef\n");
+ return errSSLRecordInternal;
+ }
+ ccrtn = CCCryptorGCMEncrypt(cryptorRef, src, len, dest);
+#if SSL_DEBUG
+ if(ccrtn) {
+ sslErrorLog("CCSymmAEADEncrypt: returned %d\n", (int)ccrtn);
+ return errSSLRecordInternal;
+ }
+#endif
+ return 0;
+}
+
+
+static
+int CCSymmAEADDecrypt(
+ const uint8_t *src,
+ uint8_t *dest,
+ size_t len,
+ SymCipherContext cipherCtx)
+{
+ CCCryptorStatus ccrtn;
+ CCCryptorRef cryptorRef = (CCCryptorRef)cipherCtx;
+
+ ASSERT(cipherCtx != NULL);
+ ASSERT(cipherCtx->cryptorRef != NULL);
+ if(cipherCtx->cryptorRef == NULL) {
+ sslErrorLog("CCSymmAEADDecrypt: NULL cryptorRef\n");
+ return errSSLRecordInternal;
+ }
+ ccrtn = CCCryptorGCMDecrypt(cryptorRef, src, len, dest);
+#if SSL_DEBUG
+ if(ccrtn) {
+ sslErrorLog("CCSymmAEADDecrypt: returned %d\n", (int)ccrtn);
+ return errSSLRecordInternal;
+ }
+#endif
+ return 0;
+}
+
+
+static
+int CCSymmAEADDone(
+ uint8_t *mac,
+ size_t *macLen,
+ SymCipherContext cipherCtx)
+{
+ CCCryptorStatus ccrtn;
+ CCCryptorRef cryptorRef = (CCCryptorRef)cipherCtx;
+
+ ASSERT(cipherCtx != NULL);
+ ASSERT(cipherCtx->cryptorRef != NULL);
+ if(cipherCtx->cryptorRef == NULL) {
+ sslErrorLog("CCSymmAEADDone: NULL cryptorRef\n");
+ return errSSLRecordInternal;
+ }
+ ccrtn = CCCryptorGCMFinal(cipherCtx->cryptorRef, mac, macLen);
+ CCCryptorStatus ccrtn2 = CCCryptorGCMReset(cipherCtx->cryptorRef);
+ if (ccrtn == kCCSuccess)
+ ccrtn = ccrtn2;
+#if SSL_DEBUG
+ if(ccrtn) {
+ sslErrorLog("CCSymmAEADDone: returned %d\n", (int)ccrtn);
+ return errSSLRecordInternal;
+ }
+#endif
+ return 0;
+}
+#endif
+
+static
+int CCSymmFinish(
+ SymCipherContext cipherCtx)
+{
+ CCCryptorRef cryptorRef = (CCCryptorRef)cipherCtx;
+
+ if(cryptorRef) {
+ CCCryptorRelease(cryptorRef);
+ }
+ return 0;
+}
+
+#endif /* KERNEL */
+
+#if ENABLE_DES
+const SSLSymmetricCipher SSLCipherDES_CBC = {
+ .params = &SSLCipherDES_CBCParams,
+ .c.cipher = {
+ .initialize = CCSymmInit,
+ .encrypt = CCSymmEncryptDecrypt,
+ .decrypt = CCSymmEncryptDecrypt
+ },
+ .finish = CCSymmFinish
+};
+#endif /* ENABLE_DES */
+
+#if ENABLE_3DES
+const SSLSymmetricCipher SSLCipher3DES_CBC = {
+ .params = &SSLCipher3DES_CBCParams,
+ .c.cipher = {
+ .initialize = CCSymmInit,
+ .encrypt = CCSymmEncryptDecrypt,
+ .decrypt = CCSymmEncryptDecrypt
+ },
+ .finish = CCSymmFinish
+};
+#endif /* ENABLE_3DES */
+
+#if ENABLE_RC4
+const SSLSymmetricCipher SSLCipherRC4_128 = {
+ .params = &SSLCipherRC4_128Params,
+ .c.cipher = {
+ .initialize = CCSymmInit,
+ .encrypt = CCSymmEncryptDecrypt,
+ .decrypt = CCSymmEncryptDecrypt
+ },
+ .finish = CCSymmFinish
+};
+#endif /* ENABLE_RC4 */
+
+#if ENABLE_RC2
+const SSLSymmetricCipher SSLCipherRC2_128 = {
+ .params = &SSLCipherRC2_128Params,
+ .c.cipher = {
+ .initialize = CCSymmInit,
+ .encrypt = CCSymmEncryptDecrypt,
+ .decrypt = CCSymmEncryptDecrypt
+ },
+ .finish = CCSymmFinish
+};
+#endif /* ENABLE_RC2*/
+
+#if ENABLE_AES
+const SSLSymmetricCipher SSLCipherAES_128_CBC = {
+ .params = &SSLCipherAES_128_CBCParams,
+ .c.cipher = {
+ .initialize = CCSymmInit,
+ .encrypt = CCSymmEncryptDecrypt,
+ .decrypt = CCSymmEncryptDecrypt
+ },
+ .finish = CCSymmFinish
+};
+#endif /* ENABLE_AES */
+
+#if ENABLE_AES256
+const SSLSymmetricCipher SSLCipherAES_256_CBC = {
+ .params = &SSLCipherAES_256_CBCParams,
+ .c.cipher = {
+ .initialize = CCSymmInit,
+ .encrypt = CCSymmEncryptDecrypt,
+ .decrypt = CCSymmEncryptDecrypt
+ },
+ .finish = CCSymmFinish
+};
+#endif /* ENABLE_AES256 */
+
+#if ENABLE_AES_GCM
+const SSLSymmetricCipher SSLCipherAES_128_GCM = {
+ .params = &SSLCipherAES_128_GCMParams,
+ .c.aead = {
+ .initialize = CCSymmInit,
+ .setIV = CCSymmAEADSetIV,
+ .update = CCSymmAddADD,
+ .encrypt = CCSymmAEADEncrypt,
+ .decrypt = CCSymmAEADDecrypt,
+ .done = CCSymmAEADDone
+ },
+ .finish = CCSymmFinish
+};
+
+const SSLSymmetricCipher SSLCipherAES_256_GCM = {
+ .params = &SSLCipherAES_256_GCMParams,
+ .c.aead = {
+ .initialize = CCSymmInit,
+ .setIV = CCSymmAEADSetIV,
+ .update = CCSymmAddADD,
+ .encrypt = CCSymmAEADEncrypt,
+ .decrypt = CCSymmAEADDecrypt,
+ .done = CCSymmAEADDone
+ },
+ .finish = CCSymmFinish
+};
+#endif /* ENABLE_AES_GCM */