/*
 * 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.h - main PKCS12 encode/decode engine. This class
 *				   corresponds to a SecPkcs12CoderRef in the public API.
 */
 
#ifndef	_PKCS12_CODER_H_
#define _PKCS12_CODER_H_

#include <security_pkcs12/SecPkcs12.h>
#include <security_pkcs12/pkcs12SafeBag.h>
#include <vector>

/*
 * This class essentially consists of the following:
 *
 * -- bags of certs, CRLs, keys, and opaque blobs in the 
 *    form of P12SafeBag subclasses.
 *
 * -- getters and setters to let the app access those bags.
 *
 * -- a decode routine which takes an encoded PFX, rips it apart,
 *    and drops what it finds into the bags of cert, CRLS, etc.
 *
 * -- an encode routine which takes the contents of the bag and 
 *    creates an encoded PFX.
 *
 * Everything else is ephemera supporting the above four things.
 *
 * Memory usage:
 * -------------
 *
 * Memory allocation is done in three flavors:
 *
 * 1. Via CF objects, when exporting contents of bags to the app.
 * 2. Via our own member variable SecNssCoder. This is used to allocate
 *    the contents of the bags (certs, etc.) which persist across
 *    API calls. 
 * 3. "Local" SecNssCoders for encode and decode. Allocated in the 
 *    stack for those two exported functions and used for all decoding
 *    and encoding. 
 *
 * The contents of the bags persist as long as an instance of 
 * P12Coder does. 
 *
 * CF objects the app gives us (e..g, passphrases)
 * are retained when get them and released in our destructor.
 *
 * CF objects we give the app are, of course, the app's responsibility. 
 *
 * Everything else is allocd locally via the SecNssCoders in encode()
 * and decode() and has a scope not exceeding those functions. 
 */
 
class P12Coder {
public:
	/* only constructor */
	P12Coder();	
	~P12Coder();
	
	/*
	 * These public functions more or less correspond to 
	 * the public functions in SecPkcs12.h.
	 */
	void setMacPassPhrase(
		CFStringRef				passphrase);
	void setEncrPassPhrase(
		CFStringRef				passphrase);
	void setMacPassKey(
		const CSSM_KEY			*passKey);
	void setEncrPassKey(
		const CSSM_KEY			*passKey);
		
	/* main decode function */
	void decode(
		CFDataRef				pfx);
	
	/* 
	 * CSP and DLDB associated with keys.
	 * CSP handle is required, DLDB optional.
	*/
	void setKeychain(
		SecKeychainRef			keychain);
	void setCsp(
		CSSM_CSP_HANDLE			cspHand)	{ mCspHand = cspHand; }
	void setDlDb(
		CSSM_DL_DB_HANDLE		dlDbHand)	{ mDlDbHand = dlDbHand; }
	
	CSSM_CSP_HANDLE	cspHand()				{ return mCspHand; }

	/* High-level import/export */
	void importFlags(
		SecPkcs12ImportFlags flags)			{ mImportFlags = flags; }
	SecPkcs12ImportFlags importFlags()	
											{ return mImportFlags; }

	void exportKeychainItems(
		CFArrayRef				items);
		
	/* getters */
	unsigned numCerts();
	unsigned numCrls();
	unsigned numKeys();
	unsigned numOpaqueBlobs();
	
	P12CertBag *getCert(
		unsigned				dex);
	P12CrlBag *getCrl(
		unsigned				dex);
	P12KeyBag *getKey(
		unsigned				dex);
	P12OpaqueBag *getOpaque(
		unsigned				dex);
		
	/* encoding */
	void encode(
		CFDataRef				*pfx);		// RETURNED
		
	void addCert(
		P12CertBag				*cert);
	void addCrl(
		P12CrlBag				*crl);
	void addKey(
		P12KeyBag				*key);
	void addOpaque(
		P12OpaqueBag			*opaque);
		
	/* little known, but public,  functions */
	SecPkcs12Mode integrityMode()		{ return mIntegrityMode; }
	SecPkcs12Mode privacyMode()			{ return mPrivacyMode; }
	void integrityMode(
		SecPkcs12Mode			mode);
	void privacyMode(
		SecPkcs12Mode			mode);
		
	/*
	 * Public API calls use our coder to create P12SafeBags
	 */
	SecNssCoder	&coder()			{ return mCoder; }
	
	/* encryption parameter getters/setters */
	unsigned weakEncrIterCount()	{ return mWeakEncrIterCount; }
	unsigned strongEncrIterCount()	{ return mStrongEncrIterCount; }
	unsigned macEncrIterCount()		{ return mMacIterCount; }
	
	void weakEncrIterCount(
		unsigned ic)				{ mWeakEncrIterCount = ic; }
	void strongEncrIterCount(
		unsigned ic)				{ mStrongEncrIterCount = ic; }
	void macEncrIterCount(
		unsigned ic)				{ mMacIterCount = ic; }

	CFDataRef weakEncrAlg();
	CFDataRef strongEncrAlg();
	
	void weakEncrAlg(
		CFDataRef				alg);
	void strongEncrAlg(
		CFDataRef				alg);
		
	/* panic button, delete anything stored in a DB during decode */
	void deleteDecodedItems();
	
	void limitPrivKeyImport(
		bool foundOneKey);
	
private:
	void init();					// one-time init from all constructors
	
	/* 
	 * Passphrase handling.
	 *
	 * These two convert the app-supplied CFStringRefs into
	 * CSSM_DATAs; if PassKeys are used, these just NULL out 
	 * the returned data. 
	 */
	const CSSM_DATA *getMacPassPhrase();
	const CSSM_DATA *getEncrPassPhrase();

	/*
	 * These return a CSSM_KEY_PTR is the app had specified
	 * PassKeys, else they return NULL.
	 */
	const CSSM_KEY *getMacPassKey();
	const CSSM_KEY *getEncrPassKey();
	
	/* in pkcs12Keychain.cpp */
	void storeDecodeResults();
	void setPrivateKeyHashes();
	void notifyKeyImport();
	P12CertBag *findCertForKey(
		P12KeyBag 				*keyBag);

	void addSecKey(
		SecKeyRef				keyRef);
	void addSecCert(
		SecCertificateRef		certRef);

	/* Lazy evaluation of module handles. */
	CSSM_CSP_HANDLE rawCspHand();
	CSSM_CL_HANDLE 	clHand();

	/* 
	 * A bunch of private encode/decode methods. This makes me
	 * long for ObjC-style categories so these would not 
	 * have to appear in this file.
	 *
	 * See implementation for comments and descriptions. 
	 */
	void encryptedDataDecrypt(
		const NSS_P7_EncryptedData 	&edata,
		SecNssCoder 				&localCdr,
		NSS_P12_PBE_Params 			*pbep,
		CSSM_DATA 					&ptext);
		
	void algIdParse(
		const CSSM_X509_ALGORITHM_IDENTIFIER &algId,
		NSS_P12_PBE_Params 			*pbeParams,	
		SecNssCoder 				&localCdr);
 
	void encryptedDataParse(
		const NSS_P7_EncryptedData 	&edata,
		SecNssCoder 				&localCdr,
		NSS_P12_PBE_Params 			*pbep);

	void shroudedKeyBagParse(
		const NSS_P12_SafeBag 		&safeBag,
		SecNssCoder 				&localCdr);
		
	void keyBagParse(
		const NSS_P12_SafeBag 		&safeBag,
		SecNssCoder 				&localCdr);
		
	void certBagParse(
		const NSS_P12_SafeBag 		&safeBag,
		SecNssCoder 				&localCdr);
		
	void crlBagParse(
		const NSS_P12_SafeBag 		&safeBag,
		SecNssCoder 				&localCdr);
		
	void secretBagParse(
		const NSS_P12_SafeBag 		&safeBag,
		SecNssCoder 				&localCdr);
		
	void safeContentsBagParse(
		const NSS_P12_SafeBag 		&safeBag,
		SecNssCoder 				&localCdr);
		
	void safeContentsParse(
		const CSSM_DATA 			&contentsBlob,
		SecNssCoder 				&localCdr);

	void authSafeElementParse(
		const NSS_P7_DecodedContentInfo *info,
		SecNssCoder 				&localCdr);
		
	void macParse(
		const NSS_P12_MacData 		&macData, 
		SecNssCoder					&localCdr);

	void authSafeParse(
		const CSSM_DATA 			&authSafeBlob,
		SecNssCoder 				&localCdr);

	/* private encoding routines */
	NSS_P7_DecodedContentInfo *safeContentsBuild(
		NSS_P12_SafeBag				**bags,
		NSS_P7_CI_Type 				type,		// CT_Data, CT_EncryptedData
		CSSM_OID 					*encrOid,	// only for CT_EncryptedData
		unsigned 					iterCount,	// ditto
		SecNssCoder					&localCdr);

	void authSafeBuild(
		NSS_P7_DecodedContentInfo 	&authSafe,
		SecNssCoder 				&localCdr);

	void encryptData(
		const CSSM_DATA				&ptext,
		CSSM_OID 					&encrOid,
		unsigned 					iterCount,
		NSS_P7_EncryptedData		&ed,
		SecNssCoder					&localCdr);

	void algIdBuild(
		CSSM_X509_ALGORITHM_IDENTIFIER	&algId,
		const CSSM_OID 				&algOid,
		const CSSM_DATA 			&salt,
		unsigned 					iterCount,
		SecNssCoder 				&localCdr);
		
	void macSignPfx(
		NSS_P12_DecodedPFX 			&pfx,
		SecNssCoder 				&localCdr);
		
	NSS_P12_SafeBag *certBagBuild(
		P12CertBag 					*cert,
		SecNssCoder 				&localCdr);

	NSS_P12_SafeBag *crlBagBuild(
		P12CrlBag 					*crl,
		SecNssCoder 				&localCdr);

	NSS_P12_SafeBag *keyBagBuild(
		P12KeyBag 					*key,
		SecNssCoder 				&localCdr);

	NSS_P12_SafeBag *opaqueBagBuild(
		P12OpaqueBag				*op,
		SecNssCoder 				&localCdr);

	/* member variables */
	SecPkcs12Mode				mPrivacyMode;
	SecPkcs12Mode				mIntegrityMode;
	
	/* passwords - as app gave us, and translated into ready-to-use
	 * unicode strings */
	CFStringRef					mMacPassphrase;
	CFStringRef					mEncrPassPhrase;
	CSSM_DATA					mMacPassData;
	CSSM_DATA					mEncrPassData;
	
	/* passphrases in key form */
	const CSSM_KEY				*mMacPassKey;
	const CSSM_KEY				*mEncrPassKey;
	
	/*
	 * App has to either set mKeychain or mCspHand. In the former
	 * case we infer both mCspHand and mDlDbHand from the keychainRef.
	 */
	SecKeychainRef				mKeychain;
	CSSM_CSP_HANDLE				mCspHand;
	CSSM_DL_DB_HANDLE			mDlDbHand;
	
	/*
	 * LimitPrivateKeyImport mechanism
	 */
	typedef enum {
		PKIS_NoLimit,			// no limit
		PKIS_AllowOne,			// allow import of at most one
		PKIS_NoMore				// found one, no more allowed
	} p12PrivKeyImportState;
	
	p12PrivKeyImportState		mPrivKeyImportState;
	
	/*
	 * Encryption/MAC parameters
	 */
	CSSM_OID					mWeakEncrAlg;		// for certs and CRLs
	CSSM_OID					mStrongEncrAlg;
	unsigned					mWeakEncrIterCount;
	unsigned					mStrongEncrIterCount;
	unsigned					mMacIterCount;

	/*
	 * Import flags
	 */
	SecPkcs12ImportFlags		mImportFlags;
	
	/* 
	 * Four individual piles of safe bags
	 */
	vector<P12CertBag *>		mCerts;
	vector<P12CrlBag *>			mCrls;
	vector<P12KeyBag *>			mKeys;
	vector<P12OpaqueBag *>		mOpaques;
	
	/*
	 * Internal CSSM module handles, lazily evaluated.
	 */
	CSSM_CSP_HANDLE				mRawCspHand;
	CSSM_CL_HANDLE				mClHand;
	
	/*
	 * The source of most (all?) of our privately allocated data
	 */
	SecNssCoder					mCoder;
};

#endif	/* _PKCS12_CODER_H_ */

