/*
 * 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@
 */
/*
 * pkcs12Templates.cpp
 */
 
#include "pkcs12Templates.h"
#include "pkcs12Utils.h"
#include <security_asn1/nssUtils.h>
#include <Security/asn1Templates.h>
#include <Security/oidsattr.h>

const SecAsn1Template NSS_P12_MacDataTemplate[] = {
    { SEC_ASN1_SEQUENCE,
      0, NULL, sizeof(NSS_P12_MacData) },
	{ SEC_ASN1_INLINE,
	  offsetof(NSS_P12_MacData,mac),
	  NSS_P7_DigestInfoTemplate },
    { SEC_ASN1_OCTET_STRING,
	  offsetof(NSS_P12_MacData,macSalt) },
	/* iterations is unsigned - right? */
	{ SEC_ASN1_INTEGER | SEC_ASN1_OPTIONAL,
	  offsetof(NSS_P12_MacData,iterations) },
    { 0, }
};

const SecAsn1Template pointerToMacDataTemplate[] = {
    { SEC_ASN1_POINTER, 0, NSS_P12_MacDataTemplate }
};

/* raw PFX with unprocessed authSafe */
const SecAsn1Template NSS_P12_RawPFXTemplate[] = {
    { SEC_ASN1_SEQUENCE,
      0, NULL, sizeof(NSS_P12_RawPFX) },
	{ SEC_ASN1_INTEGER,
	  offsetof(NSS_P12_RawPFX,version) },
	{ SEC_ASN1_INLINE,
	  offsetof(NSS_P12_RawPFX, authSafe),
	  NSS_P7_RawContentInfoTemplate },
	{ SEC_ASN1_POINTER | SEC_ASN1_OPTIONAL,
	  offsetof(NSS_P12_RawPFX, macData),
	  NSS_P12_MacDataTemplate },
    { 0, }
};

/* PFX with decoded authSafe */
extern const SecAsn1Template NSS_P12_DecodedPFXTemplate[] = {
    { SEC_ASN1_SEQUENCE,
      0, NULL, sizeof(NSS_P12_DecodedPFX) },
	{ SEC_ASN1_INTEGER,
	  offsetof(NSS_P12_DecodedPFX,version) },
	{ SEC_ASN1_INLINE,
	  offsetof(NSS_P12_DecodedPFX, authSafe),
	  NSS_P7_DecodedContentInfoTemplate },
	{ SEC_ASN1_POINTER | SEC_ASN1_OPTIONAL,
	  offsetof(NSS_P12_DecodedPFX, macData),
	  NSS_P12_MacDataTemplate },
    { 0, }
};

/* AuthenticatedSafe */
const SecAsn1Template NSS_P12_AuthenticatedSafeTemplate[] = {
	{ SEC_ASN1_SEQUENCE_OF,
	  offsetof(NSS_P12_AuthenticatedSafe, info),
	  NSS_P7_DecodedContentInfoTemplate,
	  sizeof(NSS_P12_AuthenticatedSafe) }
};

/*
 * Individual SafeBag type-specific templates here when we write 'em
 */
const SecAsn1Template NSS_P12_PtrToShroudedKeyBagTemplate[] = {
	{ SEC_ASN1_POINTER, 0, kSecAsn1EncryptedPrivateKeyInfoTemplate }
};

/*
 * CertBag via SEC_ASN1_DYNAMIC
 */
static const SecAsn1Template * NSS_P12_CertBagChooser(
	void *arg, 			// --> NSS_P12_CertBag
	Boolean enc,
	const char *buf,	// on decode, tag byte
	void *dest)			// --> NSS_P12_CertBag.bagValue
{
	NSS_P12_CertBag *bag = (NSS_P12_CertBag *)arg;
	const SecAsn1Template *templ = NULL;
	NSS_P12_CertBagType type = CT_Unknown;
	CSSM_OID *oid = &bag->bagType;
	
	if(nssCompareCssmData(oid, &CSSMOID_PKCS9_X509Certificate)) {
		templ = kSecAsn1OctetStringTemplate;
		type = CT_X509;
	}
	else if(nssCompareCssmData(oid, &CSSMOID_PKCS9_SdsiCertificate)) {
		templ = kSecAsn1IA5StringTemplate;
		type = CT_SDSI;
	}
	else {
		/* punt */
		templ = kSecAsn1AnyTemplate;
	}
	if(!enc) {
		bag->type = type;
	}
	return templ;
}

static const SecAsn1TemplateChooserPtr NSS_P12_CertBagChooserPtr = 
	NSS_P12_CertBagChooser;

const SecAsn1Template NSS_P12_CertBagTemplate[] = {
    { SEC_ASN1_SEQUENCE,
      0, NULL, sizeof(NSS_P12_CertBag) },
	{ SEC_ASN1_OBJECT_ID,
	  offsetof(NSS_P12_CertBag,bagType) },
	  /* these come in with a tag of 0xA0, context/constructed, 
	   * though I don't know why they are flagged as constructed */
	{ SEC_ASN1_DYNAMIC | SEC_ASN1_CONTEXT_SPECIFIC |
	  SEC_ASN1_EXPLICIT | SEC_ASN1_CONSTRUCTED | 0,
	  offsetof(NSS_P12_CertBag, certValue),
	  &NSS_P12_CertBagChooserPtr },
    { 0, }
};

const SecAsn1Template NSS_P12_PtrToCertBagTemplate[] = {
	{ SEC_ASN1_POINTER, 0, NSS_P12_CertBagTemplate }
};

/*
 * CrlBag via SEC_ASN1_DYNAMIC
 */
static const SecAsn1Template * NSS_P12_CrlBagChooser(
	void *arg, 			// --> NSS_P12_CrlBag
	Boolean enc,
	const char *buf,	// on decode, tag byte
	void *dest)			// --> NSS_P12_CertBag.bagValue
{
	NSS_P12_CrlBag *bag = (NSS_P12_CrlBag *)arg;
	const SecAsn1Template *templ = NULL;
	NSS_P12_CrlBagType type = CRT_Unknown;
	CSSM_OID *oid = &bag->bagType;
	
	if(nssCompareCssmData(oid, &CSSMOID_PKCS9_X509Crl)) {
		templ = kSecAsn1OctetStringTemplate;
		type = CRT_X509;
	}
	else {
		/* punt */
		templ = kSecAsn1AnyTemplate;
	}
	if(!enc) {
		bag->type = type;
	}
	return templ;
}

static const SecAsn1TemplateChooserPtr NSS_P12_CrlBagChooserPtr = 
	NSS_P12_CrlBagChooser;

const SecAsn1Template NSS_P12_CrlBagTemplate[] = {
    { SEC_ASN1_SEQUENCE,
      0, NULL, sizeof(NSS_P12_CrlBag) },
	{ SEC_ASN1_OBJECT_ID,
	  offsetof(NSS_P12_CrlBag,bagType) },
	  /* these come in with a tag of 0xA0, context/constructed, 
	   * though I don't know why they are flagged as constructed */
	{ SEC_ASN1_DYNAMIC | SEC_ASN1_CONTEXT_SPECIFIC |
	  SEC_ASN1_EXPLICIT | SEC_ASN1_CONSTRUCTED | 0,
	  offsetof(NSS_P12_CrlBag, crlValue),
	  &NSS_P12_CrlBagChooserPtr },
    { 0, }
};

const SecAsn1Template NSS_P12_PtrToCrlBagTemplate[] = {
	{ SEC_ASN1_POINTER, 0, NSS_P12_CrlBagTemplate }
};


/* the stub templates for unimplemented BagTypes */
#define NSS_P12_PtrToKeyBagTemplate				kSecAsn1PointerToAnyTemplate
#define NSS_P12_PtrToSecretBagTemplate			kSecAsn1PointerToAnyTemplate
#define NSS_P12_PtrToSafeContentsBagTemplate	kSecAsn1PointerToAnyTemplate

 
/*
 * SafeBag via SEC_ASN1_DYNAMIC
 */
static const SecAsn1Template * NSS_P12_SafeBagChooser(
	void *arg, 			// --> NSS_P12_SafeBag
	Boolean enc,
	const char *buf,	// on decode, tag byte
	void *dest)			// --> NSS_P12_SafeBag.bagValue
{
	NSS_P12_SafeBag *bag = (NSS_P12_SafeBag *)arg;
	const SecAsn1Template *templ = NULL;
	NSS_P12_SB_Type type = BT_None;
	CSSM_OID *oid = &bag->bagId;
	
	if(nssCompareCssmData(oid, &CSSMOID_PKCS12_keyBag)) {
		templ = NSS_P12_PtrToKeyBagTemplate;
		type = BT_KeyBag;
	}
	else if(nssCompareCssmData(oid, &CSSMOID_PKCS12_shroudedKeyBag)) {
		templ = NSS_P12_PtrToShroudedKeyBagTemplate;
		type = BT_ShroudedKeyBag;
	}
	else if(nssCompareCssmData(oid, &CSSMOID_PKCS12_certBag)) {
		templ = NSS_P12_PtrToCertBagTemplate;
		type = BT_CertBag;
	}
	else if(nssCompareCssmData(oid, &CSSMOID_PKCS12_crlBag)) {
		templ = NSS_P12_PtrToCrlBagTemplate;
		type = BT_CrlBag;
	}
	else if(nssCompareCssmData(oid, &CSSMOID_PKCS12_secretBag)) {
		templ = NSS_P12_PtrToSecretBagTemplate;
		type = BT_SecretBag;
	}
	else if(nssCompareCssmData(oid, &CSSMOID_PKCS12_safeContentsBag)) {
		templ = NSS_P12_PtrToSafeContentsBagTemplate;
		type = BT_SafeContentsBag;
	}
	/* add more here when we implement them */
	else {
		templ = kSecAsn1PointerToAnyTemplate;
	}
	if(!enc) {
		bag->type = type;
	}
	return templ;
}

static const SecAsn1TemplateChooserPtr NSS_P12_SafeBagChooserPtr = 
	NSS_P12_SafeBagChooser;

const SecAsn1Template NSS_P12_SafeBagTemplate[] = {
    { SEC_ASN1_SEQUENCE,
      0, NULL, sizeof(NSS_P12_SafeBag) },
    { SEC_ASN1_OBJECT_ID,
	  offsetof(NSS_P12_SafeBag,bagId) },
    { SEC_ASN1_DYNAMIC | SEC_ASN1_CONSTRUCTED |
		SEC_ASN1_EXPLICIT | SEC_ASN1_CONTEXT_SPECIFIC | 0,
	  offsetof(NSS_P12_SafeBag,bagValue),
	  &NSS_P12_SafeBagChooserPtr },
    { SEC_ASN1_OPTIONAL | SEC_ASN1_SET_OF,
        offsetof(NSS_P12_SafeBag,bagAttrs),
        kSecAsn1AttributeTemplate },
   { 0 }
};

const SecAsn1Template NSS_P12_SafeContentsTemplate[] = {
	{ SEC_ASN1_SEQUENCE_OF,
	  offsetof(NSS_P12_SafeContents, bags),
	  NSS_P12_SafeBagTemplate,
	  sizeof(NSS_P12_SafeContents) }
};

const SecAsn1Template NSS_P12_PBE_ParamsTemplate[] = {
	{ SEC_ASN1_SEQUENCE,
      0, NULL, sizeof(NSS_P12_PBE_Params) },
    { SEC_ASN1_OCTET_STRING,
	  offsetof(NSS_P12_PBE_Params,salt) },
	/* iterations is unsigned - right? */
	{ SEC_ASN1_INTEGER,
	  offsetof(NSS_P12_PBE_Params,iterations) },
	{ 0 }
};


