Quellcodebibliothek Statistik Leitseite products/sources/formale Sprachen/C/Firefox/security/nss/lib/pkcs12/   (Browser von der Mozilla Stiftung Version 136.0.1©)  Datei vom 10.2.2025 mit Größe 68 kB image not shown  

SSL p12e.c   Interaktion und
PortierbarkeitC

 
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */


#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 "secpkcs5.h"
#include "secpkcs7.h"
#include "secasn1.h"
#include "secerr.h"
#include "sechash.h"
#include "pk11func.h"
#include "p12plcy.h"
#include "p12local.h"
#include "prcpucfg.h"

extern const int NSS_PBE_DEFAULT_ITERATION_COUNT; /* defined in p7create.c */

/*
** 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 {
    PLArenaPool *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 {
    PLArenaPool *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 {
    PLArenaPool *arena;
    SEC_PKCS12ExportContext *p12exp;

    /* 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)
{
    PLArenaPool *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);
        goto loser;
    }

    safeInfo->itemCount = 0;

    /* create the encrypted safe */
    if (!SEC_PKCS5IsAlgorithmPBEAlgTag(privAlg)) {
        SECOidTag prfAlg = SEC_OID_UNKNOWN;
        /* if we have password integrity set, use that to set the integrity
         * hash algorithm to set our password PRF. If we haven't set it, just
         * let the low level code pick it */

        if (p12ctxt->integrityEnabled && p12ctxt->pwdIntegrity) {
            SECOidTag integrityAlg = p12ctxt->integrityInfo.pwdInfo.algorithm;
            prfAlg = integrityAlg;
            /* verify that integrityAlg is an HMAC */
            if (HASH_GetHashOidTagByHMACOidTag(integrityAlg) == SEC_OID_UNKNOWN) {
                /* it's not, find the hmac */
                prfAlg = HASH_GetHMACOidTagByHashOidTag(integrityAlg);
                /* if prfAlg is SEC_OID_UNKNOWN at this point we'll
                 * default to SEC_OID_HMAC_SHA1 in the low level pbe code. */

            }
        }
        if (!SEC_PKCS12CipherAllowed(privAlg, prfAlg)) {
            PORT_SetError(SEC_ERROR_BAD_EXPORT_ALGORITHM);
            goto loser;
        }
        safeInfo->cinfo = SEC_PKCS7CreateEncryptedDataWithPBEV2(SEC_OID_PKCS5_PBES2,
                                                                privAlg,
                                                                prfAlg,
                                                                0,
                                                                p12ctxt->pwfn,
                                                                p12ctxt->pwfnarg);
    } else {
        if (!SEC_PKCS12CipherAllowed(privAlg, SEC_OID_UNKNOWN)) {
            PORT_SetError(SEC_ERROR_BAD_EXPORT_ALGORITHM);
            goto loser;
        }
        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;

    if (!sec_pkcs12_encode_password(NULL, &uniPwitem, privAlg, pwitem)) {
        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 && 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(PLArenaPool *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(PLArenaPool *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;
    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;
            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(PLArenaPool *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(PLArenaPool *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;

    PORT_Assert(p12ctxt->arena == safeBag->arena);
    if (!safeBag || !p12ctxt || p12ctxt->arena != safeBag->arena) {
        PORT_SetError(SEC_ERROR_INVALID_ARGS);
        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_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;
        SECOidTag prfAlg = SEC_OID_UNKNOWN;

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

        /* if we have password integrity set, use that to set the integrity
         * hash algorithm to set our password PRF. If we haven't set it, just
         * let the low level code pick it */

        if (p12ctxt->integrityEnabled && p12ctxt->pwdIntegrity) {
            SECOidTag integrityAlg = p12ctxt->integrityInfo.pwdInfo.algorithm;
            prfAlg = integrityAlg;
            /* verify that integrityAlg is an HMAC */
            if (HASH_GetHashOidTagByHMACOidTag(integrityAlg) == SEC_OID_UNKNOWN) {
                /* it's not, find the hmac */
                prfAlg = HASH_GetHMACOidTagByHashOidTag(integrityAlg);
                /* if prfAlg is SEC_OID_UNKNOWN at this point we'll
                 * default to SEC_OID_HMAC_SHA1 in the low level pbe code. */

            }
        }

        if (!SEC_PKCS12CipherAllowed(algorithm, prfAlg)) {
            PORT_SetError(SEC_ERROR_BAD_EXPORT_ALGORITHM);
            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);
        }

        /* passing algorithm as the pbe will force the PBE code to
         * automatically handle the selection between using the algorithm
         * as a the pbe algorithm, or using the algorithm as a cipher
         * and building a pkcs5 pbe */

        epki = PK11_ExportEncryptedPrivateKeyInfoV2(slot, algorithm,
                                                    SEC_OID_UNKNOWN, prfAlg,
                                                    &uniPwitem, cert,
                                                    NSS_PBE_DEFAULT_ITERATION_COUNT,
                                                    p12ctxt->wincx);
        PK11_FreeSlot(slot);
        if (!epki) {
            PORT_SetError(SEC_ERROR_PKCS12_UNABLE_TO_EXPORT_KEY);
            goto loser;
        }

        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;
        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_PKCS12AddCertOrChainAndKey
 *      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
 *      includeCertChain - also add certs from chain to bag.
 */

SECStatus
SEC_PKCS12AddCertOrChainAndKey(SEC_PKCS12ExportContext *p12ctxt,
                               void *certSafe, void *certNestedDest,
                               CERTCertificate *cert, CERTCertDBHandle *certDb,
                               void *keySafe, void *keyNestedDest,
                               PRBool shroudKey, SECItem *pwitem,
                               SECOidTag algorithm, PRBool includeCertChain)
{
    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, includeCertChain);
    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;
}

/* like SEC_PKCS12AddCertOrChainAndKey, but always adds cert chain */
SECStatus
SEC_PKCS12AddCertAndKey(SEC_PKCS12ExportContext *p12ctxt,
                        void *certSafe, void *certNestedDest,
                        CERTCertificate *cert, CERTCertDBHandle *certDb,
                        void *keySafe, void *keyNestedDest,
                        PRBool shroudKey, SECItem *pwItem, SECOidTag algorithm)
{
    return SEC_PKCS12AddCertOrChainAndKey(p12ctxt, certSafe, certNestedDest,
                                          cert, certDb, keySafe, keyNestedDest, shroudKey, pwItem,
                                          algorithm, PR_TRUE);
}

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


/* Clean up the resources allocated by a sec_PKCS12EncoderContext. */
static void
sec_pkcs12_encoder_destroy_context(sec_PKCS12EncoderContext *p12enc)
{
    if (p12enc) {
        if (p12enc->outerA1ecx) {
            SEC_ASN1EncoderFinish(p12enc->outerA1ecx);
            p12enc->outerA1ecx = NULL;
        }
        if (p12enc->aSafeCinfo) {
            SEC_PKCS7DestroyContentInfo(p12enc->aSafeCinfo);
            p12enc->aSafeCinfo = NULL;
        }
        if (p12enc->middleP7ecx) {
            SEC_PKCS7EncoderFinish(p12enc->middleP7ecx, p12enc->p12exp->pwfn,
                                   p12enc->p12exp->pwfnarg);
            p12enc->middleP7ecx = NULL;
        }
        if (p12enc->middleA1ecx) {
            SEC_ASN1EncoderFinish(p12enc->middleA1ecx);
            p12enc->middleA1ecx = NULL;
        }
        if (p12enc->hmacCx) {
            PK11_DestroyContext(p12enc->hmacCx, PR_TRUE);
            p12enc->hmacCx = NULL;
        }
    }
}

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

static 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;
    SECItem *salt = NULL;
    SECItem pwd = { siBuffer, NULL, 0 };

    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) {
        /* 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;
        }
        PORT_CheckSuccess(SEC_PKCS7AddSigningTime(p12enc->aSafeCinfo));
    } else {
        p12enc->aSafeCinfo = SEC_PKCS7CreateData();

        /* init password pased integrity mode */
        if (p12exp->integrityEnabled) {
            PK11SymKey *symKey;
            CK_MECHANISM_TYPE hmacMechType;
            SECOidTag hmacAlgTag;
            SECOidTag hashAlgTag;

            salt = sec_pkcs12_generate_salt();

            /* 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;
            }
            if (!SEC_ASN1EncodeInteger(p12exp->arena, &(p12enc->mac.iter),
                                       NSS_PBE_DEFAULT_ITERATION_COUNT)) {
                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;
            }

            /* create the digest info */
            hmacAlgTag = p12exp->integrityInfo.pwdInfo.algorithm;
            hashAlgTag = HASH_GetHashOidTagByHMACOidTag(hmacAlgTag);
            if (hashAlgTag != SEC_OID_UNKNOWN) {
                /* if the application asks for hmac explicitly, then use
                 * pkcs5v2 mac1 encoding */

                SECAlgorithmID *algID;
                int keyLength;

                keyLength = HASH_ResultLenByOidTag(hashAlgTag);
                if (keyLength == 0) {
                    PORT_SetError(SEC_ERROR_INVALID_ALGORITHM);
                    return NULL;
                }
                /* create PB_MAC1 params */
                algID = PK11_CreatePBEV2AlgorithmID(SEC_OID_PKCS5_PBMAC1,
                                                    hmacAlgTag,
                                                    hmacAlgTag, keyLength,
                                                    NSS_PBE_DEFAULT_ITERATION_COUNT,
                                                    &p12enc->mac.macSalt);
                if (algID == NULL) {
                    goto loser;
                }
                rv = SECOID_CopyAlgorithmID(p12enc->arena,
                                            &p12enc->mac.safeMac.digestAlgorithm,
                                            algID);
                SECOID_DestroyAlgorithmID(algID, PR_TRUE);
                if (rv != SECSuccess) {
                    PORT_SetError(SEC_ERROR_NO_MEMORY);
                    goto loser;
                }
            } else if (HASH_GetHashTypeByOidTag(hmacAlgTag) != HASH_AlgNULL) {
                /* encode the algid now for sec_pkcs12_integrity_key to use */
                /* this must be a valid hash function, SECOID_SetAlgorithmID
                 * knows to encode the hash algid with an explicit
                 * null parameter */

                rv = SECOID_SetAlgorithmID(p12enc->arena,
                                           &p12enc->mac.safeMac.digestAlgorithm,
                                           hmacAlgTag, NULL);
                if (rv != SECSuccess) {
                    goto loser;
                }
            } else {
                PORT_SetError(SEC_ERROR_INVALID_ALGORITHM);
                goto loser;
            }

            /* generate HMAC key */
            SECITEM_ZfreeItem(salt, PR_TRUE);
            salt = NULL;
            symKey = sec_pkcs12_integrity_key(p12exp->slot, &p12enc->mac,
                                              &pwd, &hmacMechType, PR_FALSE,
                                              p12exp->wincx);
            SECITEM_ZfreeItem(&pwd, PR_FALSE);

            if (!symKey) {
                goto loser;
            }

            /* initialize HMAC */
            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:
    sec_pkcs12_encoder_destroy_context(p12enc);
    if (p12exp->arena != NULL) {
        PORT_ArenaRelease(p12exp->arena, mark);
    }
    if (salt) {
        SECITEM_ZfreeItem(salt, PR_TRUE);
    }
    if (pwd.data) {
        SECITEM_ZfreeItem(&pwd, PR_FALSE);
    }

    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);
        if (rv != SECSuccess) {
            goto loser;
        }
    }
    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)
{
    unsigned char hmacData[HASH_LENGTH_MAX];
    unsigned int hmacLen;
    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 */

    rv = PK11_DigestFinal(p12ecx->hmacCx, hmacData, &hmacLen, HASH_LENGTH_MAX);

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

    /* finish the digest info, algorithm ID is already set */
    rv = SECITEM_MakeItem(p12ecx->arena, &p12ecx->mac.safeMac.digest,
                          hmacData, hmacLen);
    if (rv != SECSuccess) {
        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);
    }
    PK11_DestroyContext(p12ecx->hmacCx, PR_TRUE);
    p12ecx->hmacCx = NULL;
    PORT_Memset(hmacData, 0, hmacLen);

    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
--> --------------------

--> maximum size reached

--> --------------------

Messung V0.5
C=94 H=72 G=83

¤ Diese beiden folgenden Angebotsgruppen bietet das Unternehmen0.62Angebot  Wie Sie bei der Firma Beratungs- und Dienstleistungen beauftragen können  ¤

*Eine klare Vorstellung vom Zielzustand






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.