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


/*
 * DecodedCert.cpp - object representing a snacc-decoded cert, with extensions
 * parsed and decoded (still in snacc format).
 *
 * Created 9/1/2000 by Doug Mitchell. 
 * Copyright (c) 2000 by Apple Computer. 
 */

#include "DecodedCert.h"
#include "SnaccUtils.h"
#include "cldebugging.h"
#include "AppleX509CLSession.h"
#include "CSPAttacher.h"
#include <Security/cdsaUtils.h>
#include <Security/cssmapple.h>

DecodedCert::DecodedCert(
	AppleX509CLSession	&session)
	: alloc(session),
	  mSession(session)
{
	certificateToSign = new CertificateToSign;
	reset();
}

/* one-shot constructor, decoding from DER-encoded data */
DecodedCert::DecodedCert(
	AppleX509CLSession	&session,
	const CssmData 	&encodedCert)
	: alloc(session),
	  mSession(session)
{
	reset();
	SC_decodeAsnObj(encodedCert, *this);
	decodeExtensions();
	mState = CS_DecodedCert;
}
		
DecodedCert::~DecodedCert()
{
	/* free all extensions */
	unsigned dex;
	
	for(dex=0; dex<mNumExtensions; dex++) {
		DecodedExten *exten = &mExtensions[dex];
		delete exten->extnId;
		delete exten->snaccObj;
	}
	alloc.free(mExtensions);
	reset();
}
	
/* decode TBSCert and its extensions */
void DecodedCert::decodeTbs(
	const CssmData	&encodedTbs)
{
	CASSERT(mState == CS_Empty);
	CASSERT(certificateToSign != NULL);
	try {
		SC_decodeAsnObj(encodedTbs, *certificateToSign);
	}
	catch (...) {
		errorLog0("decodeTbs: tbs.BDec failure\n");
		/* FIXME - leave in bad state? delete and clear? let's be cautious...*/
		delete certificateToSign;
		certificateToSign = new CertificateToSign;
	}
	decodeExtensions();
	mState = CS_DecodedTBS;
}

/*
 * FIXME : how to determine max encoding size at run time!?
 */
#define MAX_TEMPLATE_SIZE	(8 * 1024)

/* encode TBS component; only called from CertCreateTemplate */
void DecodedCert::encodeTbs(
	CssmOwnedData	&encodedTbs)
{
	encodeExtensions();
	CASSERT(mState == CS_Building);
	if(certificateToSign == NULL) {
		errorLog0("DecodedCert::encodeTbs: no TBS\n");
		CssmError::throwMe(CSSMERR_CL_INTERNAL_ERROR);
	}
	
	/* enforce required fields - could go deeper, maybe we should */
	if((certificateToSign->signature == NULL) ||
	   (certificateToSign->issuer == NULL) ||
	   (certificateToSign->validity == NULL) ||
	   (certificateToSign->subject == NULL) ||
	   (certificateToSign->subjectPublicKeyInfo == NULL)) {
		errorLog0("DecodedCert::encodeTbs: incomplete TBS\n");
		/* an odd, undocumented error return */
		CssmError::throwMe(CSSMERR_CL_NO_FIELD_VALUES);
	}
	SC_encodeAsnObj(*certificateToSign, encodedTbs, MAX_TEMPLATE_SIZE);
}

/*
 * Cook up CSSM_KEYUSE, gleaning as much as possible from
 * (optional) extensions. If no applicable extensions available,
 * we'll just return CSSM_KEYUSE_ANY.
 *
 * Note that the standard KeyUsage flags involving 'signing' translate
 * to verify since we're only dealing with public keys. 
 */
CSSM_KEYUSE DecodedCert::inferKeyUsage() const
{
	CSSM_KEYUSE keyUse = 0;
	DecodedExten *decodedExten;
	uint32 numFields;
	
	decodedExten = findDecodedExt(id_ce_keyUsage, false, 0, numFields);
	if(decodedExten) {
		KeyUsage *ku = dynamic_cast<KeyUsage *>(decodedExten->snaccObj);
		if(ku == NULL) {
			errorLog0("inferKeyUsage: dynamic_cast failure(1)\n");
			CssmError::throwMe(CSSMERR_CL_INTERNAL_ERROR);
		}
		if(ku->GetBit(KeyUsage::digitalSignature)) {
			keyUse |= CSSM_KEYUSE_VERIFY;
		}
		if(ku->GetBit(KeyUsage::nonRepudiation)) {
			keyUse |= CSSM_KEYUSE_VERIFY;
		}
		if(ku->GetBit(KeyUsage::keyEncipherment)) {
			keyUse |= CSSM_KEYUSE_WRAP;
		}
		if(ku->GetBit(KeyUsage::keyAgreement)) {
			keyUse |= CSSM_KEYUSE_DERIVE;
		}
		if(ku->GetBit(KeyUsage::keyCertSign)) {
			keyUse |= CSSM_KEYUSE_VERIFY;
		}
		if(ku->GetBit(KeyUsage::cRLSign)) {
			keyUse |= CSSM_KEYUSE_VERIFY;
		}
		if(ku->GetBit(KeyUsage::dataEncipherment)) {
			keyUse |= CSSM_KEYUSE_ENCRYPT;
		}
	}
	decodedExten = findDecodedExt(id_ce_extKeyUsage, false, 0, numFields);
	if(decodedExten) {
		ExtKeyUsageSyntax *eku = 
			dynamic_cast<ExtKeyUsageSyntax *>(decodedExten->snaccObj);
		if(eku == NULL) {
			errorLog0("inferKeyUsage: dynamic_cast failure(2)\n");
			CssmError::throwMe(CSSMERR_CL_INTERNAL_ERROR);
		}
		unsigned numOids = eku->Count();
		eku->SetCurrToFirst();
		unsigned oidDex;
		for(oidDex=0; oidDex<numOids; oidDex++) {
			KeyPurposeId *purp = eku->Curr();
			if(*purp == id_kp_codeSigning) {
				keyUse |= CSSM_KEYUSE_VERIFY;
			}
			/* I don't think the other purposes are useful... */
			eku->GoNext();
		}
	}
	if(keyUse == 0) {
		/* Nothing found; take the default. */
		keyUse = CSSM_KEYUSE_ANY;
	}
	return keyUse;
}

/*
 * Obtain a CSSM_KEY from a decoded cert, inferring as much as we can
 * from required fields (subjectPublicKeyInfo) and extensions (for 
 * KeyUse).
 */
CSSM_KEY_PTR DecodedCert::extractCSSMKey(
	CssmAllocator		&alloc)	const
{
	CASSERT(certificateToSign != NULL);
	SubjectPublicKeyInfo *snaccKeyInfo = certificateToSign->subjectPublicKeyInfo;
	if((snaccKeyInfo == NULL) ||
	   (snaccKeyInfo->algorithm == NULL)) {
		CssmError::throwMe(CSSMERR_CL_NO_FIELD_VALUES);
	}
	return CL_extractCSSMKey(*snaccKeyInfo, alloc, this);
}

