symCipher.c   [plain text]


/*
 * Copyright (c) 1999-2001,2005-2008,2010-2012 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 */