/*
 * Copyright (c) 2003-2004 Apple Computer, 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@
 */
 
/*
 * pkcs12Coder.cpp - Public P12Coder coder functions.
 */
 
#include "pkcs12Coder.h"
#include "pkcs12Debug.h"
#include "pkcs12Utils.h"
#include <Security/cssmerr.h>
#include <security_cdsa_utils/cuCdsaUtils.h>
#include <CoreServices/../Frameworks/CarbonCore.framework/Headers/MacErrors.h>
#include <Security/oidsalg.h>

/*
 * Default encryption parameters
 */
#define P12_ENCR_ITER_COUNT		2048
#define P12_MAC_ITER_COUNT		1
#define P12_WEAK_ENCR_ALG		CSSMOID_PKCS12_pbeWithSHAAnd40BitRC4
#define P12_STRONG_ENCR_ALG		CSSMOID_PKCS12_pbeWithSHAAnd3Key3DESCBC

/* 
 * Default import flags. 
 */
#define P12_KC_IMPORT_DEFAULT	(kSecImportCertificates | \
								 kSecImportCRLs | \
								 kSecImportKeys)

P12Coder::P12Coder()
{
	init();
}

/* one-time init from all constructors */
void P12Coder::init()
{
	mPrivacyMode = kSecPkcs12ModePassword;
	mIntegrityMode = kSecPkcs12ModePassword;
	mMacPassphrase = NULL;
	mEncrPassPhrase = NULL;
	mMacPassData.Data = NULL;
	mMacPassData.Length = 0;
	mEncrPassData.Data = NULL;
	mEncrPassData.Length = 0;
	mMacPassKey = NULL;
	mEncrPassKey = NULL;
	mKeychain = NULL;
	mCspHand = 0;
	mDlDbHand.DLHandle = 0;
	mDlDbHand.DBHandle = 0;
	mPrivKeyImportState = PKIS_NoLimit;
	mWeakEncrAlg   = CSSMOID_PKCS12_pbeWithSHAAnd40BitRC4;
	mStrongEncrAlg = CSSMOID_PKCS12_pbeWithSHAAnd3Key3DESCBC;
	mWeakEncrIterCount = P12_ENCR_ITER_COUNT;
	mStrongEncrIterCount = P12_ENCR_ITER_COUNT;
	mMacIterCount  = P12_MAC_ITER_COUNT;
	mImportFlags = P12_KC_IMPORT_DEFAULT;
	mRawCspHand = 0;
	mClHand = 0;
}

/* 
 * FIXME - can't we get vector's destructor to do this?
 */
#define deleteVect(v) 		\
{							\
	while(v.size()) {		\
		delete v[0];		\
		v.erase(v.begin());	\
	}						\
	v.clear();				\
}

P12Coder::~P12Coder()
{	
	if(mMacPassphrase) {
		CFRelease(mMacPassphrase);
	}
	if(mEncrPassPhrase) {
		CFRelease(mEncrPassPhrase);
	}
	deleteVect(mCerts);
	deleteVect(mCrls);
	deleteVect(mKeys);
	deleteVect(mOpaques);

	if(mKeychain) {
		CFRelease(mKeychain);
	}
	if(mRawCspHand) {
		cuCspDetachUnload(mRawCspHand, CSSM_TRUE);
	}
	if(mClHand) {
		cuClDetachUnload(mClHand);
	}
}

void P12Coder::setKeychain(
	SecKeychainRef			keychain)
{
	OSStatus ortn = SecKeychainGetCSPHandle(keychain, &mCspHand);
	if(ortn) {
		MacOSError::throwMe(ortn);
	}
	ortn = SecKeychainGetDLDBHandle(keychain, &mDlDbHand);
	if(ortn) {
		MacOSError::throwMe(ortn);
	}
	CFRetain(keychain);
	mKeychain = keychain;
}

/*
 * Private methods for obtaining passprases in CSSM_DATA form.
 */
const CSSM_DATA *P12Coder::getMacPassPhrase()
{
	if(mMacPassData.Data != NULL) {
		return &mMacPassData;
	}
	else if (mMacPassphrase) {
		p12ImportPassPhrase(mMacPassphrase, mCoder, mMacPassData);
		return &mMacPassData;
	}
	else {
		return NULL;
	}
}

const CSSM_DATA *P12Coder::getEncrPassPhrase()
{
	if(mEncrPassData.Data != NULL) {
		return &mEncrPassData;
	}
	else if (mEncrPassPhrase) {
		p12ImportPassPhrase(mEncrPassPhrase, mCoder, mEncrPassData);
		return &mEncrPassData;
	}
	/* no separate passphrase found, use MAC passphrase */
	return getMacPassPhrase();
}

/*
 * These return a CSSM_KEY_PTR is the app had specified
 * PassKeys, else they return NULL.
 */
const CSSM_KEY *P12Coder::getMacPassKey()
{
	return mMacPassKey;
}

const CSSM_KEY *P12Coder::getEncrPassKey()
{
	if(mEncrPassKey != NULL) {
		return mEncrPassKey;
	}
	else {
		return getMacPassKey();
	}
}

/* 
 * Lazy evaluation of module handles. 
 */
CSSM_CSP_HANDLE P12Coder::rawCspHand()
{
	if(mRawCspHand != 0) {
		return mRawCspHand;
	}
	mRawCspHand = cuCspStartup(CSSM_TRUE);
	if(mRawCspHand == 0) {
		CssmError::throwMe(CSSMERR_CL_INTERNAL_ERROR);
	}
	return mRawCspHand;
}

CSSM_CL_HANDLE P12Coder::clHand()
{
	if(mClHand != 0) {
		return mClHand;
	}
	mClHand = cuClStartup();
	if(mClHand == 0) {
		CssmError::throwMe(CSSMERR_CL_INTERNAL_ERROR);
	}
	return mClHand;
}

/*
 * These public functions more or less correspond to 
 * the public functions in SecPkcs12.h.
 */
void P12Coder::setMacPassPhrase(
	CFStringRef				passphrase)
{
	CFRetain(passphrase);
	mMacPassphrase = passphrase;
}

void P12Coder::setEncrPassPhrase(
	CFStringRef				passphrase)
{
	CFRetain(passphrase);
	mEncrPassPhrase = passphrase;
}
	
void P12Coder::setMacPassKey(
	const CSSM_KEY  *passKey)
{
	mMacPassKey = passKey;
}

void P12Coder::setEncrPassKey(
	const CSSM_KEY  *passKey)
{
	mEncrPassKey = passKey;
}
	
/* getters */
unsigned P12Coder::numCerts()
{
	return mCerts.size();
}

unsigned P12Coder::numCrls()
{
	return mCrls.size();
}

unsigned P12Coder::numKeys()
{
	return mKeys.size();
}

unsigned P12Coder::numOpaqueBlobs()
{
	return mOpaques.size();
}

P12CertBag *P12Coder::getCert(
	unsigned				dex)
{
	if(mCerts.size() < (dex + 1)) {
		MacOSError::throwMe(paramErr);
	}
	return mCerts[dex];
}

P12CrlBag *P12Coder::getCrl(
	unsigned				dex)
{
	if(mCrls.size() < (dex + 1)) {
		MacOSError::throwMe(paramErr);
	}
	return mCrls[dex];
}

P12KeyBag *P12Coder::getKey(
	unsigned				dex)
{
	if(mKeys.size() < (dex + 1)) {
		MacOSError::throwMe(paramErr);
	}
	return mKeys[dex];
}

P12OpaqueBag *P12Coder::getOpaque(
	unsigned				dex)
{
	if(mOpaques.size() < (dex + 1)) {
		MacOSError::throwMe(paramErr);
	}
	return mOpaques[dex];
}
	
/*
 * These four "add" functions are invoked by the app prior to encoding
 * and by our decoder while decoding.
 */
void P12Coder::addCert(
	P12CertBag				*cert)
{
	mCerts.push_back(cert);
}

void P12Coder::addCrl(
	P12CrlBag				*crl)
{
	mCrls.push_back(crl);
}

void P12Coder::addKey(
	P12KeyBag				*key)
{
	mKeys.push_back(key);
}

void P12Coder::addOpaque(
	P12OpaqueBag			*opaque)
{
	mOpaques.push_back(opaque);
}

	
/* little known, but public,  functions */
void P12Coder::integrityMode(
	SecPkcs12Mode			mode)
{
	if(mode != kSecPkcs12ModePassword) {
		MacOSError::throwMe(paramErr);
	}
	mIntegrityMode = mode;
}

void P12Coder::privacyMode(
	SecPkcs12Mode			mode)
{
	if(mode != kSecPkcs12ModePassword) {
		MacOSError::throwMe(paramErr);
	}
	mPrivacyMode = mode;
}

CFDataRef P12Coder::weakEncrAlg()
{
	return p12CssmDataToCf(mWeakEncrAlg);
}

CFDataRef P12Coder::strongEncrAlg()
{
	return p12CssmDataToCf(mStrongEncrAlg);
}

void P12Coder::weakEncrAlg(
	CFDataRef				alg)
{
	p12CfDataToCssm(alg, mWeakEncrAlg, mCoder);
}

void P12Coder::strongEncrAlg(
	CFDataRef				alg)
{
	p12CfDataToCssm(alg, mStrongEncrAlg, mCoder);
}

void P12Coder::limitPrivKeyImport(
	bool foundOneKey)
{
	if(foundOneKey) {
		mPrivKeyImportState = PKIS_NoMore;
	}
	else {
		mPrivKeyImportState = PKIS_AllowOne;
	}
}
