/*
 * Copyright (c) 2000-2001 Apple Computer, Inc. All Rights Reserved.
 * 
 * The contents of this file constitute Original Code as defined in and are
 * subject to the Apple Public Source License Version 1.2 (the 'License').
 * You may not use this file except in compliance with the License. Please obtain
 * a copy of the License at http://www.apple.com/publicsource and read it before
 * using this file.
 * 
 * This 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.
 */

#ifdef	BSAFE_CSP_ENABLE


//
// bsafecspi - implementation layer for C++ BSafe 4 interface
//
#ifndef _H_BSAFECSPI
#define _H_BSAFECSPI

#include <Security/CSPsession.h>
#include "bsobjects.h"
#include "AppleCSPContext.h"
#include "AppleCSPSession.h"
#include <aglobal.h>
#include <bsafe.h>

//
// The BSafe class is more of a namespace than anything else.
// Just think of it as the "static binder" for BSafe's objects.
// Note that we keep a global, static allocator. We have to; BSafe
// doesn't have any state management at that level.
//
class BSafe {
    class BSafeContext; friend class BSafeContext;
	class BSafeFactory; friend class BSafeFactory;

public:
    static void setNormAllocator(CssmAllocator *alloc)
    { assert(!normAllocator); normAllocator = alloc; }
    static void setPrivAllocator(CssmAllocator *alloc)
    { assert(!privAllocator); privAllocator = alloc; }
	
	static bool setup(
		AppleCSPSession &session,
		CSPFullPluginSession::CSPContext * &cspCtx, 
		const Context &context);

private:
    // BSafe's memory allocators
    static CssmAllocator *normAllocator;
    static CssmAllocator *privAllocator;
    friend POINTER T_malloc(unsigned int);
    friend void T_free(POINTER);
    friend POINTER T_realloc(POINTER, unsigned int);
    
    static const B_ALGORITHM_METHOD * const bsChooser[];

private:
	// BSafe-specific BinaryKey class.
	class BSafeBinaryKey : public BinaryKey {
	
	public:
		BSafeBinaryKey(
			bool 			isPub,
			uint32			alg);	// CSSM_ALGID_{RSA,DSA}
		~BSafeBinaryKey();
		void generateKeyBlob(
			CssmAllocator 		&allocator,
			CssmData			&blob,
			CSSM_KEYBLOB_FORMAT	&format);
			
		bool			isPublic() 	{ return mIsPublic; }
		uint32			alg()		{ return mAlg; }
		B_KEY_OBJ		bsKey()		{ return mBsKey; }
		
	private:
		bool			mIsPublic;
		uint32			mAlg;		// CSSM_ALGID_{RSA,DSA}
		B_KEY_OBJ		mBsKey;
	};
	
private:
	//
	// The BSafeContext class is the parent of all BSafe-used CSPContext objects.
	// It implements the CSPContext operation functions (init, update, ...) in terms
	// of pointer-to-member fields set by its subclasses. This may not be pretty, but
	// it avoids every subclass having to re-implement all CSPContext operations.
	// Beyond that, we implement a raftload of utility methods for our children.
	//
    class BSafeContext : public AppleCSPContext {
        friend class BSafe;
    public:
        BSafeContext(AppleCSPSession &session);
        virtual ~BSafeContext();
        
		// called by CSPFullPluginSession
        void init(const Context &context, bool encoding = true);
        void update(const CssmData &data);
        void update(void *inp, size_t &inSize, void *outp, size_t &outSize);
        void final(CssmData &out);
        void final(const CssmData &in);
        size_t outputSize(bool final, size_t inSize);

    protected:
		// install a BSafe algorithm into bsAlgorithm
        void setAlgorithm(B_INFO_TYPE bAlgType, const void *info = NULL);
		
		// safely create bsKey 
		void createBsKey();
		
		// set bsKey. The different versions are equivalent
        void setKeyAtom(B_INFO_TYPE bKeyInfo, const void *info);
        void setKeyFromItem(B_INFO_TYPE bKeyInfo, const BSafeItem &item)
			{ setKeyAtom(bKeyInfo, &item); }
        void setKeyFromCssmKey(B_INFO_TYPE bKeyInfo, const CssmKey &key)
			{ BSafeItem item(key.KeyData); setKeyAtom(bKeyInfo, &item); }
        void setKeyFromCssmData(B_INFO_TYPE bKeyInfo, const CssmData &keyData)
			{ BSafeItem item(keyData); setKeyAtom(bKeyInfo, &item); }
        void setKeyFromContext(const Context &context, bool required = true);
	
		void setRefKey(CssmKey &key);
		void setRsaOutSize(bool isPubKey);
		
		// create mRandom to be a suitable random-generator BSafe object (if it isn't yet)
        void setRandom();

		// trackUpdate is called during crypto-output. Hook it to keep track of data flow
        virtual void trackUpdate(size_t in, size_t out);

		// destroy bsAlgorithm and bsKey so we can start over making them
        void reset();

		// clear key state
		void destroyBsKey();
		
		// determine if we can reuse the current bsAlgorithm
        bool reusing(bool encode = true)
        {
            if (initialized && !opStarted && 
				(encode == encoding)) return true;
            encoding = encode;
            return false;
        }

    public:
		//
		// These pointers-to-member are called by the BSafeContext operations
		// (update, final). They must be set by a subclasses's init() method.
		// Not all members are used by all types of operations - check the 
		// source when in doubt.
		//
        int (*inUpdate)(B_ALGORITHM_OBJ, POINTER, unsigned int, A_SURRENDER_CTX *);
        int (*inOutUpdate)(B_ALGORITHM_OBJ, POINTER, unsigned int *, unsigned int,
                            POINTER, unsigned int, B_ALGORITHM_OBJ, A_SURRENDER_CTX *);
        int (*inFinal)(B_ALGORITHM_OBJ, POINTER, unsigned int, A_SURRENDER_CTX *);
        int (*inFinalR)(B_ALGORITHM_OBJ, POINTER, unsigned int,
                        B_ALGORITHM_OBJ, A_SURRENDER_CTX *);
        int (*outFinalR)(B_ALGORITHM_OBJ, POINTER, unsigned int *, unsigned int,
                        B_ALGORITHM_OBJ, A_SURRENDER_CTX *);
        int (*outFinal)(B_ALGORITHM_OBJ, POINTER, unsigned int *, unsigned int,
                        A_SURRENDER_CTX *);
        
    protected:

        // un-consted bsChooser for BSafe's consumption. BSafe's Bad
        static B_ALGORITHM_METHOD **chooser()
        { return const_cast<B_ALGORITHM_METHOD **>(bsChooser); }

		// a placeholder for a surrender context. Not currently used
		// @@@ should perhaps test for pthread cancel? --> thread abstraction
        static A_SURRENDER_CTX * const bsSurrender;

    protected:
        B_ALGORITHM_OBJ bsAlgorithm; // BSafe algorithm object or NULL
        B_ALGORITHM_OBJ bsRandom;	// PRNG algorithm
        bool encoding;				// encoding direction
        bool initialized;			// method init() has completed
		bool opStarted;				// method update() has been called
									// generally means that we can't reuse
									// the current bsAlgorithm
		//
		// We have a binKey only if the caller passed in a reference 
		// key. In that case we avoid deleting bsKey - which is a copy
		// of binKey.bsKey - because a BinaryKey is persistent
		// relative to this context. 
		//
		BSafeBinaryKey	*bsBinKey;
        B_KEY_OBJ 		bsKey;		// BSafe key object or NULL
        
        size_t 			mOutSize; 	// simple output size, if applicable
    };	/* BSafeContext */

	// contexts for BSafe digest operations
    class DigestContext : public BSafeContext {
    public:
		// do all work in constructor. We have no directions; thus default init() works fine
        DigestContext(
			AppleCSPSession &session,
			const Context &, 
			B_INFO_TYPE bAlgInfo, 
			size_t sz);
    };

	// common context features for BSafe cipher operations (both symmetric and asymmetric)
    class CipherContext : public BSafeContext {
	public:
		CipherContext(
			AppleCSPSession &session) :
				BSafeContext(session), 
				pending(0) {}
			
	protected:
        size_t pending;				// bytes not eaten still pending (staged only)
    public:
        void cipherInit();			// common init code (must be called from init())
    };            

	// contexts for block cipher operations using symmetric algorithms
    class BlockCipherContext : public CipherContext {
        size_t blockSize;
		uint32 cssmAlg;
		uint32 cssmMode;
		bool padEnable;
    public:
        BlockCipherContext(
			AppleCSPSession &session,
			const Context &, 
			size_t sz) :
				CipherContext(session),
			 	blockSize(sz) { }
        void init(const Context &context, bool encrypting);
        size_t inputSize(size_t outSize);
        size_t outputSize(bool final, size_t inSize);
        void minimumProgress(size_t &in, size_t &out);
        void trackUpdate(size_t in, size_t out);
	private:
		// special case for RC4
		void RC4init(const Context &context);
    };

	// context for generating public/private key pairs
    class BSafeKeyPairGenContext : public BSafeContext,
					 private AppleKeyPairGenContext  {
	public:
		BSafeKeyPairGenContext(
			AppleCSPSession &session,
			const Context &) :
				BSafeContext(session) {}

		// generate alg params, not handled by PublicKeyGenerateContext
		// For DSA only. 
        void generate(
			const Context 			&context, 
			uint32 					bitSize, 
			CssmData 				&params,
			uint32 					&attrCount, 
			Context::Attr * 		&attrs);
			
		// this one is specified in CSPFullPluginSession
		void generate(
			const Context 	&context, 
			CssmKey 		&pubKey, 
			CssmKey 		&privKey);
			
		// this one in AppleKeyPairGenContext
		void generate(
			const Context 	&context,
			BinaryKey		&pubBinKey,	
			BinaryKey		&privBinKey,
			uint32			&keySize);
	
	private:
		void setupAlgorithm(
			const Context 	&context,
			uint32			&keySize);
			
    };	/* BSafeKeyPairGenContext */

	// public key cipher operations
    class PublicKeyCipherContext : public CipherContext {
    public:
		PublicKeyCipherContext(
			AppleCSPSession &session,
			const Context &) :
				CipherContext(session) { }
        void init(const Context &context, bool encrypting);
        size_t inputSize(size_t outSize);	// unlimited
    };

	// contexts for BSafe signing/verifying operations
    class SigningContext : public BSafeContext {
        B_INFO_TYPE algorithm;
    public:
        SigningContext(
			AppleCSPSession &session,
			const Context &,
			B_INFO_TYPE bAlg, 
			size_t sz) : 
				BSafeContext(session),
				algorithm(bAlg) { mOutSize = sz; }
        void init(const Context &context, bool signing);
    };

	// contexts for BSafe MAC generation and verification
    class MacContext : public BSafeContext {
        B_INFO_TYPE algorithm;
    public:
        MacContext(
			AppleCSPSession &session,
			const Context &,
            B_INFO_TYPE bAlg, 
			size_t sz) : 
				BSafeContext(session),
				algorithm(bAlg) { mOutSize = sz; }
        void init(const Context &context, bool signing);
        void final(const CssmData &in);
    };
	
	// contexts for BSafe's random number generation
	class RandomContext : public BSafeContext {
		B_INFO_TYPE algorithm;
	public:
		RandomContext(
			AppleCSPSession &session,
			const Context &, 
			B_INFO_TYPE alg) :
				BSafeContext(session),
				algorithm(alg) { }
		void init(const Context &context, bool);
		void final(CssmData &data);
	};

	// symmetric key generation context
	class SymmetricKeyGenContext : public BSafeContext,
		private AppleSymmKeyGenContext {
    public:
		SymmetricKeyGenContext(
			AppleCSPSession &session,
			const Context 	&ctx,
			uint32			minSizeInBits,
			uint32			maxSizeInBits,
			bool			mustBeByteSized) :
				BSafeContext(session),
				AppleSymmKeyGenContext(
					minSizeInBits,
					maxSizeInBits,
					mustBeByteSized) { }
					
		void generate(
			const Context 	&context, 
			CssmKey 		&symKey, 
			CssmKey 		&dummyKey);

    };

public:
	/*
	 * Stateless, private function to map a CSSM alg and pub/priv state
	 * to B_INFO_TYPE and format. Returns true on success, false on
	 * "I don't understand this algorithm". 
	 */
	static bool bsafeAlgToInfoType(
		CSSM_ALGORITHMS		alg,
		bool				isPublic,
		B_INFO_TYPE			&infoType,	// RETURNED
		CSSM_KEYBLOB_FORMAT	&format);	// RETURNED

	/* check result of a BSafe call and throw on error */
	static void check(int status, bool isKeyOp = false);
		
	/* moved here from BSafeContext - now works on any key */
	template <class KI_Type>
	static KI_Type *getKey(B_KEY_OBJ bKey, B_INFO_TYPE type)
	{ 
		POINTER p; 
		check(B_GetKeyInfo(&p, bKey, type), true); 
		return reinterpret_cast<KI_Type *>(p); 
	}


    //
    // The context generation table - see algmaker.cpp.
    //
public:
	// Base class for Maker classes
	class MakerBase {
	public:
		virtual ~MakerBase() { }
		virtual BSafeContext *make(
			AppleCSPSession &session,
			const Context &context) const = 0;
	};

	// One entry in Maker table
	struct MakerTable {
		CSSM_ALGORITHMS 	algorithmId;
		CSSM_CONTEXT_TYPE	algClass;
		const MakerBase 	*maker;
		~MakerTable() { delete maker; }
	};
	
private:
	static bug_const MakerTable algorithms[];
	static const unsigned int algorithmCount;
	
	/*
	 * CSPKeyInfoProvider for BSafe keys
	 */
	class BSafeKeyInfoProvider : public CSPKeyInfoProvider 
	{
private:
		BSafeKeyInfoProvider(
			const CssmKey		&cssmKey);
	public:
		static CSPKeyInfoProvider *provider(
		const CssmKey &cssmKey);
		~BSafeKeyInfoProvider() { }
		void CssmKeyToBinary(
			BinaryKey			**binKey);	// RETURNED
		void QueryKeySizeInBits(
			CSSM_KEY_SIZE		&keySize);	// RETURNED
	};

}; /* BSAFE namespace */

/*
 * BSAFE Key Info types.
 */
#define BLOB_IS_PUB_KEY_INFO	0

#if		BLOB_IS_PUB_KEY_INFO

/* X beta values */
#define RSA_PUB_KEYINFO_TYPE		KI_RSAPublicBER
#define RSA_PRIV_KEYINFO_TYPE		KI_PKCS_RSAPrivateBER
#define DSA_PUB_KEYINFO_TYPE		KI_DSAPublicBER
#define DSA_PRIV_KEYINFO_TYPE		KI_DSAPrivateBER

#else	/* BLOB_IS_PUB_KEY_INFO */

#define RSA_PUB_KEYINFO_TYPE		KI_RSAPublic
#define RSA_PRIV_KEYINFO_TYPE		KI_PKCS_RSAPrivateBER
#define DSA_PUB_KEYINFO_TYPE		KI_DSAPublicBER
#define DSA_PRIV_KEYINFO_TYPE		KI_DSAPrivateBER

#endif

#endif //_H_BSAFECSP
#endif	/* BSAFE_CSP_ENABLE */
