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


//
// Session_CSP.cpp - CSR-related session functions.
//

#include "AppleX509CLSession.h"
#include "DecodedCert.h"
#include "clNameUtils.h"
#include "clNssUtils.h"
#include "cldebugging.h"
#include "CSPAttacher.h"
#include "clNssUtils.h"
#include <Security/oidsattr.h>
#include <Security/oidscert.h>
#include <Security/cssmapple.h>
#include <Security/csrTemplates.h>
#include <Security/asn1Templates.h>

/* 
 * Generate a DER-encoded CSR.
 */
void AppleX509CLSession::generateCsr(
	CSSM_CC_HANDLE 		CCHandle,
	const CSSM_APPLE_CL_CSR_REQUEST *csrReq,
	CSSM_DATA_PTR		&csrPtr)
{
	/*
	 * We use the full NSSCertRequest here; we encode the 
	 * NSSCertRequestInfo component separately to calculate
	 * its signature, then we encode the whole NSSCertRequest
	 * after dropping in the signature and SignatureAlgorithmIdentifier.
	 */ 
	NSSCertRequest certReq;
	NSSCertRequestInfo &reqInfo = certReq.reqInfo;
	PRErrorCode prtn;

	memset(&certReq, 0, sizeof(certReq));
	
	/* 
	 * Step 1: convert CSSM_APPLE_CL_CSR_REQUEST to CertificationRequestInfo.
	 * All allocs via local arena pool.
	 */
	SecNssCoder coder;
	ArenaAllocator alloc(coder);
	clIntToData(0, reqInfo.version, alloc);
	
	/* subject Name, required  */
	if(csrReq->subjectNameX509 == NULL) {
		CssmError::throwMe(CSSMERR_CL_INVALID_POINTER);
	}
	CL_cssmNameToNss(*csrReq->subjectNameX509, reqInfo.subject, coder);
	
	/* key --> CSSM_X509_SUBJECT_PUBLIC_KEY_INFO */
	CL_CSSMKeyToSubjPubKeyInfoNSS(*csrReq->subjectPublicKey, 
		reqInfo.subjectPublicKeyInfo, coder);

	/* attributes - see sm_x501if - we support one, CSSMOID_ChallengePassword,
 	 * as a printable string */
	if(csrReq->challengeString) {
		/* alloc a NULL_terminated array of NSS_Attribute pointers */
		reqInfo.attributes = (NSS_Attribute **)coder.malloc(2 * sizeof(NSS_Attribute *));
		reqInfo.attributes[1] = NULL;
		
		/* alloc one NSS_Attribute */
		reqInfo.attributes[0] = (NSS_Attribute *)coder.malloc(sizeof(NSS_Attribute));
		NSS_Attribute *attr = reqInfo.attributes[0];
		memset(attr, 0, sizeof(NSS_Attribute));
		
		 /* NULL_terminated array of attrValues */
		attr->attrValue = (CSSM_DATA **)coder.malloc(2 * sizeof(CSSM_DATA *));
		attr->attrValue[1] = NULL;
		
		/* one value - we're almost there */
		attr->attrValue[0] = (CSSM_DATA *)coder.malloc(sizeof(CSSM_DATA));
		
		/* attrType is an OID, temp, use static OID */
		attr->attrType = CSSMOID_ChallengePassword;

		/* one value, spec'd as AsnAny, we have to encode first. */		
		CSSM_DATA strData;
		strData.Data = (uint8 *)csrReq->challengeString;
		strData.Length = strlen(csrReq->challengeString);
		prtn = coder.encodeItem(&strData, kSecAsn1PrintableStringTemplate,
			*attr->attrValue[0]);
		if(prtn) {
			clErrorLog("generateCsr: error encoding challengeString\n");
			CssmError::throwMe(CSSMERR_CL_MEMORY_ERROR);
		}
	}
	
	/*
	 * Step 2: DER-encode the NSSCertRequestInfo prior to signing.
	 */
	CSSM_DATA encReqInfo;
	prtn = coder.encodeItem(&reqInfo, kSecAsn1CertRequestInfoTemplate, encReqInfo);
	if(prtn) {
		clErrorLog("generateCsr: error encoding CertRequestInfo\n");
		CssmError::throwMe(CSSMERR_CL_MEMORY_ERROR);
	}
	
	/*
	 * Step 3: sign the encoded NSSCertRequestInfo.
	 */
	CssmAutoData sig(*this);
	CssmData &infoData = CssmData::overlay(encReqInfo);
	signData(CCHandle, infoData, sig);
	 
	/*
	 * Step 4: finish up NSSCertRequest - signatureAlgorithm, signature
	 */
	certReq.signatureAlgorithm.algorithm = csrReq->signatureOid;
	/* FIXME - for now assume NULL alg params */
	CL_nullAlgParams(certReq.signatureAlgorithm);
	certReq.signature.Data = (uint8 *)sig.data();
	certReq.signature.Length = sig.length() * 8;
	
	/* 
	 * Step 5: DER-encode the finished NSSCertRequest into app space.
	 */
	CssmAutoData encCsr(*this);
	prtn = SecNssEncodeItemOdata(&certReq, kSecAsn1CertRequestTemplate, encCsr);
	if(prtn) {
		clErrorLog("generateCsr: error encoding CertRequestInfo\n");
		CssmError::throwMe(CSSMERR_CL_MEMORY_ERROR);
	}
	
	/* TBD - enc64 the result, when we have this much working */
	csrPtr = (CSSM_DATA_PTR)malloc(sizeof(CSSM_DATA));
	csrPtr->Data = (uint8 *)encCsr.data();
	csrPtr->Length = encCsr.length();
	encCsr.release();
}

/*
 * Verify CSR with its own public key. 
 */
void AppleX509CLSession::verifyCsr(
	const CSSM_DATA		*csrPtr)
{
	/*
	 * 1. Extract the public key from the CSR. We do this by decoding
	 *    the whole thing and getting a CSSM_KEY from the 
	 *    SubjectPublicKeyInfo.
	 */
	NSSCertRequest certReq;
	SecNssCoder coder;
	PRErrorCode prtn;
	
	memset(&certReq, 0, sizeof(certReq));
	prtn = coder.decodeItem(*csrPtr, kSecAsn1CertRequestTemplate, &certReq);
	if(prtn) {
		CssmError::throwMe(CSSMERR_CL_INVALID_DATA);
	}
	
	NSSCertRequestInfo &reqInfo = certReq.reqInfo;
	CSSM_KEY_PTR cssmKey = CL_extractCSSMKeyNSS(reqInfo.subjectPublicKeyInfo, 
		*this,		// alloc
		NULL);		// no DecodedCert

	/*
	 * 2. Obtain signature algorithm and parameters. 
	 */
	CSSM_X509_ALGORITHM_IDENTIFIER sigAlgId = certReq.signatureAlgorithm;
	CSSM_ALGORITHMS vfyAlg = CL_oidToAlg(sigAlgId.algorithm);
			
	/*
	 * 3. Extract the raw bits to be verified and the signature. We 
	 *    decode the CSR as a CertificationRequestSigned for this, which 
	 *    avoids the decode of the CertificationRequestInfo.
	 */
	NSS_SignedCertRequest certReqSigned;
	memset(&certReqSigned, 0, sizeof(certReqSigned));
	prtn = coder.decodeItem(*csrPtr, kSecAsn1SignedCertRequestTemplate, &certReqSigned);
	if(prtn) {
		CssmError::throwMe(CSSMERR_CL_INVALID_DATA);
	}

	CSSM_DATA sigBytes = certReqSigned.signature;
	sigBytes.Length = (sigBytes.Length + 7 ) / 8;
	CssmData &sigCdata = CssmData::overlay(sigBytes);
	CssmData &toVerify = CssmData::overlay(certReqSigned.certRequestBlob);
	
	/*
	 * 4. Attach to CSP, cook up signature context, verify signature.
	 */
	CSSM_CSP_HANDLE cspHand = getGlobalCspHand(true);
	CSSM_RETURN crtn;
	CSSM_CC_HANDLE ccHand;
	crtn = CSSM_CSP_CreateSignatureContext(cspHand,
		vfyAlg,
		NULL,			// Access Creds
		cssmKey,
		&ccHand);
	if(crtn) {
		CssmError::throwMe(crtn);
	}
	verifyData(ccHand, toVerify, sigCdata);
	CL_freeCSSMKey(cssmKey, *this);
}

