Logo Search packages:      
Sourcecode: xulrunner-1.9 version File versions

p12e.c

/* ***** BEGIN LICENSE BLOCK *****
 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
 *
 * The contents of this file are subject to the Mozilla Public License Version
 * 1.1 (the "License"); you may not use this file except in compliance with
 * the License. You may obtain a copy of the License at
 * http://www.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 * for the specific language governing rights and limitations under the
 * License.
 *
 * The Original Code is the Netscape security libraries.
 *
 * The Initial Developer of the Original Code is
 * Netscape Communications Corporation.
 * Portions created by the Initial Developer are Copyright (C) 1994-2000
 * the Initial Developer. All Rights Reserved.
 *
 * Contributor(s):
 *
 * Alternatively, the contents of this file may be used under the terms of
 * either the GNU General Public License Version 2 or later (the "GPL"), or
 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
 * in which case the provisions of the GPL or the LGPL are applicable instead
 * of those above. If you wish to allow use of your version of this file only
 * under the terms of either the GPL or the LGPL, and not to allow others to
 * use your version of this file under the terms of the MPL, indicate your
 * decision by deleting the provisions above and replace them with the notice
 * and other provisions required by the GPL or the LGPL. If you do not delete
 * the provisions above, a recipient may use your version of this file under
 * the terms of any one of the MPL, the GPL or the LGPL.
 *
 * ***** END LICENSE BLOCK ***** */

#include "p12t.h"
#include "p12.h"
#include "plarena.h"
#include "secitem.h"
#include "secoid.h"
#include "seccomon.h"
#include "secport.h"
#include "cert.h"
#include "secpkcs7.h"
#include "secasn1.h"
#include "secerr.h"
#include "pk11func.h"
#include "p12plcy.h"
#include "p12local.h"
#include "prcpucfg.h"

/*
** This PKCS12 file encoder uses numerous nested ASN.1 and PKCS7 encoder
** contexts.  It can be difficult to keep straight.  Here's a picture:
**
**  "outer"  ASN.1 encoder.  The output goes to the library caller's CB.
**  "middle" PKCS7 encoder.  Feeds    the "outer" ASN.1 encoder.
**  "middle" ASN1  encoder.  Encodes  the encrypted aSafes. 
**                           Feeds    the "middle" P7 encoder above.
**  "inner"  PKCS7 encoder.  Encrypts the "authenticated Safes" (aSafes)
**                           Feeds    the "middle" ASN.1 encoder above.
**  "inner"  ASN.1 encoder.  Encodes  the unencrypted aSafes.  
**                           Feeds    the "inner" P7 enocder above.
**
** Buffering has been added at each point where the output of an ASN.1
** encoder feeds the input of a PKCS7 encoder.
*/

/*********************************
 * Output buffer object, used to buffer output from ASN.1 encoder
 * before passing data on down to the next PKCS7 encoder.
 *********************************/

#define PK12_OUTPUT_BUFFER_SIZE  8192

struct sec_pkcs12OutputBufferStr {
    SEC_PKCS7EncoderContext * p7eCx;
    PK11Context             * hmacCx;
    unsigned int              numBytes;
    unsigned int              bufBytes;
             char             buf[PK12_OUTPUT_BUFFER_SIZE];
};
typedef struct sec_pkcs12OutputBufferStr sec_pkcs12OutputBuffer;

/*********************************
 * Structures used in exporting the PKCS 12 blob
 *********************************/

/* A SafeInfo is used for each ContentInfo which makes up the
 * sequence of safes in the AuthenticatedSafe portion of the
 * PFX structure.
 */
struct SEC_PKCS12SafeInfoStr {
    PRArenaPool *arena;

    /* information for setting up password encryption */
    SECItem pwitem;
    SECOidTag algorithm;
    PK11SymKey *encryptionKey;

    /* how many items have been stored in this safe,
     * we will skip any safe which does not contain any
     * items
      */
    unsigned int itemCount;

    /* the content info for the safe */
    SEC_PKCS7ContentInfo *cinfo;

    sec_PKCS12SafeContents *safe;
};

/* An opaque structure which contains information needed for exporting
 * certificates and keys through PKCS 12.
 */
struct SEC_PKCS12ExportContextStr {
    PRArenaPool *arena;
    PK11SlotInfo *slot;
    void *wincx;

    /* integrity information */
    PRBool integrityEnabled;
    PRBool  pwdIntegrity;
    union {
      struct sec_PKCS12PasswordModeInfo pwdInfo;
      struct sec_PKCS12PublicKeyModeInfo pubkeyInfo;
    } integrityInfo; 

    /* helper functions */
    /* retrieve the password call back */
    SECKEYGetPasswordKey pwfn;
    void *pwfnarg;

    /* safe contents bags */
    SEC_PKCS12SafeInfo **safeInfos;
    unsigned int safeInfoCount;

    /* the sequence of safes */
    sec_PKCS12AuthenticatedSafe authSafe;

    /* information needing deletion */
    CERTCertificate **certList;
};

/* structures for passing information to encoder callbacks when processing
 * data through the ASN1 engine.
 */
struct sec_pkcs12_encoder_output {
    SEC_PKCS12EncoderOutputCallback outputfn;
    void *outputarg;
};

struct sec_pkcs12_hmac_and_output_info {
    void *arg;
    struct sec_pkcs12_encoder_output output;
};

/* An encoder context which is used for the actual encoding
 * portion of PKCS 12. 
 */
typedef struct sec_PKCS12EncoderContextStr {
    PRArenaPool *arena;
    SEC_PKCS12ExportContext *p12exp;
    PK11SymKey *encryptionKey;

    /* encoder information - this is set up based on whether 
     * password based or public key pased privacy is being used
     */
    SEC_ASN1EncoderContext *outerA1ecx;
    union {
      struct sec_pkcs12_hmac_and_output_info hmacAndOutputInfo;
      struct sec_pkcs12_encoder_output       encOutput;
    } output;

    /* structures for encoding of PFX and MAC */
    sec_PKCS12PFXItem        pfx;
    sec_PKCS12MacData        mac;

    /* authenticated safe encoding tracking information */
    SEC_PKCS7ContentInfo    *aSafeCinfo;
    SEC_PKCS7EncoderContext *middleP7ecx;
    SEC_ASN1EncoderContext  *middleA1ecx;
    unsigned int             currentSafe;

    /* hmac context */
    PK11Context             *hmacCx;

    /* output buffers */
    sec_pkcs12OutputBuffer  middleBuf;
    sec_pkcs12OutputBuffer  innerBuf;

} sec_PKCS12EncoderContext;


/*********************************
 * Export setup routines
 *********************************/

/* SEC_PKCS12CreateExportContext 
 *   Creates an export context and sets the unicode and password retrieval
 *   callbacks.  This is the first call which must be made when exporting
 *   a PKCS 12 blob.
 *
 * pwfn, pwfnarg - password retrieval callback and argument.  these are
 *             required for password-authentication mode.
 */
SEC_PKCS12ExportContext *
SEC_PKCS12CreateExportContext(SECKEYGetPasswordKey pwfn, void *pwfnarg,  
                        PK11SlotInfo *slot, void *wincx)
{
    PRArenaPool *arena = NULL;
    SEC_PKCS12ExportContext *p12ctxt = NULL;

    /* allocate the arena and create the context */
    arena = PORT_NewArena(4096);
    if(!arena) {
      PORT_SetError(SEC_ERROR_NO_MEMORY);
      return NULL;
    }

    p12ctxt = (SEC_PKCS12ExportContext *)PORT_ArenaZAlloc(arena, 
                              sizeof(SEC_PKCS12ExportContext));
    if(!p12ctxt) {
      PORT_SetError(SEC_ERROR_NO_MEMORY);
      goto loser;
    }

    /* password callback for key retrieval */
    p12ctxt->pwfn = pwfn;
    p12ctxt->pwfnarg = pwfnarg;

    p12ctxt->integrityEnabled = PR_FALSE;
    p12ctxt->arena = arena;
    p12ctxt->wincx = wincx;
    p12ctxt->slot = (slot) ? PK11_ReferenceSlot(slot) : PK11_GetInternalSlot();

    return p12ctxt;

loser:
    if(arena) {
      PORT_FreeArena(arena, PR_TRUE);
    }

    return NULL;
}

/* 
 * Adding integrity mode
 */

/* SEC_PKCS12AddPasswordIntegrity 
 *    Add password integrity to the exported data.  If an integrity method
 *    has already been set, then return an error.
 *    
 *    p12ctxt - the export context
 *    pwitem - the password for integrity mode
 *    integAlg - the integrity algorithm to use for authentication.
 */
SECStatus
SEC_PKCS12AddPasswordIntegrity(SEC_PKCS12ExportContext *p12ctxt,
                         SECItem *pwitem, SECOidTag integAlg) 
{                        
    if(!p12ctxt || p12ctxt->integrityEnabled) {
      return SECFailure;
    }
   
    /* set up integrity information */
    p12ctxt->pwdIntegrity = PR_TRUE;
    p12ctxt->integrityInfo.pwdInfo.password = 
        (SECItem*)PORT_ArenaZAlloc(p12ctxt->arena, sizeof(SECItem));
    if(!p12ctxt->integrityInfo.pwdInfo.password) {
      PORT_SetError(SEC_ERROR_NO_MEMORY);
      return SECFailure;
    }
    if(SECITEM_CopyItem(p12ctxt->arena, 
                  p12ctxt->integrityInfo.pwdInfo.password, pwitem)
            != SECSuccess) {
      PORT_SetError(SEC_ERROR_NO_MEMORY);
      return SECFailure;
    }
    p12ctxt->integrityInfo.pwdInfo.algorithm = integAlg;
    p12ctxt->integrityEnabled = PR_TRUE;

    return SECSuccess;
}

/* SEC_PKCS12AddPublicKeyIntegrity
 *    Add public key integrity to the exported data.  If an integrity method
 *    has already been set, then return an error.  The certificate must be
 *    allowed to be used as a signing cert.
 *    
 *    p12ctxt - the export context
 *    cert - signer certificate
 *    certDb - the certificate database
 *    algorithm - signing algorithm
 *    keySize - size of the signing key (?)
 */
SECStatus
SEC_PKCS12AddPublicKeyIntegrity(SEC_PKCS12ExportContext *p12ctxt,
                        CERTCertificate *cert, CERTCertDBHandle *certDb,
                        SECOidTag algorithm, int keySize)
{
    if(!p12ctxt) {
      return SECFailure;
    }
    
    p12ctxt->integrityInfo.pubkeyInfo.cert = cert;
    p12ctxt->integrityInfo.pubkeyInfo.certDb = certDb;
    p12ctxt->integrityInfo.pubkeyInfo.algorithm = algorithm;
    p12ctxt->integrityInfo.pubkeyInfo.keySize = keySize;
    p12ctxt->integrityEnabled = PR_TRUE;

    return SECSuccess;
}


/*
 * Adding safes - encrypted (password/public key) or unencrypted
 *    Each of the safe creation routines return an opaque pointer which
 *    are later passed into the routines for exporting certificates and
 *    keys.
 */

/* append the newly created safeInfo to list of safeInfos in the export
 * context.  
 */
static SECStatus
sec_pkcs12_append_safe_info(SEC_PKCS12ExportContext *p12ctxt, SEC_PKCS12SafeInfo *info)
{
    void *mark = NULL, *dummy1 = NULL, *dummy2 = NULL;

    if(!p12ctxt || !info) {
      return SECFailure;
    }

    mark = PORT_ArenaMark(p12ctxt->arena);

    /* if no safeInfos have been set, create the list, otherwise expand it. */
    if(!p12ctxt->safeInfoCount) {
      p12ctxt->safeInfos = (SEC_PKCS12SafeInfo **)PORT_ArenaZAlloc(p12ctxt->arena, 
                                    2 * sizeof(SEC_PKCS12SafeInfo *));
      dummy1 = p12ctxt->safeInfos;
      p12ctxt->authSafe.encodedSafes = (SECItem **)PORT_ArenaZAlloc(p12ctxt->arena, 
                              2 * sizeof(SECItem *));
      dummy2 = p12ctxt->authSafe.encodedSafes;
    } else {
      dummy1 = PORT_ArenaGrow(p12ctxt->arena, p12ctxt->safeInfos, 
                         (p12ctxt->safeInfoCount + 1) * sizeof(SEC_PKCS12SafeInfo *),
                         (p12ctxt->safeInfoCount + 2) * sizeof(SEC_PKCS12SafeInfo *));
      p12ctxt->safeInfos = (SEC_PKCS12SafeInfo **)dummy1;
      dummy2 = PORT_ArenaGrow(p12ctxt->arena, p12ctxt->authSafe.encodedSafes, 
                         (p12ctxt->authSafe.safeCount + 1) * sizeof(SECItem *),
                         (p12ctxt->authSafe.safeCount + 2) * sizeof(SECItem *));
      p12ctxt->authSafe.encodedSafes = (SECItem**)dummy2;
    }
    if(!dummy1 || !dummy2) {
      PORT_SetError(SEC_ERROR_NO_MEMORY);
      goto loser;
    }

    /* append the new safeInfo and null terminate the list */
    p12ctxt->safeInfos[p12ctxt->safeInfoCount] = info;
    p12ctxt->safeInfos[++p12ctxt->safeInfoCount] = NULL;
    p12ctxt->authSafe.encodedSafes[p12ctxt->authSafe.safeCount] = 
        (SECItem*)PORT_ArenaZAlloc(p12ctxt->arena, sizeof(SECItem));
    if(!p12ctxt->authSafe.encodedSafes[p12ctxt->authSafe.safeCount]) {
      PORT_SetError(SEC_ERROR_NO_MEMORY);
      goto loser;
    }
    p12ctxt->authSafe.encodedSafes[++p12ctxt->authSafe.safeCount] = NULL;

    PORT_ArenaUnmark(p12ctxt->arena, mark);
    return SECSuccess;

loser:
    PORT_ArenaRelease(p12ctxt->arena, mark);
    return SECFailure;
}

/* SEC_PKCS12CreatePasswordPrivSafe
 *    Create a password privacy safe to store exported information in.
 *
 *    p12ctxt - export context
 *    pwitem - password for encryption
 *    privAlg - pbe algorithm through which encryption is done.
 */
SEC_PKCS12SafeInfo *
SEC_PKCS12CreatePasswordPrivSafe(SEC_PKCS12ExportContext *p12ctxt, 
                         SECItem *pwitem, SECOidTag privAlg)
{
    SEC_PKCS12SafeInfo *safeInfo = NULL;
    void *mark = NULL;
    PK11SlotInfo *slot = NULL;
    SECAlgorithmID *algId;
    SECItem uniPwitem = {siBuffer, NULL, 0};

    if(!p12ctxt) {
      return NULL;
    }

    /* allocate the safe info */
    mark = PORT_ArenaMark(p12ctxt->arena);
    safeInfo = (SEC_PKCS12SafeInfo *)PORT_ArenaZAlloc(p12ctxt->arena, 
                                    sizeof(SEC_PKCS12SafeInfo));
    if(!safeInfo) {
      PORT_SetError(SEC_ERROR_NO_MEMORY);
      PORT_ArenaRelease(p12ctxt->arena, mark);
      return NULL;
    }

    safeInfo->itemCount = 0;

    /* create the encrypted safe */
    safeInfo->cinfo = SEC_PKCS7CreateEncryptedData(privAlg, 0, p12ctxt->pwfn, 
                                       p12ctxt->pwfnarg);
    if(!safeInfo->cinfo) {
      PORT_SetError(SEC_ERROR_NO_MEMORY);
      goto loser;
    }
    safeInfo->arena = p12ctxt->arena;

    /* convert the password to unicode */ 
    if(!sec_pkcs12_convert_item_to_unicode(NULL, &uniPwitem, pwitem,
                                     PR_TRUE, PR_TRUE, PR_TRUE)) {
      PORT_SetError(SEC_ERROR_NO_MEMORY);
      goto loser;
    }
    if(SECITEM_CopyItem(p12ctxt->arena, &safeInfo->pwitem, &uniPwitem) != SECSuccess) {
      PORT_SetError(SEC_ERROR_NO_MEMORY);
      goto loser;
    }

    /* generate the encryption key */
    slot = PK11_ReferenceSlot(p12ctxt->slot);
    if(!slot) {
      slot = PK11_GetInternalKeySlot();
      if(!slot) {
          PORT_SetError(SEC_ERROR_NO_MEMORY);
          goto loser;
      }
    }

    algId = SEC_PKCS7GetEncryptionAlgorithm(safeInfo->cinfo);
    safeInfo->encryptionKey = PK11_PBEKeyGen(slot, algId, &uniPwitem, 
                                   PR_FALSE, p12ctxt->wincx);
    if(!safeInfo->encryptionKey) {
      goto loser;
    }

    safeInfo->arena = p12ctxt->arena;
    safeInfo->safe = NULL;
    if(sec_pkcs12_append_safe_info(p12ctxt, safeInfo) != SECSuccess) {
      goto loser;
    }

    if(uniPwitem.data) {
      SECITEM_ZfreeItem(&uniPwitem, PR_FALSE);
    }
    PORT_ArenaUnmark(p12ctxt->arena, mark);

    if (slot) {
      PK11_FreeSlot(slot);
    }
    return safeInfo;

loser:
    if (slot) {
      PK11_FreeSlot(slot);
    }
    if(safeInfo->cinfo) {
      SEC_PKCS7DestroyContentInfo(safeInfo->cinfo);
    }

    if(uniPwitem.data) {
      SECITEM_ZfreeItem(&uniPwitem, PR_FALSE);
    }

    PORT_ArenaRelease(p12ctxt->arena, mark);
    return NULL;
}

/* SEC_PKCS12CreateUnencryptedSafe 
 *    Creates an unencrypted safe within the export context.
 *
 *    p12ctxt - the export context 
 */
SEC_PKCS12SafeInfo *
SEC_PKCS12CreateUnencryptedSafe(SEC_PKCS12ExportContext *p12ctxt)
{
    SEC_PKCS12SafeInfo *safeInfo = NULL;
    void *mark = NULL;

    if(!p12ctxt) {
      return NULL;
    }

    /* create the safe info */
    mark = PORT_ArenaMark(p12ctxt->arena);
    safeInfo = (SEC_PKCS12SafeInfo *)PORT_ArenaZAlloc(p12ctxt->arena, 
                                    sizeof(SEC_PKCS12SafeInfo));
    if(!safeInfo) {
      PORT_ArenaRelease(p12ctxt->arena, mark);
      PORT_SetError(SEC_ERROR_NO_MEMORY);
      return NULL;
    }

    safeInfo->itemCount = 0;

    /* create the safe content */
    safeInfo->cinfo = SEC_PKCS7CreateData();
    if(!safeInfo->cinfo) {
      PORT_SetError(SEC_ERROR_NO_MEMORY);
      goto loser;
    }

    if(sec_pkcs12_append_safe_info(p12ctxt, safeInfo) != SECSuccess) {
      goto loser;
    }

    PORT_ArenaUnmark(p12ctxt->arena, mark);
    return safeInfo;

loser:
    if(safeInfo->cinfo) {
      SEC_PKCS7DestroyContentInfo(safeInfo->cinfo);
    }

    PORT_ArenaRelease(p12ctxt->arena, mark);
    return NULL;
}

/* SEC_PKCS12CreatePubKeyEncryptedSafe
 *    Creates a safe which is protected by public key encryption.  
 *
 *    p12ctxt - the export context
 *    certDb - the certificate database
 *    signer - the signer's certificate
 *    recipients - the list of recipient certificates.
 *    algorithm - the encryption algorithm to use
 *    keysize - the algorithms key size (?)
 */
SEC_PKCS12SafeInfo *
SEC_PKCS12CreatePubKeyEncryptedSafe(SEC_PKCS12ExportContext *p12ctxt,
                            CERTCertDBHandle *certDb,
                            CERTCertificate *signer,
                            CERTCertificate **recipients,
                            SECOidTag algorithm, int keysize) 
{
    SEC_PKCS12SafeInfo *safeInfo = NULL;
    void *mark = NULL;

    if(!p12ctxt || !signer || !recipients || !(*recipients)) {
      return NULL;
    }

    /* allocate the safeInfo */
    mark = PORT_ArenaMark(p12ctxt->arena);
    safeInfo = (SEC_PKCS12SafeInfo *)PORT_ArenaZAlloc(p12ctxt->arena, 
                                          sizeof(SEC_PKCS12SafeInfo));
    if(!safeInfo) {
      PORT_ArenaRelease(p12ctxt->arena, mark);
      PORT_SetError(SEC_ERROR_NO_MEMORY);
      return NULL;
    }

    safeInfo->itemCount = 0;
    safeInfo->arena = p12ctxt->arena;

    /* create the enveloped content info using certUsageEmailSigner currently.
     * XXX We need to eventually use something other than certUsageEmailSigner
     */
    safeInfo->cinfo = SEC_PKCS7CreateEnvelopedData(signer, certUsageEmailSigner,
                              certDb, algorithm, keysize, 
                              p12ctxt->pwfn, p12ctxt->pwfnarg);
    if(!safeInfo->cinfo) {
      PORT_SetError(SEC_ERROR_NO_MEMORY);
      goto loser;
    }

    /* add recipients */
    if(recipients) {
      unsigned int i = 0;
      while(recipients[i] != NULL) {
          SECStatus rv = SEC_PKCS7AddRecipient(safeInfo->cinfo, recipients[i],
                                     certUsageEmailRecipient, certDb);
          if(rv != SECSuccess) {
            goto loser;
          }
          i++;
      }
    }

    if(sec_pkcs12_append_safe_info(p12ctxt, safeInfo) != SECSuccess) {
      goto loser;
    }

    PORT_ArenaUnmark(p12ctxt->arena, mark);
    return safeInfo;

loser:
    if(safeInfo->cinfo) {
      SEC_PKCS7DestroyContentInfo(safeInfo->cinfo);
      safeInfo->cinfo = NULL;
    }

    PORT_ArenaRelease(p12ctxt->arena, mark);
    return NULL;
} 

/*********************************
 * Routines to handle the exporting of the keys and certificates
 *********************************/

/* creates a safe contents which safeBags will be appended to */
sec_PKCS12SafeContents *
sec_PKCS12CreateSafeContents(PRArenaPool *arena)
{
    sec_PKCS12SafeContents *safeContents;

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

    /* create the safe contents */
    safeContents = (sec_PKCS12SafeContents *)PORT_ArenaZAlloc(arena,
                                  sizeof(sec_PKCS12SafeContents));
    if(!safeContents) {
      PORT_SetError(SEC_ERROR_NO_MEMORY);
      goto loser;
    }

    /* set up the internal contents info */
    safeContents->safeBags = NULL;
    safeContents->arena = arena;
    safeContents->bagCount = 0;

    return safeContents;

loser:
    return NULL;
}   

/* appends a safe bag to a safeContents using the specified arena. 
 */
SECStatus
sec_pkcs12_append_bag_to_safe_contents(PRArenaPool *arena, 
                               sec_PKCS12SafeContents *safeContents,
                               sec_PKCS12SafeBag *safeBag)
{
    void *mark = NULL, *dummy = NULL;

    if(!arena || !safeBag || !safeContents) {
      return SECFailure;
    }

    mark = PORT_ArenaMark(arena);
    if(!mark) {
      PORT_SetError(SEC_ERROR_NO_MEMORY);
      return SECFailure;
    }

    /* allocate space for the list, or reallocate to increase space */
    if(!safeContents->safeBags) {
      safeContents->safeBags = (sec_PKCS12SafeBag **)PORT_ArenaZAlloc(arena, 
                                    (2 * sizeof(sec_PKCS12SafeBag *)));
      dummy = safeContents->safeBags;
      safeContents->bagCount = 0;
    } else {
      dummy = PORT_ArenaGrow(arena, safeContents->safeBags, 
                  (safeContents->bagCount + 1) * sizeof(sec_PKCS12SafeBag *),
                  (safeContents->bagCount + 2) * sizeof(sec_PKCS12SafeBag *));
      safeContents->safeBags = (sec_PKCS12SafeBag **)dummy;
    }

    if(!dummy) {
      PORT_ArenaRelease(arena, mark);
      PORT_SetError(SEC_ERROR_NO_MEMORY);
      return SECFailure;
    }

    /* append the bag at the end and null terminate the list */
    safeContents->safeBags[safeContents->bagCount++] = safeBag;
    safeContents->safeBags[safeContents->bagCount] = NULL;

    PORT_ArenaUnmark(arena, mark);

    return SECSuccess;
}

/* appends a safeBag to a specific safeInfo.
 */
SECStatus
sec_pkcs12_append_bag(SEC_PKCS12ExportContext *p12ctxt, 
                  SEC_PKCS12SafeInfo *safeInfo, sec_PKCS12SafeBag *safeBag)
{
    sec_PKCS12SafeContents *dest;
    SECStatus rv = SECFailure;

    if(!p12ctxt || !safeBag || !safeInfo) {
      return SECFailure;
    }

    if(!safeInfo->safe) {
      safeInfo->safe = sec_PKCS12CreateSafeContents(p12ctxt->arena);
      if(!safeInfo->safe) {
          return SECFailure;
      }
    }

    dest = safeInfo->safe;
    rv = sec_pkcs12_append_bag_to_safe_contents(p12ctxt->arena, dest, safeBag);
    if(rv == SECSuccess) {
      safeInfo->itemCount++;
    }
    
    return rv;
} 

/* Creates a safeBag of the specified type, and if bagData is specified,
 * the contents are set.  The contents could be set later by the calling
 * routine.
 */
sec_PKCS12SafeBag *
sec_PKCS12CreateSafeBag(SEC_PKCS12ExportContext *p12ctxt, SECOidTag bagType, 
                  void *bagData)
{
    sec_PKCS12SafeBag *safeBag;
    PRBool setName = PR_TRUE;
    void *mark = NULL;
    SECStatus rv = SECSuccess;
    SECOidData *oidData = NULL;

    if(!p12ctxt) {
      return NULL;
    }

    mark = PORT_ArenaMark(p12ctxt->arena);
    if(!mark) {
      PORT_SetError(SEC_ERROR_NO_MEMORY);
      return NULL;
    }

    safeBag = (sec_PKCS12SafeBag *)PORT_ArenaZAlloc(p12ctxt->arena, 
                                        sizeof(sec_PKCS12SafeBag));
    if(!safeBag) {
      PORT_ArenaRelease(p12ctxt->arena, mark);
      PORT_SetError(SEC_ERROR_NO_MEMORY);
      return NULL;
    }

    /* set the bags content based upon bag type */
    switch(bagType) {
      case SEC_OID_PKCS12_V1_KEY_BAG_ID:
          safeBag->safeBagContent.pkcs8KeyBag =
              (SECKEYPrivateKeyInfo *)bagData;
          break;
      case SEC_OID_PKCS12_V1_CERT_BAG_ID:
          safeBag->safeBagContent.certBag = (sec_PKCS12CertBag *)bagData;
          break;
      case SEC_OID_PKCS12_V1_CRL_BAG_ID:
          safeBag->safeBagContent.crlBag = (sec_PKCS12CRLBag *)bagData;
          break;
      case SEC_OID_PKCS12_V1_SECRET_BAG_ID:
          safeBag->safeBagContent.secretBag = (sec_PKCS12SecretBag *)bagData;
          break;
      case SEC_OID_PKCS12_V1_PKCS8_SHROUDED_KEY_BAG_ID:
          safeBag->safeBagContent.pkcs8ShroudedKeyBag = 
              (SECKEYEncryptedPrivateKeyInfo *)bagData;
          break;
      case SEC_OID_PKCS12_V1_SAFE_CONTENTS_BAG_ID:
          safeBag->safeBagContent.safeContents = 
              (sec_PKCS12SafeContents *)bagData;
          setName = PR_FALSE;
          break;
      default:
          goto loser;
    }

    oidData = SECOID_FindOIDByTag(bagType);
    if(oidData) {
      rv = SECITEM_CopyItem(p12ctxt->arena, &safeBag->safeBagType, &oidData->oid);
      if(rv != SECSuccess) {
          PORT_SetError(SEC_ERROR_NO_MEMORY);
          goto loser;
      }
    } else {
      goto loser;
    }
    
    safeBag->arena = p12ctxt->arena;
    PORT_ArenaUnmark(p12ctxt->arena, mark);

    return safeBag;

loser:
    if(mark) {
      PORT_ArenaRelease(p12ctxt->arena, mark);
    }

    return NULL;
}

/* Creates a new certificate bag and returns a pointer to it.  If an error
 * occurs NULL is returned.
 */
sec_PKCS12CertBag *
sec_PKCS12NewCertBag(PRArenaPool *arena, SECOidTag certType)
{
    sec_PKCS12CertBag *certBag = NULL;
    SECOidData *bagType = NULL;
    SECStatus rv;
    void *mark = NULL;

    if(!arena) {
      return NULL;
    }

    mark = PORT_ArenaMark(arena);
    certBag = (sec_PKCS12CertBag *)PORT_ArenaZAlloc(arena, 
                                        sizeof(sec_PKCS12CertBag));
    if(!certBag) {
      PORT_ArenaRelease(arena, mark);
      PORT_SetError(SEC_ERROR_NO_MEMORY);
      return NULL;
    }

    bagType = SECOID_FindOIDByTag(certType);
    if(!bagType) {
      PORT_SetError(SEC_ERROR_NO_MEMORY);
      goto loser;
    }

    rv = SECITEM_CopyItem(arena, &certBag->bagID, &bagType->oid);
    if(rv != SECSuccess) {
      PORT_SetError(SEC_ERROR_NO_MEMORY);
      goto loser;
    }
      
    PORT_ArenaUnmark(arena, mark);
    return certBag;

loser:
    PORT_ArenaRelease(arena, mark);
    return NULL;
}

/* Creates a new CRL bag and returns a pointer to it.  If an error
 * occurs NULL is returned.
 */
sec_PKCS12CRLBag *
sec_PKCS12NewCRLBag(PRArenaPool *arena, SECOidTag crlType)
{
    sec_PKCS12CRLBag *crlBag = NULL;
    SECOidData *bagType = NULL;
    SECStatus rv;
    void *mark = NULL;

    if(!arena) {
      return NULL;
    }

    mark = PORT_ArenaMark(arena);
    crlBag = (sec_PKCS12CRLBag *)PORT_ArenaZAlloc(arena, 
                                      sizeof(sec_PKCS12CRLBag));
    if(!crlBag) {
      PORT_ArenaRelease(arena, mark);
      PORT_SetError(SEC_ERROR_NO_MEMORY);
      return NULL;
    }

    bagType = SECOID_FindOIDByTag(crlType);
    if(!bagType) {
      PORT_SetError(SEC_ERROR_NO_MEMORY);
      goto loser;
    }

    rv = SECITEM_CopyItem(arena, &crlBag->bagID, &bagType->oid);
    if(rv != SECSuccess) {
      PORT_SetError(SEC_ERROR_NO_MEMORY);
      goto loser;
    }
      
    PORT_ArenaUnmark(arena, mark);
    return crlBag;

loser:
    PORT_ArenaRelease(arena, mark);
    return NULL;
}

/* sec_PKCS12AddAttributeToBag
 * adds an attribute to a safeBag.  currently, the only attributes supported
 * are those which are specified within PKCS 12.  
 *
 *    p12ctxt - the export context 
 *    safeBag - the safeBag to which attributes are appended
 *    attrType - the attribute type
 *    attrData - the attribute data
 */
SECStatus
sec_PKCS12AddAttributeToBag(SEC_PKCS12ExportContext *p12ctxt, 
                      sec_PKCS12SafeBag *safeBag, SECOidTag attrType,
                      SECItem *attrData)
{
    sec_PKCS12Attribute *attribute;
    void *mark = NULL, *dummy = NULL;
    SECOidData *oiddata = NULL;
    SECItem unicodeName = { siBuffer, NULL, 0};
    void *src = NULL;
    unsigned int nItems = 0;
    SECStatus rv;

    if(!safeBag || !p12ctxt) {
      return SECFailure;
    }

    mark = PORT_ArenaMark(safeBag->arena);

    /* allocate the attribute */
    attribute = (sec_PKCS12Attribute *)PORT_ArenaZAlloc(safeBag->arena, 
                                    sizeof(sec_PKCS12Attribute));
    if(!attribute) {
      PORT_SetError(SEC_ERROR_NO_MEMORY);
      goto loser;
    }

    /* set up the attribute */
    oiddata = SECOID_FindOIDByTag(attrType);
    if(!oiddata) {
      PORT_SetError(SEC_ERROR_NO_MEMORY);
      goto loser;
    }
    if(SECITEM_CopyItem(p12ctxt->arena, &attribute->attrType, &oiddata->oid) !=
            SECSuccess) {
      PORT_SetError(SEC_ERROR_NO_MEMORY);
      goto loser;
    }

    nItems = 1;
    switch(attrType) {
      case SEC_OID_PKCS9_LOCAL_KEY_ID:
          {
            src = attrData;
            break;
          }
      case SEC_OID_PKCS9_FRIENDLY_NAME:
          {
            if(!sec_pkcs12_convert_item_to_unicode(p12ctxt->arena, 
                              &unicodeName, attrData, PR_FALSE, 
                              PR_FALSE, PR_TRUE)) {
                goto loser;
            }
            src = &unicodeName;
            break;
          }
      default:
          goto loser;
    }

    /* append the attribute to the attribute value list  */
    attribute->attrValue = (SECItem **)PORT_ArenaZAlloc(p12ctxt->arena, 
                                  ((nItems + 1) * sizeof(SECItem *)));
    if(!attribute->attrValue) {
      PORT_SetError(SEC_ERROR_NO_MEMORY);
      goto loser;
    }

    /* XXX this will need to be changed if attributes requiring more than
     * one element are ever used.
     */
    attribute->attrValue[0] = (SECItem *)PORT_ArenaZAlloc(p12ctxt->arena, 
                                            sizeof(SECItem));
    if(!attribute->attrValue[0]) {
      PORT_SetError(SEC_ERROR_NO_MEMORY);
      goto loser;
    }
    attribute->attrValue[1] = NULL;

    rv = SECITEM_CopyItem(p12ctxt->arena, attribute->attrValue[0], 
                    (SECItem*)src);
    if(rv != SECSuccess) {
      PORT_SetError(SEC_ERROR_NO_MEMORY);
      goto loser;
    }

    /* append the attribute to the safeBag attributes */
    if(safeBag->nAttribs) {
      dummy = PORT_ArenaGrow(p12ctxt->arena, safeBag->attribs, 
                  ((safeBag->nAttribs + 1) * sizeof(sec_PKCS12Attribute *)),
                  ((safeBag->nAttribs + 2) * sizeof(sec_PKCS12Attribute *)));
      safeBag->attribs = (sec_PKCS12Attribute **)dummy;
    } else {
      safeBag->attribs = (sec_PKCS12Attribute **)PORT_ArenaZAlloc(p12ctxt->arena, 
                                    2 * sizeof(sec_PKCS12Attribute *));
      dummy = safeBag->attribs;
    }
    if(!dummy) {
      goto loser;
    }

    safeBag->attribs[safeBag->nAttribs] = attribute;
    safeBag->attribs[++safeBag->nAttribs] = NULL;

    PORT_ArenaUnmark(p12ctxt->arena, mark);
    return SECSuccess;

loser:
    if(mark) {
      PORT_ArenaRelease(p12ctxt->arena, mark);
    }

    return SECFailure;
}

/* SEC_PKCS12AddCert
 *    Adds a certificate to the data being exported.  
 *
 *    p12ctxt - the export context
 *    safe - the safeInfo to which the certificate is placed 
 *    nestedDest - if the cert is to be placed within a nested safeContents then,
 *               this value is to be specified with the destination
 *    cert - the cert to export
 *    certDb - the certificate database handle
 *    keyId - a unique identifier to associate a certificate/key pair
 *    includeCertChain - PR_TRUE if the certificate chain is to be included.
 */
SECStatus
SEC_PKCS12AddCert(SEC_PKCS12ExportContext *p12ctxt, SEC_PKCS12SafeInfo *safe, 
              void *nestedDest, CERTCertificate *cert, 
              CERTCertDBHandle *certDb, SECItem *keyId,
              PRBool includeCertChain)
{
    sec_PKCS12CertBag *certBag;
    sec_PKCS12SafeBag *safeBag;
    void *mark;
    SECStatus rv;
    SECItem nick = {siBuffer, NULL,0};

    if(!p12ctxt || !cert) {
      return SECFailure;
    }
    mark = PORT_ArenaMark(p12ctxt->arena);

    /* allocate the cert bag */
    certBag = sec_PKCS12NewCertBag(p12ctxt->arena, 
                           SEC_OID_PKCS9_X509_CERT);
    if(!certBag) {
      goto loser;
    }

    if(SECITEM_CopyItem(p12ctxt->arena, &certBag->value.x509Cert, 
                  &cert->derCert) != SECSuccess) {
      PORT_SetError(SEC_ERROR_NO_MEMORY);
      goto loser;
    }

    /* if the cert chain is to be included, we should only be exporting
     * the cert from our internal database.
     */
    if(includeCertChain) {
      CERTCertificateList *certList = CERT_CertChainFromCert(cert,
                                                 certUsageSSLClient,
                                                 PR_TRUE);
      unsigned int count = 0;
      if(!certList) {
          PORT_SetError(SEC_ERROR_NO_MEMORY);
          goto loser;
      }

      /* add cert chain */
      for(count = 0; count < (unsigned int)certList->len; count++) {
          if(SECITEM_CompareItem(&certList->certs[count], &cert->derCert)
                        != SECEqual) {
            CERTCertificate *tempCert;

            /* decode the certificate */
            /* XXX
             * This was rather silly.  The chain is constructed above
             * by finding all of the CERTCertificate's in the database.
             * Then the chain is put into a CERTCertificateList, which only
             * contains the DER.  Finally, the DER was decoded, and the
             * decoded cert was sent recursively back to this function.
             * Beyond being inefficent, this causes data loss (specifically,
             * the nickname).  Instead, for 3.4, we'll do a lookup by the
             * DER, which should return the cached entry.
             */
            tempCert = CERT_FindCertByDERCert(CERT_GetDefaultCertDB(),
                                              &certList->certs[count]);
            if(!tempCert) {
                CERT_DestroyCertificateList(certList);
                goto loser;
            }

            /* add the certificate */
            if(SEC_PKCS12AddCert(p12ctxt, safe, nestedDest, tempCert,
                         certDb, NULL, PR_FALSE) != SECSuccess) {
                CERT_DestroyCertificate(tempCert);
                CERT_DestroyCertificateList(certList);
                goto loser;
            }
            CERT_DestroyCertificate(tempCert);
          }
      }
      CERT_DestroyCertificateList(certList);
    }

    /* if the certificate has a nickname, we will set the friendly name
     * to that.
     */
    if(cert->nickname) {
        if (cert->slot && !PK11_IsInternal(cert->slot)) {
        /*
         * The cert is coming off of an external token, 
         * let's strip the token name from the nickname
         * and only add what comes after the colon as the
         * nickname. -javi
         */
          char *delimit;
          
          delimit = PORT_Strchr(cert->nickname,':');
          if (delimit == NULL) {
              nick.data = (unsigned char *)cert->nickname;
            nick.len = PORT_Strlen(cert->nickname);
          } else {
              delimit++;
              nick.data = (unsigned char *)PORT_ArenaStrdup(p12ctxt->arena,
                                                delimit);
            nick.len = PORT_Strlen(delimit);
          }
      } else {
          nick.data = (unsigned char *)cert->nickname;
          nick.len = PORT_Strlen(cert->nickname);
      }
    }

    safeBag = sec_PKCS12CreateSafeBag(p12ctxt, SEC_OID_PKCS12_V1_CERT_BAG_ID, 
                              certBag);
    if(!safeBag) {
      goto loser;
    }

    /* add the friendly name and keyId attributes, if necessary */
    if(nick.data) {
      if(sec_PKCS12AddAttributeToBag(p12ctxt, safeBag, 
                               SEC_OID_PKCS9_FRIENDLY_NAME, &nick) 
                               != SECSuccess) {
          goto loser;
      }
    }
         
    if(keyId) {
      if(sec_PKCS12AddAttributeToBag(p12ctxt, safeBag, SEC_OID_PKCS9_LOCAL_KEY_ID,
                               keyId) != SECSuccess) {
          goto loser;
      }
    }

    /* append the cert safeBag */
    if(nestedDest) {
      rv = sec_pkcs12_append_bag_to_safe_contents(p12ctxt->arena, 
                                (sec_PKCS12SafeContents*)nestedDest, 
                                 safeBag);
    } else {
      rv = sec_pkcs12_append_bag(p12ctxt, safe, safeBag);
    }

    if(rv != SECSuccess) {
      goto loser;
    }

    PORT_ArenaUnmark(p12ctxt->arena, mark);
    return SECSuccess;

loser:
    if(mark) {
      PORT_ArenaRelease(p12ctxt->arena, mark);
    }

    return SECFailure;
}

/* SEC_PKCS12AddEncryptedKey
 *    Extracts the key associated with a particular certificate and exports
 *    it.
 *
 *    p12ctxt - the export context 
 *    safe - the safeInfo to place the key in
 *    nestedDest - the nested safeContents to place a key
 *    cert - the certificate which the key belongs to
 *    shroudKey - encrypt the private key for export.  This value should 
 *          always be true.  lower level code will not allow the export
 *          of unencrypted private keys.
 *    algorithm - the algorithm with which to encrypt the private key
 *    pwitem - the password to encrypted the private key with
 *    keyId - the keyID attribute
 *    nickName - the nickname attribute
 */
static SECStatus
SEC_PKCS12AddEncryptedKey(SEC_PKCS12ExportContext *p12ctxt, 
            SECKEYEncryptedPrivateKeyInfo *epki, SEC_PKCS12SafeInfo *safe,
            void *nestedDest, SECItem *keyId, SECItem *nickName)
{
    void *mark;
    void *keyItem;
    SECOidTag keyType;
    SECStatus rv = SECFailure;
    sec_PKCS12SafeBag *returnBag;

    if(!p12ctxt || !safe || !epki) {
      return SECFailure;
    }

    mark = PORT_ArenaMark(p12ctxt->arena);

    keyItem = PORT_ArenaZAlloc(p12ctxt->arena, 
                        sizeof(SECKEYEncryptedPrivateKeyInfo));
    if(!keyItem) {
      PORT_SetError(SEC_ERROR_NO_MEMORY);
      goto loser;
    }

    rv = SECKEY_CopyEncryptedPrivateKeyInfo(p12ctxt->arena, 
                              (SECKEYEncryptedPrivateKeyInfo *)keyItem,
                              epki);
    keyType = SEC_OID_PKCS12_V1_PKCS8_SHROUDED_KEY_BAG_ID;

    if(rv != SECSuccess) {
      goto loser;
    }
      
    /* create the safe bag and set any attributes */
    returnBag = sec_PKCS12CreateSafeBag(p12ctxt, keyType, keyItem);
    if(!returnBag) {
      rv = SECFailure;
      goto loser;
    }

    if(nickName) {
      if(sec_PKCS12AddAttributeToBag(p12ctxt, returnBag, 
                               SEC_OID_PKCS9_FRIENDLY_NAME, nickName) 
                               != SECSuccess) {
          goto loser;
      }
    }
         
    if(keyId) {
      if(sec_PKCS12AddAttributeToBag(p12ctxt, returnBag, 
                                     SEC_OID_PKCS9_LOCAL_KEY_ID,
                               keyId) != SECSuccess) {
          goto loser;
      }
    }

    if(nestedDest) {
      rv = sec_pkcs12_append_bag_to_safe_contents(p12ctxt->arena, 
                                 (sec_PKCS12SafeContents*)nestedDest,
                                 returnBag);
    } else {
      rv = sec_pkcs12_append_bag(p12ctxt, safe, returnBag);
    }

loser:

    if (rv != SECSuccess) {
      PORT_ArenaRelease(p12ctxt->arena, mark);
    } else {
      PORT_ArenaUnmark(p12ctxt->arena, mark);
    }

    return rv;
}

/* SEC_PKCS12AddKeyForCert
 *    Extracts the key associated with a particular certificate and exports
 *    it.
 *
 *    p12ctxt - the export context 
 *    safe - the safeInfo to place the key in
 *    nestedDest - the nested safeContents to place a key
 *    cert - the certificate which the key belongs to
 *    shroudKey - encrypt the private key for export.  This value should 
 *          always be true.  lower level code will not allow the export
 *          of unencrypted private keys.
 *    algorithm - the algorithm with which to encrypt the private key
 *    pwitem - the password to encrypt the private key with
 *    keyId - the keyID attribute
 *    nickName - the nickname attribute
 */
SECStatus
SEC_PKCS12AddKeyForCert(SEC_PKCS12ExportContext *p12ctxt, SEC_PKCS12SafeInfo *safe, 
                  void *nestedDest, CERTCertificate *cert,
                  PRBool shroudKey, SECOidTag algorithm, SECItem *pwitem,
                  SECItem *keyId, SECItem *nickName)
{
    void *mark;
    void *keyItem;
    SECOidTag keyType;
    SECStatus rv = SECFailure;
    SECItem nickname = {siBuffer,NULL,0}, uniPwitem = {siBuffer, NULL, 0};
    sec_PKCS12SafeBag *returnBag;

    if(!p12ctxt || !cert || !safe) {
      return SECFailure;
    }

    mark = PORT_ArenaMark(p12ctxt->arena);

    /* retrieve the key based upon the type that it is and 
     * specify the type of safeBag to store the key in
     */        
    if(!shroudKey) {

      /* extract the key unencrypted.  this will most likely go away */
      SECKEYPrivateKeyInfo *pki = PK11_ExportPrivateKeyInfo(cert, 
                                                p12ctxt->wincx);
      if(!pki) {
          PORT_ArenaRelease(p12ctxt->arena, mark);
          PORT_SetError(SEC_ERROR_PKCS12_UNABLE_TO_EXPORT_KEY);
          return SECFailure;
      }   
      keyItem = PORT_ArenaZAlloc(p12ctxt->arena, sizeof(SECKEYPrivateKeyInfo));
      if(!keyItem) {
          PORT_SetError(SEC_ERROR_NO_MEMORY);
          goto loser;
      }
      rv = SECKEY_CopyPrivateKeyInfo(p12ctxt->arena, 
                               (SECKEYPrivateKeyInfo *)keyItem, pki);
      keyType = SEC_OID_PKCS12_V1_KEY_BAG_ID;
      SECKEY_DestroyPrivateKeyInfo(pki, PR_TRUE);
    } else {

      /* extract the key encrypted */
      SECKEYEncryptedPrivateKeyInfo *epki = NULL;
      PK11SlotInfo *slot = NULL;

      if(!sec_pkcs12_convert_item_to_unicode(p12ctxt->arena, &uniPwitem,
                         pwitem, PR_TRUE, PR_TRUE, PR_TRUE)) {
          PORT_SetError(SEC_ERROR_NO_MEMORY);
          goto loser;
      }

      /* we want to make sure to take the key out of the key slot */
      if(PK11_IsInternal(p12ctxt->slot)) {
          slot = PK11_GetInternalKeySlot();
      } else {
          slot = PK11_ReferenceSlot(p12ctxt->slot);
      }

      epki = PK11_ExportEncryptedPrivateKeyInfo(slot, algorithm, 
                                      &uniPwitem, cert, 1, 
                                      p12ctxt->wincx);
      PK11_FreeSlot(slot);
      
      keyItem = PORT_ArenaZAlloc(p12ctxt->arena, 
                          sizeof(SECKEYEncryptedPrivateKeyInfo));
      if(!keyItem) {
          PORT_SetError(SEC_ERROR_NO_MEMORY);
          goto loser;
      }
      if(!epki) {
          PORT_SetError(SEC_ERROR_PKCS12_UNABLE_TO_EXPORT_KEY);
          return SECFailure;
      }   
      rv = SECKEY_CopyEncryptedPrivateKeyInfo(p12ctxt->arena, 
                              (SECKEYEncryptedPrivateKeyInfo *)keyItem,
                              epki);
      keyType = SEC_OID_PKCS12_V1_PKCS8_SHROUDED_KEY_BAG_ID;
      SECKEY_DestroyEncryptedPrivateKeyInfo(epki, PR_TRUE);
    }

    if(rv != SECSuccess) {
      goto loser;
    }
      
    /* if no nickname specified, let's see if the certificate has a 
     * nickname.
     */                               
    if(!nickName) {
      if(cert->nickname) {
          nickname.data = (unsigned char *)cert->nickname;
          nickname.len = PORT_Strlen(cert->nickname);
          nickName = &nickname;
      }
    }

    /* create the safe bag and set any attributes */
    returnBag = sec_PKCS12CreateSafeBag(p12ctxt, keyType, keyItem);
    if(!returnBag) {
      rv = SECFailure;
      goto loser;
    }

    if(nickName) {
      if(sec_PKCS12AddAttributeToBag(p12ctxt, returnBag, 
                               SEC_OID_PKCS9_FRIENDLY_NAME, nickName) 
                               != SECSuccess) {
          goto loser;
      }
    }
         
    if(keyId) {
      if(sec_PKCS12AddAttributeToBag(p12ctxt, returnBag, SEC_OID_PKCS9_LOCAL_KEY_ID,
                               keyId) != SECSuccess) {
          goto loser;
      }
    }

    if(nestedDest) {
      rv = sec_pkcs12_append_bag_to_safe_contents(p12ctxt->arena,
                                (sec_PKCS12SafeContents*)nestedDest, 
                                returnBag);
    } else {
      rv = sec_pkcs12_append_bag(p12ctxt, safe, returnBag);
    }

loser:

    if (rv != SECSuccess) {
      PORT_ArenaRelease(p12ctxt->arena, mark);
    } else {
      PORT_ArenaUnmark(p12ctxt->arena, mark);
    }

    return rv;
}

/* SEC_PKCS12AddCertAndEncryptedKey
 *    Add a certificate and key pair to be exported.
 *
 *    p12ctxt - the export context 
 *    certSafe - the safeInfo where the cert is stored
 *    certNestedDest - the nested safeContents to store the cert
 *    keySafe - the safeInfo where the key is stored
 *    keyNestedDest - the nested safeContents to store the key
 *    shroudKey - extract the private key encrypted?
 *    pwitem - the password with which the key is encrypted
 *    algorithm - the algorithm with which the key is encrypted
 */
SECStatus
SEC_PKCS12AddDERCertAndEncryptedKey(SEC_PKCS12ExportContext *p12ctxt, 
            void *certSafe, void *certNestedDest, 
            SECItem *derCert, void *keySafe, 
            void *keyNestedDest, SECKEYEncryptedPrivateKeyInfo *epki,
            char *nickname)
{           
    SECStatus rv = SECFailure;
    SGNDigestInfo *digest = NULL;
    void *mark = NULL;
    CERTCertificate *cert;
    SECItem nick = {siBuffer, NULL,0}, *nickPtr = NULL; 

    if(!p12ctxt || !certSafe || !keySafe || !derCert) {
      return SECFailure;
    }

    mark = PORT_ArenaMark(p12ctxt->arena);

    cert = CERT_NewTempCertificate(CERT_GetDefaultCertDB(),
                                   derCert, NULL, PR_FALSE, PR_FALSE);
    if(!cert) {
      PORT_ArenaRelease(p12ctxt->arena, mark);
      PORT_SetError(SEC_ERROR_NO_MEMORY);
      return SECFailure;
    }
    cert->nickname = nickname;

    /* generate the thumbprint of the cert to use as a keyId */
    digest = sec_pkcs12_compute_thumbprint(&cert->derCert);
    if(!digest) {
      CERT_DestroyCertificate(cert);
      return SECFailure;
    }

    /* add the certificate */
    rv = SEC_PKCS12AddCert(p12ctxt, (SEC_PKCS12SafeInfo*)certSafe, 
                     certNestedDest, cert, NULL,
                     &digest->digest, PR_FALSE);
    if(rv != SECSuccess) {
      goto loser;
    }

    if(nickname) {
      nick.data = (unsigned char *)nickname;
      nick.len = PORT_Strlen(nickname);
      nickPtr = &nick;
    } else {
      nickPtr = NULL;
    }

    /* add the key */
    rv = SEC_PKCS12AddEncryptedKey(p12ctxt, epki, (SEC_PKCS12SafeInfo*)keySafe,
                           keyNestedDest, &digest->digest, nickPtr );
    if(rv != SECSuccess) {
      goto loser;
    }

    SGN_DestroyDigestInfo(digest);

    PORT_ArenaUnmark(p12ctxt->arena, mark);
    return SECSuccess;

loser:
    SGN_DestroyDigestInfo(digest);
    CERT_DestroyCertificate(cert);
    PORT_ArenaRelease(p12ctxt->arena, mark);
    
    return SECFailure; 
}

/* SEC_PKCS12AddCertAndKey
 *    Add a certificate and key pair to be exported.
 *
 *    p12ctxt - the export context 
 *    certSafe - the safeInfo where the cert is stored
 *    certNestedDest - the nested safeContents to store the cert
 *    keySafe - the safeInfo where the key is stored
 *    keyNestedDest - the nested safeContents to store the key
 *    shroudKey - extract the private key encrypted?
 *    pwitem - the password with which the key is encrypted
 *    algorithm - the algorithm with which the key is encrypted
 */
SECStatus
SEC_PKCS12AddCertAndKey(SEC_PKCS12ExportContext *p12ctxt, 
                  void *certSafe, void *certNestedDest, 
                  CERTCertificate *cert, CERTCertDBHandle *certDb,
                  void *keySafe, void *keyNestedDest, 
                  PRBool shroudKey, SECItem *pwitem, SECOidTag algorithm)
{           
    SECStatus rv = SECFailure;
    SGNDigestInfo *digest = NULL;
    void *mark = NULL;

    if(!p12ctxt || !certSafe || !keySafe || !cert) {
      return SECFailure;
    }

    mark = PORT_ArenaMark(p12ctxt->arena);

    /* generate the thumbprint of the cert to use as a keyId */
    digest = sec_pkcs12_compute_thumbprint(&cert->derCert);
    if(!digest) {
      PORT_ArenaRelease(p12ctxt->arena, mark);
      return SECFailure;
    }

    /* add the certificate */
    rv = SEC_PKCS12AddCert(p12ctxt, (SEC_PKCS12SafeInfo*)certSafe, 
                     (SEC_PKCS12SafeInfo*)certNestedDest, cert, certDb,
                     &digest->digest, PR_TRUE);
    if(rv != SECSuccess) {
      goto loser;
    }

    /* add the key */
    rv = SEC_PKCS12AddKeyForCert(p12ctxt, (SEC_PKCS12SafeInfo*)keySafe, 
                         keyNestedDest, cert, 
                         shroudKey, algorithm, pwitem, 
                         &digest->digest, NULL );
    if(rv != SECSuccess) {
      goto loser;
    }

    SGN_DestroyDigestInfo(digest);

    PORT_ArenaUnmark(p12ctxt->arena, mark);
    return SECSuccess;

loser:
    SGN_DestroyDigestInfo(digest);
    PORT_ArenaRelease(p12ctxt->arena, mark);
    
    return SECFailure; 
}

/* SEC_PKCS12CreateNestedSafeContents
 *    Allows nesting of safe contents to be implemented.  No limit imposed on 
 *    depth.  
 *
 *    p12ctxt - the export context 
 *    baseSafe - the base safeInfo 
 *    nestedDest - a parent safeContents (?)
 */
void *
SEC_PKCS12CreateNestedSafeContents(SEC_PKCS12ExportContext *p12ctxt,
                           void *baseSafe, void *nestedDest)
{
    sec_PKCS12SafeContents *newSafe;
    sec_PKCS12SafeBag *safeContentsBag;
    void *mark;
    SECStatus rv;

    if(!p12ctxt || !baseSafe) {
      return NULL;
    }

    mark = PORT_ArenaMark(p12ctxt->arena);

    newSafe = sec_PKCS12CreateSafeContents(p12ctxt->arena);
    if(!newSafe) {
      PORT_ArenaRelease(p12ctxt->arena, mark);
      PORT_SetError(SEC_ERROR_NO_MEMORY);
      return NULL;
    }

    /* create the safeContents safeBag */
    safeContentsBag = sec_PKCS12CreateSafeBag(p12ctxt, 
                              SEC_OID_PKCS12_V1_SAFE_CONTENTS_BAG_ID,
                              newSafe);
    if(!safeContentsBag) {
      goto loser;
    }

    /* append the safeContents to the appropriate area */
    if(nestedDest) {
      rv = sec_pkcs12_append_bag_to_safe_contents(p12ctxt->arena, 
                                 (sec_PKCS12SafeContents*)nestedDest,
                                 safeContentsBag);
    } else {
      rv = sec_pkcs12_append_bag(p12ctxt, (SEC_PKCS12SafeInfo*)baseSafe, 
                           safeContentsBag);
    }
    if(rv != SECSuccess) {
      goto loser;
    }

    PORT_ArenaUnmark(p12ctxt->arena, mark);
    return newSafe;

loser:
    PORT_ArenaRelease(p12ctxt->arena, mark);
    return NULL;
}

/*********************************
 * Encoding routines
 *********************************/

/* set up the encoder context based on information in the export context
 * and return the newly allocated enocoder context.  A return of NULL 
 * indicates an error occurred. 
 */
sec_PKCS12EncoderContext *
sec_pkcs12_encoder_start_context(SEC_PKCS12ExportContext *p12exp)
{
    sec_PKCS12EncoderContext *p12enc = NULL;
    unsigned int i, nonEmptyCnt;
    SECStatus rv;
    SECItem ignore = {0};
    void *mark;

    if(!p12exp || !p12exp->safeInfos) {
      return NULL;
    }

    /* check for any empty safes and skip them */
    i = nonEmptyCnt = 0;
    while(p12exp->safeInfos[i]) {
      if(p12exp->safeInfos[i]->itemCount) {
          nonEmptyCnt++;
      }
      i++;
    }
    if(nonEmptyCnt == 0) {
      return NULL;
    }
    p12exp->authSafe.encodedSafes[nonEmptyCnt] = NULL;

    /* allocate the encoder context */
    mark = PORT_ArenaMark(p12exp->arena);
    p12enc = PORT_ArenaZNew(p12exp->arena, sec_PKCS12EncoderContext);
    if(!p12enc) {
      PORT_SetError(SEC_ERROR_NO_MEMORY);
      return NULL;
    }

    p12enc->arena = p12exp->arena;
    p12enc->p12exp = p12exp;

    /* set up the PFX version and information */
    PORT_Memset(&p12enc->pfx, 0, sizeof(sec_PKCS12PFXItem));
    if(!SEC_ASN1EncodeInteger(p12exp->arena, &(p12enc->pfx.version), 
                        SEC_PKCS12_VERSION) ) {
      PORT_SetError(SEC_ERROR_NO_MEMORY);
      goto loser;
    }

    /* set up the authenticated safe content info based on the 
     * type of integrity being used.  this should be changed to
     * enforce integrity mode, but will not be implemented until
     * it is confirmed that integrity must be in place
     */
    if(p12exp->integrityEnabled && !p12exp->pwdIntegrity) {
      SECStatus rv;

      /* create public key integrity mode */
      p12enc->aSafeCinfo = SEC_PKCS7CreateSignedData(
                        p12exp->integrityInfo.pubkeyInfo.cert,
                        certUsageEmailSigner,
                        p12exp->integrityInfo.pubkeyInfo.certDb,
                        p12exp->integrityInfo.pubkeyInfo.algorithm,
                        NULL,
                        p12exp->pwfn,
                        p12exp->pwfnarg);
      if(!p12enc->aSafeCinfo) {
          goto loser;
      }
      if(SEC_PKCS7IncludeCertChain(p12enc->aSafeCinfo,NULL) != SECSuccess) {
          goto loser;
      }
      rv = SEC_PKCS7AddSigningTime(p12enc->aSafeCinfo);
      PORT_Assert(rv == SECSuccess);
    } else {
      p12enc->aSafeCinfo = SEC_PKCS7CreateData();

      /* init password pased integrity mode */
      if(p12exp->integrityEnabled) {
          SECItem  pwd = {siBuffer,NULL, 0};
          SECItem *salt = sec_pkcs12_generate_salt();
          PK11SymKey *symKey;
          SECItem *params;
          CK_MECHANISM_TYPE integrityMechType;
          CK_MECHANISM_TYPE hmacMechType;

          /* zero out macData and set values */
          PORT_Memset(&p12enc->mac, 0, sizeof(sec_PKCS12MacData));

          if(!salt) {
            PORT_SetError(SEC_ERROR_NO_MEMORY);
            goto loser;
          }
          if(SECITEM_CopyItem(p12exp->arena, &(p12enc->mac.macSalt), salt) 
                  != SECSuccess) {
            PORT_SetError(SEC_ERROR_NO_MEMORY);
            goto loser;
          }   

          /* generate HMAC key */
          if(!sec_pkcs12_convert_item_to_unicode(NULL, &pwd, 
                  p12exp->integrityInfo.pwdInfo.password, PR_TRUE, 
                  PR_TRUE, PR_TRUE)) {
            goto loser;
          }
          /*
           * This code only works with PKCS #12 Mac using PKCS #5 v1
           * PBA keygens. PKCS #5 v2 support will require a change to
           * the PKCS #12 spec.
           */
          params = PK11_CreatePBEParams(salt, &pwd, 1);
          SECITEM_ZfreeItem(salt, PR_TRUE);
          SECITEM_ZfreeItem(&pwd, PR_FALSE);

          /* get the PBA Mechanism to generate the key */
          switch (p12exp->integrityInfo.pwdInfo.algorithm) {
          case SEC_OID_SHA1:
            integrityMechType = CKM_PBA_SHA1_WITH_SHA1_HMAC; break;
          case SEC_OID_MD5:
            integrityMechType = CKM_NETSCAPE_PBE_MD5_HMAC_KEY_GEN;  break;
          case SEC_OID_MD2:
            integrityMechType = CKM_NETSCAPE_PBE_MD2_HMAC_KEY_GEN;  break;
          default:
            goto loser;
          }

          /* generate the key */
          symKey = PK11_KeyGen(NULL, integrityMechType, params, 20, NULL);
          PK11_DestroyPBEParams(params);
          if(!symKey) {
            goto loser;
          }

          /* initialize HMAC */
          /* Get the HMAC mechanism from the hash OID */
          hmacMechType=  sec_pkcs12_algtag_to_mech( 
                                    p12exp->integrityInfo.pwdInfo.algorithm);

          p12enc->hmacCx = PK11_CreateContextBySymKey( hmacMechType,
                                     CKA_SIGN, symKey, &ignore);

          PK11_FreeSymKey(symKey);
          if(!p12enc->hmacCx) {
            PORT_SetError(SEC_ERROR_NO_MEMORY);
            goto loser;
          }
          rv = PK11_DigestBegin(p12enc->hmacCx);
          if (rv != SECSuccess)
            goto loser;
      }
    }

    if(!p12enc->aSafeCinfo) {
      goto loser;
    }

    PORT_ArenaUnmark(p12exp->arena, mark);

    return p12enc;

loser:
    if(p12enc) {
      if(p12enc->aSafeCinfo) {
          SEC_PKCS7DestroyContentInfo(p12enc->aSafeCinfo);
      }
      if(p12enc->hmacCx) {
          PK11_DestroyContext(p12enc->hmacCx, PR_TRUE);
      }
    }
    if (p12exp->arena != NULL)
      PORT_ArenaRelease(p12exp->arena, mark);

    return NULL;
}

/* The outermost ASN.1 encoder calls this function for output.
** This function calls back to the library caller's output routine,
** which typically writes to a PKCS12 file.
 */
static void
sec_P12A1OutputCB_Outer(void *arg, const char *buf, unsigned long len,
                   int depth, SEC_ASN1EncodingPart data_kind)
{
    struct sec_pkcs12_encoder_output *output;

    output = (struct sec_pkcs12_encoder_output*)arg;
    (* output->outputfn)(output->outputarg, buf, len);
}

/* The "middle" and "inner" ASN.1 encoders call this function to output. 
** This function does HMACing, if appropriate, and then buffers the data.
** The buffered data is eventually passed down to the underlying PKCS7 encoder.
 */
static void
sec_P12A1OutputCB_HmacP7Update(void *arg, const char *buf,
                         unsigned long        len, 
                         int                  depth,
                         SEC_ASN1EncodingPart data_kind)
{
    sec_pkcs12OutputBuffer *  bufcx = (sec_pkcs12OutputBuffer *)arg;

    if(!buf || !len) 
      return;

    if (bufcx->hmacCx) {
      PK11_DigestOp(bufcx->hmacCx, (unsigned char *)buf, len);
    }

    /* buffer */
    if (bufcx->numBytes > 0) {
      int toCopy;
      if (len + bufcx->numBytes <= bufcx->bufBytes) {
          memcpy(bufcx->buf + bufcx->numBytes, buf, len);
          bufcx->numBytes += len;
          if (bufcx->numBytes < bufcx->bufBytes) 
            return;
          SEC_PKCS7EncoderUpdate(bufcx->p7eCx, bufcx->buf, bufcx->bufBytes);
          bufcx->numBytes = 0;
          return;
      } 
      toCopy = bufcx->bufBytes - bufcx->numBytes;
      memcpy(bufcx->buf + bufcx->numBytes, buf, toCopy);
      SEC_PKCS7EncoderUpdate(bufcx->p7eCx, bufcx->buf, bufcx->bufBytes);
      bufcx->numBytes = 0;
      len -= toCopy;
      buf += toCopy;
    } 
    /* buffer is presently empty */
    if (len >= bufcx->bufBytes) {
      /* Just pass it through */
      SEC_PKCS7EncoderUpdate(bufcx->p7eCx, buf, len);
    } else {
      /* copy it all into the buffer, and return */
      memcpy(bufcx->buf, buf, len);
      bufcx->numBytes = len;
    }
}

void
sec_FlushPkcs12OutputBuffer( sec_pkcs12OutputBuffer *  bufcx)
{
    if (bufcx->numBytes > 0) {
      SEC_PKCS7EncoderUpdate(bufcx->p7eCx, bufcx->buf, bufcx->numBytes);
      bufcx->numBytes = 0;
    }
}

/* Feeds the output of a PKCS7 encoder into the next outward ASN.1 encoder.
** This function is used by both the inner and middle PCS7 encoders.
*/
static void
sec_P12P7OutputCB_CallA1Update(void *arg, const char *buf, unsigned long len)
{
    SEC_ASN1EncoderContext *cx = (SEC_ASN1EncoderContext*)arg;

    if (!buf || !len) 
      return;

    SEC_ASN1EncoderUpdate(cx, buf, len);
}


/* this function encodes content infos which are part of the
 * sequence of content infos labeled AuthenticatedSafes 
 */
static SECStatus 
sec_pkcs12_encoder_asafe_process(sec_PKCS12EncoderContext *p12ecx)
{
    SEC_PKCS7EncoderContext *innerP7ecx;
    SEC_PKCS7ContentInfo    *cinfo;
    PK11SymKey              *bulkKey      = NULL;
    SEC_ASN1EncoderContext  *innerA1ecx   = NULL;
    SECStatus                rv           = SECSuccess;

    if(p12ecx->currentSafe < p12ecx->p12exp->authSafe.safeCount) {
      SEC_PKCS12SafeInfo *safeInfo;
      SECOidTag cinfoType;

      safeInfo = p12ecx->p12exp->safeInfos[p12ecx->currentSafe];

      /* skip empty safes */
      if(safeInfo->itemCount == 0) {
          return SECSuccess;
      }

      cinfo = safeInfo->cinfo;
      cinfoType = SEC_PKCS7ContentType(cinfo);

      /* determine the safe type and set the appropriate argument */
      switch(cinfoType) {
          case SEC_OID_PKCS7_DATA:
          case SEC_OID_PKCS7_ENVELOPED_DATA:
            break;
          case SEC_OID_PKCS7_ENCRYPTED_DATA:
            bulkKey = safeInfo->encryptionKey;
            PK11_SetSymKeyUserData(bulkKey, &safeInfo->pwitem, NULL);
            break;
          default:
            return SECFailure;

      }

      /* start the PKCS7 encoder */
      innerP7ecx = SEC_PKCS7EncoderStart(cinfo, 
                          sec_P12P7OutputCB_CallA1Update,
                          p12ecx->middleA1ecx, bulkKey);
      if(!innerP7ecx) {
          goto loser;
      }

      /* encode safe contents */
      p12ecx->innerBuf.p7eCx    = innerP7ecx;
      p12ecx->innerBuf.hmacCx   = NULL;
      p12ecx->innerBuf.numBytes = 0;
      p12ecx->innerBuf.bufBytes = sizeof p12ecx->innerBuf.buf;

      innerA1ecx = SEC_ASN1EncoderStart(safeInfo->safe, 
                                 sec_PKCS12SafeContentsTemplate,
                           sec_P12A1OutputCB_HmacP7Update, 
                           &p12ecx->innerBuf);
      if(!innerA1ecx) {
          goto loser;
      }   
      rv = SEC_ASN1EncoderUpdate(innerA1ecx, NULL, 0);
      SEC_ASN1EncoderFinish(innerA1ecx);
      sec_FlushPkcs12OutputBuffer( &p12ecx->innerBuf);
      innerA1ecx = NULL;
      if(rv != SECSuccess) {
          goto loser;
      }


      /* finish up safe content info */
      rv = SEC_PKCS7EncoderFinish(innerP7ecx, p12ecx->p12exp->pwfn, 
                            p12ecx->p12exp->pwfnarg);
    }
    memset(&p12ecx->innerBuf, 0, sizeof p12ecx->innerBuf);
    return SECSuccess;

loser:
    if(innerP7ecx) {
      SEC_PKCS7EncoderFinish(innerP7ecx, p12ecx->p12exp->pwfn, 
                         p12ecx->p12exp->pwfnarg);
    }

    if(innerA1ecx) {
      SEC_ASN1EncoderFinish(innerA1ecx);
    }
    memset(&p12ecx->innerBuf, 0, sizeof p12ecx->innerBuf);
    return SECFailure;
}

/* finish the HMAC and encode the macData so that it can be
 * encoded.
 */
static SECStatus
sec_Pkcs12FinishMac(sec_PKCS12EncoderContext *p12ecx)
{
    SECItem hmac = { siBuffer, NULL, 0 };
    SECStatus rv;
    SGNDigestInfo *di = NULL;
    void *dummy;

    if(!p12ecx) {
      return SECFailure;
    }

    /* make sure we are using password integrity mode */
    if(!p12ecx->p12exp->integrityEnabled) {
      return SECSuccess;
    }

    if(!p12ecx->p12exp->pwdIntegrity) {
      return SECSuccess;
    }

    /* finish the hmac */
    hmac.data = (unsigned char *)PORT_ZAlloc(SHA1_LENGTH);
    if(!hmac.data) {
      PORT_SetError(SEC_ERROR_NO_MEMORY);
      return SECFailure;
    }

    rv = PK11_DigestFinal(p12ecx->hmacCx, hmac.data, &hmac.len, SHA1_LENGTH);

    if(rv != SECSuccess) {
      PORT_SetError(SEC_ERROR_NO_MEMORY);
      goto loser;
    }

    /* create the digest info */
    di = SGN_CreateDigestInfo(p12ecx->p12exp->integrityInfo.pwdInfo.algorithm,
                        hmac.data, hmac.len);
    if(!di) {
      PORT_SetError(SEC_ERROR_NO_MEMORY);
      rv = SECFailure;
      goto loser;
    }

    rv = SGN_CopyDigestInfo(p12ecx->arena, &p12ecx->mac.safeMac, di);
    if(rv != SECSuccess) {
      PORT_SetError(SEC_ERROR_NO_MEMORY);
      goto loser;
    }

    /* encode the mac data */
    dummy = SEC_ASN1EncodeItem(p12ecx->arena, &p12ecx->pfx.encodedMacData, 
                      &p12ecx->mac, sec_PKCS12MacDataTemplate);
    if(!dummy) {
      PORT_SetError(SEC_ERROR_NO_MEMORY);
      rv = SECFailure;
    }

loser:
    if(di) {
      SGN_DestroyDigestInfo(di);
    }
    if(hmac.data) {
      SECITEM_ZfreeItem(&hmac, PR_FALSE);
    }
    PK11_DestroyContext(p12ecx->hmacCx, PR_TRUE);
    p12ecx->hmacCx = NULL;

    return rv;
}

/* pfx notify function for ASN1 encoder.  
 * We want to stop encoding once we reach the authenticated safe.  
 * At that point, the encoder will be updated via streaming
 * as the authenticated safe is  encoded. 
 */
static void
sec_pkcs12_encoder_pfx_notify(void *arg, PRBool before, void *dest, int real_depth)
{
    sec_PKCS12EncoderContext *p12ecx;

    if(!before) {
      return;
    }

    /* look for authenticated safe */
    p12ecx = (sec_PKCS12EncoderContext*)arg;
    if(dest != &p12ecx->pfx.encodedAuthSafe) {
      return;
    }

    SEC_ASN1EncoderSetTakeFromBuf(p12ecx->outerA1ecx);
    SEC_ASN1EncoderSetStreaming(p12ecx->outerA1ecx);
    SEC_ASN1EncoderClearNotifyProc(p12ecx->outerA1ecx);
}

/* SEC_PKCS12Encode
 *    Encodes the PFX item and returns it to the output function, via
 *    callback.  the output function must be capable of multiple updates.
 *    
 *    p12exp - the export context 
 *    output - the output function callback, will be called more than once,
 *           must be able to accept streaming data.
 *    outputarg - argument for the output callback.
 */
SECStatus
SEC_PKCS12Encode(SEC_PKCS12ExportContext *p12exp, 
             SEC_PKCS12EncoderOutputCallback output, void *outputarg)
{
    sec_PKCS12EncoderContext *p12enc;
    struct sec_pkcs12_encoder_output outInfo;
    SECStatus rv;

    if(!p12exp || !output) {
      return SECFailure;
    }

    /* get the encoder context */
    p12enc = sec_pkcs12_encoder_start_context(p12exp);
    if(!p12enc) {
      return SECFailure;
    }

    outInfo.outputfn = output;
    outInfo.outputarg = outputarg;

    /* set up PFX encoder, the "outer" encoder.  Set it for streaming */
    p12enc->outerA1ecx = SEC_ASN1EncoderStart(&p12enc->pfx, 
                                       sec_PKCS12PFXItemTemplate,
                               sec_P12A1OutputCB_Outer, 
                               &outInfo);
    if(!p12enc->outerA1ecx) {
      PORT_SetError(SEC_ERROR_NO_MEMORY);
      rv = SECFailure;
      goto loser;
    }
    SEC_ASN1EncoderSetStreaming(p12enc->outerA1ecx);
    SEC_ASN1EncoderSetNotifyProc(p12enc->outerA1ecx, 
                                 sec_pkcs12_encoder_pfx_notify, p12enc);
    rv = SEC_ASN1EncoderUpdate(p12enc->outerA1ecx, NULL, 0);
    if(rv != SECSuccess) {
      rv = SECFailure;
      goto loser;
    }

    /* set up asafe cinfo - the output of the encoder feeds the PFX encoder */
    p12enc->middleP7ecx = SEC_PKCS7EncoderStart(p12enc->aSafeCinfo, 
                               sec_P12P7OutputCB_CallA1Update,
                               p12enc->outerA1ecx, NULL);
    if(!p12enc->middleP7ecx) {
      rv = SECFailure;
      goto loser;
    }

    /* encode asafe */
    p12enc->middleBuf.p7eCx    = p12enc->middleP7ecx;
    p12enc->middleBuf.hmacCx   = NULL;
    p12enc->middleBuf.numBytes = 0;
    p12enc->middleBuf.bufBytes = sizeof p12enc->middleBuf.buf;

    /* Setup the "inner ASN.1 encoder for Authenticated Safes.  */
    if(p12enc->p12exp->integrityEnabled && 
       p12enc->p12exp->pwdIntegrity) {
      p12enc->middleBuf.hmacCx = p12enc->hmacCx;
    }
    p12enc->middleA1ecx = SEC_ASN1EncoderStart(&p12enc->p12exp->authSafe,
                      sec_PKCS12AuthenticatedSafeTemplate,
                      sec_P12A1OutputCB_HmacP7Update,
                      &p12enc->middleBuf);
    if(!p12enc->middleA1ecx) {
      rv = SECFailure;
      goto loser;
    }
    SEC_ASN1EncoderSetStreaming(p12enc->middleA1ecx);
    SEC_ASN1EncoderSetTakeFromBuf(p12enc->middleA1ecx); 
      
    /* encode each of the safes */               
    while(p12enc->currentSafe != p12enc->p12exp->safeInfoCount) {
      sec_pkcs12_encoder_asafe_process(p12enc);
      p12enc->currentSafe++;
    }
    SEC_ASN1EncoderClearTakeFromBuf(p12enc->middleA1ecx);
    SEC_ASN1EncoderClearStreaming(p12enc->middleA1ecx);
    SEC_ASN1EncoderUpdate(p12enc->middleA1ecx, NULL, 0);
    SEC_ASN1EncoderFinish(p12enc->middleA1ecx);

    sec_FlushPkcs12OutputBuffer( &p12enc->middleBuf);

    /* finish the encoding of the authenticated safes */
    rv = SEC_PKCS7EncoderFinish(p12enc->middleP7ecx, p12exp->pwfn, 
                        p12exp->pwfnarg);
    if(rv != SECSuccess) {
      goto loser;
    }

    SEC_ASN1EncoderClearTakeFromBuf(p12enc->outerA1ecx);
    SEC_ASN1EncoderClearStreaming(p12enc->outerA1ecx);

    /* update the mac, if necessary */
    rv = sec_Pkcs12FinishMac(p12enc);
    if(rv != SECSuccess) {
      goto loser;
    }
   
    /* finish encoding the pfx */ 
    rv = SEC_ASN1EncoderUpdate(p12enc->outerA1ecx, NULL, 0);

    SEC_ASN1EncoderFinish(p12enc->outerA1ecx);

loser:
    return rv;
}

void
SEC_PKCS12DestroyExportContext(SEC_PKCS12ExportContext *p12ecx)
{
    int i = 0;

    if(!p12ecx) {
      return;
    }

    if(p12ecx->safeInfos) {
      i = 0;
      while(p12ecx->safeInfos[i] != NULL) {
          if(p12ecx->safeInfos[i]->encryptionKey) {
            PK11_FreeSymKey(p12ecx->safeInfos[i]->encryptionKey);
          }
          if(p12ecx->safeInfos[i]->cinfo) {
            SEC_PKCS7DestroyContentInfo(p12ecx->safeInfos[i]->cinfo);
          }
          i++;
      }
    }

    PK11_FreeSlot(p12ecx->slot);

    PORT_FreeArena(p12ecx->arena, PR_TRUE);
}


/*********************************
 * All-in-one routines for exporting certificates 
 *********************************/
struct inPlaceEncodeInfo {
    PRBool error;
    SECItem outItem;
};

static void 
sec_pkcs12_in_place_encoder_output(void *arg, const char *buf, unsigned long len)
{
    struct inPlaceEncodeInfo *outInfo = (struct inPlaceEncodeInfo*)arg;

    if(!outInfo || !len || outInfo->error) {
      return;
    }

    if(!outInfo->outItem.data) {
      outInfo->outItem.data = (unsigned char*)PORT_ZAlloc(len);
      outInfo->outItem.len = 0;
    } else {
      if(!PORT_Realloc(&(outInfo->outItem.data), (outInfo->outItem.len + len))) {
          SECITEM_ZfreeItem(&(outInfo->outItem), PR_FALSE);
          outInfo->outItem.data = NULL;
          PORT_SetError(SEC_ERROR_NO_MEMORY);
          outInfo->error = PR_TRUE;
          return;
      }
    }

    PORT_Memcpy(&(outInfo->outItem.data[outInfo->outItem.len]), buf, len);
    outInfo->outItem.len += len;

    return;
}

/*
 * SEC_PKCS12ExportCertifcateAndKeyUsingPassword
 *    Exports a certificate/key pair using password-based encryption and
 *    authentication.
 *
 * pwfn, pwfnarg - password function and argument for the key database
 * cert - the certificate to export
 * certDb - certificate database
 * pwitem - the password to use
 * shroudKey - encrypt the key externally, 
 * keyShroudAlg - encryption algorithm for key
 * encryptionAlg - the algorithm with which data is encrypted
 * integrityAlg - the algorithm for integrity
 */
SECItem *
SEC_PKCS12ExportCertificateAndKeyUsingPassword(
                        SECKEYGetPasswordKey pwfn, void *pwfnarg,
                        CERTCertificate *cert, PK11SlotInfo *slot,
                        CERTCertDBHandle *certDb, SECItem *pwitem,
                        PRBool shroudKey, SECOidTag shroudAlg,
                        PRBool encryptCert, SECOidTag certEncAlg,
                        SECOidTag integrityAlg, void *wincx)
{
    struct inPlaceEncodeInfo outInfo;
    SEC_PKCS12ExportContext *p12ecx = NULL;
    SEC_PKCS12SafeInfo *keySafe, *certSafe;
    SECItem *returnItem = NULL;

    if(!cert || !pwitem || !slot) {
      return NULL;
    }

    outInfo.error = PR_FALSE;
    outInfo.outItem.data = NULL;
    outInfo.outItem.len = 0;

    p12ecx = SEC_PKCS12CreateExportContext(pwfn, pwfnarg, slot, wincx);
    if(!p12ecx) {
      return NULL;
    }

    /* set up cert safe */
    if(encryptCert) {
      certSafe = SEC_PKCS12CreatePasswordPrivSafe(p12ecx, pwitem, certEncAlg);
    } else {
      certSafe = SEC_PKCS12CreateUnencryptedSafe(p12ecx);
    }
    if(!certSafe) {
      goto loser;
    }

    /* set up key safe */
    if(shroudKey) {
      keySafe = SEC_PKCS12CreateUnencryptedSafe(p12ecx);
    } else {
      keySafe = certSafe;
    }
    if(!keySafe) {
      goto loser;
    }

    /* add integrity mode */
    if(SEC_PKCS12AddPasswordIntegrity(p12ecx, pwitem, integrityAlg) 
            != SECSuccess) {
      goto loser;
    }

    /* add cert and key pair */
    if(SEC_PKCS12AddCertAndKey(p12ecx, certSafe, NULL, cert, certDb, 
                         keySafe, NULL, shroudKey, pwitem, shroudAlg)
            != SECSuccess) {
      goto loser;
    }

    /* encode the puppy */
    if(SEC_PKCS12Encode(p12ecx, sec_pkcs12_in_place_encoder_output, &outInfo)
            != SECSuccess) {
      goto loser;
    }
    if(outInfo.error) {
      goto loser;
    }

    SEC_PKCS12DestroyExportContext(p12ecx);
      
    returnItem = SECITEM_DupItem(&outInfo.outItem);
    SECITEM_ZfreeItem(&outInfo.outItem, PR_FALSE);

    return returnItem;

loser:
    if(outInfo.outItem.data) {
      SECITEM_ZfreeItem(&(outInfo.outItem), PR_TRUE);
    }

    if(p12ecx) {
      SEC_PKCS12DestroyExportContext(p12ecx);
    }

    return NULL;
}



Generated by  Doxygen 1.6.0   Back to index