/*
 * 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.
 */


//
// transition - transition layer for CSSM API/SPI calls.
//
// This file defines all functions that connect the CSSM API (CSSM_*)
// to a plugin's SPI (CSSM_SPI_*) functions. The bulk of these functions
// is automatically generated by the Perl script transition.pl (thanks, Larry!)
// from the cssm*.h official header files, under control of the configuration
// file transition.cfg. Those that need special magic are marked "custom" in
// transition.cfg and are defined here.
//
// @@@ Reconsider CSP locking for context operations
//
#include "cssmint.h"
#include "attachfactory.h"
#include "manager.h"
#include "cssmcontext.h"
#include <Security/cssmcspi.h>
#include <Security/cssmdli.h>
#include <Security/cssmcli.h>
#include <Security/cssmaci.h>
#include <Security/cssmtpi.h>
#include <Security/cssmkrapi.h>
#include <Security/cssmkrspi.h>


//
// Names for the standard Attachment types
//
typedef StandardAttachment<CSSM_SERVICE_CSP, CSSM_SPI_CSP_FUNCS> CSPAttachment;
typedef StandardAttachment<CSSM_SERVICE_DL, CSSM_SPI_DL_FUNCS> DLAttachment;
typedef StandardAttachment<CSSM_SERVICE_CL, CSSM_SPI_CL_FUNCS> CLAttachment;
typedef StandardAttachment<CSSM_SERVICE_AC, CSSM_SPI_AC_FUNCS> ACAttachment;
typedef StandardAttachment<CSSM_SERVICE_TP, CSSM_SPI_TP_FUNCS> TPAttachment;


//
// A conditional locking class for always-right(TM) lock management.
//
class TransitLock {
public:
	Attachment &attachment;
	
	TransitLock(Attachment &att) : attachment(att)
	{
		attachment.module.safeLock();
	}
	
	~TransitLock()
	{
		attachment.module.safeUnlock();
		attachment.exit();
	}
};


//
// Context management functions
//
CSSM_RETURN CSSMAPI
CSSM_CSP_CreateSignatureContext (CSSM_CSP_HANDLE CSPHandle,
                                 CSSM_ALGORITHMS AlgorithmID,
                                 const CSSM_ACCESS_CREDENTIALS *AccessCred,
                                 const CSSM_KEY *Key,
                                 CSSM_CC_HANDLE *NewContextHandle)
{
    BEGIN_API
    HandleContext::Maker maker(CSPHandle);
    maker.setup(AccessCred);
    maker.setup(Key, CSSMERR_CSP_MISSING_ATTR_KEY);
    maker.make();
    maker.put(CSSM_ATTRIBUTE_ACCESS_CREDENTIALS, AccessCred);
    maker.put(CSSM_ATTRIBUTE_KEY, Key);
    Required(NewContextHandle) = maker(CSSM_ALGCLASS_SIGNATURE, AlgorithmID);
    END_API(CSSM)
}


CSSM_RETURN CSSMAPI
CSSM_CSP_CreateSymmetricContext (CSSM_CSP_HANDLE CSPHandle,
                                 CSSM_ALGORITHMS AlgorithmID,
                                 CSSM_ENCRYPT_MODE Mode,
                                 const CSSM_ACCESS_CREDENTIALS *AccessCred,
                                 const CSSM_KEY *Key,
                                 const CSSM_DATA *InitVector,
                                 CSSM_PADDING Padding,
                                 void *Reserved,
                                 CSSM_CC_HANDLE *NewContextHandle)
{
    BEGIN_API
    if (Reserved != NULL)
        CssmError::throwMe(CSSM_ERRCODE_INVALID_POINTER);
    HandleContext::Maker maker(CSPHandle);
    maker.setup(Mode);
    maker.setup(AccessCred);
    maker.setup(Key);
    maker.setup(InitVector);
    maker.setup(Padding);
    maker.make();
    maker.put(CSSM_ATTRIBUTE_MODE, Mode);
    maker.put(CSSM_ATTRIBUTE_ACCESS_CREDENTIALS, AccessCred);
    maker.put(CSSM_ATTRIBUTE_KEY, Key);
    maker.put(CSSM_ATTRIBUTE_INIT_VECTOR, InitVector);
    maker.put(CSSM_ATTRIBUTE_PADDING, Padding);
    Required(NewContextHandle) = maker(CSSM_ALGCLASS_SYMMETRIC, AlgorithmID);
    END_API(CSSM)
}


CSSM_RETURN CSSMAPI
CSSM_CSP_CreateDigestContext (CSSM_CSP_HANDLE CSPHandle,
                              CSSM_ALGORITHMS AlgorithmID,
                              CSSM_CC_HANDLE *NewContextHandle)
{
    BEGIN_API
    HandleContext::Maker maker(CSPHandle);
    maker.make();
    Required(NewContextHandle) = maker(CSSM_ALGCLASS_DIGEST, AlgorithmID);
    END_API(CSSM)
}


CSSM_RETURN CSSMAPI
CSSM_CSP_CreateMacContext (CSSM_CSP_HANDLE CSPHandle,
                           CSSM_ALGORITHMS AlgorithmID,
                           const CSSM_KEY *Key,
                           CSSM_CC_HANDLE *NewContextHandle)
{
    BEGIN_API
    HandleContext::Maker maker(CSPHandle);
    maker.setup(Key, CSSMERR_CSP_MISSING_ATTR_KEY);
    maker.make();
    maker.put(CSSM_ATTRIBUTE_KEY, Key);
    Required(NewContextHandle) = maker(CSSM_ALGCLASS_MAC, AlgorithmID);
    END_API(CSSM)
}


CSSM_RETURN CSSMAPI
CSSM_CSP_CreateRandomGenContext (CSSM_CSP_HANDLE CSPHandle,
                                 CSSM_ALGORITHMS AlgorithmID,
                                 const CSSM_CRYPTO_DATA *Seed,
                                 uint32 Length,
                                 CSSM_CC_HANDLE *NewContextHandle)
{
    BEGIN_API
    HandleContext::Maker maker(CSPHandle);
    maker.setup(Seed);
    maker.setup(Length, CSSMERR_CSP_INVALID_ATTR_OUTPUT_SIZE);
    maker.make();
    maker.put(CSSM_ATTRIBUTE_SEED, Seed);
    maker.put(CSSM_ATTRIBUTE_OUTPUT_SIZE, Length);
    Required(NewContextHandle) = maker(CSSM_ALGCLASS_RANDOMGEN, AlgorithmID);
    END_API(CSSM)
}


CSSM_RETURN CSSMAPI
CSSM_CSP_CreateAsymmetricContext (CSSM_CSP_HANDLE CSPHandle,
                                  CSSM_ALGORITHMS AlgorithmID,
                                  const CSSM_ACCESS_CREDENTIALS *AccessCred,
                                  const CSSM_KEY *Key,
                                  CSSM_PADDING Padding,
                                  CSSM_CC_HANDLE *NewContextHandle)
{
    BEGIN_API
    HandleContext::Maker maker(CSPHandle);
    maker.setup(AccessCred, CSSMERR_CSP_MISSING_ATTR_ACCESS_CREDENTIALS);
    maker.setup(Key, CSSMERR_CSP_MISSING_ATTR_KEY);
    maker.setup(Padding);
    maker.make();
    maker.put(CSSM_ATTRIBUTE_ACCESS_CREDENTIALS, AccessCred);
    maker.put(CSSM_ATTRIBUTE_KEY, Key);
    maker.put(CSSM_ATTRIBUTE_PADDING, Padding);
    Required(NewContextHandle) = maker(CSSM_ALGCLASS_ASYMMETRIC, AlgorithmID);
    END_API(CSSM)
}


CSSM_RETURN CSSMAPI
CSSM_CSP_CreateDeriveKeyContext (CSSM_CSP_HANDLE CSPHandle,
                                 CSSM_ALGORITHMS AlgorithmID,
                                 CSSM_KEY_TYPE DeriveKeyType,
                                 uint32 DeriveKeyLengthInBits,
                                 const CSSM_ACCESS_CREDENTIALS *AccessCred,
                                 const CSSM_KEY *BaseKey,
                                 uint32 IterationCount,
                                 const CSSM_DATA *Salt,
                                 const CSSM_CRYPTO_DATA *Seed,
                                 CSSM_CC_HANDLE *NewContextHandle)
{
    BEGIN_API
    HandleContext::Maker maker(CSPHandle);
    maker.setup(DeriveKeyType, CSSMERR_CSP_INVALID_ATTR_KEY_TYPE);
    maker.setup(DeriveKeyLengthInBits, CSSMERR_CSP_INVALID_ATTR_KEY_LENGTH);
    maker.setup(AccessCred);
    maker.setup(BaseKey);
    maker.setup(IterationCount);
    maker.setup(Salt);
    maker.setup(Seed);
    maker.make();
    maker.put(CSSM_ATTRIBUTE_KEY_TYPE, DeriveKeyType);
    maker.put(CSSM_ATTRIBUTE_KEY_LENGTH, DeriveKeyLengthInBits);
    maker.put(CSSM_ATTRIBUTE_ACCESS_CREDENTIALS, AccessCred);
    maker.put(CSSM_ATTRIBUTE_KEY, BaseKey);
    maker.put(CSSM_ATTRIBUTE_ITERATION_COUNT, IterationCount);
    maker.put(CSSM_ATTRIBUTE_SALT, Salt);
    maker.put(CSSM_ATTRIBUTE_SEED, Seed);
    Required(NewContextHandle) = maker(CSSM_ALGCLASS_DERIVEKEY, AlgorithmID);
    END_API(CSSM)
}


CSSM_RETURN CSSMAPI
CSSM_CSP_CreateKeyGenContext (CSSM_CSP_HANDLE CSPHandle,
                              CSSM_ALGORITHMS AlgorithmID,
                              uint32 KeySizeInBits,
                              const CSSM_CRYPTO_DATA *Seed,
                              const CSSM_DATA *Salt,
                              const CSSM_DATE *StartDate,
                              const CSSM_DATE *EndDate,
                              const CSSM_DATA *Params,
                              CSSM_CC_HANDLE *NewContextHandle)
{
    BEGIN_API
    HandleContext::Maker maker(CSPHandle);
    maker.setup(KeySizeInBits, CSSMERR_CSP_INVALID_ATTR_KEY_LENGTH);
    maker.setup(Seed);
    maker.setup(Salt);
    maker.setup(StartDate);
    maker.setup(EndDate);
    maker.setup(Params);
    maker.make();
    maker.put(CSSM_ATTRIBUTE_KEY_LENGTH, KeySizeInBits);
    maker.put(CSSM_ATTRIBUTE_SEED, Seed);
    maker.put(CSSM_ATTRIBUTE_SALT, Salt);
    maker.put(CSSM_ATTRIBUTE_START_DATE, StartDate);
    maker.put(CSSM_ATTRIBUTE_END_DATE, EndDate);
    maker.put(CSSM_ATTRIBUTE_ALG_PARAMS, Params);
    Required(NewContextHandle) = maker(CSSM_ALGCLASS_KEYGEN, AlgorithmID);
    END_API(CSSM)
}


CSSM_RETURN CSSMAPI
CSSM_CSP_CreatePassThroughContext (CSSM_CSP_HANDLE CSPHandle,
                                   const CSSM_KEY *Key,
                                   CSSM_CC_HANDLE *NewContextHandle)
{
    BEGIN_API
    HandleContext::Maker maker(CSPHandle);
    maker.setup(Key, CSSMERR_CSP_MISSING_ATTR_KEY);
    maker.make();
    maker.put(CSSM_ATTRIBUTE_KEY, Key);
    Required(NewContextHandle) = maker(CSSM_ALGCLASS_NONE, CSSM_ALGID_NONE);
    END_API(CSSM)
}


//
// CSSM_GetContext makes a deep copy of a CSP context and hands it to the
// caller. The result is NOT a HandleContext, but a bare Context that is
// in no dictionaries etc. User must delete it by calling CSSM_FreeContext.
//
CSSM_RETURN CSSMAPI
CSSM_GetContext (CSSM_CC_HANDLE CCHandle,
                 CSSM_CONTEXT_PTR *ContextP)
{
    BEGIN_API
    HandleContext &context = findHandle<HandleContext>(CCHandle, CSSM_ERRCODE_INVALID_CONTEXT_HANDLE);
    Context *newContext = new(context.attachment) Context(context.type(), context.algorithm());
    try {
        newContext->CSPHandle = context.attachment.handle();
        newContext->copyFrom(context, context.attachment);
        Required(ContextP) = newContext;
    } catch (...) {
        context.attachment.free(newContext);
        throw;
    }
    END_API(CSSM)
}


//
// Since we allocated all the data in one fell heap, our FreeContext
// function is disappointingly simple.
//
CSSM_RETURN CSSMAPI
CSSM_FreeContext (CSSM_CONTEXT_PTR ContextP)
{
    BEGIN_API
    Context *context = &Context::required(ContextP);
    context->destroy(context, findHandle<CSPAttachment>(context->CSPHandle, CSSM_ERRCODE_INVALID_CONTEXT_HANDLE));
    END_API(CSSM)
}


CSSM_RETURN CSSMAPI
CSSM_SetContext (CSSM_CC_HANDLE CCHandle,
                 const CSSM_CONTEXT *ContextP)
{
    BEGIN_API
    const Context &source = Context::required(ContextP);
    HandleContext &context = findHandle<HandleContext>(CCHandle, CSSM_ERRCODE_INVALID_CONTEXT_HANDLE);

    CSSM_CONTEXT_ATTRIBUTE *oldAttributes = context.ContextAttributes;
    uint32 oldCount = context.NumberOfAttributes;
	CSSM_CONTEXT_TYPE oldType = context.ContextType;
    CSSM_ALGORITHMS oldAlgorithm = context.AlgorithmType;
    
    context.copyFrom(source, context.attachment);
	context.ContextType = source.ContextType;
    context.AlgorithmType = source.AlgorithmType;
    
    if (CSSM_RETURN err = context.validateChange(CSSM_CONTEXT_EVENT_UPDATE)) {
		context.attachment.free(context.ContextAttributes);	// free rejected attribute blob
    	context.ContextAttributes = oldAttributes;	// restore...
    	context.NumberOfAttributes = oldCount;		// ... old
		context.ContextType = oldType;				//  ... values
    	context.AlgorithmType = oldAlgorithm;		//   ... in context
    	CssmError::throwMe(err);						// tell the caller it failed
    }
    
    free(oldAttributes);
    END_API(CSSM)
}


CSSM_RETURN CSSMAPI
CSSM_DeleteContext (CSSM_CC_HANDLE CCHandle)
{
    BEGIN_API
    HandleContext &context = enterContext(CCHandle);
	StLock<CountingMutex, &CountingMutex::enter, &CountingMutex::exit> 
		_(context.attachment, true);
    
    // ignore error return from CSP event notify (can't decline deletion)
    context.validateChange(CSSM_CONTEXT_EVENT_DELETE);
    	
    context.destroy(&context, context.attachment);
    END_API(CSSM)
}


//
// The GetContextAttribute API is fatally flawed. The standard implies a deep
// copy of the attribute value, but no release function exists. Instead, we
// return a shallow copy (sharing structure) which you need not release, but
// which becomes invalid when the source Context is destroyed.
//
CSSM_RETURN CSSMAPI
CSSM_GetContextAttribute (const CSSM_CONTEXT *Context,
                          uint32 AttributeType,
                          CSSM_CONTEXT_ATTRIBUTE_PTR *ContextAttribute)
{
    BEGIN_API
    CSSM_ATTRIBUTE_TYPE type = CSSM_ATTRIBUTE_TYPE(AttributeType);	// CDSA defect
    Required(ContextAttribute) = Context::required(Context).find(type);
    END_API(CSSM)
}


CSSM_RETURN CSSMAPI
CSSM_UpdateContextAttributes (CSSM_CC_HANDLE CCHandle,
                              uint32 NumberAttributes,
                              const CSSM_CONTEXT_ATTRIBUTE *ContextAttributes)
{
    BEGIN_API
    HandleContext &context = findHandle<HandleContext>(CCHandle, CSSM_ERRCODE_INVALID_CONTEXT_HANDLE);
    context.mergeAttributes(ContextAttributes, NumberAttributes);
    END_API(CSSM)
}


CSSM_RETURN CSSMAPI
CSSM_DeleteContextAttributes (CSSM_CC_HANDLE CCHandle,
                              uint32 NumberOfAttributes,
                              const CSSM_CONTEXT_ATTRIBUTE *ContextAttributes)
{
    BEGIN_API
    if (NumberOfAttributes == 0)
        return CSSM_OK;	// I suppose
    Required(ContextAttributes); // preflight
    HandleContext &context = findHandle<HandleContext>(CCHandle, CSSM_ERRCODE_INVALID_CONTEXT_HANDLE);
    for (uint32 n = 0; n < NumberOfAttributes; n++)
        context.deleteAttribute(ContextAttributes[n].AttributeType);
    END_API(CSSM)
}


//
// Miscellaneous odds and ends - these functions just need a wee bit of
// manual massaging to fit.
//
CSSM_RETURN CSSMAPI
CSSM_DigestDataClone (CSSM_CC_HANDLE ccHandle,
                      CSSM_CC_HANDLE *newCCHandle)
{
	BEGIN_API
    HandleContext &context = findHandleAndLock<HandleContext>(ccHandle, CSSM_ERRCODE_INVALID_CONTEXT_HANDLE);
	TransitLock _(context.attachment);
    HandleContext *newContext =
    	new(context.attachment) HandleContext(context.attachment, context.type(), context.algorithm());
    try {
        newContext->CSPHandle = context.attachment.handle();
        newContext->copyFrom(context, context.attachment);
		context.attachment.downcalls.DigestDataClone(context.attachment.handle(), ccHandle, newContext->handle());
        Required(newCCHandle) = newContext->handle();
    } catch (...) {
    	newContext->destroy(newContext, context.attachment);
        throw;
    }
	END_API(CSSM)
}


CSSM_RETURN CSSMAPI
CSSM_QueryKeySizeInBits (CSSM_CSP_HANDLE CSPHandle,
                         CSSM_CC_HANDLE ccHandle,
                         const CSSM_KEY *key,
                         CSSM_KEY_SIZE_PTR keySize)
{
	//@@@ could afford not to lock attachment in have-CC case
	BEGIN_API
	Required(keySize);
	Context *context;
	CSPAttachment *attachment;
	if (ccHandle == HandleObject::invalidHandle) {
		// key specified by CSPHandle and Key
		attachment = &enterAttachment<CSPAttachment>(CSPHandle);
		context = NULL;
	} else {
		// key specified by ccHandle
		HandleContext *ctx = &enterContext(ccHandle);
		try {
			attachment = &ctx->attachment;
            context = ctx;
			CSPHandle = context->CSPHandle;
			key = &context->get<CssmKey>(CSSM_ATTRIBUTE_KEY,
										 CSSMERR_CSP_INVALID_KEY);
		} catch (...) {
			attachment->exit();
			throw;
		}
	}
	TransitLock _(*attachment);
	CSSM_RETURN result = attachment->downcalls.QueryKeySizeInBits(CSPHandle,
			ccHandle, context, key, keySize);
	return result;
	END_API(CSSM)
}


CSSM_RETURN CSSMAPI
CSSM_GenerateAlgorithmParams (CSSM_CC_HANDLE CCHandle,
                              uint32 ParamBits,
                              CSSM_DATA_PTR Param)
{
	BEGIN_API
    // this function has more subtle locking than usual. Pay attention:

    // (1) Resolve context and ensure attachment exit
	HandleContext &context = enterContext(CCHandle);
    StLock<CountingMutex, &CountingMutex::enter, &CountingMutex::exit> _(context.attachment, true);
    
    // (2) Call CSP, acquiring module safe-lock for a moment (only)
	CSSM_CONTEXT_ATTRIBUTE_PTR attributes;
	uint32 count;
    {
        StLock<Module, &Module::safeLock, &Module::safeUnlock> _(context.attachment.module);
        if (CSSM_RETURN err =
                context.attachment.downcalls.GenerateAlgorithmParams(context.attachment.handle(),
                CCHandle, &context,
                ParamBits, Param,
                &count, &attributes))
            CssmError::throwMe(err);
    }
    
    // (3) Merge the attributes returned into the context, which will (again) acquire the module safe-lock internally
	if (count)
	    context.mergeAttributes(attributes, count);
    // the memory at (attributes, count) belongs to the CSP; don't free it here
	END_API(CSSM)
}


//
// Include the auto-generated functions
//
#include "transition.gen"
