Milán Major

netscape-revival

1 branch
Code

security/lib/pkcs12/p12local.c

#include "pkcs12.h"
#include "secpkcs7.h"
#include "secasn1.h"
#include "rsa.h"
#include "crypto.h"
#include "seccomon.h"
#include "secoid.h"
#include "sechash.h"
#include "secitem.h"
#include "secrng.h"
#include "p12local.h"
#include "alghmac.h"
#include "libi18n.h"

#define SALT_LENGTH	16

extern int SEC_ERROR_BAD_EXPORT_ALGORITHM;
extern int SEC_ERROR_NO_MEMORY;
extern int SEC_ERROR_PKCS12_UNABLE_TO_LOCATE_OBJECT_BY_NAME;

/* helper functions */
/* returns proper bag type template based upon object type tag */
const SEC_ASN1Template *
sec_pkcs12_choose_bag_type_old(void *src_or_dest, PRBool encoding)
{
    const SEC_ASN1Template *template;
    SEC_PKCS12SafeBag *safebag;
    SECOidData *oiddata;

    if (src_or_dest == NULL) {
	return NULL;
    }

    safebag = src_or_dest;

    oiddata = safebag->safeBagTypeTag;
    if (oiddata == NULL) {
	oiddata = SECOID_FindOID(&safebag->safeBagType);
	safebag->safeBagTypeTag = oiddata;
    }

    switch (oiddata->offset) {
	default:
	    template = SEC_PointerToAnyTemplate;
	    break;
	case SEC_OID_PKCS12_KEY_BAG_ID:
	    template = SEC_PointerToPKCS12KeyBagTemplate;
	    break;
	case SEC_OID_PKCS12_CERT_AND_CRL_BAG_ID:
	    template = SEC_PointerToPKCS12CertAndCRLBagTemplate_OLD;
	    break;
        case SEC_OID_PKCS12_SECRET_BAG_ID:
	    template = SEC_PointerToPKCS12SecretBagTemplate;
	    break;
    }
    return template;
}

const SEC_ASN1Template *
sec_pkcs12_choose_bag_type(void *src_or_dest, PRBool encoding)
{
    const SEC_ASN1Template *template;
    SEC_PKCS12SafeBag *safebag;
    SECOidData *oiddata;

    if (src_or_dest == NULL) {
	return NULL;
    }

    safebag = src_or_dest;

    oiddata = safebag->safeBagTypeTag;
    if (oiddata == NULL) {
	oiddata = SECOID_FindOID(&safebag->safeBagType);
	safebag->safeBagTypeTag = oiddata;
    }

    switch (oiddata->offset) {
	default:
	    template = SEC_AnyTemplate;
	    break;
	case SEC_OID_PKCS12_KEY_BAG_ID:
	    template = SEC_PKCS12PrivateKeyBagTemplate;
	    break;
	case SEC_OID_PKCS12_CERT_AND_CRL_BAG_ID:
	    template = SEC_PKCS12CertAndCRLBagTemplate;
	    break;
        case SEC_OID_PKCS12_SECRET_BAG_ID:
	    template = SEC_PKCS12SecretBagTemplate;
	    break;
    }
    return template;
}

/* returns proper cert crl template based upon type tag */
const SEC_ASN1Template *
sec_pkcs12_choose_cert_crl_type_old(void *src_or_dest, PRBool encoding)
{
    const SEC_ASN1Template *template;
    SEC_PKCS12CertAndCRL *certbag;
    SECOidData *oiddata;

    if (src_or_dest == NULL) {
	return NULL;
    }

    certbag = src_or_dest;
    oiddata = certbag->BagTypeTag;
    if (oiddata == NULL) {
	oiddata = SECOID_FindOID(&certbag->BagID);
	certbag->BagTypeTag = oiddata;
    }

    switch (oiddata->offset) {
	default:
	    template = SEC_PointerToAnyTemplate;
	    break;
	case SEC_OID_PKCS12_X509_CERT_CRL_BAG:
	    template = SEC_PointerToPKCS12X509CertCRLTemplate_OLD;
	    break;
	case SEC_OID_PKCS12_SDSI_CERT_BAG:
	    template = SEC_PointerToPKCS12SDSICertTemplate;
	    break;
    }
    return template;
}

const SEC_ASN1Template *
sec_pkcs12_choose_cert_crl_type(void *src_or_dest, PRBool encoding)
{
    const SEC_ASN1Template *template;
    SEC_PKCS12CertAndCRL *certbag;
    SECOidData *oiddata;

    if (src_or_dest == NULL) {
	return NULL;
    }

    certbag = src_or_dest;
    oiddata = certbag->BagTypeTag;
    if (oiddata == NULL) {
	oiddata = SECOID_FindOID(&certbag->BagID);
	certbag->BagTypeTag = oiddata;
    }

    switch (oiddata->offset) {
	default:
	    template = SEC_PointerToAnyTemplate;
	    break;
	case SEC_OID_PKCS12_X509_CERT_CRL_BAG:
	    template = SEC_PointerToPKCS12X509CertCRLTemplate;
	    break;
	case SEC_OID_PKCS12_SDSI_CERT_BAG:
	    template = SEC_PointerToPKCS12SDSICertTemplate;
	    break;
    }
    return template;
}

/* returns appropriate shroud template based on object type tag */
const SEC_ASN1Template *
sec_pkcs12_choose_shroud_type(void *src_or_dest, PRBool encoding)
{
    const SEC_ASN1Template *template;
    SEC_PKCS12ESPVKItem *espvk;
    SECOidData *oiddata;

    if (src_or_dest == NULL) {
	return NULL;
    }

    espvk = src_or_dest;
    oiddata = espvk->espvkTag;
    if (oiddata == NULL) {
	oiddata = SECOID_FindOID(&espvk->espvkOID);
 	espvk->espvkTag = oiddata;
    }

    switch (oiddata->offset) {
	default:
	    template = SEC_PointerToAnyTemplate;
	    break;
	case SEC_OID_PKCS12_PKCS8_KEY_SHROUDING:
	   template = 
		SECKEY_PointerToEncryptedPrivateKeyInfoTemplate;
	    break;
    }
    return template;
}

/* temporary hack for export compliance, we can read any
 * encrypted private keys 
 */
PRBool sec_pkcs12_export_alg_read_espvk(SECOidTag algorithm, int keyLen)
{
    switch(algorithm) {
	case SEC_OID_PKCS12_PBE_WITH_SHA1_AND_TRIPLE_DES_CBC:
	case SEC_OID_PKCS5_PBE_WITH_SHA1_AND_DES_CBC:
	case SEC_OID_PKCS12_PBE_WITH_SHA1_AND_40_BIT_RC2_CBC:
	case SEC_OID_PKCS12_PBE_WITH_SHA1_AND_128_BIT_RC2_CBC:
	case SEC_OID_PKCS12_PBE_WITH_SHA1_AND_40_BIT_RC4:
	case SEC_OID_PKCS12_PBE_WITH_SHA1_AND_128_BIT_RC4:
	case SEC_OID_PKCS12_PBE_WITH_SHA1_AND_RC2_CBC:
	case SEC_OID_PKCS12_PBE_WITH_SHA1_AND_RC4:
	case SEC_OID_PKCS5_PBE_WITH_MD5_AND_DES_CBC:
	case SEC_OID_PKCS5_PBE_WITH_MD2_AND_DES_CBC:
	    return PR_TRUE;
	default:
	    break;
    }
    return PR_FALSE;
}

/* we are only allowed to decrypt the safe if it is a 40 bit algorithm */
PRBool sec_pkcs12_export_alg_read_safe(SECOidTag algorithm, int keyLen)
{
    switch(algorithm) {
	case SEC_OID_PKCS12_PBE_WITH_SHA1_AND_TRIPLE_DES_CBC:
	case SEC_OID_PKCS5_PBE_WITH_SHA1_AND_DES_CBC:
	case SEC_OID_PKCS12_PBE_WITH_SHA1_AND_128_BIT_RC2_CBC:
	case SEC_OID_PKCS12_PBE_WITH_SHA1_AND_128_BIT_RC4:
	case SEC_OID_PKCS5_PBE_WITH_MD5_AND_DES_CBC:
	case SEC_OID_PKCS5_PBE_WITH_MD2_AND_DES_CBC:
	    PORT_SetError(SEC_ERROR_BAD_EXPORT_ALGORITHM);
	    return PR_FALSE;
	case SEC_OID_PKCS12_PBE_WITH_SHA1_AND_40_BIT_RC2_CBC:
	case SEC_OID_PKCS12_PBE_WITH_SHA1_AND_40_BIT_RC4:
	    return PR_TRUE;
	case SEC_OID_PKCS12_PBE_WITH_SHA1_AND_RC2_CBC:
	case SEC_OID_PKCS12_PBE_WITH_SHA1_AND_RC4:
	    if(keyLen <= 40) {
		return PR_TRUE;
	    }
	    return PR_FALSE;
	default:
	    break;
    }

    return PR_FALSE;
}

/* generate SALT  placing it into the character array passed in.
 * it is assumed that salt_dest is an array of appropriate size
 * XXX We might want to generate our own random context
 */
SECItem *
sec_pkcs12_generate_salt(void)
{
    SECItem *salt;

    salt = (SECItem *)PORT_ZAlloc(sizeof(SECItem));
    if(salt == NULL) {
	PORT_SetError(SEC_ERROR_NO_MEMORY);
	return NULL;
    }
    salt->data = (unsigned char *)PORT_ZAlloc(sizeof(unsigned char) * 
					      SALT_LENGTH);
    salt->len = SALT_LENGTH;
    if(salt->data == NULL) {
	PORT_SetError(SEC_ERROR_NO_MEMORY);
	SECITEM_ZfreeItem(salt, PR_TRUE);
	return NULL;
    }

    RNG_GenerateGlobalRandomBytes(salt->data, salt->len);

    return salt;
}

/* generate KEYS -- as per PKCS12 section 7.  
 * only used for MAC
 */
SECItem *
sec_pkcs12_generate_key_from_password(SECOidTag algorithm, 
				      SECItem *salt, 
				      SECItem *password) 
{
    unsigned char *pre_hash=NULL;
    unsigned char *hash_dest=NULL;
    SECStatus res;
    PRArenaPool *poolp;
    SECItem *key = NULL;
    int key_len = 0;

    if((salt == NULL) || (password == NULL)) {
	return NULL;
    }

    poolp = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
    if(poolp == NULL) {
	PORT_SetError(SEC_ERROR_NO_MEMORY);
	return NULL;
    }

    pre_hash = (unsigned char *)PORT_ArenaZAlloc(poolp, sizeof(char) * 
						 (salt->len+password->len));
    if(pre_hash == NULL) {
	PORT_SetError(SEC_ERROR_NO_MEMORY);
	goto loser;
    }

    hash_dest = (unsigned char *)PORT_ArenaZAlloc(poolp, 
					sizeof(unsigned char) * SHA1_LENGTH);
    if(hash_dest == NULL) {
	PORT_SetError(SEC_ERROR_NO_MEMORY);
	goto loser;
    }

    PORT_Memcpy(pre_hash, salt->data, salt->len);
    /* handle password of 0 length case */
    if(password->len > 0) {
	PORT_Memcpy(&(pre_hash[salt->len]), password->data, password->len);
    }

    res = SHA1_HashBuf(hash_dest, pre_hash, (salt->len+password->len));
    if(res == SECFailure) {
	PORT_SetError(SEC_ERROR_NO_MEMORY);
	goto loser;
    }

    switch(algorithm) {
	case SEC_OID_SHA1:
	    if(key_len == 0)
		key_len = 16;
	    key = (SECItem *)PORT_ZAlloc(sizeof(SECItem));
	    if(key == NULL) {
		PORT_SetError(SEC_ERROR_NO_MEMORY);
		goto loser;
	    }
	    key->data = (unsigned char *)PORT_ZAlloc(sizeof(unsigned char) 
						     * key_len);
	    if(key->data == NULL) {
		PORT_SetError(SEC_ERROR_NO_MEMORY);
		goto loser;
	    }
	    key->len = key_len;
	    PORT_Memcpy(key->data, &hash_dest[SHA1_LENGTH-key->len], key->len);
	    break;
	default:
	    goto loser;
	    break;
    }

    PORT_FreeArena(poolp, PR_TRUE);
    return key;

loser:
    PORT_FreeArena(poolp, PR_TRUE);
    if(key != NULL) {
	SECITEM_ZfreeItem(key, PR_TRUE);
    }
    return NULL;
}

/* MAC is generated per PKCS 12 section 6.  It is expected that key, msg
 * and mac_dest are pre allocated, non-NULL arrays.  msg_len is passed in
 * because it is not known how long the message actually is.  String
 * manipulation routines will not necessarily work because msg may have
 * imbedded NULLs
 */
static SECItem *
sec_pkcs12_generate_old_mac(SECItem *key, 
			    SECItem *msg)
{
    SECStatus res;
    PRArenaPool *temparena = NULL;
    unsigned char *hash_dest=NULL, *hash_src1=NULL, *hash_src2 = NULL;
    int i;
    SECItem *mac = NULL;

    if((key == NULL) || (msg == NULL))
        goto loser;

    /* allocate return item */
    mac = (SECItem *)PORT_ZAlloc(sizeof(SECItem));
    if(mac == NULL)
    	return NULL;
    mac->data = (unsigned char *)PORT_ZAlloc(sizeof(unsigned char)
    	* SHA1_LENGTH);
    mac->len = SHA1_LENGTH;
    if(mac->data == NULL)
	goto loser;

    /* allocate temporary items */
    temparena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
    if(temparena == NULL)
	goto loser;

    hash_src1 = (unsigned char *)PORT_ArenaZAlloc(temparena,
    	sizeof(unsigned char) * (16+msg->len));
    if(hash_src1 == NULL)
        goto loser;

    hash_src2 = (unsigned char *)PORT_ArenaZAlloc(temparena,
   	sizeof(unsigned char) * (SHA1_LENGTH+16));
    if(hash_src2 == NULL)
        goto loser;

    hash_dest = (unsigned char *)PORT_ArenaZAlloc(temparena, 
   	sizeof(unsigned char) * SHA1_LENGTH);
    if(hash_dest == NULL)
        goto loser;

    /* perform mac'ing as per PKCS 12 */

    /* first round of hashing */
    for(i = 0; i < 16; i++)
	hash_src1[i] = key->data[i] ^ 0x36;
    PORT_Memcpy(&(hash_src1[16]), msg->data, msg->len);
    res = SHA1_HashBuf(hash_dest, hash_src1, (16+msg->len));
    if(res == SECFailure)
	goto loser;

    /* second round of hashing */
    for(i = 0; i < 16; i++)
	hash_src2[i] = key->data[i] ^ 0x5c;
    PORT_Memcpy(&(hash_src2[16]), hash_dest, SHA1_LENGTH);
    res = SHA1_HashBuf(mac->data, hash_src2, SHA1_LENGTH+16);
    if(res == SECFailure)
	goto loser;

    PORT_FreeArena(temparena, PR_TRUE);
    return mac;

loser:
    if(temparena != NULL)
	PORT_FreeArena(temparena, PR_TRUE);
    if(mac != NULL)
	SECITEM_ZfreeItem(mac, PR_TRUE);
    return NULL;
}

/* MAC is generated per PKCS 12 section 6.  It is expected that key, msg
 * and mac_dest are pre allocated, non-NULL arrays.  msg_len is passed in
 * because it is not known how long the message actually is.  String
 * manipulation routines will not necessarily work because msg may have
 * imbedded NULLs
 */
SECItem *
sec_pkcs12_generate_mac(SECItem *key, 
			SECItem *msg,
			PRBool old_method)
{
    SECStatus res = SECFailure;
    SECItem *mac = NULL;
    HMACContext *cx;

    if((key == NULL) || (msg == NULL)) {
	return NULL;
    }

    if(old_method == PR_TRUE) {
	return sec_pkcs12_generate_old_mac(key, msg);
    }

    /* allocate return item */
    mac = (SECItem *)PORT_ZAlloc(sizeof(SECItem));
    if(mac == NULL) {
    	return NULL;
    }
    mac->data = (unsigned char *)PORT_ZAlloc(sizeof(unsigned char)
    					     * SHA1_LENGTH);
    mac->len = SHA1_LENGTH;
    if(mac->data == NULL) {
	PORT_Free(mac);
	return NULL;
    }

    /* compute MAC using HMAC */
    cx = HMAC_Create(SEC_OID_SHA1, key->data, key->len);
    if(cx != NULL) {
	HMAC_Begin(cx);
	HMAC_Update(cx, msg->data, msg->len);
	res = HMAC_Finish(cx, mac->data, &mac->len, SHA1_LENGTH);
	HMAC_Destroy(cx);
    }


    if(res != SECSuccess) {
	SECITEM_ZfreeItem(mac, PR_TRUE);
	mac = NULL;
    }

    return mac;
}

/* compute the thumbprint of the DER cert and create a digest info
 * to store it in and return the digest info.
 * a return of NULL indicates an error.
 */
SGNDigestInfo *
sec_pkcs12_compute_thumbprint(SECItem *der_cert)
{
    SGNDigestInfo *thumb = NULL;
    SECItem digest;
    PRArenaPool *temparena = NULL;
    SECStatus rv = SECFailure;

    if(der_cert == NULL)
	return NULL;

    temparena = PORT_NewArena(SEC_ASN1_DEFAULT_ARENA_SIZE);
    if(temparena == NULL) {
	return NULL;
    }

    digest.data = (unsigned char *)PORT_ArenaZAlloc(temparena,
						    sizeof(unsigned char) * 
						    SHA1_LENGTH);
    /* digest data and create digest info */
    if(digest.data != NULL) {
	digest.len = SHA1_LENGTH;
	rv = SHA1_HashBuf(digest.data, der_cert->data, der_cert->len);
	if(rv == SECSuccess) {
	    thumb = SGN_CreateDigestInfo(SEC_OID_SHA1, 
					 digest.data, 
					 digest.len);
	} else {
	    PORT_SetError(SEC_ERROR_NO_MEMORY);
	}
    } else {
	PORT_SetError(SEC_ERROR_NO_MEMORY);
    }

    PORT_FreeArena(temparena, PR_TRUE);

    return thumb;
}

PRBool 
sec_pkcs12_compare_thumbprint(SGNDigestInfo *a, SGNDigestInfo *b)
{
    return (SGN_CompareDigestInfo(a, b) == SECEqual ? PR_TRUE : PR_FALSE);
}

/* convert a text item to unicode and return
 * NULL indicates an error 
 */
SECItem *
sec_pkcs12_convert_item_to_unicode(SECItem *textItem, PRBool swapUnicodeBytes)
{
    SECItem *uniItem;
    uint32 uniBufLen, retLen;

    if(textItem == NULL) {
	return NULL;
    }

    /* allocate space */
    uniItem = (SECItem *)PORT_ZAlloc(sizeof(SECItem));
    if(uniItem == NULL) {
	PORT_SetError(SEC_ERROR_NO_MEMORY);
	return NULL;
    }
    
    if(textItem->len == 0) {
	uniItem->data = PORT_ZAlloc(2);
	if(uniItem->data == NULL) {
	    goto loser;
	}

	uniItem->len = 2;
	return uniItem;
    }

    /* convert the text and allocate proper buffer */
    uniBufLen = INTL_TextToUnicodeLen(CS_ASCII, textItem->data, textItem->len);
    if(uniBufLen <= 0) {
	goto loser;
    }
    uniItem->data = (unsigned char *)PORT_ZAlloc(uniBufLen * 
    						 sizeof(unsigned short));
    uniItem->len = uniBufLen * 2;
    if(uniItem->data == NULL) {
	goto loser;
    }
    /* convert to unicode */
    retLen = INTL_TextToUnicode(CS_ASCII, textItem->data, textItem->len,
    				(unsigned short *)uniItem->data, uniBufLen);
    if(retLen+1 != uniBufLen) {
	PORT_Free(uniItem->data);
	goto loser;
    }

    if(swapUnicodeBytes) {
	if(sec_pkcs12_swap_unicode_bytes(uniItem) == SECFailure) {
	    PORT_Free(uniItem->data);
	    goto loser;
	}
    }
    
    return uniItem;

loser: 
    PORT_Free(uniItem);
    PORT_SetError(SEC_ERROR_NO_MEMORY);
    return NULL;
}

/* convert item from unicode */
SECItem *
sec_pkcs12_convert_item_from_unicode(SECItem *uniItem)
{
    SECItem *textItem;
    uint32 textBufLen;

    if(uniItem == NULL) {
	return NULL;
    }

    textItem = PORT_Alloc(sizeof(SECItem));
    if(textItem == NULL) {
	PORT_SetError(SEC_ERROR_NO_MEMORY);
	return NULL;
    }
   
    /* get length & set up buffers */ 
    textBufLen = INTL_UnicodeToStrLen(CS_ASCII, (unsigned short *)uniItem->data, 
    					uniItem->len / 2);
    if(textBufLen <= 0) {
	goto loser;
    }
    textItem->data = (unsigned char *)PORT_ZAlloc(textBufLen * 
    						 sizeof(unsigned short));
    textItem->len = textBufLen;
    if(textItem->data == NULL) {
	goto loser;
    }

    /* convert text */
    INTL_UnicodeToStr(CS_ASCII, (INTL_Unicode*)uniItem->data, 
			uniItem->len / 2, textItem->data,
			textItem->len);
    
    return textItem;

loser: 
    PORT_Free(textItem);
    PORT_SetError(SEC_ERROR_NO_MEMORY);
    return NULL;
}

/* create a virtual password per PKCS 12, the password is converted
 * to unicode, the salt is prepended to it, and then the whole thing
 * is returned */
SECItem *
sec_pkcs12_create_virtual_password(SECItem *password, SECItem *salt,
				   PRBool swap)
{
    SECItem *uniPwd, *retPwd = NULL;

    if((password == NULL) || (salt == NULL)) {
	return NULL;
    }

    uniPwd = sec_pkcs12_convert_item_to_unicode(password, swap);
    if(uniPwd == NULL) {
	return NULL;
    }

    retPwd = (SECItem *)PORT_ZAlloc(sizeof(SECItem));
    if(retPwd == NULL) {
	goto loser;
    }

    /* allocate space and copy proper data */
    retPwd->len = uniPwd->len + salt->len;
    retPwd->data = (unsigned char *)PORT_Alloc(retPwd->len);
    if(retPwd->data == NULL) {
	PORT_Free(retPwd);
	goto loser;
    }

    PORT_Memcpy(retPwd->data, salt->data, salt->len);
    PORT_Memcpy((retPwd->data + salt->len), uniPwd->data, uniPwd->len);

    SECITEM_ZfreeItem(uniPwd, PR_TRUE);

    return retPwd;

loser:
    PORT_SetError(SEC_ERROR_NO_MEMORY);
    SECITEM_ZfreeItem(uniPwd, PR_TRUE);
    return NULL;
}

/* appends a shrouded key to a key bag.  this is used for exporting
 * to store externally wrapped keys.  it is used when importing to convert
 * old items to new
 */
SECStatus 
sec_pkcs12_append_shrouded_key(SEC_PKCS12BaggageItem *bag,
				SEC_PKCS12ESPVKItem *espvk)
{
    int size;
    void *mark = NULL, *dummy = NULL;

    if((bag == NULL) || (espvk == NULL))
	return SECFailure;

    mark = PORT_ArenaMark(bag->poolp);

    /* grow the list */
    size = (bag->nEspvks + 1) * sizeof(SEC_PKCS12ESPVKItem *);
    dummy = (SEC_PKCS12ESPVKItem **)PORT_ArenaGrow(bag->poolp,
	    				bag->espvks, size, 
	    				size + sizeof(SEC_PKCS12ESPVKItem *));

    if(dummy == NULL) {
	PORT_SetError(SEC_ERROR_NO_MEMORY);
	goto loser;
    }

    bag->espvks[bag->nEspvks] = espvk;
    bag->nEspvks++;
    bag->espvks[bag->nEspvks] = NULL;

    return SECSuccess;

loser:
    PORT_ArenaRelease(bag->poolp, mark);
    return SECFailure;
}

/* search a certificate list for a nickname, a thumbprint, or both
 * within a certificate bag.  if the certificate could not be
 * found or an error occurs, NULL is returned;
 */
static SEC_PKCS12CertAndCRL *
sec_pkcs12_find_cert_in_certbag(SEC_PKCS12CertAndCRLBag *certbag,
				SECItem *nickname, SGNDigestInfo *thumbprint)
{
    PRBool search_both = PR_FALSE, search_nickname = PR_FALSE;
    int i, j;

    if((certbag == NULL) || ((nickname == NULL) && (thumbprint == NULL))) {
	return NULL;
    }

    if(thumbprint && nickname) {
	search_both = PR_TRUE;
    }

    if(nickname) {
	search_nickname = PR_TRUE;
    }

search_again:  
    i = 0;
    while(certbag->certAndCRLs[i] != NULL) {
	SEC_PKCS12CertAndCRL *cert = certbag->certAndCRLs[i];

	if(SECOID_FindOIDTag(&cert->BagID) == SEC_OID_PKCS12_X509_CERT_CRL_BAG) {

	    /* check nicknames */
	    if(search_nickname) {
		if(SECITEM_CompareItem(nickname, &cert->nickname) == SECEqual) {
		    return cert;
		}
	    } else {
	    /* check thumbprints */
		SECItem **derCertList;

		/* get pointer to certificate list, does not need to
		 * be freed since it is within the arena which will
		 * be freed later.
		 */
		derCertList = SEC_PKCS7GetCertificateList(&cert->value.x509->certOrCRL);
		j = 0;
		if(derCertList != NULL) {
		    while(derCertList[j] != NULL) {
			SECComparison eq;
			SGNDigestInfo *di;
			di = sec_pkcs12_compute_thumbprint(derCertList[j]);
			if(di) {
			    eq = SGN_CompareDigestInfo(thumbprint, di);
			    SGN_DestroyDigestInfo(di);
			    if(eq == SECEqual) {
				/* copy the derCert for later reference */
				cert->value.x509->derLeafCert = derCertList[j];
				return cert;
			    }
			} else {
			    /* an error occurred */
			    return NULL;
			}
			j++;
		    }
		}
	    }
	}

	i++;
    }

    if(search_both) {
	search_both = PR_FALSE;
	search_nickname = PR_FALSE;
	goto search_again;
    }

    return NULL;
}

/* search a key list for a nickname, a thumbprint, or both
 * within a key bag.  if the key could not be
 * found or an error occurs, NULL is returned;
 */
static SEC_PKCS12PrivateKey *
sec_pkcs12_find_key_in_keybag(SEC_PKCS12PrivateKeyBag *keybag,
			      SECItem *nickname, SGNDigestInfo *thumbprint)
{
    PRBool search_both = PR_FALSE, search_nickname = PR_FALSE;
    int i, j;

    if((keybag == NULL) || ((nickname == NULL) && (thumbprint == NULL))) {
	return NULL;
    }

    if(keybag->privateKeys == NULL) {
	return NULL;
    }

    if(thumbprint && nickname) {
	search_both = PR_TRUE;
    }

    if(nickname) {
	search_nickname = PR_TRUE;
    }

search_again:  
    i = 0;
    while(keybag->privateKeys[i] != NULL) {
	SEC_PKCS12PrivateKey *key = keybag->privateKeys[i];

	/* check nicknames */
	if(search_nickname) {
	    if(SECITEM_CompareItem(nickname, &key->pvkData.nickname) == SECEqual) {
		return key;
	    }
	} else {
	    /* check digests */
	    SGNDigestInfo **assocCerts = key->pvkData.assocCerts;
	    if((assocCerts == NULL) || (assocCerts[0] == NULL)) {
		return NULL;
	    }

	    j = 0;
	    while(assocCerts[j] != NULL) {
		SECComparison eq;
		eq = SGN_CompareDigestInfo(thumbprint, assocCerts[j]);
		if(eq == SECEqual) {
		    return key;
		}
		j++;
	    }
	}
	i++;
    }

    if(search_both) {
	search_both = PR_FALSE;
	search_nickname = PR_FALSE;
	goto search_again;
    }

    return NULL;
}

/* seach the safe first then try the baggage bag 
 *  safe and bag contain certs and keys to search
 *  objType is the object type to look for
 *  bagType is the type of bag that was found by sec_pkcs12_find_object
 *  index is the entity in safe->safeContents or bag->unencSecrets which
 *    is being searched
 *  nickname and thumbprint are the search criteria
 * 
 * a return of null indicates no match
 */
static void *
sec_pkcs12_try_find(SEC_PKCS12SafeContents *safe,
		  SEC_PKCS12BaggageItem *bag,
		  SECOidTag objType, SECOidTag bagType, int index,
		  SECItem *nickname, SGNDigestInfo *thumbprint)
{
    PRBool searchSafe;
    int i = index;

    if((safe == NULL) && (bag == NULL)) {
	return NULL;
    }

    searchSafe = (safe == NULL ? PR_FALSE : PR_TRUE);
    switch(objType) {
	case SEC_OID_PKCS12_CERT_AND_CRL_BAG_ID:
	    if(objType == bagType) {
		SEC_PKCS12CertAndCRLBag *certBag;

		if(searchSafe) {
		    certBag = safe->contents[i]->safeContent.certAndCRLBag;
		} else {
		    certBag = bag->unencSecrets[i]->safeContent.certAndCRLBag;
		}
		return sec_pkcs12_find_cert_in_certbag(certBag, nickname, 
							thumbprint);
	    }
	    break;
	case SEC_OID_PKCS12_KEY_BAG_ID:
	    if(objType == bagType) {
		SEC_PKCS12PrivateKeyBag *keyBag;

		if(searchSafe) {
		    keyBag = safe->contents[i]->safeContent.keyBag;
		} else {
		    keyBag = bag->unencSecrets[i]->safeContent.keyBag;
		}
		return sec_pkcs12_find_key_in_keybag(keyBag, nickname, 
							 thumbprint);
	    }
	    break;
	default:
	    break;
    }

    return NULL;
}

/* searches both the baggage and the safe areas looking for
 * object of specified type matching either the nickname or the 
 * thumbprint specified.
 *
 * safe and baggage store certs and keys
 * objType is the OID for the bag type to be searched:
 *   SEC_OID_PKCS12_KEY_BAG_ID, or 
 *   SEC_OID_PKCS12_CERT_AND_CRL_BAG_ID
 * nickname and thumbprint are the search criteria
 * 
 * if no match found, NULL returned and error set
 */
void *
sec_pkcs12_find_object(SEC_PKCS12SafeContents *safe,
			SEC_PKCS12Baggage *baggage,
			SECOidTag objType,
			SECItem *nickname,
			SGNDigestInfo *thumbprint)
{
    int i, j;
    PRBool found = PR_FALSE;
    PRBool problem = PR_FALSE;
    void *retItem;
   
    if(((safe == NULL) && (thumbprint == NULL)) ||
       ((nickname == NULL) && (thumbprint == NULL))) {
	return NULL;
    }    

    i = 0;
    if((safe != NULL) && (safe->contents != NULL)) {
	while(safe->contents[i] != NULL) {
	    SECOidTag bagType = SECOID_FindOIDTag(&safe->contents[i]->safeBagType);
	    retItem = sec_pkcs12_try_find(safe, NULL, objType, bagType, i,
	    				  nickname, thumbprint);
	    if(retItem != NULL) {
		return retItem;
	    }
	    i++;
	}
    }

    if((baggage != NULL) && (baggage->bags != NULL)) {
	i = 0;
	while(baggage->bags[i] != NULL) {
	    SEC_PKCS12BaggageItem *xbag = baggage->bags[i];
	    j = 0;
	    if(xbag->unencSecrets != NULL) {
		while(xbag->unencSecrets[j] != NULL) {
		    SECOidTag bagType;
		    bagType = SECOID_FindOIDTag(&xbag->unencSecrets[j]->safeBagType);
		    retItem = sec_pkcs12_try_find(NULL, xbag, objType, bagType,
		    				  j, nickname, thumbprint);
		    if(retItem != NULL) {
			return retItem;
		    }
		    j++;
		}
	    }
	    i++;
	}
    }

    PORT_SetError(SEC_ERROR_PKCS12_UNABLE_TO_LOCATE_OBJECT_BY_NAME);
    return NULL;
}

SECStatus 
sec_pkcs12_copy_nickname(PRArenaPool *poolp, SECItem *a, SECItem *b)
{
    if((a == NULL) || (b == NULL) || (poolp == NULL)) {
	return SECFailure;
    }

   
    /* XXX this is added because routines using nickname do not
     * support know length unless string is null terminated
     */
    PORT_ArenaGrow(poolp, &b->data, b->len, b->len+1);
    b->data[b->len] = 0;

    if(a != b) {
	/* increment to copy the NULL */
	b->len++;
	SECITEM_CopyItem(poolp, a, b);
	b->len--;
	a->len--;
    }

    return SECSuccess;
}

SECStatus
sec_pkcs12_der_code_cert_crl(SEC_PKCS12CertAndCRL *cert, PRBool encode)
{
    void *dummy;
    const SEC_ASN1Template *template;
    void *src, *dest;
    SEC_PKCS12X509CertCRL *test_cert;
    PRArenaPool *arena;

    arena = PORT_NewArena(2048);
    test_cert = PORT_ArenaZAlloc(arena, sizeof(SEC_PKCS12X509CertCRL));

    if(cert == NULL) 
	return SECFailure;

    dest = &cert->derValue;
    switch(SECOID_FindOIDTag(&cert->BagID)) {
	case SEC_OID_PKCS12_X509_CERT_CRL_BAG:
	    src = cert->value.x509;
	    template = SEC_PKCS12X509CertCRLTemplate;
	    break;
	case SEC_OID_PKCS12_SDSI_CERT_BAG:
	    src = cert->value.sdsi;
	    template = SEC_PKCS12SDSICertTemplate;
	    break;
	default:
	    return SECFailure;
    }

    dummy = NULL;
    if(encode == PR_TRUE) {
	SEC_PKCS12CertAndCRL *ts = PORT_ArenaZAlloc(arena, sizeof(SEC_PKCS12CertAndCRL));
	dummy = SEC_ASN1EncodeItem(arena, dest, src, template);
    } else {
	SECStatus rv;
	rv = SEC_ASN1DecodeItem(cert->poolp, src, template, dest);
	if(rv == SECSuccess) {
	    dummy = src; 
	}
    }

    return (dummy ? SECSuccess : SECFailure);
}

SECStatus 
sec_pkcs12_der_code_cert_bag(SEC_PKCS12CertAndCRLBag *certbag, PRBool encode)
{
    int i;
    SECStatus rv;

    i = 0;
    if(certbag == NULL) {
	return SECFailure;
    }

    if(certbag->certAndCRLs == NULL) {
	/* if we encounter a NULL list on encoding -- there is an error.
	 * if we encounter a NULL list on decoding, that is the way the
	 * decoder is written...
	 */
	return (encode ? SECFailure : SECSuccess);
    }

    while(certbag->certAndCRLs[i] != NULL) {

	/* propagate the arena pool, just in case */
	certbag->certAndCRLs[i]->poolp = certbag->poolp;
	rv = sec_pkcs12_der_code_cert_crl(certbag->certAndCRLs[i], encode);

	if(rv != SECSuccess) {
	    return SECFailure;
	}

	i++;
    }

    return SECSuccess;
}

SECStatus
sec_pkcs12_der_code_safe_bag(SEC_PKCS12SafeBag *bag, PRBool encode)
{
    void *dummy = NULL;
    const SEC_ASN1Template *template;
    void *src, *dest;
    SECStatus rv;
    PRArenaPool *pool;
    SEC_PKCS12CertAndCRLBag *ts;

    pool = PORT_NewArena(2048);
    ts = PORT_ArenaZAlloc(pool, sizeof(SEC_PKCS12CertAndCRLBag));

    if(bag == NULL) 
	return SECFailure;

    rv = SECSuccess;
    dest = &bag->derSafeContent;
    switch(SECOID_FindOIDTag(&bag->safeBagType)) {
	case SEC_OID_PKCS12_KEY_BAG_ID:
	    src = bag->safeContent.keyBag;
	    template = SEC_PKCS12PrivateKeyBagTemplate;
	    break;
	case SEC_OID_PKCS12_SECRET_BAG_ID:
	    src = bag->safeContent.secretBag;
	    template = SEC_PKCS12SecretBagTemplate;
	    break;
	case SEC_OID_PKCS12_CERT_AND_CRL_BAG_ID:
	    src = bag->safeContent.certAndCRLBag;
	    template = SEC_PKCS12CodedCertAndCRLBagTemplate;
	    bag->safeContent.certAndCRLBag->poolp = bag->poolp;
	    rv = sec_pkcs12_der_code_cert_bag(bag->safeContent.certAndCRLBag, encode);
	    break;
	default:
	    return SECFailure;
    }

    if(encode == PR_TRUE) {
	dummy = SEC_ASN1EncodeItem(bag->poolp, dest, src, template);
	SEC_ASN1DecodeItem(bag->poolp, src, template, dest);
    } else {
	SECStatus rv;
	rv = SEC_ASN1DecodeItem(bag->poolp, src, template, dest);
	if(rv == SECSuccess) {
	    dummy = src; 
	}
    }

    return (dummy ? SECSuccess : SECFailure);
}

SECStatus 
sec_pkcs12_prepare_for_der_code_safe(SEC_PKCS12SafeContents *safe, PRBool encode)
{
    int i;
    SECStatus rv;

    if(safe == NULL) {
	return SECFailure;
    }

    if(safe->contents == NULL) {
	/* if we encounter a NULL list on encoding -- there is an error.
	 * if we encounter a NULL list on decoding, that is the way the
	 * decoder is written...
	 */
	return (encode ? SECFailure : SECSuccess);
    }

    i = 0;
    while(safe->contents[i] != NULL) {
	SECOidTag bagType = SECOID_FindOIDTag(&safe->contents[i]->safeBagType);
	switch(bagType) {
	    case SEC_OID_PKCS12_KEY_BAG_ID:
	    case SEC_OID_PKCS12_SECRET_BAG_ID:
	    case SEC_OID_PKCS12_CERT_AND_CRL_BAG_ID:
		/* propagate arena pool as necessary */
		safe->contents[i]->poolp = safe->poolp;

		rv = sec_pkcs12_der_code_safe_bag(safe->contents[i], encode);
		break;
	    default:
		return SECFailure;
	}
	i++;
    }

    return SECSuccess;
}

SECStatus 
sec_pkcs12_prepare_for_der_code_baggage(SEC_PKCS12Baggage *baggage, PRBool encode)
{
    int i, j;
    SECStatus rv;

    if(baggage == NULL) {
	return SECFailure;
    }

    if(baggage->bags == NULL) {
	/* if we encounter a NULL list on encoding -- there is an error.
	 * if we encounter a NULL list on decoding, that is the way the
	 * decoder is written...
	 */
	return (encode ? SECFailure : SECSuccess);
    }

    i = 0;
    while(baggage->bags[i] != NULL) {
	SEC_PKCS12BaggageItem *bag = baggage->bags[i];
	/* propagate arena pool, just in case */
	bag->poolp = baggage->poolp;

	if(bag->unencSecrets != NULL) {
	    j = 0;

	    while(bag->unencSecrets[j] != NULL) {
		/* propagate arena pool */
		bag->unencSecrets[j]->poolp = bag->poolp;
		rv = sec_pkcs12_der_code_safe_bag(bag->unencSecrets[j], encode);
		if(rv == SECFailure) {
		    return SECFailure;
		}
		j++;
	    }
	}
	i++;
    }

    return SECSuccess;
}

/* swap the byte order on a unicode string */
SECStatus
sec_pkcs12_swap_unicode_bytes(SECItem *uniItem)
{
    unsigned int i;
    unsigned char a;

    if((uniItem == NULL) || (uniItem->len % 2)) {
	return SECFailure;
    }

    for(i = 0; i < uniItem->len; i += 2) {
	a = uniItem->data[i];
	uniItem->data[i] = uniItem->data[i+1];
	uniItem->data[i+1] = a;
    }

    return SECSuccess;
}

/* converts a UniCode nickname to ascii and stores it in the requested
 * location.  
 */
SECStatus 
sec_pkcs12_copy_and_convert_unicode_string(PRArenaPool *poolp, SECItem *dest,
					   SECItem *src, PRBool swapUnicode)
{
    SECItem *textItem;
    SECStatus rv = SECSuccess;

    if((src == NULL) || (dest == NULL) || (src == dest)) {
	return SECFailure;
    }

    if(swapUnicode) {
	if(sec_pkcs12_swap_unicode_bytes(src) != SECSuccess) {
	    return SECFailure;
	}
    }

    textItem = sec_pkcs12_convert_item_from_unicode(src);
    if(textItem == NULL) {
	return SECFailure;
    }

    rv = SECITEM_CopyItem(poolp, dest, textItem);
    SECITEM_ZfreeItem(textItem, PR_TRUE);

    if(rv == SECFailure) {
	PORT_SetError(SEC_ERROR_NO_MEMORY);
    }

    return rv;
}

/* pkcs 12 templates */
static SEC_ChooseASN1TemplateFunc sec_pkcs12_shroud_chooser =
    sec_pkcs12_choose_shroud_type;

const SEC_ASN1Template SEC_PKCS12CodedSafeBagTemplate[] =
{
    { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(SEC_PKCS12SafeBag) },
    { SEC_ASN1_OBJECT_ID, offsetof(SEC_PKCS12SafeBag, safeBagType) },
    { SEC_ASN1_ANY, offsetof(SEC_PKCS12SafeBag, derSafeContent) },
    { 0 }
};

const SEC_ASN1Template SEC_PKCS12CodedCertBagTemplate[] =
{
    { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(SEC_PKCS12CertAndCRL) },
    { SEC_ASN1_OBJECT_ID, offsetof(SEC_PKCS12CertAndCRL, BagID) },
    { SEC_ASN1_ANY, offsetof(SEC_PKCS12CertAndCRL, derValue) },
    { 0 }
};

const SEC_ASN1Template SEC_PKCS12CodedCertAndCRLBagTemplate[] =
{
    { SEC_ASN1_SET_OF, offsetof(SEC_PKCS12CertAndCRLBag, certAndCRLs),
	SEC_PKCS12CodedCertBagTemplate },
};

const SEC_ASN1Template SEC_PKCS12ESPVKItemTemplate_OLD[] = 
{
    { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(SEC_PKCS12ESPVKItem) },
    { SEC_ASN1_OBJECT_ID, offsetof(SEC_PKCS12ESPVKItem, espvkOID) },
    { SEC_ASN1_INLINE, offsetof(SEC_PKCS12ESPVKItem, espvkData),
	SEC_PKCS12PVKSupportingDataTemplate_OLD },
    { SEC_ASN1_EXPLICIT | SEC_ASN1_CONSTRUCTED | SEC_ASN1_CONTEXT_SPECIFIC |
	SEC_ASN1_DYNAMIC | 0, offsetof(SEC_PKCS12ESPVKItem, espvkCipherText),
	&sec_pkcs12_shroud_chooser },
    { 0 }
};

const SEC_ASN1Template SEC_PKCS12ESPVKItemTemplate[] = 
{
    { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(SEC_PKCS12ESPVKItem) },
    { SEC_ASN1_OBJECT_ID, offsetof(SEC_PKCS12ESPVKItem, espvkOID) },
    { SEC_ASN1_INLINE, offsetof(SEC_PKCS12ESPVKItem, espvkData),
	SEC_PKCS12PVKSupportingDataTemplate },
    { SEC_ASN1_EXPLICIT | SEC_ASN1_CONSTRUCTED | SEC_ASN1_CONTEXT_SPECIFIC |
	SEC_ASN1_DYNAMIC | 0, offsetof(SEC_PKCS12ESPVKItem, espvkCipherText),
	&sec_pkcs12_shroud_chooser },
    { 0 }
};

const SEC_ASN1Template SEC_PKCS12PVKAdditionalDataTemplate[] =
{
    { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(SEC_PKCS12PVKAdditionalData) },
    { SEC_ASN1_OBJECT_ID, 
	offsetof(SEC_PKCS12PVKAdditionalData, pvkAdditionalType) },
    { SEC_ASN1_CONSTRUCTED | SEC_ASN1_CONTEXT_SPECIFIC | 0,
	offsetof(SEC_PKCS12PVKAdditionalData, pvkAdditionalContent) },
    { 0 }
};

const SEC_ASN1Template SEC_PKCS12PVKSupportingDataTemplate_OLD[] =
{
    { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(SEC_PKCS12PVKSupportingData) },
    { SEC_ASN1_SET_OF, offsetof(SEC_PKCS12PVKSupportingData, assocCerts),
	sgn_DigestInfoTemplate },
    { SEC_ASN1_OPTIONAL | SEC_ASN1_BOOLEAN, 
	offsetof(SEC_PKCS12PVKSupportingData, regenerable) },
    { SEC_ASN1_PRINTABLE_STRING, 
	offsetof(SEC_PKCS12PVKSupportingData, nickname) },
    { SEC_ASN1_ANY | SEC_ASN1_OPTIONAL,
	offsetof(SEC_PKCS12PVKSupportingData, pvkAdditionalDER) },
    { 0 }
};

const SEC_ASN1Template SEC_PKCS12PVKSupportingDataTemplate[] =
{
    { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(SEC_PKCS12PVKSupportingData) },
    { SEC_ASN1_SET_OF, offsetof(SEC_PKCS12PVKSupportingData, assocCerts),
	sgn_DigestInfoTemplate },
    { SEC_ASN1_OPTIONAL | SEC_ASN1_BOOLEAN, 
	offsetof(SEC_PKCS12PVKSupportingData, regenerable) },
    { SEC_ASN1_BMP_STRING, 
	offsetof(SEC_PKCS12PVKSupportingData, uniNickName) },
    { SEC_ASN1_ANY | SEC_ASN1_OPTIONAL,
	offsetof(SEC_PKCS12PVKSupportingData, pvkAdditionalDER) },
    { 0 }
};

const SEC_ASN1Template SEC_PKCS12BaggageItemTemplate[] =
{
    { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(SEC_PKCS12BaggageItem) },
    { SEC_ASN1_SET_OF, offsetof(SEC_PKCS12BaggageItem, espvks),
	SEC_PKCS12ESPVKItemTemplate },
    { SEC_ASN1_SET_OF, offsetof(SEC_PKCS12BaggageItem, unencSecrets),
	SEC_PKCS12SafeBagTemplate },
    /*{ SEC_ASN1_SET_OF, offsetof(SEC_PKCS12BaggageItem, unencSecrets),
	SEC_PKCS12CodedSafeBagTemplate }, */
    { 0 }
};

const SEC_ASN1Template SEC_PKCS12BaggageTemplate[] =
{
    { SEC_ASN1_SET_OF, offsetof(SEC_PKCS12Baggage, bags),
	SEC_PKCS12BaggageItemTemplate },
};

const SEC_ASN1Template SEC_PKCS12BaggageTemplate_OLD[] =
{
    { SEC_ASN1_SET_OF, offsetof(SEC_PKCS12Baggage_OLD, espvks),
	SEC_PKCS12ESPVKItemTemplate_OLD },
};

static SEC_ChooseASN1TemplateFunc sec_pkcs12_bag_chooser =
	sec_pkcs12_choose_bag_type;

static SEC_ChooseASN1TemplateFunc sec_pkcs12_bag_chooser_old =
	sec_pkcs12_choose_bag_type_old;

const SEC_ASN1Template SEC_PKCS12SafeBagTemplate_OLD[] =
{
    { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(SEC_PKCS12SafeBag) },
    { SEC_ASN1_OBJECT_ID, offsetof(SEC_PKCS12SafeBag, safeBagType) },
    { SEC_ASN1_DYNAMIC | SEC_ASN1_CONSTRUCTED | SEC_ASN1_EXPLICIT |
	SEC_ASN1_CONTEXT_SPECIFIC | 0,
        offsetof(SEC_PKCS12SafeBag, safeContent),
	&sec_pkcs12_bag_chooser_old },
    { 0 }
};

const SEC_ASN1Template SEC_PKCS12SafeBagTemplate[] =
{
    { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(SEC_PKCS12SafeBag) },
    { SEC_ASN1_OBJECT_ID, offsetof(SEC_PKCS12SafeBag, safeBagType) },
    { SEC_ASN1_DYNAMIC | SEC_ASN1_POINTER,
        offsetof(SEC_PKCS12SafeBag, safeContent),
	&sec_pkcs12_bag_chooser },
    { SEC_ASN1_OPTIONAL | SEC_ASN1_BMP_STRING,
	offsetof(SEC_PKCS12SafeBag, uniSafeBagName) },
    { 0 }
};

const SEC_ASN1Template SEC_PKCS12SafeContentsTemplate_OLD[] =
{
    { SEC_ASN1_SET_OF,
	offsetof(SEC_PKCS12SafeContents, contents),
	SEC_PKCS12SafeBagTemplate_OLD }
};

const SEC_ASN1Template SEC_PKCS12SafeContentsTemplate[] =
{
    { SEC_ASN1_SET_OF,
	offsetof(SEC_PKCS12SafeContents, contents),
	SEC_PKCS12SafeBagTemplate }  /* here */
};

const SEC_ASN1Template SEC_PKCS12PrivateKeyTemplate[] =
{
    { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(SEC_PKCS12PrivateKey) },
    { SEC_ASN1_INLINE, offsetof(SEC_PKCS12PrivateKey, pvkData),
	SEC_PKCS12PVKSupportingDataTemplate },
    { SEC_ASN1_INLINE, offsetof(SEC_PKCS12PrivateKey, pkcs8data),
	SECKEY_PrivateKeyInfoTemplate },
    { 0 }
};

const SEC_ASN1Template SEC_PKCS12PrivateKeyBagTemplate[] =
{
    { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(SEC_PKCS12PrivateKeyBag) },
    { SEC_ASN1_SET_OF, offsetof(SEC_PKCS12PrivateKeyBag, privateKeys),
	SEC_PKCS12PrivateKeyTemplate },
    { 0 }
};

const SEC_ASN1Template SEC_PKCS12X509CertCRLTemplate_OLD[] =
{
    { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(SEC_PKCS12X509CertCRL) },
    { SEC_ASN1_INLINE, offsetof(SEC_PKCS12X509CertCRL, certOrCRL),
	sec_PKCS7ContentInfoTemplate },
    { SEC_ASN1_INLINE, offsetof(SEC_PKCS12X509CertCRL, thumbprint),
	sgn_DigestInfoTemplate },
    { 0 }
};

const SEC_ASN1Template SEC_PKCS12X509CertCRLTemplate[] =
{
    { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(SEC_PKCS12X509CertCRL) },
    { SEC_ASN1_INLINE, offsetof(SEC_PKCS12X509CertCRL, certOrCRL),
	sec_PKCS7ContentInfoTemplate },
    { 0 }
};

const SEC_ASN1Template SEC_PKCS12SDSICertTemplate[] =
{
    { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(SEC_PKCS12X509CertCRL) },
    { SEC_ASN1_IA5_STRING, offsetof(SEC_PKCS12SDSICert, value) },
    { 0 }
};

static SEC_ChooseASN1TemplateFunc sec_pkcs12_cert_crl_chooser_old =
	sec_pkcs12_choose_cert_crl_type_old;

static SEC_ChooseASN1TemplateFunc sec_pkcs12_cert_crl_chooser =
	sec_pkcs12_choose_cert_crl_type;

const SEC_ASN1Template SEC_PKCS12CertAndCRLTemplate_OLD[] =
{
    { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(SEC_PKCS12CertAndCRL) },
    { SEC_ASN1_OBJECT_ID, offsetof(SEC_PKCS12CertAndCRL, BagID) },
    { SEC_ASN1_CONTEXT_SPECIFIC | SEC_ASN1_EXPLICIT |
	SEC_ASN1_DYNAMIC | SEC_ASN1_CONSTRUCTED | 0,
	offsetof(SEC_PKCS12CertAndCRL, value),
	&sec_pkcs12_cert_crl_chooser_old },
    { 0 }
};

const SEC_ASN1Template SEC_PKCS12CertAndCRLTemplate[] =
{
    { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(SEC_PKCS12CertAndCRL) },
    { SEC_ASN1_OBJECT_ID, offsetof(SEC_PKCS12CertAndCRL, BagID) },
    { SEC_ASN1_DYNAMIC | SEC_ASN1_CONSTRUCTED | SEC_ASN1_EXPLICIT |
	SEC_ASN1_CONTEXT_SPECIFIC | 0, 
	offsetof(SEC_PKCS12CertAndCRL, value),
	&sec_pkcs12_cert_crl_chooser },
    { 0 }
};

const SEC_ASN1Template SEC_PKCS12CertAndCRLBagTemplate[] =
{
    { SEC_ASN1_SET_OF, offsetof(SEC_PKCS12CertAndCRLBag, certAndCRLs),
	SEC_PKCS12CertAndCRLTemplate },
};

const SEC_ASN1Template SEC_PKCS12CertAndCRLBagTemplate_OLD[] =
{
    { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(SEC_PKCS12CertAndCRLBag) },
    { SEC_ASN1_SET_OF, offsetof(SEC_PKCS12CertAndCRLBag, certAndCRLs),
	SEC_PKCS12CertAndCRLTemplate_OLD },
    { 0 }
};

const SEC_ASN1Template SEC_PKCS12SecretAdditionalTemplate[] =
{
    { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(SEC_PKCS12SecretAdditional) },
    { SEC_ASN1_OBJECT_ID,
	offsetof(SEC_PKCS12SecretAdditional, secretAdditionalType) },
    { SEC_ASN1_CONTEXT_SPECIFIC | SEC_ASN1_EXPLICIT,
	offsetof(SEC_PKCS12SecretAdditional, secretAdditionalContent) },
    { 0 }
};

const SEC_ASN1Template SEC_PKCS12SecretTemplate[] =
{
    { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(SEC_PKCS12Secret) },
    { SEC_ASN1_BMP_STRING, offsetof(SEC_PKCS12Secret, uniSecretName) },
    { SEC_ASN1_ANY, offsetof(SEC_PKCS12Secret, value) },
    { SEC_ASN1_INLINE | SEC_ASN1_OPTIONAL,
	offsetof(SEC_PKCS12Secret, secretAdditional),
	SEC_PKCS12SecretAdditionalTemplate },
    { 0 }
};

const SEC_ASN1Template SEC_PKCS12SecretItemTemplate[] = 
{
    { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(SEC_PKCS12Secret) },
    { SEC_ASN1_INLINE | SEC_ASN1_CONTEXT_SPECIFIC | 0,
	offsetof(SEC_PKCS12SecretItem, secret), SEC_PKCS12SecretTemplate },
    { SEC_ASN1_INLINE | SEC_ASN1_CONTEXT_SPECIFIC | 1,
	offsetof(SEC_PKCS12SecretItem, subFolder), SEC_PKCS12SafeBagTemplate },
    { 0 }
};

const SEC_ASN1Template SEC_PKCS12SecretBagTemplate[] =
{
    { SEC_ASN1_SET_OF, offsetof(SEC_PKCS12SecretBag, secrets),
	SEC_PKCS12SecretItemTemplate },
};

const SEC_ASN1Template SEC_PKCS12MacDataTemplate[] =
{
    { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(SEC_PKCS12PFXItem) },
    { SEC_ASN1_INLINE, offsetof(SEC_PKCS12MacData, safeMac),
	sgn_DigestInfoTemplate },
    { SEC_ASN1_BIT_STRING, offsetof(SEC_PKCS12MacData, macSalt) },
    { 0 }
};

const SEC_ASN1Template SEC_PKCS12PFXItemTemplate[] =
{
    { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(SEC_PKCS12PFXItem) },
    { SEC_ASN1_OPTIONAL |
	SEC_ASN1_CONSTRUCTED | SEC_ASN1_CONTEXT_SPECIFIC | 0, 
	offsetof(SEC_PKCS12PFXItem, macData), SEC_PKCS12MacDataTemplate },
    { SEC_ASN1_CONSTRUCTED | SEC_ASN1_CONTEXT_SPECIFIC | 1, 
	offsetof(SEC_PKCS12PFXItem, authSafe), 
	sec_PKCS7ContentInfoTemplate },
    { 0 }
};

const SEC_ASN1Template SEC_PKCS12PFXItemTemplate_OLD[] =
{
    { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(SEC_PKCS12PFXItem) },
    { SEC_ASN1_OPTIONAL |
	SEC_ASN1_CONSTRUCTED | SEC_ASN1_CONTEXT_SPECIFIC | 0, 
	offsetof(SEC_PKCS12PFXItem, old_safeMac), sgn_DigestInfoTemplate },
    { SEC_ASN1_OPTIONAL | SEC_ASN1_BIT_STRING,
	offsetof(SEC_PKCS12PFXItem, old_macSalt) },
    { SEC_ASN1_CONSTRUCTED | SEC_ASN1_CONTEXT_SPECIFIC | 1, 
	offsetof(SEC_PKCS12PFXItem, authSafe), 
	sec_PKCS7ContentInfoTemplate },
    { 0 }
};

const SEC_ASN1Template SEC_PKCS12AuthenticatedSafeTemplate[] =
{
    { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(SEC_PKCS12AuthenticatedSafe) },
    { SEC_ASN1_OPTIONAL | SEC_ASN1_INTEGER, 
	offsetof(SEC_PKCS12AuthenticatedSafe, version) }, 
    { SEC_ASN1_OPTIONAL | SEC_ASN1_OBJECT_ID,
	offsetof(SEC_PKCS12AuthenticatedSafe, transportMode) },
    { SEC_ASN1_BIT_STRING | SEC_ASN1_OPTIONAL,
	offsetof(SEC_PKCS12AuthenticatedSafe, privacySalt) },
    { SEC_ASN1_OPTIONAL | SEC_ASN1_SET_OF, 
	offsetof(SEC_PKCS12AuthenticatedSafe, baggage.bags), 
	SEC_PKCS12BaggageItemTemplate },
    { SEC_ASN1_POINTER,
	offsetof(SEC_PKCS12AuthenticatedSafe, safe),
	sec_PKCS7ContentInfoTemplate },
    { 0 }
};

const SEC_ASN1Template SEC_PKCS12AuthenticatedSafeTemplate_OLD[] =
{
    { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(SEC_PKCS12AuthenticatedSafe) },
    { SEC_ASN1_OPTIONAL | SEC_ASN1_INTEGER, 
	offsetof(SEC_PKCS12AuthenticatedSafe, version) }, 
    { SEC_ASN1_OPTIONAL | SEC_ASN1_INTEGER,
	offsetof(SEC_PKCS12AuthenticatedSafe, transportMode) },
    { SEC_ASN1_BIT_STRING,
	offsetof(SEC_PKCS12AuthenticatedSafe, privacySalt) },
    { SEC_ASN1_OPTIONAL | SEC_ASN1_CONSTRUCTED | 
    	SEC_ASN1_CONTEXT_SPECIFIC | 0, 
	offsetof(SEC_PKCS12AuthenticatedSafe, old_baggage), 
	SEC_PKCS12BaggageTemplate_OLD },
    { SEC_ASN1_CONSTRUCTED | SEC_ASN1_CONTEXT_SPECIFIC | 1,
	offsetof(SEC_PKCS12AuthenticatedSafe, old_safe),
	sec_PKCS7ContentInfoTemplate },
    { 0 }
};

const SEC_ASN1Template SEC_PointerToPKCS12KeyBagTemplate[] =
{
    { SEC_ASN1_POINTER, 0, SEC_PKCS12PrivateKeyBagTemplate }
};

const SEC_ASN1Template SEC_PointerToPKCS12CertAndCRLBagTemplate_OLD[] =
{
    { SEC_ASN1_POINTER, 0, SEC_PKCS12CertAndCRLBagTemplate_OLD }
};

const SEC_ASN1Template SEC_PointerToPKCS12CertAndCRLBagTemplate[] =
{
    { SEC_ASN1_POINTER, 0, SEC_PKCS12CertAndCRLBagTemplate }
};

const SEC_ASN1Template SEC_PointerToPKCS12SecretBagTemplate[] =
{
    { SEC_ASN1_POINTER, 0, SEC_PKCS12SecretBagTemplate }
};

const SEC_ASN1Template SEC_PointerToPKCS12X509CertCRLTemplate_OLD[] =
{
    { SEC_ASN1_POINTER, 0, SEC_PKCS12X509CertCRLTemplate_OLD }
};

const SEC_ASN1Template SEC_PointerToPKCS12X509CertCRLTemplate[] =
{
    { SEC_ASN1_POINTER, 0, SEC_PKCS12X509CertCRLTemplate }
};

const SEC_ASN1Template SEC_PointerToPKCS12SDSICertTemplate[] =
{
    { SEC_ASN1_POINTER, 0, SEC_PKCS12SDSICertTemplate }
};