appleCdsa.c   [plain text]


/*
 * Copyright (c) 2000-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@
 */

/*
 * appleCdsa.c - CDSA based implementation of sslCrypto.h interfaces.
 */

#include "ssl.h"
#if USE_CDSA_CRYPTO

#include "appleCdsa.h"
#include "sslCrypto.h"

#include "CipherSuite.h"
#include "sslContext.h"
#include "sslMemory.h"
#include "sslUtils.h"
#include "sslDebug.h"
#include "sslBER.h"
#include "ModuleAttacher.h"

#ifndef	_SSL_KEYCHAIN_H_
#include "sslKeychain.h"
#endif

#include <string.h>
#include <stdlib.h>
#include <assert.h>

#include <Security/cssm.h>
#include <Security/cssmapple.h>
#include <Security/Security.h>
#include <Security/SecTrustPriv.h>
#include <Security/SecPolicyPriv.h>
#include <Security/SecKeyPriv.h>

/* X.509 includes, from cssmapi */
#include <Security/x509defs.h>         /* x.509 function and type defs */
#include <Security/oidsalg.h>
#include <Security/oidscert.h>

#pragma mark -
#pragma mark Utilities

/*
 * Set up a Raw symmetric key with specified algorithm and key bits.
 */
OSStatus sslSetUpSymmKey(
	CSSM_KEY_PTR	symKey,
	CSSM_ALGORITHMS	alg,
	CSSM_KEYUSE		keyUse, 		// CSSM_KEYUSE_ENCRYPT, etc.
	CSSM_BOOL		copyKey,		// true: copy keyData   false: set by reference
	uint8 			*keyData,
	size_t		keyDataLen)		// in bytes
{
	OSStatus serr;
	CSSM_KEYHEADER *hdr;

	memset(symKey, 0, sizeof(CSSM_KEY));
	if(copyKey) {
		serr = stSetUpCssmData(&symKey->KeyData, keyDataLen);
		if(serr) {
			return serr;
		}
		memmove(symKey->KeyData.Data, keyData, keyDataLen);
	}
	else {
		symKey->KeyData.Data = keyData;
		symKey->KeyData.Length = keyDataLen;
	}

	/* set up the header */
	hdr = &symKey->KeyHeader;
	hdr->BlobType = CSSM_KEYBLOB_RAW;
	hdr->Format = CSSM_KEYBLOB_RAW_FORMAT_OCTET_STRING;
	hdr->AlgorithmId = alg;
	hdr->KeyClass = CSSM_KEYCLASS_SESSION_KEY;
	hdr->LogicalKeySizeInBits = keyDataLen * 8;
	hdr->KeyAttr = CSSM_KEYATTR_MODIFIABLE | CSSM_KEYATTR_EXTRACTABLE;
	hdr->KeyUsage = keyUse;
	hdr->WrapAlgorithmId = CSSM_ALGID_NONE;
	return noErr;
}

/*
 * Free a CSSM_KEY - its CSP resources, KCItemRef, and the key itself.
 */
OSStatus sslFreeKey(
	CSSM_CSP_HANDLE		cspHand,
	CSSM_KEY_PTR		*key,		/* so we can null it out */
	#if		ST_KC_KEYS_NEED_REF
	SecKeychainRef	*kcItem)
	#else
	void			*kcItem)
	#endif
{
	assert(key != NULL);

	if(*key != NULL) {
		if(cspHand != 0) {
			CSSM_FreeKey(cspHand, NULL, *key, CSSM_FALSE);
		}
		stAppFree(*key, NULL);		// key mallocd by CL using our callback
		*key = NULL;
	}
	#if		ST_KC_KEYS_NEED_REF
	if((kcItem != NULL) && (*kcItem != NULL)) {
		KCReleaseItem(kcItem);		/* does this NULL the referent? */
		*kcItem = NULL;
	}
	#endif
	return noErr;
}

/*
 * Standard app-level memory functions required by CDSA.
 */
void * stAppMalloc (size_t size, void *allocRef) {
	return( malloc(size) );
}
void stAppFree (void *mem_ptr, void *allocRef) {
	free(mem_ptr);
 	return;
}
void * stAppRealloc (void *ptr, size_t size, void *allocRef) {
	return( realloc( ptr, size ) );
}
void * stAppCalloc (uint32_t num, size_t size, void *allocRef) {
	return( calloc( num, size ) );
}

/*
 * Ensure there's a connection to ctx->cspHand. If there
 * already is one, fine.
 * Note that as of 12/18/00, we assume we're connected to
 * all modules all the time (since we do an attachToAll() in
 * SSLNewContext()).
 */
OSStatus attachToCsp(SSLContext *ctx)
{
	assert(ctx != NULL);
	if(ctx->cspHand != 0) {
		return noErr;
	}
	else {
		return errSSLModuleAttach;
	}
}

/*
 * Connect to TP, CL; reusable.
 */
OSStatus attachToCl(SSLContext *ctx)
{
	assert(ctx != NULL);
	if(ctx->clHand != 0) {
		return noErr;
	}
	else {
		return errSSLModuleAttach;
	}
}

OSStatus attachToTp(SSLContext *ctx)
{
	assert(ctx != NULL);
	if(ctx->tpHand != 0) {
		return noErr;
	}
	else {
		return errSSLModuleAttach;
	}
}

/*
 * Convenience function - attach to CSP, CL, TP. Reusable.
 */
OSStatus attachToAll(SSLContext *ctx)
{
	CSSM_RETURN crtn;

	assert(ctx != NULL);
	crtn = attachToModules(&ctx->cspHand, &ctx->clHand, &ctx->tpHand);
	if(crtn) {
	   return errSSLModuleAttach;
	}
	else {
		return noErr;
	}
}

OSStatus detachFromAll(SSLContext *ctx)
{
	#if	0
	/* No more, attachments are kept on a global basis */
	assert(ctx != NULL);
	if(ctx->cspHand != 0) {
		CSSM_ModuleDetach(ctx->cspHand);
		ctx->cspHand = 0;
	}
	if(ctx->tpHand != 0) {
		CSSM_ModuleDetach(ctx->tpHand);
		ctx->tpHand = 0;
	}
	if(ctx->clHand != 0) {
		CSSM_ModuleDetach(ctx->clHand);
		ctx->clHand = 0;
	}
	#endif	/* 0 */
	return noErr;
}

/*
 * Add a CSSM_ATTRIBUTE_RSA_BLINDING attribute to
 * specified crypto context.
 */
static CSSM_RETURN sslAddBlindingAttr(
	CSSM_CC_HANDLE ccHand)
{
	CSSM_CONTEXT_ATTRIBUTE	newAttr;
	CSSM_RETURN				crtn;

	newAttr.AttributeType     = CSSM_ATTRIBUTE_RSA_BLINDING;
	newAttr.AttributeLength   = sizeof(uint32_t);
	newAttr.Attribute.Uint32  = 1;
	crtn = CSSM_UpdateContextAttributes(ccHand, 1, &newAttr);
	if(crtn) {
		stPrintCdsaError("CSSM_UpdateContextAttributes", crtn);
	}
	return crtn;
}

/* Get CSP, key in CSSM format from a SecKeyRef */
static OSStatus sslGetKeyParts(
	SecKeyRef			keyRef,
	const CSSM_KEY		**cssmKey,
	CSSM_CSP_HANDLE		*cspHand)
{
	OSStatus ortn = SecKeyGetCSSMKey(keyRef, cssmKey);
	if(ortn) {
		sslErrorLog("sslGetKeyParts: SecKeyGetCSSMKey err %d\n",
			(int)ortn);
		return ortn;
	}
	ortn = SecKeyGetCSPHandle(keyRef, cspHand);
	if(ortn) {
		sslErrorLog("sslGetKeyParts: SecKeyGetCSPHandle err %d\n",
			(int)ortn);
	}
	return ortn;
}

/* Return the first certificate reference from the supplied array
 * whose data matches the given certificate, or NULL if none match.
 */
 static SecCertificateRef sslGetMatchingCertInArray(
	SecCertificateRef	certRef,
	CFArrayRef          certArray)
{
    CSSM_DATA certData;
    OSStatus ortn;
    int idx, count;

    if(certRef == NULL || certArray == NULL) {
        return NULL;
    }
    ortn = SecCertificateGetData(certRef, &certData);
    if(!ortn) {
        count = CFArrayGetCount(certArray);
        for(idx=0; idx<count; idx++) {
            CSSM_DATA aData = { 0, NULL };
            SecCertificateRef aCert = (SecCertificateRef)CFArrayGetValueAtIndex(certArray, idx);
            ortn = SecCertificateGetData(aCert, &aData);
            if (!ortn && aData.Length == certData.Length &&
                !memcmp(aData.Data, certData.Data, certData.Length)) {
                return aCert;
            }
        }
	}
    return NULL;
}

#pragma mark -
#pragma mark CSSM_DATA routines

CSSM_DATA_PTR stMallocCssmData(
	size_t size)
{
	CSSM_DATA_PTR rtn = (CSSM_DATA_PTR)stAppMalloc(sizeof(CSSM_DATA), NULL);

	if(rtn == NULL) {
		return NULL;
	}
	rtn->Length = size;
	if(size == 0) {
		rtn->Data = NULL;
	}
	else {
		rtn->Data = (uint8 *)stAppMalloc(size, NULL);
	}
	return rtn;
}

void stFreeCssmData(
	CSSM_DATA_PTR data,
	CSSM_BOOL freeStruct)
{
	if(data == NULL) {
		return;
	}
	if(data->Data != NULL) {
		stAppFree(data->Data, NULL);
		data->Data   = NULL;
	}
	data->Length = 0;
	if(freeStruct) {
		stAppFree(data, NULL);
	}
}

/*
 * Ensure that indicated CSSM_DATA_PTR can handle 'length' bytes of data.
 * Malloc the Data ptr if necessary.
 */
OSStatus stSetUpCssmData(
	CSSM_DATA_PTR 	data,
	size_t		length)
{
	assert(data != NULL);
	if(data->Length == 0) {
		data->Data = (uint8 *)stAppMalloc(length, NULL);
		if(data->Data == NULL) {
			return memFullErr;
		}
	}
	else if(data->Length < length) {
		sslErrorLog("stSetUpCssmData: length too small\n");
		return memFullErr;
	}
	data->Length = length;
	return noErr;
}

/* All signature ops are "raw", with digest step done by us */
static OSStatus sslKeyToSigAlg(
	const CSSM_KEY *cssmKey,
	CSSM_ALGORITHMS *sigAlg)	/* RETURNED */

{
	OSStatus ortn = noErr;
	switch(cssmKey->KeyHeader.AlgorithmId) {
		case CSSM_ALGID_RSA:
			*sigAlg = CSSM_ALGID_RSA;
			break;
		case CSSM_ALGID_DSA:
			*sigAlg = CSSM_ALGID_DSA;
			break;
		case CSSM_ALGID_ECDSA:
			*sigAlg = CSSM_ALGID_ECDSA;
			break;
		default:
			ortn = errSSLBadConfiguration;
			break;
	}
	return ortn;
}

#pragma mark -
#pragma mark Public CSP Functions

/*
 * Raw RSA/DSA sign/verify.
 */
OSStatus sslRawSign(
	SSLContext		*ctx,
	SecKeyRef		privKeyRef,
	const UInt8		*plainText,
	size_t			plainTextLen,
	UInt8			*sig,			// mallocd by caller; RETURNED
	size_t			sigLen,			// available
	size_t			*actualBytes)	// RETURNED
{
	CSSM_CC_HANDLE			sigHand = 0;
	CSSM_RETURN				crtn;
	OSStatus				serr;
	CSSM_DATA				sigData;
	CSSM_DATA				ptextData;
	CSSM_CSP_HANDLE			cspHand;
	const CSSM_KEY 			*privKey;
	const CSSM_ACCESS_CREDENTIALS	*creds;

	assert(ctx != NULL);
	if((privKeyRef == NULL)	||
	   (plainText == NULL)	||
	   (sig == NULL)		||
	   (actualBytes == NULL)) {
		sslErrorLog("sslRsaRawSign: bad arguments\n");
		return errSSLInternal;
	}
	*actualBytes = 0;

	/* Get CSP, signing key in CSSM format */
	serr = sslGetKeyParts(privKeyRef, &privKey, &cspHand);
	if(serr) {
		return serr;
	}
	assert(privKey->KeyHeader.KeyClass == CSSM_KEYCLASS_PRIVATE_KEY);

	CSSM_ALGORITHMS sigAlg;
	serr = sslKeyToSigAlg(privKey, &sigAlg);
	if(serr) {
		return serr;
	}

	/*
	 * Get default creds
	 * FIXME: per 3420180, this needs to allow app-specified creds via
	 * an new API
	 */
	serr = SecKeyGetCredentials(privKeyRef,
		CSSM_ACL_AUTHORIZATION_SIGN,
		kSecCredentialTypeDefault,
		&creds);
	if(serr) {
		sslErrorLog("sslRawSign: SecKeyGetCredentials err %lu\n", (unsigned long)serr);
		return serr;
	}

	crtn = CSSM_CSP_CreateSignatureContext(cspHand,
		sigAlg,
		creds,
		privKey,
		&sigHand);
	if(crtn) {
		stPrintCdsaError("CSSM_CSP_CreateSignatureContext (1)", crtn);
		return errSSLCrypto;
	}

	if((ctx->rsaBlindingEnable) &&
	   (privKey->KeyHeader.AlgorithmId == CSSM_ALGID_RSA)) {
		/*
		 * Turn on RSA blinding to defeat timing attacks
		 */
		crtn = sslAddBlindingAttr(sigHand);
		if(crtn) {
			return crtn;
		}
	}

	ptextData.Data = (uint8 *)plainText;
	ptextData.Length = plainTextLen;

	/* caller better get this right, or the SignData will fail */
	sigData.Data = sig;
	sigData.Length = sigLen;

	crtn = CSSM_SignData(sigHand,
		&ptextData,
		1,
		CSSM_ALGID_NONE,	// digestAlg for raw sign
		&sigData);
	if(crtn) {
		stPrintCdsaError("CSSM_SignData", crtn);
		serr = errSSLCrypto;
	}
	else {
		*actualBytes = sigData.Length;
		serr = noErr;
	}
	if(sigHand != 0) {
		CSSM_DeleteContext(sigHand);
	}
	return serr;
}

OSStatus sslRawVerify(
	SSLContext			*ctx,
	const SSLPubKey     *sslPubKey,
	const UInt8			*plainText,
	size_t				plainTextLen,
	const UInt8			*sig,
	size_t				sigLen)
{
	CSSM_CC_HANDLE			sigHand = 0;
	CSSM_RETURN				crtn;
	OSStatus				serr;
	CSSM_DATA				sigData;
	CSSM_DATA				ptextData;
	const CSSM_KEY          *pubKey;
	CSSM_CSP_HANDLE         cspHand;

	assert(ctx != NULL);
	if((sslPubKey == NULL)       ||
	   (sslPubKey->key == NULL)  ||
	   (sslPubKey->csp == 0)     ||
	   (plainText == NULL)       ||
	   (sig == NULL)) {
		sslErrorLog("sslRawVerify: bad arguments\n");
		return errSSLInternal;
	}

	pubKey = &sslPubKey->key;
	cspHand = sslPubKey->csp;

	CSSM_ALGORITHMS sigAlg;
	serr = sslKeyToSigAlg(pubKey, &sigAlg);
	if(serr) {
		return serr;
	}
	crtn = CSSM_CSP_CreateSignatureContext(cspHand,
		sigAlg,
		NULL,				// passPhrase
		pubKey,
		&sigHand);
	if(sigHand == 0) {
		stPrintCdsaError("CSSM_CSP_CreateSignatureContext (2)", crtn);
		return errSSLCrypto;
	}

	ptextData.Data = (uint8 *)plainText;
	ptextData.Length = plainTextLen;
	sigData.Data = (uint8 *)sig;
	sigData.Length = sigLen;

	crtn = CSSM_VerifyData(sigHand,
		&ptextData,
		1,
		CSSM_ALGID_NONE,		// digestAlg
		&sigData);
	if(crtn) {
		stPrintCdsaError("CSSM_VerifyData", crtn);
		serr = errSSLCrypto;
	}
	else {
		serr = noErr;
	}
	if(sigHand != 0) {
		CSSM_DeleteContext(sigHand);
	}
	return serr;
}

/*
 * Encrypt/Decrypt
 */
OSStatus sslRsaEncrypt(
	SSLContext			*ctx,
	const CSSM_KEY		*pubKey,
	CSSM_CSP_HANDLE		cspHand,
	CSSM_PADDING		padding,			// CSSM_PADDING_PKCS1, CSSM_PADDING_APPLE_SSLv2
	const UInt8			*plainText,
	size_t				plainTextLen,
	UInt8				*cipherText,		// mallocd by caller; RETURNED
	size_t				cipherTextLen,		// available
	size_t				*actualBytes)		// RETURNED
{
	CSSM_DATA 		ctextData = {0, NULL};
	CSSM_DATA 		ptextData;
	CSSM_DATA		remData = {0, NULL};
	CSSM_CC_HANDLE 	cryptHand = 0;
	OSStatus		serr = errSSLInternal;
	CSSM_RETURN		crtn;
	size_t			bytesMoved = 0;
	CSSM_ACCESS_CREDENTIALS	creds;

	assert(ctx != NULL);
	assert(actualBytes != NULL);
	*actualBytes = 0;

	if((pubKey == NULL) || (cspHand == 0)) {
		sslErrorLog("sslRsaEncrypt: bad pubKey/cspHand\n");
		return errSSLInternal;
	}
	assert(pubKey->KeyHeader.KeyClass == CSSM_KEYCLASS_PUBLIC_KEY);

	#if		RSA_PUB_KEY_USAGE_HACK
	((CSSM_KEY_PTR)pubKey)->KeyHeader.KeyUsage |= CSSM_KEYUSE_ENCRYPT;
	#endif
	memset(&creds, 0, sizeof(CSSM_ACCESS_CREDENTIALS));

	crtn = CSSM_CSP_CreateAsymmetricContext(cspHand,
		CSSM_ALGID_RSA,
		&creds,
		pubKey,
		padding,
		&cryptHand);
	if(crtn) {
		stPrintCdsaError("CSSM_CSP_CreateAsymmetricContext", crtn);
		return errSSLCrypto;
	}
	ptextData.Data = (uint8 *)plainText;
	ptextData.Length = plainTextLen;

	/*
	 * Have CSP malloc ciphertext
	 */
	crtn = CSSM_EncryptData(cryptHand,
		&ptextData,
		1,
		&ctextData,
		1,
		&bytesMoved,
		&remData);
	if(crtn == CSSM_OK) {
		/*
		 * ciphertext in both ctextData and remData; ensure it'll fit
		 * in caller's buf & copy
		 */
		if(bytesMoved > cipherTextLen) {
			sslErrorLog("sslRsaEncrypt overflow; cipherTextLen %lu bytesMoved %lu\n",
				cipherTextLen, bytesMoved);
			serr = errSSLCrypto;
		}
		else {
			size_t toMoveCtext;
			size_t toMoveRem;

			*actualBytes = bytesMoved;
			/*
			 * Snag valid data from ctextData - its length or bytesMoved,
			 * whichever is less
			 */
			if(ctextData.Length > bytesMoved) {
				/* everything's in ctext */
				toMoveCtext = bytesMoved;
				toMoveRem = 0;
			}
			else {
				/* must be some in remData too */
				toMoveCtext = ctextData.Length;
				toMoveRem = bytesMoved - toMoveCtext;		// remainder
			}
			if(toMoveCtext) {
				memmove(cipherText, ctextData.Data, toMoveCtext);
			}
			if(toMoveRem) {
				memmove(cipherText + toMoveCtext, remData.Data,
					toMoveRem);
			}
			serr = noErr;
		}
	}
	else {
		stPrintCdsaError("CSSM_EncryptData", crtn);
		serr = errSSLCrypto;
	}
	if(cryptHand != 0) {
		CSSM_DeleteContext(cryptHand);
	}

	/* free data mallocd by CSP */
	stFreeCssmData(&ctextData, CSSM_FALSE);
	stFreeCssmData(&remData, CSSM_FALSE);
	return serr;
}

OSStatus sslRsaDecrypt(
	SSLContext			*ctx,
	SecKeyRef			privKeyRef,
	CSSM_PADDING		padding,			// CSSM_PADDING_PKCS1, CSSM_PADDING_APPLE_SSLv2
	const UInt8			*cipherText,
	size_t				cipherTextLen,
	UInt8				*plainText,			// mallocd by caller; RETURNED
	size_t				plainTextLen,		// available
	size_t				*actualBytes)		// RETURNED
{
	CSSM_DATA 		ptextData = {0, NULL};
	CSSM_DATA 		ctextData;
	CSSM_DATA		remData = {0, NULL};
	CSSM_CC_HANDLE 	cryptHand = 0;
	OSStatus		serr = errSSLInternal;
	CSSM_RETURN		crtn;
	size_t			bytesMoved = 0;
	CSSM_CSP_HANDLE	cspHand;
	const CSSM_KEY 	*privKey;
	const CSSM_ACCESS_CREDENTIALS	*creds;

	assert(ctx != NULL);
	assert(actualBytes != NULL);
	*actualBytes = 0;

	if(privKeyRef == NULL) {
		sslErrorLog("sslRsaDecrypt: bad privKey\n");
		return errSSLInternal;
	}

	/* Get CSP, signing key in CSSM format */
	serr = sslGetKeyParts(privKeyRef, &privKey, &cspHand);
	if(serr) {
		return serr;
	}
	assert(privKey->KeyHeader.KeyClass == CSSM_KEYCLASS_PRIVATE_KEY);

	/*
	 * Get default creds
	 * FIXME: per 3420180, this needs to allow app-specified creds via
	 * an new API
	 */
	serr = SecKeyGetCredentials(privKeyRef,
		CSSM_ACL_AUTHORIZATION_DECRYPT,
		kSecCredentialTypeDefault,
		&creds);
	if(serr) {
		sslErrorLog("sslRsaDecrypt: SecKeyGetCredentials err %lu\n", (unsigned long)serr);
		return serr;
	}
	crtn = CSSM_CSP_CreateAsymmetricContext(cspHand,
		CSSM_ALGID_RSA,
		creds,
		privKey,
		padding,
		&cryptHand);
	if(crtn) {
		stPrintCdsaError("CSSM_CSP_CreateAsymmetricContext", crtn);
		return errSSLCrypto;
	}
	ctextData.Data = (uint8 *)cipherText;
	ctextData.Length = cipherTextLen;

	if((ctx->rsaBlindingEnable) &&
	   (privKey->KeyHeader.AlgorithmId == CSSM_ALGID_RSA)) {
		/*
		 * Turn on RSA blinding to defeat timing attacks
		 */
		crtn = sslAddBlindingAttr(cryptHand);
		if(crtn) {
			return crtn;
		}
	}

	/*
	 * Have CSP malloc plaintext
	 */
	crtn = CSSM_DecryptData(cryptHand,
		&ctextData,
		1,
		&ptextData,
		1,
		&bytesMoved,
		&remData);
	if(crtn == CSSM_OK) {
		/*
		 * plaintext in both ptextData and remData; ensure it'll fit
		 * in caller's buf & copy
		 */
		if(bytesMoved > plainTextLen) {
			sslErrorLog("sslRsaDecrypt overflow; plainTextLen %lu bytesMoved %lu\n",
				plainTextLen, bytesMoved);
			serr = errSSLCrypto;
		}
		else {
			size_t toMovePtext;
			size_t toMoveRem;

			*actualBytes = bytesMoved;
			/*
			 * Snag valid data from ptextData - its length or bytesMoved,
			 * whichever is less
			 */
			if(ptextData.Length > bytesMoved) {
				/* everything's in ptext */
				toMovePtext = bytesMoved;
				toMoveRem = 0;
			}
			else {
				/* must be some in remData too */
				toMovePtext = ptextData.Length;
				toMoveRem = bytesMoved - toMovePtext;		// remainder
			}
			if(toMovePtext) {
				memmove(plainText, ptextData.Data, toMovePtext);
			}
			if(toMoveRem) {
				memmove(plainText + toMovePtext, remData.Data,
					toMoveRem);
			}
			serr = noErr;
		}
	}
	else {
		stPrintCdsaError("CSSM_DecryptData", crtn);
		serr = errSSLCrypto;
	}
	if(cryptHand != 0) {
		CSSM_DeleteContext(cryptHand);
	}

	/* free data mallocd by CSP */
	stFreeCssmData(&ptextData, CSSM_FALSE);
	stFreeCssmData(&remData, CSSM_FALSE);
	return serr;
}

/*
 * Obtain size of key in bytes.
 */
uint32_t sslPrivKeyLengthInBytes(const SSLPrivKey *sslKey)
{
    const CSSM_KEY *cssmKey;

	assert(sslKey != NULL);
	assert(sslKey->key != NULL);
	err = SecKeyGetCSSMKey(sslKey->key, &cssmKey);
	if(err) {
		sslErrorLog("sslKeyLengthInBytes: SecKeyGetCSSMKey err %d\n", (int)err);
		return 0;
	}

	return (((cssmKey->KeyHeader.LogicalKeySizeInBits) + 7) / 8);
}

/*
 * Obtain size of key in bytes.
 */
uint32_t sslPubKeyLengthInBytes(const SSLPubKey *sslKey)
{
    const CSSM_KEY *cssmKey;

	assert(sslKey != NULL);
	assert(sslKey->key != NULL);

	return (((sslKey->key.KeyHeader.LogicalKeySizeInBits) + 7) / 8);
}


/*
 * Obtain maximum size of signature in bytes. A bit of a kludge; we could
 * ask the CSP to do this but that would be kind of expensive.
 */
OSStatus sslGetMaxSigSize(
	const CSSM_KEY	*privKey,
	uint32_t		*maxSigSize)
{
	OSStatus ortn = noErr;
	assert(privKey != NULL);
	assert(privKey->KeyHeader.KeyClass == CSSM_KEYCLASS_PRIVATE_KEY);
	switch(privKey->KeyHeader.AlgorithmId) {
		case CSSM_ALGID_RSA:
			*maxSigSize = sslPrivKeyLengthInBytes(privKey);
			break;
		case CSSM_ALGID_DSA:
		{
			/* DSA sig is DER sequence of two 160-bit integers */
			uint32_t sizeOfOneInt;
			sizeOfOneInt = (160 / 8) +	// the raw contents
							1 +			// possible leading zero
							2;			// tag + length (assume DER, not BER)
			*maxSigSize = (2 * sizeOfOneInt) + 5;
			break;
		}
		default:
			ortn = errSSLBadConfiguration;
			break;
	}
	return ortn;
}
/*
 * Get raw key bits from an RSA public key.
 */
OSStatus sslGetPubKeyBits(
	SSLContext			*ctx,
	const CSSM_KEY		*pubKey,
	CSSM_CSP_HANDLE		cspHand,
	SSLBuffer			*modulus,		// data mallocd and RETURNED
	SSLBuffer			*exponent)		// data mallocd and RETURNED
{
	CSSM_KEY			wrappedKey;
	CSSM_BOOL			didWrap = CSSM_FALSE;
	const CSSM_KEYHEADER *hdr;
	SSLBuffer			pubKeyBlob;
	OSStatus			srtn;

	assert(ctx != NULL);
	assert(modulus != NULL);
	assert(exponent != NULL);
	assert(pubKey != NULL);

	hdr = &pubKey->KeyHeader;
	if(hdr->KeyClass != CSSM_KEYCLASS_PUBLIC_KEY) {
		sslErrorLog("sslGetPubKeyBits: bad keyClass (%ld)\n", (long)hdr->KeyClass);
		return errSSLInternal;
	}
	if(hdr->AlgorithmId != CSSM_ALGID_RSA) {
		sslErrorLog("sslGetPubKeyBits: bad AlgorithmId (%ld)\n", (long)hdr->AlgorithmId);
		return errSSLInternal;
	}

	/* Note currently ALL public keys are raw, obtained from the CL... */
	assert(hdr->BlobType == CSSM_KEYBLOB_RAW);

	/*
	 * Handle possible reference format - I think it should be in
	 * blob form since it came from the DL, but conversion is
	 * simple.
	 */
	switch(hdr->BlobType) {
		case CSSM_KEYBLOB_RAW:
			/* easy case */
			CSSM_TO_SSLBUF(&pubKey->KeyData, &pubKeyBlob);
			break;

		case CSSM_KEYBLOB_REFERENCE:

			sslErrorLog("sslGetPubKeyBits: bad BlobType (%ld)\n",
				(long)hdr->BlobType);
			return errSSLInternal;

			#if 0
			/*
			 * Convert to a blob via "NULL wrap"; no wrapping key,
			 * ALGID_NONE
			 */
			srtn = attachToCsp(ctx);
			if(srtn) {
				return srtn;
			}
			memset(&creds, 0, sizeof(CSSM_ACCESS_CREDENTIALS));
			crtn = CSSM_CSP_CreateSymmetricContext(ctx->cspHand,
					CSSM_ALGID_NONE,
					CSSM_ALGMODE_NONE,
					&creds,			// creds
					pubKey,
					NULL,			// InitVector
					CSSM_PADDING_NONE,
					0,			// reserved
					&ccHand);
			if(crtn) {
				stPrintCdsaError("sslGetPubKeyBits: CreateSymmetricContext failure", crtn);
				return errSSLCrypto;
			}
			memset(&wrappedKey, 0, sizeof(CSSM_KEY));
			crtn = CSSM_WrapKey(ccHand,
				&creds,
				pubKey,
				NULL,			// descriptiveData
				&wrappedKey);
			CSSM_DeleteContext(ccHand);
			if(crtn) {
				stPrintCdsaError("CSSM_WrapKey", crtn);
				return errSSLCrypto;
			}
			hdr = &wrappedKey.KeyHeader;
			if(hdr->BlobType != CSSM_KEYBLOB_RAW) {
				sslErrorLog("sslGetPubKeyBits: bad BlobType (%ld) after WrapKey\n",
					hdr->BlobType);
				return errSSLCrypto;
			}
			didWrap = CSSM_TRUE;
			CSSM_TO_SSLBUF(&wrappedKey.KeyData, &pubKeyBlob);
			break;
			#endif	/* 0 */

		default:
			sslErrorLog("sslGetPubKeyBits: bad BlobType (%ld)\n",
				(long)hdr->BlobType);
			return errSSLInternal;

	}	/* switch BlobType */

	assert(hdr->BlobType == CSSM_KEYBLOB_RAW);
	srtn = sslDecodeRsaBlob(&pubKeyBlob, modulus, exponent);
	if(didWrap) {
		CSSM_FreeKey(ctx->cspHand, NULL, &wrappedKey, CSSM_FALSE);
	}
	return srtn;
}

/*
 * Given raw RSA key bits, cook up a CSSM_KEY_PTR. Used in
 * Server-initiated key exchange.
 */
OSStatus sslGetPubKeyFromBits(
	SSLContext			*ctx,
	const SSLBuffer		*modulus,
	const SSLBuffer		*exponent,
	CSSM_KEY_PTR		*pubKey,		// mallocd and RETURNED
	CSSM_CSP_HANDLE		*cspHand)		// RETURNED
{
	CSSM_KEY_PTR		key = NULL;
	OSStatus				serr;
	SSLBuffer			blob;
	CSSM_KEYHEADER_PTR	hdr;
	CSSM_KEY_SIZE		keySize;
	CSSM_RETURN			crtn;

	assert((ctx != NULL) && (modulus != NULL) && (exponent != NULL));
	assert((pubKey != NULL) && (cspHand != NULL));

	*pubKey = NULL;
	*cspHand = 0;

	serr = attachToCsp(ctx);
	if(serr) {
		return serr;
	}
	serr = sslEncodeRsaBlob(modulus, exponent, &blob);
	if(serr) {
		return serr;
	}

	/* the rest is boilerplate, cook up a good-looking public key */
	key = (CSSM_KEY_PTR)sslMalloc(sizeof(CSSM_KEY));
	if(key == NULL) {
		return memFullErr;
	}
	memset(key, 0, sizeof(CSSM_KEY));
	hdr = &key->KeyHeader;

    hdr->HeaderVersion = CSSM_KEYHEADER_VERSION;
    /* key_ptr->KeyHeader.CspId is unknown (remains 0) */
    hdr->BlobType = CSSM_KEYBLOB_RAW;
    hdr->AlgorithmId = CSSM_ALGID_RSA;
    hdr->Format = CSSM_KEYBLOB_RAW_FORMAT_PKCS1;
    hdr->KeyClass = CSSM_KEYCLASS_PUBLIC_KEY;
    /* comply with ASA requirements */
    hdr->KeyUsage = CSSM_KEYUSE_VERIFY;
    hdr->KeyAttr = CSSM_KEYATTR_EXTRACTABLE;
    /* key_ptr->KeyHeader.StartDate is unknown  (remains 0) */
    /* key_ptr->KeyHeader.EndDate is unknown  (remains 0) */
    hdr->WrapAlgorithmId = CSSM_ALGID_NONE;
    hdr->WrapMode = CSSM_ALGMODE_NONE;

	/* blob->data was mallocd by sslEncodeRsaBlob, pass it over to
	 * actual key */
	SSLBUF_TO_CSSM(&blob, &key->KeyData);

	/*
	 * Get keySizeInBits. This also serves to validate the key blob
	 * we just cooked up.
	 */
    crtn = CSSM_QueryKeySizeInBits(ctx->cspHand, CSSM_INVALID_HANDLE, key, &keySize);
	if(crtn) {
    	stPrintCdsaError("sslGetPubKeyFromBits: QueryKeySizeInBits\n", crtn);
		serr = errSSLCrypto;
    	goto abort;
	}

	/* success */
    hdr->LogicalKeySizeInBits = keySize.EffectiveKeySizeInBits;
    *pubKey = key;
    *cspHand = ctx->cspHand;
	return noErr;

abort:
	/* note this frees the blob */
	sslFreeKey(ctx->cspHand, &key, NULL);
	return serr;
}

#ifdef UNUSED_FUNCTIONS
/*
 * NULL-unwrap a raw key to a ref key. Caller must free the returned key.
 */
static OSStatus sslNullUnwrapKey(
	CSSM_CSP_HANDLE cspHand,
	CSSM_KEY_PTR rawKey,
	CSSM_KEY_PTR refKey)
{
	CSSM_DATA descData = {0, 0};
	CSSM_RETURN crtn;
	CSSM_CC_HANDLE ccHand;
	CSSM_ACCESS_CREDENTIALS	creds;
	CSSM_DATA labelData = {4, (uint8 *)"none"};
	uint32 keyAttr;
	OSStatus ortn = noErr;

	memset(&creds, 0, sizeof(CSSM_ACCESS_CREDENTIALS));
	memset(refKey, 0, sizeof(CSSM_KEY));

	crtn = CSSM_CSP_CreateSymmetricContext(cspHand,
			CSSM_ALGID_NONE,
			CSSM_ALGMODE_NONE,
			&creds,
			NULL,			// unwrappingKey
			NULL,			// initVector
			CSSM_PADDING_NONE,
			0,				// Params
			&ccHand);
	if(crtn) {
	stPrintCdsaError("sslNullUnwrapKey: CSSM_CSP_CreateSymmetricContext\n", crtn);
		return errSSLCrypto;
	}

	keyAttr = rawKey->KeyHeader.KeyAttr;
	keyAttr &= ~(CSSM_KEYATTR_ALWAYS_SENSITIVE | CSSM_KEYATTR_NEVER_EXTRACTABLE |
				 CSSM_KEYATTR_MODIFIABLE);
	keyAttr |= CSSM_KEYATTR_RETURN_REF;
	if(rawKey->KeyHeader.KeyClass == CSSM_KEYCLASS_PUBLIC_KEY) {
		/* CSP policy */
		keyAttr |= CSSM_KEYATTR_EXTRACTABLE;
	}
	crtn = CSSM_UnwrapKey(ccHand,
		NULL,				// PublicKey
		rawKey,
		rawKey->KeyHeader.KeyUsage,
		keyAttr,
		&labelData,
		NULL,				// CredAndAclEntry
		refKey,
		&descData);			// required
	if(crtn != CSSM_OK) {
	stPrintCdsaError("sslNullUnwrapKey: CSSM_UnwrapKey\n", crtn);
		ortn = errSSLCrypto;
	}
	if(CSSM_DeleteContext(ccHand)) {
		printf("CSSM_DeleteContext failure\n");
	}
	return crtn;
}
#endif

/*
 * NULL-wrap a ref key to a raw key. Caller must free the returned key.
 */
static OSStatus sslNullWrapKey(
	CSSM_CSP_HANDLE cspHand,
	CSSM_KEY_PTR refKey,
	CSSM_KEY_PTR rawKey)
{
	CSSM_DATA descData = {0, 0};
	CSSM_RETURN crtn;
	CSSM_CC_HANDLE ccHand;
	CSSM_ACCESS_CREDENTIALS	creds;
	uint32 keyAttr;
	OSStatus ortn = noErr;

	memset(&creds, 0, sizeof(CSSM_ACCESS_CREDENTIALS));
	memset(rawKey, 0, sizeof(CSSM_KEY));

	crtn = CSSM_CSP_CreateSymmetricContext(cspHand,
			CSSM_ALGID_NONE,
			CSSM_ALGMODE_NONE,
			&creds,
			NULL,			// unwrappingKey
			NULL,			// initVector
			CSSM_PADDING_NONE,
			0,				// Params
			&ccHand);
	if(crtn) {
	stPrintCdsaError("sslNullWrapKey: CSSM_CSP_CreateSymmetricContext\n", crtn);
		return errSSLCrypto;
	}

	keyAttr = rawKey->KeyHeader.KeyAttr;
	keyAttr &= ~(CSSM_KEYATTR_ALWAYS_SENSITIVE | CSSM_KEYATTR_NEVER_EXTRACTABLE |
				 CSSM_KEYATTR_MODIFIABLE);
	keyAttr |= CSSM_KEYATTR_RETURN_DATA | CSSM_KEYATTR_EXTRACTABLE;
	crtn = CSSM_WrapKey(ccHand,
		&creds,
		refKey,
		&descData,
		rawKey);
	if(crtn != CSSM_OK) {
	stPrintCdsaError("sslNullWrapKey: CSSM_WrapKey\n", crtn);
		ortn = errSSLCrypto;
	}
	if(CSSM_DeleteContext(ccHand)) {
		printf("CSSM_DeleteContext failure\n");
	}
	return crtn;
}

#pragma mark -
#pragma mark Public Certificate Functions

/*
 * Given a DER-encoded cert, obtain its public key as a CSSM_KEY_PTR.
 * Caller must CSSM_FreeKey and free the CSSM_KEY_PTR itself.
 *
 * For now, the returned cspHand is a copy of ctx->cspHand, so it
 * doesn't have to be detached later - this may change.
 *
 * Update: since CSSM_CL_CertGetKeyInfo() doesn't provide a means for
 * us to tell the CL what CSP to use, we really have no way of knowing
 * what is going on here...we return the process-wide (bare) cspHand,
 * which is currently always able to deal with this raw public key.
 */
OSStatus sslPubKeyFromCert(
	SSLContext 			*ctx,
	const SSLBuffer		*derCert,
	SSLPubKey           *pubKey)		// RETURNED
{
	OSStatus 			serr;
	CSSM_DATA		certData;
	CSSM_RETURN		crtn;

	assert(ctx != NULL);
	assert(pubKey != NULL);

	pubKey->key = NULL;
	pubKey->cspHand = 0;

	serr = attachToCl(ctx);
	if(serr) {
		return serr;
	}
	serr = attachToCsp(ctx);
	if(serr) {
		return serr;
	}
	SSLBUF_TO_CSSM(derCert, &certData);
	crtn = CSSM_CL_CertGetKeyInfo(ctx->clHand, &certData, &pubKey->key);
	if(crtn) {
		return errSSLBadCert;
	}
	else {
		pubKey->cspHand = ctx->cspHand;
		return noErr;
	}
}

/*
 * Release each element in a CFArray.
 */
static void sslReleaseArray(
	CFArrayRef a)
{
	CFIndex num = CFArrayGetCount(a);
	CFIndex dex;
	for(dex=0; dex<num; dex++) {
		CFTypeRef elmt = (CFTypeRef)CFArrayGetValueAtIndex(a, dex);
		secdebug("sslcert", "Freeing cert %p", elmt);
		CFRelease(elmt);
	}
}

/*
 * Verify a chain of DER-encoded certs.
 * Last cert in the chain is leaf.
 *
 * If arePeerCerts is true, host name verification is enabled and we
 * save the resulting SecTrustRef in ctx->peerSecTrust. Otherwise
 * we're just validating our own certs; no host name checking and
 * peerSecTrust is transient.
 */
 OSStatus sslVerifyCertChain(
	SSLContext				*ctx,
	const SSLCertificate	*certChain,
	bool					arePeerCerts)
{
	uint32_t					numCerts;
	int 						i;
	OSStatus					serr;
	SSLCertificate				*c = (SSLCertificate *)certChain;
	CSSM_RETURN					crtn;
	CSSM_APPLE_TP_SSL_OPTIONS	sslOpts;
	CSSM_APPLE_TP_ACTION_DATA	tpActionData;
	SecPolicyRef				policy = NULL;
	SecPolicySearchRef			policySearch = NULL;
	CFDataRef					actionData = NULL;
	CSSM_DATA					sslOptsData;
	CFMutableArrayRef			anchors = NULL;
	SecCertificateRef 			cert;			// only lives in CFArrayRefs
	SecTrustResultType			secTrustResult;
	CFMutableArrayRef			kcList = NULL;
	SecTrustRef					theTrust = NULL;

	if(ctx->peerSecTrust && arePeerCerts) {
		/* renegotiate - start with a new SecTrustRef */
		CFRelease(ctx->peerSecTrust);
		ctx->peerSecTrust = NULL;
	}

	numCerts = SSLGetCertificateChainLength(certChain);
	if(numCerts == 0) {
		/* nope */
		return errSSLBadCert;
	}

	/*
	 * SSLCertificate chain --> CFArrayRef of SecCertificateRefs.
	 * TP Cert group has root at the end, opposite of
	 * SSLCertificate chain.
	 */
	CFMutableArrayRef certGroup = CFArrayCreateMutable(NULL, numCerts,
		&kCFTypeArrayCallBacks);
	if(certGroup == NULL) {
		return memFullErr;
	}
	/* subsequent errors to errOut: */

	for(i=numCerts-1; i>=0; i--) {
		CSSM_DATA cdata;
		SSLBUF_TO_CSSM(&c->derCert, &cdata);
		serr = SecCertificateCreateFromData(&cdata,	CSSM_CERT_X_509v3,
			CSSM_CERT_ENCODING_DER, &cert);
		if(serr) {
			goto errOut;
		}
		/*
		 * Can't set a value at index i when there is an empty element
		 * at i=1!
		 */
		secdebug("sslcert", "Adding cert %p", cert);
		CFArrayInsertValueAtIndex(certGroup, 0, cert);
		c = c->next;
	}

	/*
	 * Cook up an SSL-specific SecPolicyRef. This will persists as part
	 * of the SecTrustRef object we'll be creating.
	 */
	serr = SecPolicySearchCreate(CSSM_CERT_X_509v3,
		&CSSMOID_APPLE_TP_SSL,
		NULL,
		&policySearch);
	if(serr) {
		sslErrorLog("***sslVerifyCertChain: SecPolicySearchCreate rtn %d\n",
			(int)serr);
		goto errOut;
	}
	serr = SecPolicySearchCopyNext(policySearch, &policy);
	if(serr) {
		sslErrorLog("***sslVerifyCertChain: SecPolicySearchCopyNext rtn %d\n",
			(int)serr);
		goto errOut;
	}
	sslOpts.Version = CSSM_APPLE_TP_SSL_OPTS_VERSION;
	if(arePeerCerts) {
		sslOpts.ServerNameLen = ctx->peerDomainNameLen;
		sslOpts.ServerName = ctx->peerDomainName;
	}
	else {
		sslOpts.ServerNameLen = 0;
		sslOpts.ServerName = NULL;
	}
	sslOpts.Flags = 0;
	if(ctx->protocolSide == kSSLServerSide) {
		/* we're evaluating a client cert */
		sslOpts.Flags |= CSSM_APPLE_TP_SSL_CLIENT;
	}
	sslOptsData.Data = (uint8 *)&sslOpts;
	sslOptsData.Length = sizeof(sslOpts);
	serr = SecPolicySetValue(policy, &sslOptsData);
	if(serr) {
		sslErrorLog("***sslVerifyCertChain: SecPolicySetValue rtn %d\n",
			(int)serr);
		goto errOut;
	}

	/* now a SecTrustRef */
	serr = SecTrustCreateWithCertificates(certGroup, policy, &theTrust);
	if(serr) {
		sslErrorLog("***sslVerifyCertChain: SecTrustCreateWithCertificates "
			"rtn %d\n",	(int)serr);
		goto errOut;
	}

	/* anchors - default, or ours? */
	if(ctx->trustedCerts != NULL) {
		serr = SecTrustSetAnchorCertificates(theTrust, ctx->trustedCerts);
		if(serr) {
			sslErrorLog("***sslVerifyCertChain: SecTrustSetAnchorCertificates "
				"rtn %d\n",	(int)serr);
			goto errOut;
		}
	}
	tpActionData.Version = CSSM_APPLE_TP_ACTION_VERSION;
	tpActionData.ActionFlags = 0;
	if(ctx->allowExpiredCerts) {
		tpActionData.ActionFlags |= CSSM_TP_ACTION_ALLOW_EXPIRED;
	}
	if(ctx->allowExpiredRoots) {
		tpActionData.ActionFlags |= CSSM_TP_ACTION_ALLOW_EXPIRED_ROOT;
	}
	actionData = CFDataCreate(NULL, (UInt8 *)&tpActionData, sizeof(tpActionData));

	serr = SecTrustSetParameters(theTrust, CSSM_TP_ACTION_DEFAULT,
		actionData);
	if(serr) {
		sslErrorLog("***sslVerifyCertChain: SecTrustSetParameters rtn %d\n",
			(int)serr);
		goto errOut;
	}

	#if 0
	/* Disabled for Radar 3421314 */
	/*
	 * Avoid searching user keychains for intermediate certs by specifying
	 * an empty array of keychains
	 */
	kcList = CFArrayCreateMutable(NULL, 0, NULL);
	if(kcList == NULL) {
		sslErrorLog("***sslVerifyCertChain: error creating null kcList\n");
		serr = memFullErr;
		goto errOut;
	}
	serr = SecTrustSetKeychains(theTrust, kcList);
	if(serr) {
		sslErrorLog("***sslVerifyCertChain: SecTrustSetKeychains rtn %d\n",
			(int)serr);
		goto errOut;
	}
	#endif

	/*
	 * Save this no matter what if we're evaluating peer certs.
	 * We do a retain here so we can unconditionally release theTrust
	 * at the end of this routine in case of previous error or
	 * !arePeerCerts.
	 */
	if(arePeerCerts) {
		ctx->peerSecTrust = theTrust;
		CFRetain(theTrust);
	}

	if(!ctx->enableCertVerify) {
		/* trivial case, this is caller's responsibility */
		serr = noErr;
		goto errOut;
	}

    /*
     * If the caller provided a list of trusted leaf certs, check them here
     */
	if(ctx->trustedLeafCerts) {
		if (sslGetMatchingCertInArray((SecCertificateRef)CFArrayGetValueAtIndex(certGroup, 0),
			ctx->trustedLeafCerts)) {
			serr = noErr;
			goto errOut;
		}
	}

	/*
	 * Here we go; hand it over to SecTrust/TP.
	 */
	serr = SecTrustEvaluate(theTrust, &secTrustResult);
	if(serr) {
		sslErrorLog("***sslVerifyCertChain: SecTrustEvaluate rtn %d\n",
			(int)serr);
		goto errOut;
	}
	switch(secTrustResult) {
		case kSecTrustResultUnspecified:
			/* cert chain valid, no special UserTrust assignments */
		case kSecTrustResultProceed:
			/* cert chain valid AND user explicitly trusts this */
			crtn = CSSM_OK;
			break;
		case kSecTrustResultDeny:
		case kSecTrustResultConfirm:
			/*
			 * Cert chain may well have verified OK, but user has flagged
			 * one of these certs as untrustable.
			 */
			crtn = CSSMERR_TP_NOT_TRUSTED;
			break;
		default:
		{
			OSStatus osCrtn;
			serr = SecTrustGetCssmResultCode(theTrust, &osCrtn);
			if(serr) {
				sslErrorLog("***sslVerifyCertChain: SecTrustGetCssmResultCode"
					" rtn %d\n", (int)serr);
				goto errOut;
			}
			crtn = osCrtn;
		}
	}
	if(crtn) {
		/* get some detailed error info */
		switch(crtn) {
			case CSSMERR_TP_INVALID_ANCHOR_CERT:
				/* root found but we don't trust it */
				if(ctx->allowAnyRoot) {
					serr = noErr;
					sslErrorLog("***Warning: accepting unknown root cert\n");
				}
				else {
					serr = errSSLUnknownRootCert;
				}
				break;
			case CSSMERR_TP_NOT_TRUSTED:
				/* no root, not even in implicit SSL roots */
				if(ctx->allowAnyRoot) {
					sslErrorLog("***Warning: accepting unverified cert chain\n");
					serr = noErr;
				}
				else {
					serr = errSSLNoRootCert;
				}
				break;
			case CSSMERR_TP_CERT_EXPIRED:
				assert(!ctx->allowExpiredCerts);
				serr = errSSLCertExpired;
				break;
			case CSSMERR_TP_CERT_NOT_VALID_YET:
				serr = errSSLCertNotYetValid;
				break;
			case CSSMERR_APPLETP_HOSTNAME_MISMATCH:
				serr = errSSLHostNameMismatch;
				break;
			case errSecInvalidTrustSettings:
				/* these get passed along unmodified */
				serr = crtn;
				break;
			default:
				stPrintCdsaError("sslVerifyCertChain: SecTrustEvaluate returned",
						crtn);
				serr = errSSLXCertChainInvalid;
				break;
		}
	} 	/* SecTrustEvaluate error */

errOut:
	/*
	 * Free up resources - certGroup, policy, etc. Note that most of these
	 * will actually persist as long as the current SSLContext does since
	 * peerSecTrust holds references to these.
	 */
	if(policy) {
		CFRelease(policy);
	}
	if(policySearch) {
		CFRelease(policySearch);
	}
	if(actionData) {
		CFRelease(actionData);
	}
	if(anchors) {
		sslReleaseArray(anchors);
		CFRelease(anchors);
	}
	if(certGroup) {
		sslReleaseArray(certGroup);
		CFRelease(certGroup);
	}
	if(kcList) {
		/* empty, no contents to release */
		CFRelease(kcList);
	}
	if(theTrust) {
		CFRelease(theTrust);
	}
	return serr;
}

#ifndef	NDEBUG
void stPrintCdsaError(const char *op, CSSM_RETURN crtn)
{
	cssmPerror(op, crtn);
}
#endif

#pragma mark -
#pragma mark Diffie-Hellman Support

/*
 * Generate a Diffie-Hellman key pair. Algorithm parameters always
 * come from the server, so on client side we have the parameters
 * as two SSLBuffers. On server side we have the pre-encoded block
 * which comes from ServerDhParams.
 */
OSStatus sslDhGenKeyPairClient(
	SSLContext		*ctx,
	const SSLBuffer	*prime,
	const SSLBuffer	*generator,
	CSSM_KEY_PTR	publicKey,		// RETURNED
	CSSM_KEY_PTR	privateKey)		// RETURNED
{
	assert((prime->data != NULL) && (generator->data != NULL));
	if(prime->data && !generator->data) {
		return errSSLProtocol;
	}
	if(!prime->data && generator->data) {
		return errSSLProtocol;
	}

	SSLBuffer sParam;
	OSStatus ortn = sslEncodeDhParams(prime, generator, &sParam);
	if(ortn) {
		sslErrorLog("***sslDhGenerateKeyPairClient: DH param error\n");
		return ortn;
	}
	ortn = sslDhGenerateKeyPair(ctx, &sParam, prime->length * 8, publicKey, privateKey);
	SSLFreeBuffer(&sParam, ctx);
	return ortn;
}

OSStatus sslDhGenerateKeyPair(
	SSLContext		*ctx,
	const SSLBuffer	*paramBlob,
	uint32_t		keySizeInBits,
	CSSM_KEY_PTR	publicKey,		// RETURNED
	CSSM_KEY_PTR	privateKey)		// RETURNED
{
	CSSM_RETURN		crtn;
	CSSM_CC_HANDLE 	ccHandle;
	CSSM_DATA		labelData = {8, (uint8 *)"tempKey"};
	OSStatus		ortn = noErr;
	CSSM_DATA		cParamBlob;

	assert(ctx != NULL);
	assert(ctx->cspHand != 0);

	memset(publicKey, 0, sizeof(CSSM_KEY));
	memset(privateKey, 0, sizeof(CSSM_KEY));
	SSLBUF_TO_CSSM(paramBlob, &cParamBlob);

	crtn = CSSM_CSP_CreateKeyGenContext(ctx->cspHand,
		CSSM_ALGID_DH,
		keySizeInBits,
		NULL,					// Seed
		NULL,					// Salt
		NULL,					// StartDate
		NULL,					// EndDate
		&cParamBlob,
		&ccHandle);
	if(crtn) {
		stPrintCdsaError("DH CSSM_CSP_CreateKeyGenContext", crtn);
		return errSSLCrypto;
	}

	crtn = CSSM_GenerateKeyPair(ccHandle,
		CSSM_KEYUSE_DERIVE,		// only legal use of a Diffie-Hellman key
		CSSM_KEYATTR_RETURN_DATA | CSSM_KEYATTR_EXTRACTABLE,
		&labelData,
		publicKey,
		/* private key specification */
		CSSM_KEYUSE_DERIVE,
		CSSM_KEYATTR_RETURN_REF,
		&labelData,				// same labels
		NULL,					// CredAndAclEntry
		privateKey);
	if(crtn) {
		stPrintCdsaError("DH CSSM_GenerateKeyPair", crtn);
		ortn = errSSLCrypto;
	}
	CSSM_DeleteContext(ccHandle);
	return ortn;
}

/*
 * Perform Diffie-Hellman key exchange.
 * Valid on entry:
 *    	ctx->dhPrivate
 *		ctx->dhPeerPublic
 *
 * This generates deriveSizeInBits of key-exchanged data.
 */

/* the alg isn't important; we just want to be able to cook up lots of bits */
#define DERIVE_KEY_ALG			CSSM_ALGID_RC5

OSStatus sslDhKeyExchange(
	SSLContext		*ctx,
	uint32_t		deriveSizeInBits,
	SSLBuffer		*exchanged)
{
	CSSM_RETURN 			crtn;
	CSSM_ACCESS_CREDENTIALS	creds;
	CSSM_CC_HANDLE			ccHandle;
	CSSM_DATA				labelData = {8, (uint8 *)"tempKey"};
	CSSM_KEY				derivedKey;
	OSStatus				ortn = noErr;

	assert(ctx != NULL);
	assert(ctx->cspHand != 0);
	assert(ctx->dhPrivate != NULL);
	if(ctx->dhPeerPublic.length == 0) {
		/* comes from peer, don't panic */
		sslErrorLog("cdsaDhKeyExchange: null peer public key\n");
		return errSSLProtocol;
	}

	memset(&creds, 0, sizeof(CSSM_ACCESS_CREDENTIALS));
	memset(&derivedKey, 0, sizeof(CSSM_KEY));

	crtn = CSSM_CSP_CreateDeriveKeyContext(ctx->cspHand,
		CSSM_ALGID_DH,
		DERIVE_KEY_ALG,
		deriveSizeInBits,
		&creds,
		ctx->dhPrivate,	// BaseKey
		0,				// IterationCount
		0,				// Salt
		0,				// Seed
		&ccHandle);
	if(crtn) {
		stPrintCdsaError("DH CSSM_CSP_CreateDeriveKeyContext", crtn);
		return errSSLCrypto;
	}

	/* public key passed in as CSSM_DATA *Param */
	CSSM_DATA theirPubKeyData;
	SSLBUF_TO_CSSM(&ctx->dhPeerPublic, &theirPubKeyData);

	crtn = CSSM_DeriveKey(ccHandle,
		&theirPubKeyData,
		CSSM_KEYUSE_ANY,
		CSSM_KEYATTR_RETURN_DATA | CSSM_KEYATTR_EXTRACTABLE,
		&labelData,
		NULL,				// cread/acl
		&derivedKey);
	if(crtn) {
		stPrintCdsaError("DH CSSM_DeriveKey", crtn);
		ortn = errSSLCrypto;
	}
	else {
		CSSM_TO_SSLBUF(&derivedKey.KeyData, exchanged);
	}
	CSSM_DeleteContext(ccHandle);
	return ortn;
}

#pragma mark -
#pragma mark *** ECDSA support ***

/* specify either 32-bit integer or a pointer as an added attribute value */
typedef enum {
	CAT_Uint32,
	CAT_Ptr
} ContextAttrType;

/*
 * Given a context specified via a CSSM_CC_HANDLE, add a new
 * CSSM_CONTEXT_ATTRIBUTE to the context as specified by AttributeType,
 * AttributeLength, and an untyped pointer.
 */
static CSSM_RETURN sslAddContextAttribute(CSSM_CC_HANDLE CCHandle,
	uint32 AttributeType,
	uint32 AttributeLength,
	ContextAttrType attrType,
	/* specify exactly one of these */
	const void *AttributePtr,
	uint32 attributeInt)
{
	CSSM_CONTEXT_ATTRIBUTE		newAttr;
	CSSM_RETURN					crtn;

	newAttr.AttributeType     = AttributeType;
	newAttr.AttributeLength   = AttributeLength;
	if(attrType == CAT_Uint32) {
		newAttr.Attribute.Uint32  = attributeInt;
	}
	else {
		newAttr.Attribute.Data    = (CSSM_DATA_PTR)AttributePtr;
	}
	crtn = CSSM_UpdateContextAttributes(CCHandle, 1, &newAttr);
	if(crtn) {
		stPrintCdsaError("CSSM_UpdateContextAttributes", crtn);
	}
	return crtn;
}

/*
 * Generate ECDH key pair with the given SSL_ECDSA_NamedCurve.
 * Private key, in ref form, is placed in ctx->ecdhPrivate.
 * Public key, in ECPoint form - which can NOT be used as
 * a key in any CSP ops - is placed in ecdhExchangePublic.
 */
OSStatus sslEcdhGenerateKeyPair(
	SSLContext *ctx,
	SSL_ECDSA_NamedCurve namedCurve)
{
	CSSM_RETURN		crtn;
	CSSM_CC_HANDLE 	ccHandle = 0;
	CSSM_DATA		labelData = {8, (uint8 *)"ecdsaKey"};
	OSStatus		ortn = noErr;
	CSSM_KEY		pubKey;
	uint32			keySizeInBits;

	assert(ctx != NULL);
	assert(ctx->cspHand != 0);
	sslFreeKey(ctx->ecdhPrivCspHand, &ctx->ecdhPrivate, NULL);
    SSLFreeBuffer(&ctx->ecdhExchangePublic, ctx);

	switch(namedCurve) {
		case SSL_Curve_secp256r1:
			keySizeInBits = 256;
			break;
		case SSL_Curve_secp384r1:
			keySizeInBits = 384;
			break;
		case SSL_Curve_secp521r1:
			keySizeInBits = 521;
			break;
		default:
			/* should not have gotten this far */
			sslErrorLog("sslEcdhGenerateKeyPair: bad namedCurve (%u)\n",
				(unsigned)namedCurve);
			return errSSLInternal;
	}

	ctx->ecdhPrivate = (CSSM_KEY *)sslMalloc(sizeof(CSSM_KEY));

	memset(ctx->ecdhPrivate, 0, sizeof(CSSM_KEY));
	memset(&pubKey, 0, sizeof(CSSM_KEY));

	crtn = CSSM_CSP_CreateKeyGenContext(ctx->cspHand,
		CSSM_ALGID_ECDSA,
		keySizeInBits,
		NULL,					// Seed
		NULL,					// Salt
		NULL,					// StartDate
		NULL,					// EndDate
		NULL,					// Params
		&ccHandle);
	if(crtn) {
		stPrintCdsaError("ECDH CSSM_CSP_CreateKeyGenContext", crtn);
		return errSSLCrypto;
	}
	/* subsequent errors to errOut: */

	/*
	 * Here's how we get the raw ECPoint form of a public key
	 */
	crtn = sslAddContextAttribute(ccHandle,
		CSSM_ATTRIBUTE_PUBLIC_KEY_FORMAT,
		sizeof(uint32),
		CAT_Uint32,
		NULL,
		CSSM_KEYBLOB_RAW_FORMAT_OCTET_STRING);
	if(crtn) {
		ortn = errSSLCrypto;
		goto errOut;
	}

	crtn = CSSM_GenerateKeyPair(ccHandle,
		/* public key specification */
		CSSM_KEYUSE_DERIVE,		// only legal use - right?
		CSSM_KEYATTR_RETURN_DATA | CSSM_KEYATTR_EXTRACTABLE,
		&labelData,
		&pubKey,
		/* private key specification */
		CSSM_KEYUSE_DERIVE,
		CSSM_KEYATTR_RETURN_REF,
		&labelData,				// same labels
		NULL,					// CredAndAclEntry
		ctx->ecdhPrivate);
	if(crtn) {
		stPrintCdsaError("ECDH CSSM_GenerateKeyPair", crtn);
		ortn = errSSLCrypto;
		goto errOut;
	}
	ctx->ecdhPrivCspHand = ctx->cspHand;

	/*
	 * Take that public key data, drop it into ecdhExchangePublic,
	 * and free the key.
	 */
    ortn = SSLCopyBufferFromData(pubKey.KeyData.Data, pubKey.KeyData.Length,
				&ctx->ecdhExchangePublic);
	CSSM_FreeKey(ctx->cspHand, NULL, &pubKey, CSSM_FALSE);

errOut:
	if(ccHandle != 0) {
		CSSM_DeleteContext(ccHandle);
	}
	return ortn;

}

/*
 * Perform ECDH key exchange. Obtained key material is the same
 * size as our private key.
 *
 * On entry, ecdhPrivate is our private key. The peer's public key
 * is either ctx->ecdhPeerPublic for ECDHE exchange, or
 * ctx->peerPubKey for ECDH exchange.
 */
OSStatus sslEcdhKeyExchange(
	SSLContext		*ctx,
	SSLBuffer		*exchanged)
{
	CSSM_RETURN 			crtn;
	CSSM_ACCESS_CREDENTIALS	creds;
	const CSSM_ACCESS_CREDENTIALS *secCreds;
	const CSSM_ACCESS_CREDENTIALS *useCreds = &creds;
	CSSM_CC_HANDLE			ccHandle;
	CSSM_DATA				labelData = {8, (uint8 *)"tempKey"};
	CSSM_KEY				derivedKey;
	OSStatus				ortn = noErr;
	CSSM_KEY				rawKey;
	bool					useRefKeys = false;
	uint32					keyAttr;
	SSLBuffer				pubKeyBits = {0, NULL};

	assert(ctx != NULL);
	assert(ctx->ecdhPrivCspHand != 0);
	assert(ctx->ecdhPrivate != NULL);

	memset(&creds, 0, sizeof(CSSM_ACCESS_CREDENTIALS));
	memset(&derivedKey, 0, sizeof(CSSM_KEY));
	memset(&rawKey, 0, sizeof(CSSM_KEY));

	/*
	 * If we're using an actual CSSM_KEY for the peer public key, and its
	 * cspHand differs from our private key, we do things a bit different -
	 * our key is in the CSPDL space, and it has a Sec-style ACL for which
	 * we need creds. Also, the peer public key and the derived key have
	 * to be in reference form.
	 */
	switch(ctx->selectedCipherSpec.keyExchangeMethod) {
		case SSL_ECDH_ECDSA:
		case SSL_ECDH_RSA:
			if(ctx->cspHand != ctx->ecdhPrivCspHand) {
				useRefKeys = true;
			}
			break;
		default:
			break;
	}
	if(useRefKeys) {
		assert(ctx->signingPrivKeyRef != NULL);
		ortn = SecKeyGetCredentials(ctx->signingPrivKeyRef,
			CSSM_ACL_AUTHORIZATION_DERIVE,
			kSecCredentialTypeDefault,
			&secCreds);
		if(ortn) {
			stPrintCdsaError("ECDH SecKeyGetCredentials", ortn);
			return ortn;
		}
		useCreds = secCreds;
	}
	crtn = CSSM_CSP_CreateDeriveKeyContext(ctx->ecdhPrivCspHand,
		CSSM_ALGID_ECDH,
		DERIVE_KEY_ALG,
		ctx->ecdhPrivate->KeyHeader.LogicalKeySizeInBits,
		useCreds,
		ctx->ecdhPrivate,	// BaseKey
		0,					// IterationCount
		0,					// Salt
		0,					// Seed
		&ccHandle);
	if(crtn) {
		stPrintCdsaError("ECDH CSSM_CSP_CreateDeriveKeyContext", crtn);
		return errSSLCrypto;
	}
	/* subsequent errors to errOut: */

	CSSM_DATA theirPubKeyData = {0, NULL};

	switch(ctx->selectedCipherSpec.keyExchangeMethod) {
		case SSL_ECDHE_ECDSA:
		case SSL_ECDHE_RSA:
			/* public key passed in as CSSM_DATA *Param */
			if(ctx->ecdhPeerPublic.length == 0) {
				/* comes from peer, don't panic */
				sslErrorLog("sslEcdhKeyExchange: null peer public key\n");
				ortn = errSSLProtocol;
				goto errOut;
			}
			SSLBUF_TO_CSSM(&ctx->ecdhPeerPublic, &theirPubKeyData);
			break;
		case SSL_ECDH_ECDSA:
		case SSL_ECDH_RSA:
			/* add pub key as a context attr */
			if(ctx->peerPubKey == NULL) {
			   sslErrorLog("sslEcdhKeyExchange: no peer key\n");
			   ortn = errSSLInternal;
			   goto errOut;
			}

			/*
			 * If we're using CSPDL, extract the raw public key bits in ECPoint
			 * form and transmit as CSSM_DATA *Param.
			 * The securityd can't transmit a public key in a DeriveKey context
			 * in any form.
			 */
			if(useRefKeys) {
				ortn = sslEcdsaPubKeyBits(ctx->peerPubKey, &pubKeyBits);
				if(ortn) {
					goto errOut;
				}
				SSLBUF_TO_CSSM(&pubKeyBits, &theirPubKeyData);
			}
			else {
				crtn = sslAddContextAttribute(ccHandle,
					CSSM_ATTRIBUTE_PUBLIC_KEY,
					sizeof(CSSM_KEY),
					CAT_Ptr,
					(void *)ctx->peerPubKey,
					0);
				if(crtn) {
					stPrintCdsaError("AddContextAttribute(CSSM_ATTRIBUTE_PUBLIC_KEY)",
						crtn);
					ortn = errSSLInternal;
					goto errOut;
				}
			}
			break;
		default:
			/* shouldn't be here */
			assert(0);
			ortn = errSSLInternal;
			goto errOut;
	}

	if(useRefKeys) {
		keyAttr = CSSM_KEYATTR_RETURN_REF | CSSM_KEYATTR_EXTRACTABLE;
	}
	else {
		keyAttr = CSSM_KEYATTR_RETURN_DATA | CSSM_KEYATTR_EXTRACTABLE;
	}
	crtn = CSSM_DeriveKey(ccHandle,
		&theirPubKeyData,
		CSSM_KEYUSE_ANY,
		keyAttr,
		&labelData,
		NULL,				// cred/acl
		&derivedKey);
	if(crtn) {
		stPrintCdsaError("ECDH CSSM_DeriveKey", crtn);
		ortn = errSSLCrypto;
		goto errOut;
	}

	if(useRefKeys) {
		/*
		 * one more step: NULL-wrap the generated ref key to something we
		 * can use
		 */
		ortn = sslNullWrapKey(ctx->ecdhPrivCspHand, &derivedKey, &rawKey);
		if(ortn) {
			goto errOut;
		}
		ortn = SSLCopyBufferFromData(rawKey.KeyData.Data, rawKey.KeyData.Length,
			exchanged);
	}
	else {
		ortn = SSLCopyBufferFromData(derivedKey.KeyData.Data,
				derivedKey.KeyData.Length, exchanged);
	}
errOut:
	CSSM_DeleteContext(ccHandle);
	if(useRefKeys) {
		if(pubKeyBits.length) {
			SSLFreeBuffer(&pubKeyBits, ctx);
		}
		if(rawKey.KeyData.Length) {
			CSSM_FreeKey(ctx->ecdhPrivCspHand, NULL, &rawKey, CSSM_FALSE);
		}
	}
	return ortn;
}


/*
 * After ciphersuite negotiation is complete, verify that we have
 * the capability of actually performing the selected cipher.
 * Currently we just verify that we have a cert and private signing
 * key, if needed, and that the signing key's algorithm matches the
 * expected key exchange method.
 *
 * This is currently called from FindCipherSpec(), after it sets
 * ctx->selectedCipherSuite to a (supposedly) valid value, and from
 * sslBuildCipherSuiteArray(), in server mode (pre-negotiation) only.
 */
OSStatus sslVerifySelectedCipher(
	SSLContext *ctx,
	const SSLCipherSpec *selectedCipherSpec)
{
	if(ctx->protocolSide == kSSLClientSide) {
		return noErr;
	}
	#if 	SSL_PAC_SERVER_ENABLE
	if((ctx->masterSecretCallback != NULL) &&
	   (ctx->sessionTicket.data != NULL)) {
		/* EAP via PAC resumption; we can do it */
		return noErr;
	}
	#endif	/* SSL_PAC_SERVER_ENABLE */

	CSSM_ALGORITHMS requireAlg = CSSM_ALGID_NONE;
    if(selectedCipherSpec == NULL) {
		return errSSLInternal;
    }
    switch (selectedCipherSpec->keyExchangeMethod) {
		case SSL_RSA:
        case SSL_RSA_EXPORT:
		case SSL_DH_RSA:
		case SSL_DH_RSA_EXPORT:
		case SSL_DHE_RSA:
		case SSL_DHE_RSA_EXPORT:
			requireAlg = CSSM_ALGID_RSA;
			break;
 		case SSL_DHE_DSS:
		case SSL_DHE_DSS_EXPORT:
 		case SSL_DH_DSS:
		case SSL_DH_DSS_EXPORT:
			requireAlg = CSSM_ALGID_DSA;
			break;
		case SSL_DH_anon:
		case SSL_DH_anon_EXPORT:
			/* CSSM_ALGID_NONE, no signing key */
			break;
		/*
		 * When SSL_ECDSA_SERVER is true and we support ECDSA on the server side,
		 * we'll need to add some logic here...
		 */
		#if SSL_ECDSA_SERVER
		#error Work needed in sslVerifySelectedCipher
		#endif

		default:
			/* needs update per cipherSpecs.c */
			assert(0);
			return errSSLInternal;
    }
	if(requireAlg == CSSM_ALGID_NONE) {
		return noErr;
	}

	/* private signing key required */
	if(ctx->signingPrivKeyRef == NULL) {
		sslErrorLog("sslVerifySelectedCipher: no signing key\n");
		return errSSLBadConfiguration;
	}
	{
		const CSSM_KEY *cssmKey;
		OSStatus ortn = SecKeyGetCSSMKey(ctx->signingPrivKeyRef, &cssmKey);
		if(ortn) {
			sslErrorLog("sslVerifySelectedCipher: SecKeyGetCSSMKey err %d\n",
			(int)ortn);
			return ortn;
		}
		if(cssmKey->KeyHeader.AlgorithmId != requireAlg) {
			sslErrorLog("sslVerifySelectedCipher: signing key alg mismatch\n");
			return errSSLBadConfiguration;
		}
	}
	return noErr;
}

#endif /* USE_CDSA_CRYPTO */