tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

p12e.c (70123B)


      1 /* This Source Code Form is subject to the terms of the Mozilla Public
      2 * License, v. 2.0. If a copy of the MPL was not distributed with this
      3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      4 
      5 #include "p12t.h"
      6 #include "p12.h"
      7 #include "plarena.h"
      8 #include "secitem.h"
      9 #include "secoid.h"
     10 #include "seccomon.h"
     11 #include "secport.h"
     12 #include "cert.h"
     13 #include "secpkcs5.h"
     14 #include "secpkcs7.h"
     15 #include "secasn1.h"
     16 #include "secerr.h"
     17 #include "sechash.h"
     18 #include "pk11func.h"
     19 #include "p12plcy.h"
     20 #include "p12local.h"
     21 #include "prcpucfg.h"
     22 
     23 extern const int NSS_PBE_DEFAULT_ITERATION_COUNT; /* defined in p7create.c */
     24 
     25 /*
     26 ** This PKCS12 file encoder uses numerous nested ASN.1 and PKCS7 encoder
     27 ** contexts.  It can be difficult to keep straight.  Here's a picture:
     28 **
     29 **  "outer"  ASN.1 encoder.  The output goes to the library caller's CB.
     30 **  "middle" PKCS7 encoder.  Feeds    the "outer" ASN.1 encoder.
     31 **  "middle" ASN1  encoder.  Encodes  the encrypted aSafes.
     32 **                           Feeds    the "middle" P7 encoder above.
     33 **  "inner"  PKCS7 encoder.  Encrypts the "authenticated Safes" (aSafes)
     34 **                           Feeds    the "middle" ASN.1 encoder above.
     35 **  "inner"  ASN.1 encoder.  Encodes  the unencrypted aSafes.
     36 **                           Feeds    the "inner" P7 enocder above.
     37 **
     38 ** Buffering has been added at each point where the output of an ASN.1
     39 ** encoder feeds the input of a PKCS7 encoder.
     40 */
     41 
     42 /*********************************
     43 * Output buffer object, used to buffer output from ASN.1 encoder
     44 * before passing data on down to the next PKCS7 encoder.
     45 *********************************/
     46 
     47 #define PK12_OUTPUT_BUFFER_SIZE 8192
     48 
     49 struct sec_pkcs12OutputBufferStr {
     50    SEC_PKCS7EncoderContext *p7eCx;
     51    PK11Context *hmacCx;
     52    unsigned int numBytes;
     53    unsigned int bufBytes;
     54    char buf[PK12_OUTPUT_BUFFER_SIZE];
     55 };
     56 typedef struct sec_pkcs12OutputBufferStr sec_pkcs12OutputBuffer;
     57 
     58 /*********************************
     59 * Structures used in exporting the PKCS 12 blob
     60 *********************************/
     61 
     62 /* A SafeInfo is used for each ContentInfo which makes up the
     63 * sequence of safes in the AuthenticatedSafe portion of the
     64 * PFX structure.
     65 */
     66 struct SEC_PKCS12SafeInfoStr {
     67    PLArenaPool *arena;
     68 
     69    /* information for setting up password encryption */
     70    SECItem pwitem;
     71    SECOidTag algorithm;
     72    PK11SymKey *encryptionKey;
     73 
     74    /* how many items have been stored in this safe,
     75     * we will skip any safe which does not contain any
     76     * items
     77     */
     78    unsigned int itemCount;
     79 
     80    /* the content info for the safe */
     81    SEC_PKCS7ContentInfo *cinfo;
     82 
     83    sec_PKCS12SafeContents *safe;
     84 };
     85 
     86 /* An opaque structure which contains information needed for exporting
     87 * certificates and keys through PKCS 12.
     88 */
     89 struct SEC_PKCS12ExportContextStr {
     90    PLArenaPool *arena;
     91    PK11SlotInfo *slot;
     92    void *wincx;
     93 
     94    /* integrity information */
     95    PRBool integrityEnabled;
     96    PRBool pwdIntegrity;
     97    union {
     98        struct sec_PKCS12PasswordModeInfo pwdInfo;
     99        struct sec_PKCS12PublicKeyModeInfo pubkeyInfo;
    100    } integrityInfo;
    101 
    102    /* helper functions */
    103    /* retrieve the password call back */
    104    SECKEYGetPasswordKey pwfn;
    105    void *pwfnarg;
    106 
    107    /* safe contents bags */
    108    SEC_PKCS12SafeInfo **safeInfos;
    109    unsigned int safeInfoCount;
    110 
    111    /* the sequence of safes */
    112    sec_PKCS12AuthenticatedSafe authSafe;
    113 
    114    /* information needing deletion */
    115    CERTCertificate **certList;
    116 };
    117 
    118 /* structures for passing information to encoder callbacks when processing
    119 * data through the ASN1 engine.
    120 */
    121 struct sec_pkcs12_encoder_output {
    122    SEC_PKCS12EncoderOutputCallback outputfn;
    123    void *outputarg;
    124 };
    125 
    126 struct sec_pkcs12_hmac_and_output_info {
    127    void *arg;
    128    struct sec_pkcs12_encoder_output output;
    129 };
    130 
    131 /* An encoder context which is used for the actual encoding
    132 * portion of PKCS 12.
    133 */
    134 typedef struct sec_PKCS12EncoderContextStr {
    135    PLArenaPool *arena;
    136    SEC_PKCS12ExportContext *p12exp;
    137 
    138    /* encoder information - this is set up based on whether
    139     * password based or public key pased privacy is being used
    140     */
    141    SEC_ASN1EncoderContext *outerA1ecx;
    142    union {
    143        struct sec_pkcs12_hmac_and_output_info hmacAndOutputInfo;
    144        struct sec_pkcs12_encoder_output encOutput;
    145    } output;
    146 
    147    /* structures for encoding of PFX and MAC */
    148    sec_PKCS12PFXItem pfx;
    149    sec_PKCS12MacData mac;
    150 
    151    /* authenticated safe encoding tracking information */
    152    SEC_PKCS7ContentInfo *aSafeCinfo;
    153    SEC_PKCS7EncoderContext *middleP7ecx;
    154    SEC_ASN1EncoderContext *middleA1ecx;
    155    unsigned int currentSafe;
    156 
    157    /* hmac context */
    158    PK11Context *hmacCx;
    159 
    160    /* output buffers */
    161    sec_pkcs12OutputBuffer middleBuf;
    162    sec_pkcs12OutputBuffer innerBuf;
    163 
    164 } sec_PKCS12EncoderContext;
    165 
    166 /*********************************
    167 * Export setup routines
    168 *********************************/
    169 
    170 /* SEC_PKCS12CreateExportContext
    171 *   Creates an export context and sets the unicode and password retrieval
    172 *   callbacks.  This is the first call which must be made when exporting
    173 *   a PKCS 12 blob.
    174 *
    175 * pwfn, pwfnarg - password retrieval callback and argument.  these are
    176 *                 required for password-authentication mode.
    177 */
    178 SEC_PKCS12ExportContext *
    179 SEC_PKCS12CreateExportContext(SECKEYGetPasswordKey pwfn, void *pwfnarg,
    180                              PK11SlotInfo *slot, void *wincx)
    181 {
    182    PLArenaPool *arena = NULL;
    183    SEC_PKCS12ExportContext *p12ctxt = NULL;
    184 
    185    /* allocate the arena and create the context */
    186    arena = PORT_NewArena(4096);
    187    if (!arena) {
    188        PORT_SetError(SEC_ERROR_NO_MEMORY);
    189        return NULL;
    190    }
    191 
    192    p12ctxt = (SEC_PKCS12ExportContext *)PORT_ArenaZAlloc(arena,
    193                                                          sizeof(SEC_PKCS12ExportContext));
    194    if (!p12ctxt) {
    195        PORT_SetError(SEC_ERROR_NO_MEMORY);
    196        goto loser;
    197    }
    198 
    199    /* password callback for key retrieval */
    200    p12ctxt->pwfn = pwfn;
    201    p12ctxt->pwfnarg = pwfnarg;
    202 
    203    p12ctxt->integrityEnabled = PR_FALSE;
    204    p12ctxt->arena = arena;
    205    p12ctxt->wincx = wincx;
    206    p12ctxt->slot = (slot) ? PK11_ReferenceSlot(slot) : PK11_GetInternalSlot();
    207 
    208    return p12ctxt;
    209 
    210 loser:
    211    if (arena) {
    212        PORT_FreeArena(arena, PR_TRUE);
    213    }
    214 
    215    return NULL;
    216 }
    217 
    218 /*
    219 * Adding integrity mode
    220 */
    221 
    222 /* SEC_PKCS12AddPasswordIntegrity
    223 *      Add password integrity to the exported data.  If an integrity method
    224 *      has already been set, then return an error.
    225 *
    226 *      p12ctxt - the export context
    227 *      pwitem - the password for integrity mode
    228 *      integAlg - the integrity algorithm to use for authentication.
    229 */
    230 SECStatus
    231 SEC_PKCS12AddPasswordIntegrity(SEC_PKCS12ExportContext *p12ctxt,
    232                               SECItem *pwitem, SECOidTag integAlg)
    233 {
    234    if (!p12ctxt || p12ctxt->integrityEnabled) {
    235        return SECFailure;
    236    }
    237 
    238    /* set up integrity information */
    239    p12ctxt->pwdIntegrity = PR_TRUE;
    240    p12ctxt->integrityInfo.pwdInfo.password =
    241        (SECItem *)PORT_ArenaZAlloc(p12ctxt->arena, sizeof(SECItem));
    242    if (!p12ctxt->integrityInfo.pwdInfo.password) {
    243        PORT_SetError(SEC_ERROR_NO_MEMORY);
    244        return SECFailure;
    245    }
    246    if (SECITEM_CopyItem(p12ctxt->arena,
    247                         p12ctxt->integrityInfo.pwdInfo.password, pwitem) != SECSuccess) {
    248        PORT_SetError(SEC_ERROR_NO_MEMORY);
    249        return SECFailure;
    250    }
    251    p12ctxt->integrityInfo.pwdInfo.algorithm = integAlg;
    252    p12ctxt->integrityEnabled = PR_TRUE;
    253 
    254    return SECSuccess;
    255 }
    256 
    257 /* SEC_PKCS12AddPublicKeyIntegrity
    258 *      Add public key integrity to the exported data.  If an integrity method
    259 *      has already been set, then return an error.  The certificate must be
    260 *      allowed to be used as a signing cert.
    261 *
    262 *      p12ctxt - the export context
    263 *      cert - signer certificate
    264 *      certDb - the certificate database
    265 *      algorithm - signing algorithm
    266 *      keySize - size of the signing key (?)
    267 */
    268 SECStatus
    269 SEC_PKCS12AddPublicKeyIntegrity(SEC_PKCS12ExportContext *p12ctxt,
    270                                CERTCertificate *cert, CERTCertDBHandle *certDb,
    271                                SECOidTag algorithm, int keySize)
    272 {
    273    if (!p12ctxt) {
    274        return SECFailure;
    275    }
    276 
    277    p12ctxt->integrityInfo.pubkeyInfo.cert = cert;
    278    p12ctxt->integrityInfo.pubkeyInfo.certDb = certDb;
    279    p12ctxt->integrityInfo.pubkeyInfo.algorithm = algorithm;
    280    p12ctxt->integrityInfo.pubkeyInfo.keySize = keySize;
    281    p12ctxt->integrityEnabled = PR_TRUE;
    282 
    283    return SECSuccess;
    284 }
    285 
    286 /*
    287 * Adding safes - encrypted (password/public key) or unencrypted
    288 *      Each of the safe creation routines return an opaque pointer which
    289 *      are later passed into the routines for exporting certificates and
    290 *      keys.
    291 */
    292 
    293 /* append the newly created safeInfo to list of safeInfos in the export
    294 * context.
    295 */
    296 static SECStatus
    297 sec_pkcs12_append_safe_info(SEC_PKCS12ExportContext *p12ctxt, SEC_PKCS12SafeInfo *info)
    298 {
    299    void *mark = NULL, *dummy1 = NULL, *dummy2 = NULL;
    300 
    301    if (!p12ctxt || !info) {
    302        return SECFailure;
    303    }
    304 
    305    mark = PORT_ArenaMark(p12ctxt->arena);
    306 
    307    /* if no safeInfos have been set, create the list, otherwise expand it. */
    308    if (!p12ctxt->safeInfoCount) {
    309        p12ctxt->safeInfos = (SEC_PKCS12SafeInfo **)PORT_ArenaZAlloc(p12ctxt->arena,
    310                                                                     2 * sizeof(SEC_PKCS12SafeInfo *));
    311        dummy1 = p12ctxt->safeInfos;
    312        p12ctxt->authSafe.encodedSafes = (SECItem **)PORT_ArenaZAlloc(p12ctxt->arena,
    313                                                                      2 * sizeof(SECItem *));
    314        dummy2 = p12ctxt->authSafe.encodedSafes;
    315    } else {
    316        dummy1 = PORT_ArenaGrow(p12ctxt->arena, p12ctxt->safeInfos,
    317                                (p12ctxt->safeInfoCount + 1) * sizeof(SEC_PKCS12SafeInfo *),
    318                                (p12ctxt->safeInfoCount + 2) * sizeof(SEC_PKCS12SafeInfo *));
    319        p12ctxt->safeInfos = (SEC_PKCS12SafeInfo **)dummy1;
    320        dummy2 = PORT_ArenaGrow(p12ctxt->arena, p12ctxt->authSafe.encodedSafes,
    321                                (p12ctxt->authSafe.safeCount + 1) * sizeof(SECItem *),
    322                                (p12ctxt->authSafe.safeCount + 2) * sizeof(SECItem *));
    323        p12ctxt->authSafe.encodedSafes = (SECItem **)dummy2;
    324    }
    325    if (!dummy1 || !dummy2) {
    326        PORT_SetError(SEC_ERROR_NO_MEMORY);
    327        goto loser;
    328    }
    329 
    330    /* append the new safeInfo and null terminate the list */
    331    p12ctxt->safeInfos[p12ctxt->safeInfoCount] = info;
    332    p12ctxt->safeInfos[++p12ctxt->safeInfoCount] = NULL;
    333    p12ctxt->authSafe.encodedSafes[p12ctxt->authSafe.safeCount] =
    334        (SECItem *)PORT_ArenaZAlloc(p12ctxt->arena, sizeof(SECItem));
    335    if (!p12ctxt->authSafe.encodedSafes[p12ctxt->authSafe.safeCount]) {
    336        PORT_SetError(SEC_ERROR_NO_MEMORY);
    337        goto loser;
    338    }
    339    p12ctxt->authSafe.encodedSafes[++p12ctxt->authSafe.safeCount] = NULL;
    340 
    341    PORT_ArenaUnmark(p12ctxt->arena, mark);
    342    return SECSuccess;
    343 
    344 loser:
    345    PORT_ArenaRelease(p12ctxt->arena, mark);
    346    return SECFailure;
    347 }
    348 
    349 /* SEC_PKCS12CreatePasswordPrivSafe
    350 *      Create a password privacy safe to store exported information in.
    351 *
    352 *      p12ctxt - export context
    353 *      pwitem - password for encryption
    354 *      privAlg - pbe algorithm through which encryption is done.
    355 */
    356 SEC_PKCS12SafeInfo *
    357 SEC_PKCS12CreatePasswordPrivSafe(SEC_PKCS12ExportContext *p12ctxt,
    358                                 SECItem *pwitem, SECOidTag privAlg)
    359 {
    360    SEC_PKCS12SafeInfo *safeInfo = NULL;
    361    void *mark = NULL;
    362    PK11SlotInfo *slot = NULL;
    363    SECAlgorithmID *algId;
    364    SECItem uniPwitem = { siBuffer, NULL, 0 };
    365 
    366    if (!p12ctxt) {
    367        return NULL;
    368    }
    369 
    370    /* allocate the safe info */
    371    mark = PORT_ArenaMark(p12ctxt->arena);
    372    safeInfo = (SEC_PKCS12SafeInfo *)PORT_ArenaZAlloc(p12ctxt->arena,
    373                                                      sizeof(SEC_PKCS12SafeInfo));
    374    if (!safeInfo) {
    375        PORT_SetError(SEC_ERROR_NO_MEMORY);
    376        goto loser;
    377    }
    378 
    379    safeInfo->itemCount = 0;
    380 
    381    /* create the encrypted safe */
    382    if (!SEC_PKCS5IsAlgorithmPBEAlgTag(privAlg)) {
    383        SECOidTag prfAlg = SEC_OID_UNKNOWN;
    384        /* if we have password integrity set, use that to set the integrity
    385         * hash algorithm to set our password PRF. If we haven't set it, just
    386         * let the low level code pick it */
    387        if (p12ctxt->integrityEnabled && p12ctxt->pwdIntegrity) {
    388            SECOidTag integrityAlg = p12ctxt->integrityInfo.pwdInfo.algorithm;
    389            prfAlg = integrityAlg;
    390            /* verify that integrityAlg is an HMAC */
    391            if (HASH_GetHashOidTagByHMACOidTag(integrityAlg) == SEC_OID_UNKNOWN) {
    392                /* it's not, find the hmac */
    393                prfAlg = HASH_GetHMACOidTagByHashOidTag(integrityAlg);
    394                /* if prfAlg is SEC_OID_UNKNOWN at this point we'll
    395                 * default to SEC_OID_HMAC_SHA1 in the low level pbe code. */
    396            }
    397        }
    398        if (!SEC_PKCS12CipherAllowed(privAlg, prfAlg)) {
    399            PORT_SetError(SEC_ERROR_BAD_EXPORT_ALGORITHM);
    400            goto loser;
    401        }
    402        safeInfo->cinfo = SEC_PKCS7CreateEncryptedDataWithPBEV2(SEC_OID_PKCS5_PBES2,
    403                                                                privAlg,
    404                                                                prfAlg,
    405                                                                0,
    406                                                                p12ctxt->pwfn,
    407                                                                p12ctxt->pwfnarg);
    408    } else {
    409        if (!SEC_PKCS12CipherAllowed(privAlg, SEC_OID_UNKNOWN)) {
    410            PORT_SetError(SEC_ERROR_BAD_EXPORT_ALGORITHM);
    411            goto loser;
    412        }
    413        safeInfo->cinfo = SEC_PKCS7CreateEncryptedData(privAlg, 0, p12ctxt->pwfn,
    414                                                       p12ctxt->pwfnarg);
    415    }
    416    if (!safeInfo->cinfo) {
    417        PORT_SetError(SEC_ERROR_NO_MEMORY);
    418        goto loser;
    419    }
    420    safeInfo->arena = p12ctxt->arena;
    421 
    422    if (!sec_pkcs12_encode_password(NULL, &uniPwitem, privAlg, pwitem)) {
    423        PORT_SetError(SEC_ERROR_NO_MEMORY);
    424        goto loser;
    425    }
    426    if (SECITEM_CopyItem(p12ctxt->arena, &safeInfo->pwitem, &uniPwitem) != SECSuccess) {
    427        PORT_SetError(SEC_ERROR_NO_MEMORY);
    428        goto loser;
    429    }
    430 
    431    /* generate the encryption key */
    432    slot = PK11_ReferenceSlot(p12ctxt->slot);
    433    if (!slot) {
    434        slot = PK11_GetInternalKeySlot();
    435        if (!slot) {
    436            PORT_SetError(SEC_ERROR_NO_MEMORY);
    437            goto loser;
    438        }
    439    }
    440 
    441    algId = SEC_PKCS7GetEncryptionAlgorithm(safeInfo->cinfo);
    442    safeInfo->encryptionKey = PK11_PBEKeyGen(slot, algId, &uniPwitem,
    443                                             PR_FALSE, p12ctxt->wincx);
    444    if (!safeInfo->encryptionKey) {
    445        goto loser;
    446    }
    447 
    448    safeInfo->arena = p12ctxt->arena;
    449    safeInfo->safe = NULL;
    450    if (sec_pkcs12_append_safe_info(p12ctxt, safeInfo) != SECSuccess) {
    451        goto loser;
    452    }
    453 
    454    if (uniPwitem.data) {
    455        SECITEM_ZfreeItem(&uniPwitem, PR_FALSE);
    456    }
    457    PORT_ArenaUnmark(p12ctxt->arena, mark);
    458 
    459    if (slot) {
    460        PK11_FreeSlot(slot);
    461    }
    462    return safeInfo;
    463 
    464 loser:
    465    if (slot) {
    466        PK11_FreeSlot(slot);
    467    }
    468    if (safeInfo && safeInfo->cinfo) {
    469        SEC_PKCS7DestroyContentInfo(safeInfo->cinfo);
    470    }
    471 
    472    if (uniPwitem.data) {
    473        SECITEM_ZfreeItem(&uniPwitem, PR_FALSE);
    474    }
    475 
    476    PORT_ArenaRelease(p12ctxt->arena, mark);
    477    return NULL;
    478 }
    479 
    480 /* SEC_PKCS12CreateUnencryptedSafe
    481 *      Creates an unencrypted safe within the export context.
    482 *
    483 *      p12ctxt - the export context
    484 */
    485 SEC_PKCS12SafeInfo *
    486 SEC_PKCS12CreateUnencryptedSafe(SEC_PKCS12ExportContext *p12ctxt)
    487 {
    488    SEC_PKCS12SafeInfo *safeInfo = NULL;
    489    void *mark = NULL;
    490 
    491    if (!p12ctxt) {
    492        return NULL;
    493    }
    494 
    495    /* create the safe info */
    496    mark = PORT_ArenaMark(p12ctxt->arena);
    497    safeInfo = (SEC_PKCS12SafeInfo *)PORT_ArenaZAlloc(p12ctxt->arena,
    498                                                      sizeof(SEC_PKCS12SafeInfo));
    499    if (!safeInfo) {
    500        PORT_ArenaRelease(p12ctxt->arena, mark);
    501        PORT_SetError(SEC_ERROR_NO_MEMORY);
    502        return NULL;
    503    }
    504 
    505    safeInfo->itemCount = 0;
    506 
    507    /* create the safe content */
    508    safeInfo->cinfo = SEC_PKCS7CreateData();
    509    if (!safeInfo->cinfo) {
    510        PORT_SetError(SEC_ERROR_NO_MEMORY);
    511        goto loser;
    512    }
    513 
    514    if (sec_pkcs12_append_safe_info(p12ctxt, safeInfo) != SECSuccess) {
    515        goto loser;
    516    }
    517 
    518    PORT_ArenaUnmark(p12ctxt->arena, mark);
    519    return safeInfo;
    520 
    521 loser:
    522    if (safeInfo->cinfo) {
    523        SEC_PKCS7DestroyContentInfo(safeInfo->cinfo);
    524    }
    525 
    526    PORT_ArenaRelease(p12ctxt->arena, mark);
    527    return NULL;
    528 }
    529 
    530 /* SEC_PKCS12CreatePubKeyEncryptedSafe
    531 *      Creates a safe which is protected by public key encryption.
    532 *
    533 *      p12ctxt - the export context
    534 *      certDb - the certificate database
    535 *      signer - the signer's certificate
    536 *      recipients - the list of recipient certificates.
    537 *      algorithm - the encryption algorithm to use
    538 *      keysize - the algorithms key size (?)
    539 */
    540 SEC_PKCS12SafeInfo *
    541 SEC_PKCS12CreatePubKeyEncryptedSafe(SEC_PKCS12ExportContext *p12ctxt,
    542                                    CERTCertDBHandle *certDb,
    543                                    CERTCertificate *signer,
    544                                    CERTCertificate **recipients,
    545                                    SECOidTag algorithm, int keysize)
    546 {
    547    SEC_PKCS12SafeInfo *safeInfo = NULL;
    548    void *mark = NULL;
    549 
    550    if (!p12ctxt || !signer || !recipients || !(*recipients)) {
    551        return NULL;
    552    }
    553 
    554    /* allocate the safeInfo */
    555    mark = PORT_ArenaMark(p12ctxt->arena);
    556    safeInfo = (SEC_PKCS12SafeInfo *)PORT_ArenaZAlloc(p12ctxt->arena,
    557                                                      sizeof(SEC_PKCS12SafeInfo));
    558    if (!safeInfo) {
    559        PORT_ArenaRelease(p12ctxt->arena, mark);
    560        PORT_SetError(SEC_ERROR_NO_MEMORY);
    561        return NULL;
    562    }
    563 
    564    safeInfo->itemCount = 0;
    565    safeInfo->arena = p12ctxt->arena;
    566 
    567    /* create the enveloped content info using certUsageEmailSigner currently.
    568     * XXX We need to eventually use something other than certUsageEmailSigner
    569     */
    570    safeInfo->cinfo = SEC_PKCS7CreateEnvelopedData(signer, certUsageEmailSigner,
    571                                                   certDb, algorithm, keysize,
    572                                                   p12ctxt->pwfn, p12ctxt->pwfnarg);
    573    if (!safeInfo->cinfo) {
    574        PORT_SetError(SEC_ERROR_NO_MEMORY);
    575        goto loser;
    576    }
    577 
    578    /* add recipients */
    579    if (recipients) {
    580        unsigned int i = 0;
    581        while (recipients[i] != NULL) {
    582            SECStatus rv = SEC_PKCS7AddRecipient(safeInfo->cinfo, recipients[i],
    583                                                 certUsageEmailRecipient, certDb);
    584            if (rv != SECSuccess) {
    585                goto loser;
    586            }
    587            i++;
    588        }
    589    }
    590 
    591    if (sec_pkcs12_append_safe_info(p12ctxt, safeInfo) != SECSuccess) {
    592        goto loser;
    593    }
    594 
    595    PORT_ArenaUnmark(p12ctxt->arena, mark);
    596    return safeInfo;
    597 
    598 loser:
    599    if (safeInfo->cinfo) {
    600        SEC_PKCS7DestroyContentInfo(safeInfo->cinfo);
    601        safeInfo->cinfo = NULL;
    602    }
    603 
    604    PORT_ArenaRelease(p12ctxt->arena, mark);
    605    return NULL;
    606 }
    607 
    608 /*********************************
    609 * Routines to handle the exporting of the keys and certificates
    610 *********************************/
    611 
    612 /* creates a safe contents which safeBags will be appended to */
    613 sec_PKCS12SafeContents *
    614 sec_PKCS12CreateSafeContents(PLArenaPool *arena)
    615 {
    616    sec_PKCS12SafeContents *safeContents;
    617 
    618    if (arena == NULL) {
    619        return NULL;
    620    }
    621 
    622    /* create the safe contents */
    623    safeContents = (sec_PKCS12SafeContents *)PORT_ArenaZAlloc(arena,
    624                                                              sizeof(sec_PKCS12SafeContents));
    625    if (!safeContents) {
    626        PORT_SetError(SEC_ERROR_NO_MEMORY);
    627        goto loser;
    628    }
    629 
    630    /* set up the internal contents info */
    631    safeContents->safeBags = NULL;
    632    safeContents->arena = arena;
    633    safeContents->bagCount = 0;
    634 
    635    return safeContents;
    636 
    637 loser:
    638    return NULL;
    639 }
    640 
    641 /* appends a safe bag to a safeContents using the specified arena.
    642 */
    643 SECStatus
    644 sec_pkcs12_append_bag_to_safe_contents(PLArenaPool *arena,
    645                                       sec_PKCS12SafeContents *safeContents,
    646                                       sec_PKCS12SafeBag *safeBag)
    647 {
    648    void *mark = NULL, *dummy = NULL;
    649 
    650    if (!arena || !safeBag || !safeContents) {
    651        return SECFailure;
    652    }
    653 
    654    mark = PORT_ArenaMark(arena);
    655    if (!mark) {
    656        PORT_SetError(SEC_ERROR_NO_MEMORY);
    657        return SECFailure;
    658    }
    659 
    660    /* allocate space for the list, or reallocate to increase space */
    661    if (!safeContents->safeBags) {
    662        safeContents->safeBags = (sec_PKCS12SafeBag **)PORT_ArenaZAlloc(arena,
    663                                                                        (2 * sizeof(sec_PKCS12SafeBag *)));
    664        dummy = safeContents->safeBags;
    665        safeContents->bagCount = 0;
    666    } else {
    667        dummy = PORT_ArenaGrow(arena, safeContents->safeBags,
    668                               (safeContents->bagCount + 1) * sizeof(sec_PKCS12SafeBag *),
    669                               (safeContents->bagCount + 2) * sizeof(sec_PKCS12SafeBag *));
    670        safeContents->safeBags = (sec_PKCS12SafeBag **)dummy;
    671    }
    672 
    673    if (!dummy) {
    674        PORT_ArenaRelease(arena, mark);
    675        PORT_SetError(SEC_ERROR_NO_MEMORY);
    676        return SECFailure;
    677    }
    678 
    679    /* append the bag at the end and null terminate the list */
    680    safeContents->safeBags[safeContents->bagCount++] = safeBag;
    681    safeContents->safeBags[safeContents->bagCount] = NULL;
    682 
    683    PORT_ArenaUnmark(arena, mark);
    684 
    685    return SECSuccess;
    686 }
    687 
    688 /* appends a safeBag to a specific safeInfo.
    689 */
    690 SECStatus
    691 sec_pkcs12_append_bag(SEC_PKCS12ExportContext *p12ctxt,
    692                      SEC_PKCS12SafeInfo *safeInfo, sec_PKCS12SafeBag *safeBag)
    693 {
    694    sec_PKCS12SafeContents *dest;
    695    SECStatus rv = SECFailure;
    696 
    697    if (!p12ctxt || !safeBag || !safeInfo) {
    698        return SECFailure;
    699    }
    700 
    701    if (!safeInfo->safe) {
    702        safeInfo->safe = sec_PKCS12CreateSafeContents(p12ctxt->arena);
    703        if (!safeInfo->safe) {
    704            return SECFailure;
    705        }
    706    }
    707 
    708    dest = safeInfo->safe;
    709    rv = sec_pkcs12_append_bag_to_safe_contents(p12ctxt->arena, dest, safeBag);
    710    if (rv == SECSuccess) {
    711        safeInfo->itemCount++;
    712    }
    713 
    714    return rv;
    715 }
    716 
    717 /* Creates a safeBag of the specified type, and if bagData is specified,
    718 * the contents are set.  The contents could be set later by the calling
    719 * routine.
    720 */
    721 sec_PKCS12SafeBag *
    722 sec_PKCS12CreateSafeBag(SEC_PKCS12ExportContext *p12ctxt, SECOidTag bagType,
    723                        void *bagData)
    724 {
    725    sec_PKCS12SafeBag *safeBag;
    726    void *mark = NULL;
    727    SECStatus rv = SECSuccess;
    728    SECOidData *oidData = NULL;
    729 
    730    if (!p12ctxt) {
    731        return NULL;
    732    }
    733 
    734    mark = PORT_ArenaMark(p12ctxt->arena);
    735    if (!mark) {
    736        PORT_SetError(SEC_ERROR_NO_MEMORY);
    737        return NULL;
    738    }
    739 
    740    safeBag = (sec_PKCS12SafeBag *)PORT_ArenaZAlloc(p12ctxt->arena,
    741                                                    sizeof(sec_PKCS12SafeBag));
    742    if (!safeBag) {
    743        PORT_ArenaRelease(p12ctxt->arena, mark);
    744        PORT_SetError(SEC_ERROR_NO_MEMORY);
    745        return NULL;
    746    }
    747 
    748    /* set the bags content based upon bag type */
    749    switch (bagType) {
    750        case SEC_OID_PKCS12_V1_KEY_BAG_ID:
    751            safeBag->safeBagContent.pkcs8KeyBag =
    752                (SECKEYPrivateKeyInfo *)bagData;
    753            break;
    754        case SEC_OID_PKCS12_V1_CERT_BAG_ID:
    755            safeBag->safeBagContent.certBag = (sec_PKCS12CertBag *)bagData;
    756            break;
    757        case SEC_OID_PKCS12_V1_CRL_BAG_ID:
    758            safeBag->safeBagContent.crlBag = (sec_PKCS12CRLBag *)bagData;
    759            break;
    760        case SEC_OID_PKCS12_V1_SECRET_BAG_ID:
    761            safeBag->safeBagContent.secretBag = (sec_PKCS12SecretBag *)bagData;
    762            break;
    763        case SEC_OID_PKCS12_V1_PKCS8_SHROUDED_KEY_BAG_ID:
    764            safeBag->safeBagContent.pkcs8ShroudedKeyBag =
    765                (SECKEYEncryptedPrivateKeyInfo *)bagData;
    766            break;
    767        case SEC_OID_PKCS12_V1_SAFE_CONTENTS_BAG_ID:
    768            safeBag->safeBagContent.safeContents =
    769                (sec_PKCS12SafeContents *)bagData;
    770            break;
    771        default:
    772            goto loser;
    773    }
    774 
    775    oidData = SECOID_FindOIDByTag(bagType);
    776    if (oidData) {
    777        rv = SECITEM_CopyItem(p12ctxt->arena, &safeBag->safeBagType, &oidData->oid);
    778        if (rv != SECSuccess) {
    779            PORT_SetError(SEC_ERROR_NO_MEMORY);
    780            goto loser;
    781        }
    782    } else {
    783        goto loser;
    784    }
    785 
    786    safeBag->arena = p12ctxt->arena;
    787    PORT_ArenaUnmark(p12ctxt->arena, mark);
    788 
    789    return safeBag;
    790 
    791 loser:
    792    if (mark) {
    793        PORT_ArenaRelease(p12ctxt->arena, mark);
    794    }
    795 
    796    return NULL;
    797 }
    798 
    799 /* Creates a new certificate bag and returns a pointer to it.  If an error
    800 * occurs NULL is returned.
    801 */
    802 sec_PKCS12CertBag *
    803 sec_PKCS12NewCertBag(PLArenaPool *arena, SECOidTag certType)
    804 {
    805    sec_PKCS12CertBag *certBag = NULL;
    806    SECOidData *bagType = NULL;
    807    SECStatus rv;
    808    void *mark = NULL;
    809 
    810    if (!arena) {
    811        return NULL;
    812    }
    813 
    814    mark = PORT_ArenaMark(arena);
    815    certBag = (sec_PKCS12CertBag *)PORT_ArenaZAlloc(arena,
    816                                                    sizeof(sec_PKCS12CertBag));
    817    if (!certBag) {
    818        PORT_ArenaRelease(arena, mark);
    819        PORT_SetError(SEC_ERROR_NO_MEMORY);
    820        return NULL;
    821    }
    822 
    823    bagType = SECOID_FindOIDByTag(certType);
    824    if (!bagType) {
    825        PORT_SetError(SEC_ERROR_NO_MEMORY);
    826        goto loser;
    827    }
    828 
    829    rv = SECITEM_CopyItem(arena, &certBag->bagID, &bagType->oid);
    830    if (rv != SECSuccess) {
    831        PORT_SetError(SEC_ERROR_NO_MEMORY);
    832        goto loser;
    833    }
    834 
    835    PORT_ArenaUnmark(arena, mark);
    836    return certBag;
    837 
    838 loser:
    839    PORT_ArenaRelease(arena, mark);
    840    return NULL;
    841 }
    842 
    843 /* Creates a new CRL bag and returns a pointer to it.  If an error
    844 * occurs NULL is returned.
    845 */
    846 sec_PKCS12CRLBag *
    847 sec_PKCS12NewCRLBag(PLArenaPool *arena, SECOidTag crlType)
    848 {
    849    sec_PKCS12CRLBag *crlBag = NULL;
    850    SECOidData *bagType = NULL;
    851    SECStatus rv;
    852    void *mark = NULL;
    853 
    854    if (!arena) {
    855        return NULL;
    856    }
    857 
    858    mark = PORT_ArenaMark(arena);
    859    crlBag = (sec_PKCS12CRLBag *)PORT_ArenaZAlloc(arena,
    860                                                  sizeof(sec_PKCS12CRLBag));
    861    if (!crlBag) {
    862        PORT_ArenaRelease(arena, mark);
    863        PORT_SetError(SEC_ERROR_NO_MEMORY);
    864        return NULL;
    865    }
    866 
    867    bagType = SECOID_FindOIDByTag(crlType);
    868    if (!bagType) {
    869        PORT_SetError(SEC_ERROR_NO_MEMORY);
    870        goto loser;
    871    }
    872 
    873    rv = SECITEM_CopyItem(arena, &crlBag->bagID, &bagType->oid);
    874    if (rv != SECSuccess) {
    875        PORT_SetError(SEC_ERROR_NO_MEMORY);
    876        goto loser;
    877    }
    878 
    879    PORT_ArenaUnmark(arena, mark);
    880    return crlBag;
    881 
    882 loser:
    883    PORT_ArenaRelease(arena, mark);
    884    return NULL;
    885 }
    886 
    887 /* sec_PKCS12AddAttributeToBag
    888 * adds an attribute to a safeBag.  currently, the only attributes supported
    889 * are those which are specified within PKCS 12.
    890 *
    891 *      p12ctxt - the export context
    892 *      safeBag - the safeBag to which attributes are appended
    893 *      attrType - the attribute type
    894 *      attrData - the attribute data
    895 */
    896 SECStatus
    897 sec_PKCS12AddAttributeToBag(SEC_PKCS12ExportContext *p12ctxt,
    898                            sec_PKCS12SafeBag *safeBag, SECOidTag attrType,
    899                            SECItem *attrData)
    900 {
    901    sec_PKCS12Attribute *attribute;
    902    void *mark = NULL, *dummy = NULL;
    903    SECOidData *oiddata = NULL;
    904    SECItem unicodeName = { siBuffer, NULL, 0 };
    905    void *src = NULL;
    906    unsigned int nItems = 0;
    907    SECStatus rv;
    908 
    909    PORT_Assert(p12ctxt->arena == safeBag->arena);
    910    if (!safeBag || !p12ctxt || p12ctxt->arena != safeBag->arena) {
    911        PORT_SetError(SEC_ERROR_INVALID_ARGS);
    912        return SECFailure;
    913    }
    914 
    915    mark = PORT_ArenaMark(safeBag->arena);
    916 
    917    /* allocate the attribute */
    918    attribute = (sec_PKCS12Attribute *)PORT_ArenaZAlloc(safeBag->arena,
    919                                                        sizeof(sec_PKCS12Attribute));
    920    if (!attribute) {
    921        PORT_SetError(SEC_ERROR_NO_MEMORY);
    922        goto loser;
    923    }
    924 
    925    /* set up the attribute */
    926    oiddata = SECOID_FindOIDByTag(attrType);
    927    if (!oiddata) {
    928        PORT_SetError(SEC_ERROR_NO_MEMORY);
    929        goto loser;
    930    }
    931    if (SECITEM_CopyItem(p12ctxt->arena, &attribute->attrType, &oiddata->oid) !=
    932        SECSuccess) {
    933        PORT_SetError(SEC_ERROR_NO_MEMORY);
    934        goto loser;
    935    }
    936 
    937    nItems = 1;
    938    switch (attrType) {
    939        case SEC_OID_PKCS9_LOCAL_KEY_ID: {
    940            src = attrData;
    941            break;
    942        }
    943        case SEC_OID_PKCS9_FRIENDLY_NAME: {
    944            if (!sec_pkcs12_convert_item_to_unicode(p12ctxt->arena,
    945                                                    &unicodeName, attrData, PR_FALSE,
    946                                                    PR_FALSE, PR_TRUE)) {
    947                goto loser;
    948            }
    949            src = &unicodeName;
    950            break;
    951        }
    952        default:
    953            goto loser;
    954    }
    955 
    956    /* append the attribute to the attribute value list  */
    957    attribute->attrValue = (SECItem **)PORT_ArenaZAlloc(p12ctxt->arena,
    958                                                        ((nItems + 1) * sizeof(SECItem *)));
    959    if (!attribute->attrValue) {
    960        PORT_SetError(SEC_ERROR_NO_MEMORY);
    961        goto loser;
    962    }
    963 
    964    /* XXX this will need to be changed if attributes requiring more than
    965     * one element are ever used.
    966     */
    967    attribute->attrValue[0] = (SECItem *)PORT_ArenaZAlloc(p12ctxt->arena,
    968                                                          sizeof(SECItem));
    969    if (!attribute->attrValue[0]) {
    970        PORT_SetError(SEC_ERROR_NO_MEMORY);
    971        goto loser;
    972    }
    973    attribute->attrValue[1] = NULL;
    974 
    975    rv = SECITEM_CopyItem(p12ctxt->arena, attribute->attrValue[0],
    976                          (SECItem *)src);
    977    if (rv != SECSuccess) {
    978        PORT_SetError(SEC_ERROR_NO_MEMORY);
    979        goto loser;
    980    }
    981 
    982    /* append the attribute to the safeBag attributes */
    983    if (safeBag->nAttribs) {
    984        dummy = PORT_ArenaGrow(p12ctxt->arena, safeBag->attribs,
    985                               ((safeBag->nAttribs + 1) * sizeof(sec_PKCS12Attribute *)),
    986                               ((safeBag->nAttribs + 2) * sizeof(sec_PKCS12Attribute *)));
    987        safeBag->attribs = (sec_PKCS12Attribute **)dummy;
    988    } else {
    989        safeBag->attribs = (sec_PKCS12Attribute **)PORT_ArenaZAlloc(p12ctxt->arena,
    990                                                                    2 * sizeof(sec_PKCS12Attribute *));
    991        dummy = safeBag->attribs;
    992    }
    993    if (!dummy) {
    994        goto loser;
    995    }
    996 
    997    safeBag->attribs[safeBag->nAttribs] = attribute;
    998    safeBag->attribs[++safeBag->nAttribs] = NULL;
    999 
   1000    PORT_ArenaUnmark(p12ctxt->arena, mark);
   1001    return SECSuccess;
   1002 
   1003 loser:
   1004    if (mark) {
   1005        PORT_ArenaRelease(p12ctxt->arena, mark);
   1006    }
   1007 
   1008    return SECFailure;
   1009 }
   1010 
   1011 /* SEC_PKCS12AddCert
   1012 *      Adds a certificate to the data being exported.
   1013 *
   1014 *      p12ctxt - the export context
   1015 *      safe - the safeInfo to which the certificate is placed
   1016 *      nestedDest - if the cert is to be placed within a nested safeContents then,
   1017 *                   this value is to be specified with the destination
   1018 *      cert - the cert to export
   1019 *      certDb - the certificate database handle
   1020 *      keyId - a unique identifier to associate a certificate/key pair
   1021 *      includeCertChain - PR_TRUE if the certificate chain is to be included.
   1022 */
   1023 SECStatus
   1024 SEC_PKCS12AddCert(SEC_PKCS12ExportContext *p12ctxt, SEC_PKCS12SafeInfo *safe,
   1025                  void *nestedDest, CERTCertificate *cert,
   1026                  CERTCertDBHandle *certDb, SECItem *keyId,
   1027                  PRBool includeCertChain)
   1028 {
   1029    sec_PKCS12CertBag *certBag;
   1030    sec_PKCS12SafeBag *safeBag;
   1031    void *mark;
   1032    SECStatus rv;
   1033    SECItem nick = { siBuffer, NULL, 0 };
   1034 
   1035    if (!p12ctxt || !cert) {
   1036        return SECFailure;
   1037    }
   1038    mark = PORT_ArenaMark(p12ctxt->arena);
   1039 
   1040    /* allocate the cert bag */
   1041    certBag = sec_PKCS12NewCertBag(p12ctxt->arena,
   1042                                   SEC_OID_PKCS9_X509_CERT);
   1043    if (!certBag) {
   1044        goto loser;
   1045    }
   1046 
   1047    if (SECITEM_CopyItem(p12ctxt->arena, &certBag->value.x509Cert,
   1048                         &cert->derCert) != SECSuccess) {
   1049        PORT_SetError(SEC_ERROR_NO_MEMORY);
   1050        goto loser;
   1051    }
   1052 
   1053    /* if the cert chain is to be included, we should only be exporting
   1054     * the cert from our internal database.
   1055     */
   1056    if (includeCertChain) {
   1057        CERTCertificateList *certList = CERT_CertChainFromCert(cert,
   1058                                                               certUsageSSLClient,
   1059                                                               PR_TRUE);
   1060        unsigned int count = 0;
   1061        if (!certList) {
   1062            PORT_SetError(SEC_ERROR_NO_MEMORY);
   1063            goto loser;
   1064        }
   1065 
   1066        /* add cert chain */
   1067        for (count = 0; count < (unsigned int)certList->len; count++) {
   1068            if (SECITEM_CompareItem(&certList->certs[count], &cert->derCert) != SECEqual) {
   1069                CERTCertificate *tempCert;
   1070 
   1071                /* decode the certificate */
   1072                /* XXX
   1073                 * This was rather silly.  The chain is constructed above
   1074                 * by finding all of the CERTCertificate's in the database.
   1075                 * Then the chain is put into a CERTCertificateList, which only
   1076                 * contains the DER.  Finally, the DER was decoded, and the
   1077                 * decoded cert was sent recursively back to this function.
   1078                 * Beyond being inefficent, this causes data loss (specifically,
   1079                 * the nickname).  Instead, for 3.4, we'll do a lookup by the
   1080                 * DER, which should return the cached entry.
   1081                 */
   1082                tempCert = CERT_FindCertByDERCert(CERT_GetDefaultCertDB(),
   1083                                                  &certList->certs[count]);
   1084                if (!tempCert) {
   1085                    CERT_DestroyCertificateList(certList);
   1086                    goto loser;
   1087                }
   1088 
   1089                /* add the certificate */
   1090                if (SEC_PKCS12AddCert(p12ctxt, safe, nestedDest, tempCert,
   1091                                      certDb, NULL, PR_FALSE) != SECSuccess) {
   1092                    CERT_DestroyCertificate(tempCert);
   1093                    CERT_DestroyCertificateList(certList);
   1094                    goto loser;
   1095                }
   1096                CERT_DestroyCertificate(tempCert);
   1097            }
   1098        }
   1099        CERT_DestroyCertificateList(certList);
   1100    }
   1101 
   1102    /* if the certificate has a nickname, we will set the friendly name
   1103     * to that.
   1104     */
   1105    if (cert->nickname) {
   1106        if (cert->slot && !PK11_IsInternal(cert->slot)) {
   1107            /*
   1108             * The cert is coming off of an external token,
   1109             * let's strip the token name from the nickname
   1110             * and only add what comes after the colon as the
   1111             * nickname. -javi
   1112             */
   1113            char *delimit;
   1114 
   1115            delimit = PORT_Strchr(cert->nickname, ':');
   1116            if (delimit == NULL) {
   1117                nick.data = (unsigned char *)cert->nickname;
   1118                nick.len = PORT_Strlen(cert->nickname);
   1119            } else {
   1120                delimit++;
   1121                nick.data = (unsigned char *)PORT_ArenaStrdup(p12ctxt->arena,
   1122                                                              delimit);
   1123                nick.len = PORT_Strlen(delimit);
   1124            }
   1125        } else {
   1126            nick.data = (unsigned char *)cert->nickname;
   1127            nick.len = PORT_Strlen(cert->nickname);
   1128        }
   1129    }
   1130 
   1131    safeBag = sec_PKCS12CreateSafeBag(p12ctxt, SEC_OID_PKCS12_V1_CERT_BAG_ID,
   1132                                      certBag);
   1133    if (!safeBag) {
   1134        goto loser;
   1135    }
   1136 
   1137    /* add the friendly name and keyId attributes, if necessary */
   1138    if (nick.data) {
   1139        if (sec_PKCS12AddAttributeToBag(p12ctxt, safeBag,
   1140                                        SEC_OID_PKCS9_FRIENDLY_NAME, &nick) != SECSuccess) {
   1141            goto loser;
   1142        }
   1143    }
   1144 
   1145    if (keyId) {
   1146        if (sec_PKCS12AddAttributeToBag(p12ctxt, safeBag, SEC_OID_PKCS9_LOCAL_KEY_ID,
   1147                                        keyId) != SECSuccess) {
   1148            goto loser;
   1149        }
   1150    }
   1151 
   1152    /* append the cert safeBag */
   1153    if (nestedDest) {
   1154        rv = sec_pkcs12_append_bag_to_safe_contents(p12ctxt->arena,
   1155                                                    (sec_PKCS12SafeContents *)nestedDest,
   1156                                                    safeBag);
   1157    } else {
   1158        rv = sec_pkcs12_append_bag(p12ctxt, safe, safeBag);
   1159    }
   1160 
   1161    if (rv != SECSuccess) {
   1162        goto loser;
   1163    }
   1164 
   1165    PORT_ArenaUnmark(p12ctxt->arena, mark);
   1166    return SECSuccess;
   1167 
   1168 loser:
   1169    if (mark) {
   1170        PORT_ArenaRelease(p12ctxt->arena, mark);
   1171    }
   1172 
   1173    return SECFailure;
   1174 }
   1175 
   1176 /* SEC_PKCS12AddKeyForCert
   1177 *      Extracts the key associated with a particular certificate and exports
   1178 *      it.
   1179 *
   1180 *      p12ctxt - the export context
   1181 *      safe - the safeInfo to place the key in
   1182 *      nestedDest - the nested safeContents to place a key
   1183 *      cert - the certificate which the key belongs to
   1184 *      shroudKey - encrypt the private key for export.  This value should
   1185 *              always be true.  lower level code will not allow the export
   1186 *              of unencrypted private keys.
   1187 *      algorithm - the algorithm with which to encrypt the private key
   1188 *      pwitem - the password to encrypt the private key with
   1189 *      keyId - the keyID attribute
   1190 *      nickName - the nickname attribute
   1191 */
   1192 SECStatus
   1193 SEC_PKCS12AddKeyForCert(SEC_PKCS12ExportContext *p12ctxt, SEC_PKCS12SafeInfo *safe,
   1194                        void *nestedDest, CERTCertificate *cert,
   1195                        PRBool shroudKey, SECOidTag algorithm, SECItem *pwitem,
   1196                        SECItem *keyId, SECItem *nickName)
   1197 {
   1198    void *mark;
   1199    void *keyItem;
   1200    SECOidTag keyType;
   1201    SECStatus rv = SECFailure;
   1202    SECItem nickname = { siBuffer, NULL, 0 }, uniPwitem = { siBuffer, NULL, 0 };
   1203    sec_PKCS12SafeBag *returnBag;
   1204 
   1205    if (!p12ctxt || !cert || !safe) {
   1206        return SECFailure;
   1207    }
   1208 
   1209    mark = PORT_ArenaMark(p12ctxt->arena);
   1210 
   1211    /* retrieve the key based upon the type that it is and
   1212     * specify the type of safeBag to store the key in
   1213     */
   1214    if (!shroudKey) {
   1215 
   1216        /* extract the key unencrypted.  this will most likely go away */
   1217        SECKEYPrivateKeyInfo *pki = PK11_ExportPrivateKeyInfo(cert,
   1218                                                              p12ctxt->wincx);
   1219        if (!pki) {
   1220            PORT_ArenaRelease(p12ctxt->arena, mark);
   1221            PORT_SetError(SEC_ERROR_PKCS12_UNABLE_TO_EXPORT_KEY);
   1222            return SECFailure;
   1223        }
   1224        keyItem = PORT_ArenaZAlloc(p12ctxt->arena, sizeof(SECKEYPrivateKeyInfo));
   1225        if (!keyItem) {
   1226            PORT_SetError(SEC_ERROR_NO_MEMORY);
   1227            goto loser;
   1228        }
   1229        rv = SECKEY_CopyPrivateKeyInfo(p12ctxt->arena,
   1230                                       (SECKEYPrivateKeyInfo *)keyItem, pki);
   1231        keyType = SEC_OID_PKCS12_V1_KEY_BAG_ID;
   1232        SECKEY_DestroyPrivateKeyInfo(pki, PR_TRUE);
   1233    } else {
   1234 
   1235        /* extract the key encrypted */
   1236        SECKEYEncryptedPrivateKeyInfo *epki = NULL;
   1237        PK11SlotInfo *slot = NULL;
   1238        SECOidTag prfAlg = SEC_OID_UNKNOWN;
   1239 
   1240        if (!sec_pkcs12_encode_password(p12ctxt->arena, &uniPwitem, algorithm,
   1241                                        pwitem)) {
   1242            PORT_SetError(SEC_ERROR_NO_MEMORY);
   1243            goto loser;
   1244        }
   1245 
   1246        /* if we have password integrity set, use that to set the integrity
   1247         * hash algorithm to set our password PRF. If we haven't set it, just
   1248         * let the low level code pick it */
   1249        if (p12ctxt->integrityEnabled && p12ctxt->pwdIntegrity) {
   1250            SECOidTag integrityAlg = p12ctxt->integrityInfo.pwdInfo.algorithm;
   1251            prfAlg = integrityAlg;
   1252            /* verify that integrityAlg is an HMAC */
   1253            if (HASH_GetHashOidTagByHMACOidTag(integrityAlg) == SEC_OID_UNKNOWN) {
   1254                /* it's not, find the hmac */
   1255                prfAlg = HASH_GetHMACOidTagByHashOidTag(integrityAlg);
   1256                /* if prfAlg is SEC_OID_UNKNOWN at this point we'll
   1257                 * default to SEC_OID_HMAC_SHA1 in the low level pbe code. */
   1258            }
   1259        }
   1260 
   1261        if (!SEC_PKCS12CipherAllowed(algorithm, prfAlg)) {
   1262            PORT_SetError(SEC_ERROR_BAD_EXPORT_ALGORITHM);
   1263            goto loser;
   1264        }
   1265 
   1266        /* we want to make sure to take the key out of the key slot */
   1267        if (PK11_IsInternal(p12ctxt->slot)) {
   1268            slot = PK11_GetInternalKeySlot();
   1269        } else {
   1270            slot = PK11_ReferenceSlot(p12ctxt->slot);
   1271        }
   1272 
   1273        /* passing algorithm as the pbe will force the PBE code to
   1274         * automatically handle the selection between using the algorithm
   1275         * as a the pbe algorithm, or using the algorithm as a cipher
   1276         * and building a pkcs5 pbe */
   1277        epki = PK11_ExportEncryptedPrivateKeyInfoV2(slot, algorithm,
   1278                                                    SEC_OID_UNKNOWN, prfAlg,
   1279                                                    &uniPwitem, cert,
   1280                                                    NSS_PBE_DEFAULT_ITERATION_COUNT,
   1281                                                    p12ctxt->wincx);
   1282        PK11_FreeSlot(slot);
   1283        if (!epki) {
   1284            PORT_SetError(SEC_ERROR_PKCS12_UNABLE_TO_EXPORT_KEY);
   1285            goto loser;
   1286        }
   1287 
   1288        keyItem = PORT_ArenaZAlloc(p12ctxt->arena,
   1289                                   sizeof(SECKEYEncryptedPrivateKeyInfo));
   1290        if (!keyItem) {
   1291            PORT_SetError(SEC_ERROR_NO_MEMORY);
   1292            goto loser;
   1293        }
   1294        rv = SECKEY_CopyEncryptedPrivateKeyInfo(p12ctxt->arena,
   1295                                                (SECKEYEncryptedPrivateKeyInfo *)keyItem,
   1296                                                epki);
   1297        keyType = SEC_OID_PKCS12_V1_PKCS8_SHROUDED_KEY_BAG_ID;
   1298        SECKEY_DestroyEncryptedPrivateKeyInfo(epki, PR_TRUE);
   1299    }
   1300 
   1301    if (rv != SECSuccess) {
   1302        goto loser;
   1303    }
   1304 
   1305    /* if no nickname specified, let's see if the certificate has a
   1306     * nickname.
   1307     */
   1308    if (!nickName) {
   1309        if (cert->nickname) {
   1310            nickname.data = (unsigned char *)cert->nickname;
   1311            nickname.len = PORT_Strlen(cert->nickname);
   1312            nickName = &nickname;
   1313        }
   1314    }
   1315 
   1316    /* create the safe bag and set any attributes */
   1317    returnBag = sec_PKCS12CreateSafeBag(p12ctxt, keyType, keyItem);
   1318    if (!returnBag) {
   1319        rv = SECFailure;
   1320        goto loser;
   1321    }
   1322 
   1323    if (nickName) {
   1324        if (sec_PKCS12AddAttributeToBag(p12ctxt, returnBag,
   1325                                        SEC_OID_PKCS9_FRIENDLY_NAME, nickName) != SECSuccess) {
   1326            goto loser;
   1327        }
   1328    }
   1329 
   1330    if (keyId) {
   1331        if (sec_PKCS12AddAttributeToBag(p12ctxt, returnBag, SEC_OID_PKCS9_LOCAL_KEY_ID,
   1332                                        keyId) != SECSuccess) {
   1333            goto loser;
   1334        }
   1335    }
   1336 
   1337    if (nestedDest) {
   1338        rv = sec_pkcs12_append_bag_to_safe_contents(p12ctxt->arena,
   1339                                                    (sec_PKCS12SafeContents *)nestedDest,
   1340                                                    returnBag);
   1341    } else {
   1342        rv = sec_pkcs12_append_bag(p12ctxt, safe, returnBag);
   1343    }
   1344 
   1345 loser:
   1346 
   1347    if (rv != SECSuccess) {
   1348        PORT_ArenaRelease(p12ctxt->arena, mark);
   1349    } else {
   1350        PORT_ArenaUnmark(p12ctxt->arena, mark);
   1351    }
   1352 
   1353    return rv;
   1354 }
   1355 
   1356 /* SEC_PKCS12AddCertOrChainAndKey
   1357 *      Add a certificate and key pair to be exported.
   1358 *
   1359 *      p12ctxt          - the export context
   1360 *      certSafe         - the safeInfo where the cert is stored
   1361 *      certNestedDest   - the nested safeContents to store the cert
   1362 *      keySafe          - the safeInfo where the key is stored
   1363 *      keyNestedDest    - the nested safeContents to store the key
   1364 *      shroudKey        - extract the private key encrypted?
   1365 *      pwitem           - the password with which the key is encrypted
   1366 *      algorithm        - the algorithm with which the key is encrypted
   1367 *      includeCertChain - also add certs from chain to bag.
   1368 */
   1369 SECStatus
   1370 SEC_PKCS12AddCertOrChainAndKey(SEC_PKCS12ExportContext *p12ctxt,
   1371                               void *certSafe, void *certNestedDest,
   1372                               CERTCertificate *cert, CERTCertDBHandle *certDb,
   1373                               void *keySafe, void *keyNestedDest,
   1374                               PRBool shroudKey, SECItem *pwitem,
   1375                               SECOidTag algorithm, PRBool includeCertChain)
   1376 {
   1377    SECStatus rv = SECFailure;
   1378    SGNDigestInfo *digest = NULL;
   1379    void *mark = NULL;
   1380 
   1381    if (!p12ctxt || !certSafe || !keySafe || !cert) {
   1382        return SECFailure;
   1383    }
   1384 
   1385    mark = PORT_ArenaMark(p12ctxt->arena);
   1386 
   1387    /* generate the thumbprint of the cert to use as a keyId */
   1388    digest = sec_pkcs12_compute_thumbprint(&cert->derCert);
   1389    if (!digest) {
   1390        PORT_ArenaRelease(p12ctxt->arena, mark);
   1391        return SECFailure;
   1392    }
   1393 
   1394    /* add the certificate */
   1395    rv = SEC_PKCS12AddCert(p12ctxt, (SEC_PKCS12SafeInfo *)certSafe,
   1396                           (SEC_PKCS12SafeInfo *)certNestedDest, cert, certDb,
   1397                           &digest->digest, includeCertChain);
   1398    if (rv != SECSuccess) {
   1399        goto loser;
   1400    }
   1401 
   1402    /* add the key */
   1403    rv = SEC_PKCS12AddKeyForCert(p12ctxt, (SEC_PKCS12SafeInfo *)keySafe,
   1404                                 keyNestedDest, cert,
   1405                                 shroudKey, algorithm, pwitem,
   1406                                 &digest->digest, NULL);
   1407    if (rv != SECSuccess) {
   1408        goto loser;
   1409    }
   1410 
   1411    SGN_DestroyDigestInfo(digest);
   1412 
   1413    PORT_ArenaUnmark(p12ctxt->arena, mark);
   1414    return SECSuccess;
   1415 
   1416 loser:
   1417    SGN_DestroyDigestInfo(digest);
   1418    PORT_ArenaRelease(p12ctxt->arena, mark);
   1419 
   1420    return SECFailure;
   1421 }
   1422 
   1423 /* like SEC_PKCS12AddCertOrChainAndKey, but always adds cert chain */
   1424 SECStatus
   1425 SEC_PKCS12AddCertAndKey(SEC_PKCS12ExportContext *p12ctxt,
   1426                        void *certSafe, void *certNestedDest,
   1427                        CERTCertificate *cert, CERTCertDBHandle *certDb,
   1428                        void *keySafe, void *keyNestedDest,
   1429                        PRBool shroudKey, SECItem *pwItem, SECOidTag algorithm)
   1430 {
   1431    return SEC_PKCS12AddCertOrChainAndKey(p12ctxt, certSafe, certNestedDest,
   1432                                          cert, certDb, keySafe, keyNestedDest, shroudKey, pwItem,
   1433                                          algorithm, PR_TRUE);
   1434 }
   1435 
   1436 /* SEC_PKCS12CreateNestedSafeContents
   1437 *      Allows nesting of safe contents to be implemented.  No limit imposed on
   1438 *      depth.
   1439 *
   1440 *      p12ctxt - the export context
   1441 *      baseSafe - the base safeInfo
   1442 *      nestedDest - a parent safeContents (?)
   1443 */
   1444 void *
   1445 SEC_PKCS12CreateNestedSafeContents(SEC_PKCS12ExportContext *p12ctxt,
   1446                                   void *baseSafe, void *nestedDest)
   1447 {
   1448    sec_PKCS12SafeContents *newSafe;
   1449    sec_PKCS12SafeBag *safeContentsBag;
   1450    void *mark;
   1451    SECStatus rv;
   1452 
   1453    if (!p12ctxt || !baseSafe) {
   1454        return NULL;
   1455    }
   1456 
   1457    mark = PORT_ArenaMark(p12ctxt->arena);
   1458 
   1459    newSafe = sec_PKCS12CreateSafeContents(p12ctxt->arena);
   1460    if (!newSafe) {
   1461        PORT_ArenaRelease(p12ctxt->arena, mark);
   1462        PORT_SetError(SEC_ERROR_NO_MEMORY);
   1463        return NULL;
   1464    }
   1465 
   1466    /* create the safeContents safeBag */
   1467    safeContentsBag = sec_PKCS12CreateSafeBag(p12ctxt,
   1468                                              SEC_OID_PKCS12_V1_SAFE_CONTENTS_BAG_ID,
   1469                                              newSafe);
   1470    if (!safeContentsBag) {
   1471        goto loser;
   1472    }
   1473 
   1474    /* append the safeContents to the appropriate area */
   1475    if (nestedDest) {
   1476        rv = sec_pkcs12_append_bag_to_safe_contents(p12ctxt->arena,
   1477                                                    (sec_PKCS12SafeContents *)nestedDest,
   1478                                                    safeContentsBag);
   1479    } else {
   1480        rv = sec_pkcs12_append_bag(p12ctxt, (SEC_PKCS12SafeInfo *)baseSafe,
   1481                                   safeContentsBag);
   1482    }
   1483    if (rv != SECSuccess) {
   1484        goto loser;
   1485    }
   1486 
   1487    PORT_ArenaUnmark(p12ctxt->arena, mark);
   1488    return newSafe;
   1489 
   1490 loser:
   1491    PORT_ArenaRelease(p12ctxt->arena, mark);
   1492    return NULL;
   1493 }
   1494 
   1495 /*********************************
   1496 * Encoding routines
   1497 *********************************/
   1498 
   1499 /* Clean up the resources allocated by a sec_PKCS12EncoderContext. */
   1500 static void
   1501 sec_pkcs12_encoder_destroy_context(sec_PKCS12EncoderContext *p12enc)
   1502 {
   1503    if (p12enc) {
   1504        if (p12enc->outerA1ecx) {
   1505            SEC_ASN1EncoderFinish(p12enc->outerA1ecx);
   1506            p12enc->outerA1ecx = NULL;
   1507        }
   1508        if (p12enc->aSafeCinfo) {
   1509            SEC_PKCS7DestroyContentInfo(p12enc->aSafeCinfo);
   1510            p12enc->aSafeCinfo = NULL;
   1511        }
   1512        if (p12enc->middleP7ecx) {
   1513            SEC_PKCS7EncoderFinish(p12enc->middleP7ecx, p12enc->p12exp->pwfn,
   1514                                   p12enc->p12exp->pwfnarg);
   1515            p12enc->middleP7ecx = NULL;
   1516        }
   1517        if (p12enc->middleA1ecx) {
   1518            SEC_ASN1EncoderFinish(p12enc->middleA1ecx);
   1519            p12enc->middleA1ecx = NULL;
   1520        }
   1521        if (p12enc->hmacCx) {
   1522            PK11_DestroyContext(p12enc->hmacCx, PR_TRUE);
   1523            p12enc->hmacCx = NULL;
   1524        }
   1525    }
   1526 }
   1527 
   1528 /* set up the encoder context based on information in the export context
   1529 * and return the newly allocated enocoder context.  A return of NULL
   1530 * indicates an error occurred.
   1531 */
   1532 static sec_PKCS12EncoderContext *
   1533 sec_pkcs12_encoder_start_context(SEC_PKCS12ExportContext *p12exp)
   1534 {
   1535    sec_PKCS12EncoderContext *p12enc = NULL;
   1536    unsigned int i, nonEmptyCnt;
   1537    SECStatus rv;
   1538    SECItem ignore = { 0 };
   1539    void *mark;
   1540    SECItem *salt = NULL;
   1541    SECItem pwd = { siBuffer, NULL, 0 };
   1542 
   1543    if (!p12exp || !p12exp->safeInfos) {
   1544        return NULL;
   1545    }
   1546 
   1547    /* check for any empty safes and skip them */
   1548    i = nonEmptyCnt = 0;
   1549    while (p12exp->safeInfos[i]) {
   1550        if (p12exp->safeInfos[i]->itemCount) {
   1551            nonEmptyCnt++;
   1552        }
   1553        i++;
   1554    }
   1555    if (nonEmptyCnt == 0) {
   1556        return NULL;
   1557    }
   1558    p12exp->authSafe.encodedSafes[nonEmptyCnt] = NULL;
   1559 
   1560    /* allocate the encoder context */
   1561    mark = PORT_ArenaMark(p12exp->arena);
   1562    p12enc = PORT_ArenaZNew(p12exp->arena, sec_PKCS12EncoderContext);
   1563    if (!p12enc) {
   1564        PORT_SetError(SEC_ERROR_NO_MEMORY);
   1565        return NULL;
   1566    }
   1567 
   1568    p12enc->arena = p12exp->arena;
   1569    p12enc->p12exp = p12exp;
   1570 
   1571    /* set up the PFX version and information */
   1572    PORT_Memset(&p12enc->pfx, 0, sizeof(sec_PKCS12PFXItem));
   1573    if (!SEC_ASN1EncodeInteger(p12exp->arena, &(p12enc->pfx.version),
   1574                               SEC_PKCS12_VERSION)) {
   1575        PORT_SetError(SEC_ERROR_NO_MEMORY);
   1576        goto loser;
   1577    }
   1578 
   1579    /* set up the authenticated safe content info based on the
   1580     * type of integrity being used.  this should be changed to
   1581     * enforce integrity mode, but will not be implemented until
   1582     * it is confirmed that integrity must be in place
   1583     */
   1584    if (p12exp->integrityEnabled && !p12exp->pwdIntegrity) {
   1585        /* create public key integrity mode */
   1586        p12enc->aSafeCinfo = SEC_PKCS7CreateSignedData(
   1587            p12exp->integrityInfo.pubkeyInfo.cert,
   1588            certUsageEmailSigner,
   1589            p12exp->integrityInfo.pubkeyInfo.certDb,
   1590            p12exp->integrityInfo.pubkeyInfo.algorithm,
   1591            NULL,
   1592            p12exp->pwfn,
   1593            p12exp->pwfnarg);
   1594        if (!p12enc->aSafeCinfo) {
   1595            goto loser;
   1596        }
   1597        if (SEC_PKCS7IncludeCertChain(p12enc->aSafeCinfo, NULL) != SECSuccess) {
   1598            goto loser;
   1599        }
   1600        PORT_CheckSuccess(SEC_PKCS7AddSigningTime(p12enc->aSafeCinfo));
   1601    } else {
   1602        p12enc->aSafeCinfo = SEC_PKCS7CreateData();
   1603 
   1604        /* init password pased integrity mode */
   1605        if (p12exp->integrityEnabled) {
   1606            PK11SymKey *symKey;
   1607            CK_MECHANISM_TYPE hmacMechType;
   1608            SECOidTag hmacAlgTag;
   1609            SECOidTag hashAlgTag;
   1610 
   1611            salt = sec_pkcs12_generate_salt();
   1612 
   1613            /* zero out macData and set values */
   1614            PORT_Memset(&p12enc->mac, 0, sizeof(sec_PKCS12MacData));
   1615 
   1616            if (!salt) {
   1617                PORT_SetError(SEC_ERROR_NO_MEMORY);
   1618                goto loser;
   1619            }
   1620            if (SECITEM_CopyItem(p12exp->arena, &(p12enc->mac.macSalt), salt) != SECSuccess) {
   1621                PORT_SetError(SEC_ERROR_NO_MEMORY);
   1622                goto loser;
   1623            }
   1624            if (!SEC_ASN1EncodeInteger(p12exp->arena, &(p12enc->mac.iter),
   1625                                       NSS_PBE_DEFAULT_ITERATION_COUNT)) {
   1626                goto loser;
   1627            }
   1628 
   1629            /* generate HMAC key */
   1630            if (!sec_pkcs12_convert_item_to_unicode(NULL, &pwd,
   1631                                                    p12exp->integrityInfo.pwdInfo.password, PR_TRUE,
   1632                                                    PR_TRUE, PR_TRUE)) {
   1633                goto loser;
   1634            }
   1635 
   1636            /* create the digest info */
   1637            hmacAlgTag = p12exp->integrityInfo.pwdInfo.algorithm;
   1638            hashAlgTag = HASH_GetHashOidTagByHMACOidTag(hmacAlgTag);
   1639            if (hashAlgTag != SEC_OID_UNKNOWN) {
   1640                /* if the application asks for hmac explicitly, then use
   1641                 * pkcs5v2 mac1 encoding */
   1642                SECAlgorithmID *algID;
   1643                int keyLength;
   1644 
   1645                keyLength = HASH_ResultLenByOidTag(hashAlgTag);
   1646                if (keyLength == 0) {
   1647                    PORT_SetError(SEC_ERROR_INVALID_ALGORITHM);
   1648                    return NULL;
   1649                }
   1650                /* create PB_MAC1 params */
   1651                algID = PK11_CreatePBEV2AlgorithmID(SEC_OID_PKCS5_PBMAC1,
   1652                                                    hmacAlgTag,
   1653                                                    hmacAlgTag, keyLength,
   1654                                                    NSS_PBE_DEFAULT_ITERATION_COUNT,
   1655                                                    &p12enc->mac.macSalt);
   1656                if (algID == NULL) {
   1657                    goto loser;
   1658                }
   1659                rv = SECOID_CopyAlgorithmID(p12enc->arena,
   1660                                            &p12enc->mac.safeMac.digestAlgorithm,
   1661                                            algID);
   1662                SECOID_DestroyAlgorithmID(algID, PR_TRUE);
   1663                if (rv != SECSuccess) {
   1664                    PORT_SetError(SEC_ERROR_NO_MEMORY);
   1665                    goto loser;
   1666                }
   1667            } else if (HASH_GetHashTypeByOidTag(hmacAlgTag) != HASH_AlgNULL) {
   1668                /* encode the algid now for sec_pkcs12_integrity_key to use */
   1669                /* this must be a valid hash function, SECOID_SetAlgorithmID
   1670                 * knows to encode the hash algid with an explicit
   1671                 * null parameter */
   1672                rv = SECOID_SetAlgorithmID(p12enc->arena,
   1673                                           &p12enc->mac.safeMac.digestAlgorithm,
   1674                                           hmacAlgTag, NULL);
   1675                if (rv != SECSuccess) {
   1676                    goto loser;
   1677                }
   1678            } else {
   1679                PORT_SetError(SEC_ERROR_INVALID_ALGORITHM);
   1680                goto loser;
   1681            }
   1682 
   1683            /* generate HMAC key */
   1684            SECITEM_ZfreeItem(salt, PR_TRUE);
   1685            salt = NULL;
   1686            symKey = sec_pkcs12_integrity_key(p12exp->slot, &p12enc->mac,
   1687                                              &pwd, &hmacMechType, PR_FALSE,
   1688                                              p12exp->wincx);
   1689            SECITEM_ZfreeItem(&pwd, PR_FALSE);
   1690 
   1691            if (!symKey) {
   1692                goto loser;
   1693            }
   1694 
   1695            /* initialize HMAC */
   1696            p12enc->hmacCx = PK11_CreateContextBySymKey(hmacMechType,
   1697                                                        CKA_SIGN, symKey, &ignore);
   1698 
   1699            PK11_FreeSymKey(symKey);
   1700            if (!p12enc->hmacCx) {
   1701                PORT_SetError(SEC_ERROR_NO_MEMORY);
   1702                goto loser;
   1703            }
   1704            rv = PK11_DigestBegin(p12enc->hmacCx);
   1705            if (rv != SECSuccess)
   1706                goto loser;
   1707        }
   1708    }
   1709 
   1710    if (!p12enc->aSafeCinfo) {
   1711        goto loser;
   1712    }
   1713 
   1714    PORT_ArenaUnmark(p12exp->arena, mark);
   1715 
   1716    return p12enc;
   1717 
   1718 loser:
   1719    sec_pkcs12_encoder_destroy_context(p12enc);
   1720    if (p12exp->arena != NULL) {
   1721        PORT_ArenaRelease(p12exp->arena, mark);
   1722    }
   1723    if (salt) {
   1724        SECITEM_ZfreeItem(salt, PR_TRUE);
   1725    }
   1726    if (pwd.data) {
   1727        SECITEM_ZfreeItem(&pwd, PR_FALSE);
   1728    }
   1729 
   1730    return NULL;
   1731 }
   1732 
   1733 /* The outermost ASN.1 encoder calls this function for output.
   1734 ** This function calls back to the library caller's output routine,
   1735 ** which typically writes to a PKCS12 file.
   1736 */
   1737 static void
   1738 sec_P12A1OutputCB_Outer(void *arg, const char *buf, unsigned long len,
   1739                        int depth, SEC_ASN1EncodingPart data_kind)
   1740 {
   1741    struct sec_pkcs12_encoder_output *output;
   1742 
   1743    output = (struct sec_pkcs12_encoder_output *)arg;
   1744    (*output->outputfn)(output->outputarg, buf, len);
   1745 }
   1746 
   1747 /* The "middle" and "inner" ASN.1 encoders call this function to output.
   1748 ** This function does HMACing, if appropriate, and then buffers the data.
   1749 ** The buffered data is eventually passed down to the underlying PKCS7 encoder.
   1750 */
   1751 static void
   1752 sec_P12A1OutputCB_HmacP7Update(void *arg, const char *buf,
   1753                               unsigned long len,
   1754                               int depth,
   1755                               SEC_ASN1EncodingPart data_kind)
   1756 {
   1757    sec_pkcs12OutputBuffer *bufcx = (sec_pkcs12OutputBuffer *)arg;
   1758 
   1759    if (!buf || !len)
   1760        return;
   1761 
   1762    if (bufcx->hmacCx) {
   1763        PK11_DigestOp(bufcx->hmacCx, (unsigned char *)buf, len);
   1764    }
   1765 
   1766    /* buffer */
   1767    if (bufcx->numBytes > 0) {
   1768        int toCopy;
   1769        if (len + bufcx->numBytes <= bufcx->bufBytes) {
   1770            memcpy(bufcx->buf + bufcx->numBytes, buf, len);
   1771            bufcx->numBytes += len;
   1772            if (bufcx->numBytes < bufcx->bufBytes)
   1773                return;
   1774            SEC_PKCS7EncoderUpdate(bufcx->p7eCx, bufcx->buf, bufcx->bufBytes);
   1775            bufcx->numBytes = 0;
   1776            return;
   1777        }
   1778        toCopy = bufcx->bufBytes - bufcx->numBytes;
   1779        memcpy(bufcx->buf + bufcx->numBytes, buf, toCopy);
   1780        SEC_PKCS7EncoderUpdate(bufcx->p7eCx, bufcx->buf, bufcx->bufBytes);
   1781        bufcx->numBytes = 0;
   1782        len -= toCopy;
   1783        buf += toCopy;
   1784    }
   1785    /* buffer is presently empty */
   1786    if (len >= bufcx->bufBytes) {
   1787        /* Just pass it through */
   1788        SEC_PKCS7EncoderUpdate(bufcx->p7eCx, buf, len);
   1789    } else {
   1790        /* copy it all into the buffer, and return */
   1791        memcpy(bufcx->buf, buf, len);
   1792        bufcx->numBytes = len;
   1793    }
   1794 }
   1795 
   1796 void
   1797 sec_FlushPkcs12OutputBuffer(sec_pkcs12OutputBuffer *bufcx)
   1798 {
   1799    if (bufcx->numBytes > 0) {
   1800        SEC_PKCS7EncoderUpdate(bufcx->p7eCx, bufcx->buf, bufcx->numBytes);
   1801        bufcx->numBytes = 0;
   1802    }
   1803 }
   1804 
   1805 /* Feeds the output of a PKCS7 encoder into the next outward ASN.1 encoder.
   1806 ** This function is used by both the inner and middle PCS7 encoders.
   1807 */
   1808 static void
   1809 sec_P12P7OutputCB_CallA1Update(void *arg, const char *buf, unsigned long len)
   1810 {
   1811    SEC_ASN1EncoderContext *cx = (SEC_ASN1EncoderContext *)arg;
   1812 
   1813    if (!buf || !len)
   1814        return;
   1815 
   1816    SEC_ASN1EncoderUpdate(cx, buf, len);
   1817 }
   1818 
   1819 /* this function encodes content infos which are part of the
   1820 * sequence of content infos labeled AuthenticatedSafes
   1821 */
   1822 static SECStatus
   1823 sec_pkcs12_encoder_asafe_process(sec_PKCS12EncoderContext *p12ecx)
   1824 {
   1825    SEC_PKCS7EncoderContext *innerP7ecx;
   1826    SEC_PKCS7ContentInfo *cinfo;
   1827    PK11SymKey *bulkKey = NULL;
   1828    SEC_ASN1EncoderContext *innerA1ecx = NULL;
   1829    SECStatus rv = SECSuccess;
   1830 
   1831    if (p12ecx->currentSafe < p12ecx->p12exp->authSafe.safeCount) {
   1832        SEC_PKCS12SafeInfo *safeInfo;
   1833        SECOidTag cinfoType;
   1834 
   1835        safeInfo = p12ecx->p12exp->safeInfos[p12ecx->currentSafe];
   1836 
   1837        /* skip empty safes */
   1838        if (safeInfo->itemCount == 0) {
   1839            return SECSuccess;
   1840        }
   1841 
   1842        cinfo = safeInfo->cinfo;
   1843        cinfoType = SEC_PKCS7ContentType(cinfo);
   1844 
   1845        /* determine the safe type and set the appropriate argument */
   1846        switch (cinfoType) {
   1847            case SEC_OID_PKCS7_DATA:
   1848            case SEC_OID_PKCS7_ENVELOPED_DATA:
   1849                break;
   1850            case SEC_OID_PKCS7_ENCRYPTED_DATA:
   1851                bulkKey = safeInfo->encryptionKey;
   1852                PK11_SetSymKeyUserData(bulkKey, &safeInfo->pwitem, NULL);
   1853                break;
   1854            default:
   1855                return SECFailure;
   1856        }
   1857 
   1858        /* start the PKCS7 encoder */
   1859        innerP7ecx = SEC_PKCS7EncoderStart(cinfo,
   1860                                           sec_P12P7OutputCB_CallA1Update,
   1861                                           p12ecx->middleA1ecx, bulkKey);
   1862        if (!innerP7ecx) {
   1863            goto loser;
   1864        }
   1865 
   1866        /* encode safe contents */
   1867        p12ecx->innerBuf.p7eCx = innerP7ecx;
   1868        p12ecx->innerBuf.hmacCx = NULL;
   1869        p12ecx->innerBuf.numBytes = 0;
   1870        p12ecx->innerBuf.bufBytes = sizeof p12ecx->innerBuf.buf;
   1871 
   1872        innerA1ecx = SEC_ASN1EncoderStart(safeInfo->safe,
   1873                                          sec_PKCS12SafeContentsTemplate,
   1874                                          sec_P12A1OutputCB_HmacP7Update,
   1875                                          &p12ecx->innerBuf);
   1876        if (!innerA1ecx) {
   1877            goto loser;
   1878        }
   1879        rv = SEC_ASN1EncoderUpdate(innerA1ecx, NULL, 0);
   1880        SEC_ASN1EncoderFinish(innerA1ecx);
   1881        sec_FlushPkcs12OutputBuffer(&p12ecx->innerBuf);
   1882        innerA1ecx = NULL;
   1883        if (rv != SECSuccess) {
   1884            goto loser;
   1885        }
   1886 
   1887        /* finish up safe content info */
   1888        rv = SEC_PKCS7EncoderFinish(innerP7ecx, p12ecx->p12exp->pwfn,
   1889                                    p12ecx->p12exp->pwfnarg);
   1890        if (rv != SECSuccess) {
   1891            goto loser;
   1892        }
   1893    }
   1894    memset(&p12ecx->innerBuf, 0, sizeof p12ecx->innerBuf);
   1895    return SECSuccess;
   1896 
   1897 loser:
   1898    if (innerP7ecx) {
   1899        SEC_PKCS7EncoderFinish(innerP7ecx, p12ecx->p12exp->pwfn,
   1900                               p12ecx->p12exp->pwfnarg);
   1901    }
   1902 
   1903    if (innerA1ecx) {
   1904        SEC_ASN1EncoderFinish(innerA1ecx);
   1905    }
   1906    memset(&p12ecx->innerBuf, 0, sizeof p12ecx->innerBuf);
   1907    return SECFailure;
   1908 }
   1909 
   1910 /* finish the HMAC and encode the macData so that it can be
   1911 * encoded.
   1912 */
   1913 static SECStatus
   1914 sec_Pkcs12FinishMac(sec_PKCS12EncoderContext *p12ecx)
   1915 {
   1916    unsigned char hmacData[HASH_LENGTH_MAX];
   1917    unsigned int hmacLen;
   1918    SECStatus rv;
   1919    SGNDigestInfo *di = NULL;
   1920    void *dummy;
   1921 
   1922    if (!p12ecx) {
   1923        return SECFailure;
   1924    }
   1925 
   1926    /* make sure we are using password integrity mode */
   1927    if (!p12ecx->p12exp->integrityEnabled) {
   1928        return SECSuccess;
   1929    }
   1930 
   1931    if (!p12ecx->p12exp->pwdIntegrity) {
   1932        return SECSuccess;
   1933    }
   1934 
   1935    /* finish the hmac */
   1936 
   1937    rv = PK11_DigestFinal(p12ecx->hmacCx, hmacData, &hmacLen, HASH_LENGTH_MAX);
   1938 
   1939    if (rv != SECSuccess) {
   1940        PORT_SetError(SEC_ERROR_NO_MEMORY);
   1941        goto loser;
   1942    }
   1943 
   1944    /* finish the digest info, algorithm ID is already set */
   1945    rv = SECITEM_MakeItem(p12ecx->arena, &p12ecx->mac.safeMac.digest,
   1946                          hmacData, hmacLen);
   1947    if (rv != SECSuccess) {
   1948        goto loser;
   1949    }
   1950 
   1951    /* encode the mac data */
   1952    dummy = SEC_ASN1EncodeItem(p12ecx->arena, &p12ecx->pfx.encodedMacData,
   1953                               &p12ecx->mac, sec_PKCS12MacDataTemplate);
   1954    if (!dummy) {
   1955        PORT_SetError(SEC_ERROR_NO_MEMORY);
   1956        rv = SECFailure;
   1957    }
   1958 
   1959 loser:
   1960    if (di) {
   1961        SGN_DestroyDigestInfo(di);
   1962    }
   1963    PK11_DestroyContext(p12ecx->hmacCx, PR_TRUE);
   1964    p12ecx->hmacCx = NULL;
   1965    PORT_Memset(hmacData, 0, hmacLen);
   1966 
   1967    return rv;
   1968 }
   1969 
   1970 /* pfx notify function for ASN1 encoder.
   1971 * We want to stop encoding once we reach the authenticated safe.
   1972 * At that point, the encoder will be updated via streaming
   1973 * as the authenticated safe is  encoded.
   1974 */
   1975 static void
   1976 sec_pkcs12_encoder_pfx_notify(void *arg, PRBool before, void *dest, int real_depth)
   1977 {
   1978    sec_PKCS12EncoderContext *p12ecx;
   1979 
   1980    if (!before) {
   1981        return;
   1982    }
   1983 
   1984    /* look for authenticated safe */
   1985    p12ecx = (sec_PKCS12EncoderContext *)arg;
   1986    if (dest != &p12ecx->pfx.encodedAuthSafe) {
   1987        return;
   1988    }
   1989 
   1990    SEC_ASN1EncoderSetTakeFromBuf(p12ecx->outerA1ecx);
   1991    SEC_ASN1EncoderSetStreaming(p12ecx->outerA1ecx);
   1992    SEC_ASN1EncoderClearNotifyProc(p12ecx->outerA1ecx);
   1993 }
   1994 
   1995 /* SEC_PKCS12Encode
   1996 *      Encodes the PFX item and returns it to the output function, via
   1997 *      callback.  the output function must be capable of multiple updates.
   1998 *
   1999 *      p12exp - the export context
   2000 *      output - the output function callback, will be called more than once,
   2001 *               must be able to accept streaming data.
   2002 *      outputarg - argument for the output callback.
   2003 */
   2004 SECStatus
   2005 SEC_PKCS12Encode(SEC_PKCS12ExportContext *p12exp,
   2006                 SEC_PKCS12EncoderOutputCallback output, void *outputarg)
   2007 {
   2008    sec_PKCS12EncoderContext *p12enc;
   2009    struct sec_pkcs12_encoder_output outInfo;
   2010    SECStatus rv;
   2011 
   2012    if (!p12exp || !output) {
   2013        return SECFailure;
   2014    }
   2015 
   2016    /* get the encoder context */
   2017    p12enc = sec_pkcs12_encoder_start_context(p12exp);
   2018    if (!p12enc) {
   2019        return SECFailure;
   2020    }
   2021 
   2022    outInfo.outputfn = output;
   2023    outInfo.outputarg = outputarg;
   2024 
   2025    /* set up PFX encoder, the "outer" encoder.  Set it for streaming */
   2026    p12enc->outerA1ecx = SEC_ASN1EncoderStart(&p12enc->pfx,
   2027                                              sec_PKCS12PFXItemTemplate,
   2028                                              sec_P12A1OutputCB_Outer,
   2029                                              &outInfo);
   2030    if (!p12enc->outerA1ecx) {
   2031        PORT_SetError(SEC_ERROR_NO_MEMORY);
   2032        rv = SECFailure;
   2033        goto loser;
   2034    }
   2035    SEC_ASN1EncoderSetStreaming(p12enc->outerA1ecx);
   2036    SEC_ASN1EncoderSetNotifyProc(p12enc->outerA1ecx,
   2037                                 sec_pkcs12_encoder_pfx_notify, p12enc);
   2038    rv = SEC_ASN1EncoderUpdate(p12enc->outerA1ecx, NULL, 0);
   2039    if (rv != SECSuccess) {
   2040        rv = SECFailure;
   2041        goto loser;
   2042    }
   2043 
   2044    /* set up asafe cinfo - the output of the encoder feeds the PFX encoder */
   2045    p12enc->middleP7ecx = SEC_PKCS7EncoderStart(p12enc->aSafeCinfo,
   2046                                                sec_P12P7OutputCB_CallA1Update,
   2047                                                p12enc->outerA1ecx, NULL);
   2048    if (!p12enc->middleP7ecx) {
   2049        rv = SECFailure;
   2050        goto loser;
   2051    }
   2052 
   2053    /* encode asafe */
   2054    p12enc->middleBuf.p7eCx = p12enc->middleP7ecx;
   2055    p12enc->middleBuf.hmacCx = NULL;
   2056    p12enc->middleBuf.numBytes = 0;
   2057    p12enc->middleBuf.bufBytes = sizeof p12enc->middleBuf.buf;
   2058 
   2059    /* Setup the "inner ASN.1 encoder for Authenticated Safes.  */
   2060    if (p12enc->p12exp->integrityEnabled &&
   2061        p12enc->p12exp->pwdIntegrity) {
   2062        p12enc->middleBuf.hmacCx = p12enc->hmacCx;
   2063    }
   2064    p12enc->middleA1ecx = SEC_ASN1EncoderStart(&p12enc->p12exp->authSafe,
   2065                                               sec_PKCS12AuthenticatedSafeTemplate,
   2066                                               sec_P12A1OutputCB_HmacP7Update,
   2067                                               &p12enc->middleBuf);
   2068    if (!p12enc->middleA1ecx) {
   2069        rv = SECFailure;
   2070        goto loser;
   2071    }
   2072    SEC_ASN1EncoderSetStreaming(p12enc->middleA1ecx);
   2073    SEC_ASN1EncoderSetTakeFromBuf(p12enc->middleA1ecx);
   2074 
   2075    /* encode each of the safes */
   2076    while (p12enc->currentSafe != p12enc->p12exp->safeInfoCount) {
   2077        sec_pkcs12_encoder_asafe_process(p12enc);
   2078        p12enc->currentSafe++;
   2079    }
   2080    SEC_ASN1EncoderClearTakeFromBuf(p12enc->middleA1ecx);
   2081    SEC_ASN1EncoderClearStreaming(p12enc->middleA1ecx);
   2082    SEC_ASN1EncoderUpdate(p12enc->middleA1ecx, NULL, 0);
   2083    SEC_ASN1EncoderFinish(p12enc->middleA1ecx);
   2084    p12enc->middleA1ecx = NULL;
   2085 
   2086    sec_FlushPkcs12OutputBuffer(&p12enc->middleBuf);
   2087 
   2088    /* finish the encoding of the authenticated safes */
   2089    rv = SEC_PKCS7EncoderFinish(p12enc->middleP7ecx, p12exp->pwfn,
   2090                                p12exp->pwfnarg);
   2091    p12enc->middleP7ecx = NULL;
   2092    if (rv != SECSuccess) {
   2093        goto loser;
   2094    }
   2095 
   2096    SEC_ASN1EncoderClearTakeFromBuf(p12enc->outerA1ecx);
   2097    SEC_ASN1EncoderClearStreaming(p12enc->outerA1ecx);
   2098 
   2099    /* update the mac, if necessary */
   2100    rv = sec_Pkcs12FinishMac(p12enc);
   2101    if (rv != SECSuccess) {
   2102        goto loser;
   2103    }
   2104 
   2105    /* finish encoding the pfx */
   2106    rv = SEC_ASN1EncoderUpdate(p12enc->outerA1ecx, NULL, 0);
   2107 
   2108    SEC_ASN1EncoderFinish(p12enc->outerA1ecx);
   2109    p12enc->outerA1ecx = NULL;
   2110 
   2111 loser:
   2112    sec_pkcs12_encoder_destroy_context(p12enc);
   2113    return rv;
   2114 }
   2115 
   2116 void
   2117 SEC_PKCS12DestroyExportContext(SEC_PKCS12ExportContext *p12ecx)
   2118 {
   2119    int i = 0;
   2120 
   2121    if (!p12ecx) {
   2122        return;
   2123    }
   2124 
   2125    if (p12ecx->safeInfos) {
   2126        i = 0;
   2127        while (p12ecx->safeInfos[i] != NULL) {
   2128            if (p12ecx->safeInfos[i]->encryptionKey) {
   2129                PK11_FreeSymKey(p12ecx->safeInfos[i]->encryptionKey);
   2130            }
   2131            if (p12ecx->safeInfos[i]->cinfo) {
   2132                SEC_PKCS7DestroyContentInfo(p12ecx->safeInfos[i]->cinfo);
   2133            }
   2134            i++;
   2135        }
   2136    }
   2137 
   2138    PK11_FreeSlot(p12ecx->slot);
   2139 
   2140    PORT_FreeArena(p12ecx->arena, PR_TRUE);
   2141 }