Back to index

lightning-sunbird  0.9+nobinonly
p12e.c
Go to the documentation of this file.
00001 /* ***** BEGIN LICENSE BLOCK *****
00002  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00003  *
00004  * The contents of this file are subject to the Mozilla Public License Version
00005  * 1.1 (the "License"); you may not use this file except in compliance with
00006  * the License. You may obtain a copy of the License at
00007  * http://www.mozilla.org/MPL/
00008  *
00009  * Software distributed under the License is distributed on an "AS IS" basis,
00010  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00011  * for the specific language governing rights and limitations under the
00012  * License.
00013  *
00014  * The Original Code is the Netscape security libraries.
00015  *
00016  * The Initial Developer of the Original Code is
00017  * Netscape Communications Corporation.
00018  * Portions created by the Initial Developer are Copyright (C) 1994-2000
00019  * the Initial Developer. All Rights Reserved.
00020  *
00021  * Contributor(s):
00022  *
00023  * Alternatively, the contents of this file may be used under the terms of
00024  * either the GNU General Public License Version 2 or later (the "GPL"), or
00025  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00026  * in which case the provisions of the GPL or the LGPL are applicable instead
00027  * of those above. If you wish to allow use of your version of this file only
00028  * under the terms of either the GPL or the LGPL, and not to allow others to
00029  * use your version of this file under the terms of the MPL, indicate your
00030  * decision by deleting the provisions above and replace them with the notice
00031  * and other provisions required by the GPL or the LGPL. If you do not delete
00032  * the provisions above, a recipient may use your version of this file under
00033  * the terms of any one of the MPL, the GPL or the LGPL.
00034  *
00035  * ***** END LICENSE BLOCK ***** */
00036 
00037 #include "nssrenam.h"
00038 #include "p12t.h"
00039 #include "p12.h"
00040 #include "plarena.h"
00041 #include "secitem.h"
00042 #include "secoid.h"
00043 #include "seccomon.h"
00044 #include "secport.h"
00045 #include "cert.h"
00046 #include "secpkcs7.h"
00047 #include "secasn1.h"
00048 #include "secerr.h"
00049 #include "pk11func.h"
00050 #include "p12plcy.h"
00051 #include "p12local.h"
00052 #include "prcpucfg.h"
00053 
00054 /*
00055 ** This PKCS12 file encoder uses numerous nested ASN.1 and PKCS7 encoder
00056 ** contexts.  It can be difficult to keep straight.  Here's a picture:
00057 **
00058 **  "outer"  ASN.1 encoder.  The output goes to the library caller's CB.
00059 **  "middle" PKCS7 encoder.  Feeds    the "outer" ASN.1 encoder.
00060 **  "middle" ASN1  encoder.  Encodes  the encrypted aSafes. 
00061 **                           Feeds    the "middle" P7 encoder above.
00062 **  "inner"  PKCS7 encoder.  Encrypts the "authenticated Safes" (aSafes)
00063 **                           Feeds    the "middle" ASN.1 encoder above.
00064 **  "inner"  ASN.1 encoder.  Encodes  the unencrypted aSafes.  
00065 **                           Feeds    the "inner" P7 enocder above.
00066 **
00067 ** Buffering has been added at each point where the output of an ASN.1
00068 ** encoder feeds the input of a PKCS7 encoder.
00069 */
00070 
00071 /*********************************
00072  * Output buffer object, used to buffer output from ASN.1 encoder
00073  * before passing data on down to the next PKCS7 encoder.
00074  *********************************/
00075 
00076 #define PK12_OUTPUT_BUFFER_SIZE  8192
00077 
00078 struct sec_pkcs12OutputBufferStr {
00079     SEC_PKCS7EncoderContext * p7eCx;
00080     PK11Context             * hmacCx;
00081     unsigned int              numBytes;
00082     unsigned int              bufBytes;
00083              char             buf[PK12_OUTPUT_BUFFER_SIZE];
00084 };
00085 typedef struct sec_pkcs12OutputBufferStr sec_pkcs12OutputBuffer;
00086 
00087 /*********************************
00088  * Structures used in exporting the PKCS 12 blob
00089  *********************************/
00090 
00091 /* A SafeInfo is used for each ContentInfo which makes up the
00092  * sequence of safes in the AuthenticatedSafe portion of the
00093  * PFX structure.
00094  */
00095 struct SEC_PKCS12SafeInfoStr {
00096     PRArenaPool *arena;
00097 
00098     /* information for setting up password encryption */
00099     SECItem pwitem;
00100     SECOidTag algorithm;
00101     PK11SymKey *encryptionKey;
00102 
00103     /* how many items have been stored in this safe,
00104      * we will skip any safe which does not contain any
00105      * items
00106       */
00107     unsigned int itemCount;
00108 
00109     /* the content info for the safe */
00110     SEC_PKCS7ContentInfo *cinfo;
00111 
00112     sec_PKCS12SafeContents *safe;
00113 };
00114 
00115 /* An opaque structure which contains information needed for exporting
00116  * certificates and keys through PKCS 12.
00117  */
00118 struct SEC_PKCS12ExportContextStr {
00119     PRArenaPool *arena;
00120     PK11SlotInfo *slot;
00121     void *wincx;
00122 
00123     /* integrity information */
00124     PRBool integrityEnabled;
00125     PRBool    pwdIntegrity;
00126     union {
00127        struct sec_PKCS12PasswordModeInfo pwdInfo;
00128        struct sec_PKCS12PublicKeyModeInfo pubkeyInfo;
00129     } integrityInfo; 
00130 
00131     /* helper functions */
00132     /* retrieve the password call back */
00133     SECKEYGetPasswordKey pwfn;
00134     void *pwfnarg;
00135 
00136     /* safe contents bags */
00137     SEC_PKCS12SafeInfo **safeInfos;
00138     unsigned int safeInfoCount;
00139 
00140     /* the sequence of safes */
00141     sec_PKCS12AuthenticatedSafe authSafe;
00142 
00143     /* information needing deletion */
00144     CERTCertificate **certList;
00145 };
00146 
00147 /* structures for passing information to encoder callbacks when processing
00148  * data through the ASN1 engine.
00149  */
00150 struct sec_pkcs12_encoder_output {
00151     SEC_PKCS12EncoderOutputCallback outputfn;
00152     void *outputarg;
00153 };
00154 
00155 struct sec_pkcs12_hmac_and_output_info {
00156     void *arg;
00157     struct sec_pkcs12_encoder_output output;
00158 };
00159 
00160 /* An encoder context which is used for the actual encoding
00161  * portion of PKCS 12. 
00162  */
00163 typedef struct sec_PKCS12EncoderContextStr {
00164     PRArenaPool *arena;
00165     SEC_PKCS12ExportContext *p12exp;
00166     PK11SymKey *encryptionKey;
00167 
00168     /* encoder information - this is set up based on whether 
00169      * password based or public key pased privacy is being used
00170      */
00171     SEC_ASN1EncoderContext *outerA1ecx;
00172     union {
00173        struct sec_pkcs12_hmac_and_output_info hmacAndOutputInfo;
00174        struct sec_pkcs12_encoder_output       encOutput;
00175     } output;
00176 
00177     /* structures for encoding of PFX and MAC */
00178     sec_PKCS12PFXItem        pfx;
00179     sec_PKCS12MacData        mac;
00180 
00181     /* authenticated safe encoding tracking information */
00182     SEC_PKCS7ContentInfo    *aSafeCinfo;
00183     SEC_PKCS7EncoderContext *middleP7ecx;
00184     SEC_ASN1EncoderContext  *middleA1ecx;
00185     unsigned int             currentSafe;
00186 
00187     /* hmac context */
00188     PK11Context             *hmacCx;
00189 
00190     /* output buffers */
00191     sec_pkcs12OutputBuffer  middleBuf;
00192     sec_pkcs12OutputBuffer  innerBuf;
00193 
00194 } sec_PKCS12EncoderContext;
00195 
00196 
00197 /*********************************
00198  * Export setup routines
00199  *********************************/
00200 
00201 /* SEC_PKCS12CreateExportContext 
00202  *   Creates an export context and sets the unicode and password retrieval
00203  *   callbacks.  This is the first call which must be made when exporting
00204  *   a PKCS 12 blob.
00205  *
00206  * pwfn, pwfnarg - password retrieval callback and argument.  these are
00207  *               required for password-authentication mode.
00208  */
00209 SEC_PKCS12ExportContext *
00210 SEC_PKCS12CreateExportContext(SECKEYGetPasswordKey pwfn, void *pwfnarg,  
00211                            PK11SlotInfo *slot, void *wincx)
00212 {
00213     PRArenaPool *arena = NULL;
00214     SEC_PKCS12ExportContext *p12ctxt = NULL;
00215 
00216     /* allocate the arena and create the context */
00217     arena = PORT_NewArena(4096);
00218     if(!arena) {
00219        PORT_SetError(SEC_ERROR_NO_MEMORY);
00220        return NULL;
00221     }
00222 
00223     p12ctxt = (SEC_PKCS12ExportContext *)PORT_ArenaZAlloc(arena, 
00224                                    sizeof(SEC_PKCS12ExportContext));
00225     if(!p12ctxt) {
00226        PORT_SetError(SEC_ERROR_NO_MEMORY);
00227        goto loser;
00228     }
00229 
00230     /* password callback for key retrieval */
00231     p12ctxt->pwfn = pwfn;
00232     p12ctxt->pwfnarg = pwfnarg;
00233 
00234     p12ctxt->integrityEnabled = PR_FALSE;
00235     p12ctxt->arena = arena;
00236     p12ctxt->wincx = wincx;
00237     p12ctxt->slot = (slot) ? PK11_ReferenceSlot(slot) : PK11_GetInternalSlot();
00238 
00239     return p12ctxt;
00240 
00241 loser:
00242     if(arena) {
00243        PORT_FreeArena(arena, PR_TRUE);
00244     }
00245 
00246     return NULL;
00247 }
00248 
00249 /* 
00250  * Adding integrity mode
00251  */
00252 
00253 /* SEC_PKCS12AddPasswordIntegrity 
00254  *     Add password integrity to the exported data.  If an integrity method
00255  *     has already been set, then return an error.
00256  *     
00257  *     p12ctxt - the export context
00258  *     pwitem - the password for integrity mode
00259  *     integAlg - the integrity algorithm to use for authentication.
00260  */
00261 SECStatus
00262 SEC_PKCS12AddPasswordIntegrity(SEC_PKCS12ExportContext *p12ctxt,
00263                             SECItem *pwitem, SECOidTag integAlg) 
00264 {                           
00265     if(!p12ctxt || p12ctxt->integrityEnabled) {
00266        return SECFailure;
00267     }
00268    
00269     /* set up integrity information */
00270     p12ctxt->pwdIntegrity = PR_TRUE;
00271     p12ctxt->integrityInfo.pwdInfo.password = 
00272         (SECItem*)PORT_ArenaZAlloc(p12ctxt->arena, sizeof(SECItem));
00273     if(!p12ctxt->integrityInfo.pwdInfo.password) {
00274        PORT_SetError(SEC_ERROR_NO_MEMORY);
00275        return SECFailure;
00276     }
00277     if(SECITEM_CopyItem(p12ctxt->arena, 
00278                      p12ctxt->integrityInfo.pwdInfo.password, pwitem)
00279               != SECSuccess) {
00280        PORT_SetError(SEC_ERROR_NO_MEMORY);
00281        return SECFailure;
00282     }
00283     p12ctxt->integrityInfo.pwdInfo.algorithm = integAlg;
00284     p12ctxt->integrityEnabled = PR_TRUE;
00285 
00286     return SECSuccess;
00287 }
00288 
00289 /* SEC_PKCS12AddPublicKeyIntegrity
00290  *     Add public key integrity to the exported data.  If an integrity method
00291  *     has already been set, then return an error.  The certificate must be
00292  *     allowed to be used as a signing cert.
00293  *     
00294  *     p12ctxt - the export context
00295  *     cert - signer certificate
00296  *     certDb - the certificate database
00297  *     algorithm - signing algorithm
00298  *     keySize - size of the signing key (?)
00299  */
00300 SECStatus
00301 SEC_PKCS12AddPublicKeyIntegrity(SEC_PKCS12ExportContext *p12ctxt,
00302                             CERTCertificate *cert, CERTCertDBHandle *certDb,
00303                             SECOidTag algorithm, int keySize)
00304 {
00305     if(!p12ctxt) {
00306        return SECFailure;
00307     }
00308     
00309     p12ctxt->integrityInfo.pubkeyInfo.cert = cert;
00310     p12ctxt->integrityInfo.pubkeyInfo.certDb = certDb;
00311     p12ctxt->integrityInfo.pubkeyInfo.algorithm = algorithm;
00312     p12ctxt->integrityInfo.pubkeyInfo.keySize = keySize;
00313     p12ctxt->integrityEnabled = PR_TRUE;
00314 
00315     return SECSuccess;
00316 }
00317 
00318 
00319 /*
00320  * Adding safes - encrypted (password/public key) or unencrypted
00321  *     Each of the safe creation routines return an opaque pointer which
00322  *     are later passed into the routines for exporting certificates and
00323  *     keys.
00324  */
00325 
00326 /* append the newly created safeInfo to list of safeInfos in the export
00327  * context.  
00328  */
00329 static SECStatus
00330 sec_pkcs12_append_safe_info(SEC_PKCS12ExportContext *p12ctxt, SEC_PKCS12SafeInfo *info)
00331 {
00332     void *mark = NULL, *dummy1 = NULL, *dummy2 = NULL;
00333 
00334     if(!p12ctxt || !info) {
00335        return SECFailure;
00336     }
00337 
00338     mark = PORT_ArenaMark(p12ctxt->arena);
00339 
00340     /* if no safeInfos have been set, create the list, otherwise expand it. */
00341     if(!p12ctxt->safeInfoCount) {
00342        p12ctxt->safeInfos = (SEC_PKCS12SafeInfo **)PORT_ArenaZAlloc(p12ctxt->arena, 
00343                                          2 * sizeof(SEC_PKCS12SafeInfo *));
00344        dummy1 = p12ctxt->safeInfos;
00345        p12ctxt->authSafe.encodedSafes = (SECItem **)PORT_ArenaZAlloc(p12ctxt->arena, 
00346                                    2 * sizeof(SECItem *));
00347        dummy2 = p12ctxt->authSafe.encodedSafes;
00348     } else {
00349        dummy1 = PORT_ArenaGrow(p12ctxt->arena, p12ctxt->safeInfos, 
00350                             (p12ctxt->safeInfoCount + 1) * sizeof(SEC_PKCS12SafeInfo *),
00351                             (p12ctxt->safeInfoCount + 2) * sizeof(SEC_PKCS12SafeInfo *));
00352        p12ctxt->safeInfos = (SEC_PKCS12SafeInfo **)dummy1;
00353        dummy2 = PORT_ArenaGrow(p12ctxt->arena, p12ctxt->authSafe.encodedSafes, 
00354                             (p12ctxt->authSafe.safeCount + 1) * sizeof(SECItem *),
00355                             (p12ctxt->authSafe.safeCount + 2) * sizeof(SECItem *));
00356        p12ctxt->authSafe.encodedSafes = (SECItem**)dummy2;
00357     }
00358     if(!dummy1 || !dummy2) {
00359        PORT_SetError(SEC_ERROR_NO_MEMORY);
00360        goto loser;
00361     }
00362 
00363     /* append the new safeInfo and null terminate the list */
00364     p12ctxt->safeInfos[p12ctxt->safeInfoCount] = info;
00365     p12ctxt->safeInfos[++p12ctxt->safeInfoCount] = NULL;
00366     p12ctxt->authSafe.encodedSafes[p12ctxt->authSafe.safeCount] = 
00367         (SECItem*)PORT_ArenaZAlloc(p12ctxt->arena, sizeof(SECItem));
00368     if(!p12ctxt->authSafe.encodedSafes[p12ctxt->authSafe.safeCount]) {
00369        PORT_SetError(SEC_ERROR_NO_MEMORY);
00370        goto loser;
00371     }
00372     p12ctxt->authSafe.encodedSafes[++p12ctxt->authSafe.safeCount] = NULL;
00373 
00374     PORT_ArenaUnmark(p12ctxt->arena, mark);
00375     return SECSuccess;
00376 
00377 loser:
00378     PORT_ArenaRelease(p12ctxt->arena, mark);
00379     return SECFailure;
00380 }
00381 
00382 /* SEC_PKCS12CreatePasswordPrivSafe
00383  *     Create a password privacy safe to store exported information in.
00384  *
00385  *     p12ctxt - export context
00386  *     pwitem - password for encryption
00387  *     privAlg - pbe algorithm through which encryption is done.
00388  */
00389 SEC_PKCS12SafeInfo *
00390 SEC_PKCS12CreatePasswordPrivSafe(SEC_PKCS12ExportContext *p12ctxt, 
00391                              SECItem *pwitem, SECOidTag privAlg)
00392 {
00393     SEC_PKCS12SafeInfo *safeInfo = NULL;
00394     void *mark = NULL;
00395     PK11SlotInfo *slot = NULL;
00396     SECAlgorithmID *algId;
00397     SECItem uniPwitem = {siBuffer, NULL, 0};
00398 
00399     if(!p12ctxt) {
00400        return NULL;
00401     }
00402 
00403     /* allocate the safe info */
00404     mark = PORT_ArenaMark(p12ctxt->arena);
00405     safeInfo = (SEC_PKCS12SafeInfo *)PORT_ArenaZAlloc(p12ctxt->arena, 
00406                                           sizeof(SEC_PKCS12SafeInfo));
00407     if(!safeInfo) {
00408        PORT_SetError(SEC_ERROR_NO_MEMORY);
00409        PORT_ArenaRelease(p12ctxt->arena, mark);
00410        return NULL;
00411     }
00412 
00413     safeInfo->itemCount = 0;
00414 
00415     /* create the encrypted safe */
00416     safeInfo->cinfo = SEC_PKCS7CreateEncryptedData(privAlg, 0, p12ctxt->pwfn, 
00417                                              p12ctxt->pwfnarg);
00418     if(!safeInfo->cinfo) {
00419        PORT_SetError(SEC_ERROR_NO_MEMORY);
00420        goto loser;
00421     }
00422     safeInfo->arena = p12ctxt->arena;
00423 
00424     /* convert the password to unicode */ 
00425     if(!sec_pkcs12_convert_item_to_unicode(NULL, &uniPwitem, pwitem,
00426                                           PR_TRUE, PR_TRUE, PR_TRUE)) {
00427        PORT_SetError(SEC_ERROR_NO_MEMORY);
00428        goto loser;
00429     }
00430     if(SECITEM_CopyItem(p12ctxt->arena, &safeInfo->pwitem, &uniPwitem) != SECSuccess) {
00431        PORT_SetError(SEC_ERROR_NO_MEMORY);
00432        goto loser;
00433     }
00434 
00435     /* generate the encryption key */
00436     slot = PK11_ReferenceSlot(p12ctxt->slot);
00437     if(!slot) {
00438        slot = PK11_GetInternalKeySlot();
00439        if(!slot) {
00440            PORT_SetError(SEC_ERROR_NO_MEMORY);
00441            goto loser;
00442        }
00443     }
00444 
00445     algId = SEC_PKCS7GetEncryptionAlgorithm(safeInfo->cinfo);
00446     safeInfo->encryptionKey = PK11_PBEKeyGen(slot, algId, &uniPwitem, 
00447                                         PR_FALSE, p12ctxt->wincx);
00448     if(!safeInfo->encryptionKey) {
00449        goto loser;
00450     }
00451 
00452     safeInfo->arena = p12ctxt->arena;
00453     safeInfo->safe = NULL;
00454     if(sec_pkcs12_append_safe_info(p12ctxt, safeInfo) != SECSuccess) {
00455        goto loser;
00456     }
00457 
00458     if(uniPwitem.data) {
00459        SECITEM_ZfreeItem(&uniPwitem, PR_FALSE);
00460     }
00461     PORT_ArenaUnmark(p12ctxt->arena, mark);
00462 
00463     if (slot) {
00464        PK11_FreeSlot(slot);
00465     }
00466     return safeInfo;
00467 
00468 loser:
00469     if (slot) {
00470        PK11_FreeSlot(slot);
00471     }
00472     if(safeInfo->cinfo) {
00473        SEC_PKCS7DestroyContentInfo(safeInfo->cinfo);
00474     }
00475 
00476     if(uniPwitem.data) {
00477        SECITEM_ZfreeItem(&uniPwitem, PR_FALSE);
00478     }
00479 
00480     PORT_ArenaRelease(p12ctxt->arena, mark);
00481     return NULL;
00482 }
00483 
00484 /* SEC_PKCS12CreateUnencryptedSafe 
00485  *     Creates an unencrypted safe within the export context.
00486  *
00487  *     p12ctxt - the export context 
00488  */
00489 SEC_PKCS12SafeInfo *
00490 SEC_PKCS12CreateUnencryptedSafe(SEC_PKCS12ExportContext *p12ctxt)
00491 {
00492     SEC_PKCS12SafeInfo *safeInfo = NULL;
00493     void *mark = NULL;
00494 
00495     if(!p12ctxt) {
00496        return NULL;
00497     }
00498 
00499     /* create the safe info */
00500     mark = PORT_ArenaMark(p12ctxt->arena);
00501     safeInfo = (SEC_PKCS12SafeInfo *)PORT_ArenaZAlloc(p12ctxt->arena, 
00502                                          sizeof(SEC_PKCS12SafeInfo));
00503     if(!safeInfo) {
00504        PORT_ArenaRelease(p12ctxt->arena, mark);
00505        PORT_SetError(SEC_ERROR_NO_MEMORY);
00506        return NULL;
00507     }
00508 
00509     safeInfo->itemCount = 0;
00510 
00511     /* create the safe content */
00512     safeInfo->cinfo = SEC_PKCS7CreateData();
00513     if(!safeInfo->cinfo) {
00514        PORT_SetError(SEC_ERROR_NO_MEMORY);
00515        goto loser;
00516     }
00517 
00518     if(sec_pkcs12_append_safe_info(p12ctxt, safeInfo) != SECSuccess) {
00519        goto loser;
00520     }
00521 
00522     PORT_ArenaUnmark(p12ctxt->arena, mark);
00523     return safeInfo;
00524 
00525 loser:
00526     if(safeInfo->cinfo) {
00527        SEC_PKCS7DestroyContentInfo(safeInfo->cinfo);
00528     }
00529 
00530     PORT_ArenaRelease(p12ctxt->arena, mark);
00531     return NULL;
00532 }
00533 
00534 /* SEC_PKCS12CreatePubKeyEncryptedSafe
00535  *     Creates a safe which is protected by public key encryption.  
00536  *
00537  *     p12ctxt - the export context
00538  *     certDb - the certificate database
00539  *     signer - the signer's certificate
00540  *     recipients - the list of recipient certificates.
00541  *     algorithm - the encryption algorithm to use
00542  *     keysize - the algorithms key size (?)
00543  */
00544 SEC_PKCS12SafeInfo *
00545 SEC_PKCS12CreatePubKeyEncryptedSafe(SEC_PKCS12ExportContext *p12ctxt,
00546                                 CERTCertDBHandle *certDb,
00547                                 CERTCertificate *signer,
00548                                 CERTCertificate **recipients,
00549                                 SECOidTag algorithm, int keysize) 
00550 {
00551     SEC_PKCS12SafeInfo *safeInfo = NULL;
00552     void *mark = NULL;
00553 
00554     if(!p12ctxt || !signer || !recipients || !(*recipients)) {
00555        return NULL;
00556     }
00557 
00558     /* allocate the safeInfo */
00559     mark = PORT_ArenaMark(p12ctxt->arena);
00560     safeInfo = (SEC_PKCS12SafeInfo *)PORT_ArenaZAlloc(p12ctxt->arena, 
00561                                                 sizeof(SEC_PKCS12SafeInfo));
00562     if(!safeInfo) {
00563        PORT_ArenaRelease(p12ctxt->arena, mark);
00564        PORT_SetError(SEC_ERROR_NO_MEMORY);
00565        return NULL;
00566     }
00567 
00568     safeInfo->itemCount = 0;
00569     safeInfo->arena = p12ctxt->arena;
00570 
00571     /* create the enveloped content info using certUsageEmailSigner currently.
00572      * XXX We need to eventually use something other than certUsageEmailSigner
00573      */
00574     safeInfo->cinfo = SEC_PKCS7CreateEnvelopedData(signer, certUsageEmailSigner,
00575                                    certDb, algorithm, keysize, 
00576                                    p12ctxt->pwfn, p12ctxt->pwfnarg);
00577     if(!safeInfo->cinfo) {
00578        PORT_SetError(SEC_ERROR_NO_MEMORY);
00579        goto loser;
00580     }
00581 
00582     /* add recipients */
00583     if(recipients) {
00584        unsigned int i = 0;
00585        while(recipients[i] != NULL) {
00586            SECStatus rv = SEC_PKCS7AddRecipient(safeInfo->cinfo, recipients[i],
00587                                           certUsageEmailRecipient, certDb);
00588            if(rv != SECSuccess) {
00589               goto loser;
00590            }
00591            i++;
00592        }
00593     }
00594 
00595     if(sec_pkcs12_append_safe_info(p12ctxt, safeInfo) != SECSuccess) {
00596        goto loser;
00597     }
00598 
00599     PORT_ArenaUnmark(p12ctxt->arena, mark);
00600     return safeInfo;
00601 
00602 loser:
00603     if(safeInfo->cinfo) {
00604        SEC_PKCS7DestroyContentInfo(safeInfo->cinfo);
00605        safeInfo->cinfo = NULL;
00606     }
00607 
00608     PORT_ArenaRelease(p12ctxt->arena, mark);
00609     return NULL;
00610 } 
00611 
00612 /*********************************
00613  * Routines to handle the exporting of the keys and certificates
00614  *********************************/
00615 
00616 /* creates a safe contents which safeBags will be appended to */
00617 sec_PKCS12SafeContents *
00618 sec_PKCS12CreateSafeContents(PRArenaPool *arena)
00619 {
00620     sec_PKCS12SafeContents *safeContents;
00621 
00622     if(arena == NULL) {
00623        return NULL; 
00624     }
00625 
00626     /* create the safe contents */
00627     safeContents = (sec_PKCS12SafeContents *)PORT_ArenaZAlloc(arena,
00628                                        sizeof(sec_PKCS12SafeContents));
00629     if(!safeContents) {
00630        PORT_SetError(SEC_ERROR_NO_MEMORY);
00631        goto loser;
00632     }
00633 
00634     /* set up the internal contents info */
00635     safeContents->safeBags = NULL;
00636     safeContents->arena = arena;
00637     safeContents->bagCount = 0;
00638 
00639     return safeContents;
00640 
00641 loser:
00642     return NULL;
00643 }   
00644 
00645 /* appends a safe bag to a safeContents using the specified arena. 
00646  */
00647 SECStatus
00648 sec_pkcs12_append_bag_to_safe_contents(PRArenaPool *arena, 
00649                                    sec_PKCS12SafeContents *safeContents,
00650                                    sec_PKCS12SafeBag *safeBag)
00651 {
00652     void *mark = NULL, *dummy = NULL;
00653 
00654     if(!arena || !safeBag || !safeContents) {
00655        return SECFailure;
00656     }
00657 
00658     mark = PORT_ArenaMark(arena);
00659     if(!mark) {
00660        PORT_SetError(SEC_ERROR_NO_MEMORY);
00661        return SECFailure;
00662     }
00663 
00664     /* allocate space for the list, or reallocate to increase space */
00665     if(!safeContents->safeBags) {
00666        safeContents->safeBags = (sec_PKCS12SafeBag **)PORT_ArenaZAlloc(arena, 
00667                                           (2 * sizeof(sec_PKCS12SafeBag *)));
00668        dummy = safeContents->safeBags;
00669        safeContents->bagCount = 0;
00670     } else {
00671        dummy = PORT_ArenaGrow(arena, safeContents->safeBags, 
00672                      (safeContents->bagCount + 1) * sizeof(sec_PKCS12SafeBag *),
00673                      (safeContents->bagCount + 2) * sizeof(sec_PKCS12SafeBag *));
00674        safeContents->safeBags = (sec_PKCS12SafeBag **)dummy;
00675     }
00676 
00677     if(!dummy) {
00678        PORT_ArenaRelease(arena, mark);
00679        PORT_SetError(SEC_ERROR_NO_MEMORY);
00680        return SECFailure;
00681     }
00682 
00683     /* append the bag at the end and null terminate the list */
00684     safeContents->safeBags[safeContents->bagCount++] = safeBag;
00685     safeContents->safeBags[safeContents->bagCount] = NULL;
00686 
00687     PORT_ArenaUnmark(arena, mark);
00688 
00689     return SECSuccess;
00690 }
00691 
00692 /* appends a safeBag to a specific safeInfo.
00693  */
00694 SECStatus
00695 sec_pkcs12_append_bag(SEC_PKCS12ExportContext *p12ctxt, 
00696                     SEC_PKCS12SafeInfo *safeInfo, sec_PKCS12SafeBag *safeBag)
00697 {
00698     sec_PKCS12SafeContents *dest;
00699     SECStatus rv = SECFailure;
00700 
00701     if(!p12ctxt || !safeBag || !safeInfo) {
00702        return SECFailure;
00703     }
00704 
00705     if(!safeInfo->safe) {
00706        safeInfo->safe = sec_PKCS12CreateSafeContents(p12ctxt->arena);
00707        if(!safeInfo->safe) {
00708            return SECFailure;
00709        }
00710     }
00711 
00712     dest = safeInfo->safe;
00713     rv = sec_pkcs12_append_bag_to_safe_contents(p12ctxt->arena, dest, safeBag);
00714     if(rv == SECSuccess) {
00715        safeInfo->itemCount++;
00716     }
00717     
00718     return rv;
00719 } 
00720 
00721 /* Creates a safeBag of the specified type, and if bagData is specified,
00722  * the contents are set.  The contents could be set later by the calling
00723  * routine.
00724  */
00725 sec_PKCS12SafeBag *
00726 sec_PKCS12CreateSafeBag(SEC_PKCS12ExportContext *p12ctxt, SECOidTag bagType, 
00727                      void *bagData)
00728 {
00729     sec_PKCS12SafeBag *safeBag;
00730     PRBool setName = PR_TRUE;
00731     void *mark = NULL;
00732     SECStatus rv = SECSuccess;
00733     SECOidData *oidData = NULL;
00734 
00735     if(!p12ctxt) {
00736        return NULL;
00737     }
00738 
00739     mark = PORT_ArenaMark(p12ctxt->arena);
00740     if(!mark) {
00741        PORT_SetError(SEC_ERROR_NO_MEMORY);
00742        return NULL;
00743     }
00744 
00745     safeBag = (sec_PKCS12SafeBag *)PORT_ArenaZAlloc(p12ctxt->arena, 
00746                                               sizeof(sec_PKCS12SafeBag));
00747     if(!safeBag) {
00748        PORT_ArenaRelease(p12ctxt->arena, mark);
00749        PORT_SetError(SEC_ERROR_NO_MEMORY);
00750        return NULL;
00751     }
00752 
00753     /* set the bags content based upon bag type */
00754     switch(bagType) {
00755        case SEC_OID_PKCS12_V1_KEY_BAG_ID:
00756            safeBag->safeBagContent.pkcs8KeyBag =
00757                (SECKEYPrivateKeyInfo *)bagData;
00758            break;
00759        case SEC_OID_PKCS12_V1_CERT_BAG_ID:
00760            safeBag->safeBagContent.certBag = (sec_PKCS12CertBag *)bagData;
00761            break;
00762        case SEC_OID_PKCS12_V1_CRL_BAG_ID:
00763            safeBag->safeBagContent.crlBag = (sec_PKCS12CRLBag *)bagData;
00764            break;
00765        case SEC_OID_PKCS12_V1_SECRET_BAG_ID:
00766            safeBag->safeBagContent.secretBag = (sec_PKCS12SecretBag *)bagData;
00767            break;
00768        case SEC_OID_PKCS12_V1_PKCS8_SHROUDED_KEY_BAG_ID:
00769            safeBag->safeBagContent.pkcs8ShroudedKeyBag = 
00770                (SECKEYEncryptedPrivateKeyInfo *)bagData;
00771            break;
00772        case SEC_OID_PKCS12_V1_SAFE_CONTENTS_BAG_ID:
00773            safeBag->safeBagContent.safeContents = 
00774                (sec_PKCS12SafeContents *)bagData;
00775            setName = PR_FALSE;
00776            break;
00777        default:
00778            goto loser;
00779     }
00780 
00781     oidData = SECOID_FindOIDByTag(bagType);
00782     if(oidData) {
00783        rv = SECITEM_CopyItem(p12ctxt->arena, &safeBag->safeBagType, &oidData->oid);
00784        if(rv != SECSuccess) {
00785            PORT_SetError(SEC_ERROR_NO_MEMORY);
00786            goto loser;
00787        }
00788     } else {
00789        goto loser;
00790     }
00791     
00792     safeBag->arena = p12ctxt->arena;
00793     PORT_ArenaUnmark(p12ctxt->arena, mark);
00794 
00795     return safeBag;
00796 
00797 loser:
00798     if(mark) {
00799        PORT_ArenaRelease(p12ctxt->arena, mark);
00800     }
00801 
00802     return NULL;
00803 }
00804 
00805 /* Creates a new certificate bag and returns a pointer to it.  If an error
00806  * occurs NULL is returned.
00807  */
00808 sec_PKCS12CertBag *
00809 sec_PKCS12NewCertBag(PRArenaPool *arena, SECOidTag certType)
00810 {
00811     sec_PKCS12CertBag *certBag = NULL;
00812     SECOidData *bagType = NULL;
00813     SECStatus rv;
00814     void *mark = NULL;
00815 
00816     if(!arena) {
00817        return NULL;
00818     }
00819 
00820     mark = PORT_ArenaMark(arena);
00821     certBag = (sec_PKCS12CertBag *)PORT_ArenaZAlloc(arena, 
00822                                               sizeof(sec_PKCS12CertBag));
00823     if(!certBag) {
00824        PORT_ArenaRelease(arena, mark);
00825        PORT_SetError(SEC_ERROR_NO_MEMORY);
00826        return NULL;
00827     }
00828 
00829     bagType = SECOID_FindOIDByTag(certType);
00830     if(!bagType) {
00831        PORT_SetError(SEC_ERROR_NO_MEMORY);
00832        goto loser;
00833     }
00834 
00835     rv = SECITEM_CopyItem(arena, &certBag->bagID, &bagType->oid);
00836     if(rv != SECSuccess) {
00837        PORT_SetError(SEC_ERROR_NO_MEMORY);
00838        goto loser;
00839     }
00840        
00841     PORT_ArenaUnmark(arena, mark);
00842     return certBag;
00843 
00844 loser:
00845     PORT_ArenaRelease(arena, mark);
00846     return NULL;
00847 }
00848 
00849 /* Creates a new CRL bag and returns a pointer to it.  If an error
00850  * occurs NULL is returned.
00851  */
00852 sec_PKCS12CRLBag *
00853 sec_PKCS12NewCRLBag(PRArenaPool *arena, SECOidTag crlType)
00854 {
00855     sec_PKCS12CRLBag *crlBag = NULL;
00856     SECOidData *bagType = NULL;
00857     SECStatus rv;
00858     void *mark = NULL;
00859 
00860     if(!arena) {
00861        return NULL;
00862     }
00863 
00864     mark = PORT_ArenaMark(arena);
00865     crlBag = (sec_PKCS12CRLBag *)PORT_ArenaZAlloc(arena, 
00866                                             sizeof(sec_PKCS12CRLBag));
00867     if(!crlBag) {
00868        PORT_ArenaRelease(arena, mark);
00869        PORT_SetError(SEC_ERROR_NO_MEMORY);
00870        return NULL;
00871     }
00872 
00873     bagType = SECOID_FindOIDByTag(crlType);
00874     if(!bagType) {
00875        PORT_SetError(SEC_ERROR_NO_MEMORY);
00876        goto loser;
00877     }
00878 
00879     rv = SECITEM_CopyItem(arena, &crlBag->bagID, &bagType->oid);
00880     if(rv != SECSuccess) {
00881        PORT_SetError(SEC_ERROR_NO_MEMORY);
00882        goto loser;
00883     }
00884        
00885     PORT_ArenaUnmark(arena, mark);
00886     return crlBag;
00887 
00888 loser:
00889     PORT_ArenaRelease(arena, mark);
00890     return NULL;
00891 }
00892 
00893 /* sec_PKCS12AddAttributeToBag
00894  * adds an attribute to a safeBag.  currently, the only attributes supported
00895  * are those which are specified within PKCS 12.  
00896  *
00897  *     p12ctxt - the export context 
00898  *     safeBag - the safeBag to which attributes are appended
00899  *     attrType - the attribute type
00900  *     attrData - the attribute data
00901  */
00902 SECStatus
00903 sec_PKCS12AddAttributeToBag(SEC_PKCS12ExportContext *p12ctxt, 
00904                          sec_PKCS12SafeBag *safeBag, SECOidTag attrType,
00905                          SECItem *attrData)
00906 {
00907     sec_PKCS12Attribute *attribute;
00908     void *mark = NULL, *dummy = NULL;
00909     SECOidData *oiddata = NULL;
00910     SECItem unicodeName = { siBuffer, NULL, 0};
00911     void *src = NULL;
00912     unsigned int nItems = 0;
00913     SECStatus rv;
00914 
00915     if(!safeBag || !p12ctxt) {
00916        return SECFailure;
00917     }
00918 
00919     mark = PORT_ArenaMark(safeBag->arena);
00920 
00921     /* allocate the attribute */
00922     attribute = (sec_PKCS12Attribute *)PORT_ArenaZAlloc(safeBag->arena, 
00923                                           sizeof(sec_PKCS12Attribute));
00924     if(!attribute) {
00925        PORT_SetError(SEC_ERROR_NO_MEMORY);
00926        goto loser;
00927     }
00928 
00929     /* set up the attribute */
00930     oiddata = SECOID_FindOIDByTag(attrType);
00931     if(!oiddata) {
00932        PORT_SetError(SEC_ERROR_NO_MEMORY);
00933        goto loser;
00934     }
00935     if(SECITEM_CopyItem(p12ctxt->arena, &attribute->attrType, &oiddata->oid) !=
00936               SECSuccess) {
00937        PORT_SetError(SEC_ERROR_NO_MEMORY);
00938        goto loser;
00939     }
00940 
00941     nItems = 1;
00942     switch(attrType) {
00943        case SEC_OID_PKCS9_LOCAL_KEY_ID:
00944            {
00945               src = attrData;
00946               break;
00947            }
00948        case SEC_OID_PKCS9_FRIENDLY_NAME:
00949            {
00950               if(!sec_pkcs12_convert_item_to_unicode(p12ctxt->arena, 
00951                                    &unicodeName, attrData, PR_FALSE, 
00952                                    PR_FALSE, PR_TRUE)) {
00953                   goto loser;
00954               }
00955               src = &unicodeName;
00956               break;
00957            }
00958        default:
00959            goto loser;
00960     }
00961 
00962     /* append the attribute to the attribute value list  */
00963     attribute->attrValue = (SECItem **)PORT_ArenaZAlloc(p12ctxt->arena, 
00964                                        ((nItems + 1) * sizeof(SECItem *)));
00965     if(!attribute->attrValue) {
00966        PORT_SetError(SEC_ERROR_NO_MEMORY);
00967        goto loser;
00968     }
00969 
00970     /* XXX this will need to be changed if attributes requiring more than
00971      * one element are ever used.
00972      */
00973     attribute->attrValue[0] = (SECItem *)PORT_ArenaZAlloc(p12ctxt->arena, 
00974                                                    sizeof(SECItem));
00975     if(!attribute->attrValue[0]) {
00976        PORT_SetError(SEC_ERROR_NO_MEMORY);
00977        goto loser;
00978     }
00979     attribute->attrValue[1] = NULL;
00980 
00981     rv = SECITEM_CopyItem(p12ctxt->arena, attribute->attrValue[0], 
00982                        (SECItem*)src);
00983     if(rv != SECSuccess) {
00984        PORT_SetError(SEC_ERROR_NO_MEMORY);
00985        goto loser;
00986     }
00987 
00988     /* append the attribute to the safeBag attributes */
00989     if(safeBag->nAttribs) {
00990        dummy = PORT_ArenaGrow(p12ctxt->arena, safeBag->attribs, 
00991                      ((safeBag->nAttribs + 1) * sizeof(sec_PKCS12Attribute *)),
00992                      ((safeBag->nAttribs + 2) * sizeof(sec_PKCS12Attribute *)));
00993        safeBag->attribs = (sec_PKCS12Attribute **)dummy;
00994     } else {
00995        safeBag->attribs = (sec_PKCS12Attribute **)PORT_ArenaZAlloc(p12ctxt->arena, 
00996                                           2 * sizeof(sec_PKCS12Attribute *));
00997        dummy = safeBag->attribs;
00998     }
00999     if(!dummy) {
01000        goto loser;
01001     }
01002 
01003     safeBag->attribs[safeBag->nAttribs] = attribute;
01004     safeBag->attribs[++safeBag->nAttribs] = NULL;
01005 
01006     PORT_ArenaUnmark(p12ctxt->arena, mark);
01007     return SECSuccess;
01008 
01009 loser:
01010     if(mark) {
01011        PORT_ArenaRelease(p12ctxt->arena, mark);
01012     }
01013 
01014     return SECFailure;
01015 }
01016 
01017 /* SEC_PKCS12AddCert
01018  *     Adds a certificate to the data being exported.  
01019  *
01020  *     p12ctxt - the export context
01021  *     safe - the safeInfo to which the certificate is placed 
01022  *     nestedDest - if the cert is to be placed within a nested safeContents then,
01023  *                 this value is to be specified with the destination
01024  *     cert - the cert to export
01025  *     certDb - the certificate database handle
01026  *     keyId - a unique identifier to associate a certificate/key pair
01027  *     includeCertChain - PR_TRUE if the certificate chain is to be included.
01028  */
01029 SECStatus
01030 SEC_PKCS12AddCert(SEC_PKCS12ExportContext *p12ctxt, SEC_PKCS12SafeInfo *safe, 
01031                 void *nestedDest, CERTCertificate *cert, 
01032                 CERTCertDBHandle *certDb, SECItem *keyId,
01033                 PRBool includeCertChain)
01034 {
01035     sec_PKCS12CertBag *certBag;
01036     sec_PKCS12SafeBag *safeBag;
01037     void *mark;
01038     SECStatus rv;
01039     SECItem nick = {siBuffer, NULL,0};
01040 
01041     if(!p12ctxt || !cert) {
01042        return SECFailure;
01043     }
01044     mark = PORT_ArenaMark(p12ctxt->arena);
01045 
01046     /* allocate the cert bag */
01047     certBag = sec_PKCS12NewCertBag(p12ctxt->arena, 
01048                                SEC_OID_PKCS9_X509_CERT);
01049     if(!certBag) {
01050        goto loser;
01051     }
01052 
01053     if(SECITEM_CopyItem(p12ctxt->arena, &certBag->value.x509Cert, 
01054                      &cert->derCert) != SECSuccess) {
01055        PORT_SetError(SEC_ERROR_NO_MEMORY);
01056        goto loser;
01057     }
01058 
01059     /* if the cert chain is to be included, we should only be exporting
01060      * the cert from our internal database.
01061      */
01062     if(includeCertChain) {
01063        CERTCertificateList *certList = CERT_CertChainFromCert(cert,
01064                                                         certUsageSSLClient,
01065                                                         PR_TRUE);
01066        unsigned int count = 0;
01067        if(!certList) {
01068            PORT_SetError(SEC_ERROR_NO_MEMORY);
01069            goto loser;
01070        }
01071 
01072        /* add cert chain */
01073        for(count = 0; count < (unsigned int)certList->len; count++) {
01074            if(SECITEM_CompareItem(&certList->certs[count], &cert->derCert)
01075                             != SECEqual) {
01076               CERTCertificate *tempCert;
01077 
01078               /* decode the certificate */
01079               /* XXX
01080                * This was rather silly.  The chain is constructed above
01081                * by finding all of the CERTCertificate's in the database.
01082                * Then the chain is put into a CERTCertificateList, which only
01083                * contains the DER.  Finally, the DER was decoded, and the
01084                * decoded cert was sent recursively back to this function.
01085                * Beyond being inefficent, this causes data loss (specifically,
01086                * the nickname).  Instead, for 3.4, we'll do a lookup by the
01087                * DER, which should return the cached entry.
01088                */
01089               tempCert = CERT_FindCertByDERCert(CERT_GetDefaultCertDB(),
01090                                                 &certList->certs[count]);
01091               if(!tempCert) {
01092                   CERT_DestroyCertificateList(certList);
01093                   goto loser;
01094               }
01095 
01096               /* add the certificate */
01097               if(SEC_PKCS12AddCert(p12ctxt, safe, nestedDest, tempCert,
01098                              certDb, NULL, PR_FALSE) != SECSuccess) {
01099                   CERT_DestroyCertificate(tempCert);
01100                   CERT_DestroyCertificateList(certList);
01101                   goto loser;
01102               }
01103               CERT_DestroyCertificate(tempCert);
01104            }
01105        }
01106        CERT_DestroyCertificateList(certList);
01107     }
01108 
01109     /* if the certificate has a nickname, we will set the friendly name
01110      * to that.
01111      */
01112     if(cert->nickname) {
01113         if (cert->slot && !PK11_IsInternal(cert->slot)) {
01114          /*
01115           * The cert is coming off of an external token, 
01116           * let's strip the token name from the nickname
01117           * and only add what comes after the colon as the
01118           * nickname. -javi
01119           */
01120            char *delimit;
01121            
01122            delimit = PORT_Strchr(cert->nickname,':');
01123            if (delimit == NULL) {
01124                nick.data = (unsigned char *)cert->nickname;
01125               nick.len = PORT_Strlen(cert->nickname);
01126            } else {
01127                delimit++;
01128                nick.data = (unsigned char *)PORT_ArenaStrdup(p12ctxt->arena,
01129                                                        delimit);
01130               nick.len = PORT_Strlen(delimit);
01131            }
01132        } else {
01133            nick.data = (unsigned char *)cert->nickname;
01134            nick.len = PORT_Strlen(cert->nickname);
01135        }
01136     }
01137 
01138     safeBag = sec_PKCS12CreateSafeBag(p12ctxt, SEC_OID_PKCS12_V1_CERT_BAG_ID, 
01139                                   certBag);
01140     if(!safeBag) {
01141        goto loser;
01142     }
01143 
01144     /* add the friendly name and keyId attributes, if necessary */
01145     if(nick.data) {
01146        if(sec_PKCS12AddAttributeToBag(p12ctxt, safeBag, 
01147                                    SEC_OID_PKCS9_FRIENDLY_NAME, &nick) 
01148                                    != SECSuccess) {
01149            goto loser;
01150        }
01151     }
01152           
01153     if(keyId) {
01154        if(sec_PKCS12AddAttributeToBag(p12ctxt, safeBag, SEC_OID_PKCS9_LOCAL_KEY_ID,
01155                                    keyId) != SECSuccess) {
01156            goto loser;
01157        }
01158     }
01159 
01160     /* append the cert safeBag */
01161     if(nestedDest) {
01162        rv = sec_pkcs12_append_bag_to_safe_contents(p12ctxt->arena, 
01163                                      (sec_PKCS12SafeContents*)nestedDest, 
01164                                       safeBag);
01165     } else {
01166        rv = sec_pkcs12_append_bag(p12ctxt, safe, safeBag);
01167     }
01168 
01169     if(rv != SECSuccess) {
01170        goto loser;
01171     }
01172 
01173     PORT_ArenaUnmark(p12ctxt->arena, mark);
01174     return SECSuccess;
01175 
01176 loser:
01177     if(mark) {
01178        PORT_ArenaRelease(p12ctxt->arena, mark);
01179     }
01180 
01181     return SECFailure;
01182 }
01183 
01184 /* SEC_PKCS12AddEncryptedKey
01185  *     Extracts the key associated with a particular certificate and exports
01186  *     it.
01187  *
01188  *     p12ctxt - the export context 
01189  *     safe - the safeInfo to place the key in
01190  *     nestedDest - the nested safeContents to place a key
01191  *     cert - the certificate which the key belongs to
01192  *     shroudKey - encrypt the private key for export.  This value should 
01193  *            always be true.  lower level code will not allow the export
01194  *            of unencrypted private keys.
01195  *     algorithm - the algorithm with which to encrypt the private key
01196  *     pwitem - the password to encrypted the private key with
01197  *     keyId - the keyID attribute
01198  *     nickName - the nickname attribute
01199  */
01200 static SECStatus
01201 SEC_PKCS12AddEncryptedKey(SEC_PKCS12ExportContext *p12ctxt, 
01202               SECKEYEncryptedPrivateKeyInfo *epki, SEC_PKCS12SafeInfo *safe,
01203               void *nestedDest, SECItem *keyId, SECItem *nickName)
01204 {
01205     void *mark;
01206     void *keyItem;
01207     SECOidTag keyType;
01208     SECStatus rv = SECFailure;
01209     sec_PKCS12SafeBag *returnBag;
01210 
01211     if(!p12ctxt || !safe || !epki) {
01212        return SECFailure;
01213     }
01214 
01215     mark = PORT_ArenaMark(p12ctxt->arena);
01216 
01217     keyItem = PORT_ArenaZAlloc(p12ctxt->arena, 
01218                             sizeof(SECKEYEncryptedPrivateKeyInfo));
01219     if(!keyItem) {
01220        PORT_SetError(SEC_ERROR_NO_MEMORY);
01221        goto loser;
01222     }
01223 
01224     rv = SECKEY_CopyEncryptedPrivateKeyInfo(p12ctxt->arena, 
01225                                    (SECKEYEncryptedPrivateKeyInfo *)keyItem,
01226                                    epki);
01227     keyType = SEC_OID_PKCS12_V1_PKCS8_SHROUDED_KEY_BAG_ID;
01228 
01229     if(rv != SECSuccess) {
01230        goto loser;
01231     }
01232        
01233     /* create the safe bag and set any attributes */
01234     returnBag = sec_PKCS12CreateSafeBag(p12ctxt, keyType, keyItem);
01235     if(!returnBag) {
01236        rv = SECFailure;
01237        goto loser;
01238     }
01239 
01240     if(nickName) {
01241        if(sec_PKCS12AddAttributeToBag(p12ctxt, returnBag, 
01242                                    SEC_OID_PKCS9_FRIENDLY_NAME, nickName) 
01243                                    != SECSuccess) {
01244            goto loser;
01245        }
01246     }
01247           
01248     if(keyId) {
01249        if(sec_PKCS12AddAttributeToBag(p12ctxt, returnBag, 
01250                                       SEC_OID_PKCS9_LOCAL_KEY_ID,
01251                                    keyId) != SECSuccess) {
01252            goto loser;
01253        }
01254     }
01255 
01256     if(nestedDest) {
01257        rv = sec_pkcs12_append_bag_to_safe_contents(p12ctxt->arena, 
01258                                       (sec_PKCS12SafeContents*)nestedDest,
01259                                       returnBag);
01260     } else {
01261        rv = sec_pkcs12_append_bag(p12ctxt, safe, returnBag);
01262     }
01263 
01264 loser:
01265 
01266     if (rv != SECSuccess) {
01267        PORT_ArenaRelease(p12ctxt->arena, mark);
01268     } else {
01269        PORT_ArenaUnmark(p12ctxt->arena, mark);
01270     }
01271 
01272     return rv;
01273 }
01274 
01275 /* SEC_PKCS12AddKeyForCert
01276  *     Extracts the key associated with a particular certificate and exports
01277  *     it.
01278  *
01279  *     p12ctxt - the export context 
01280  *     safe - the safeInfo to place the key in
01281  *     nestedDest - the nested safeContents to place a key
01282  *     cert - the certificate which the key belongs to
01283  *     shroudKey - encrypt the private key for export.  This value should 
01284  *            always be true.  lower level code will not allow the export
01285  *            of unencrypted private keys.
01286  *     algorithm - the algorithm with which to encrypt the private key
01287  *     pwitem - the password to encrypt the private key with
01288  *     keyId - the keyID attribute
01289  *     nickName - the nickname attribute
01290  */
01291 SECStatus
01292 SEC_PKCS12AddKeyForCert(SEC_PKCS12ExportContext *p12ctxt, SEC_PKCS12SafeInfo *safe, 
01293                      void *nestedDest, CERTCertificate *cert,
01294                      PRBool shroudKey, SECOidTag algorithm, SECItem *pwitem,
01295                      SECItem *keyId, SECItem *nickName)
01296 {
01297     void *mark;
01298     void *keyItem;
01299     SECOidTag keyType;
01300     SECStatus rv = SECFailure;
01301     SECItem nickname = {siBuffer,NULL,0}, uniPwitem = {siBuffer, NULL, 0};
01302     sec_PKCS12SafeBag *returnBag;
01303 
01304     if(!p12ctxt || !cert || !safe) {
01305        return SECFailure;
01306     }
01307 
01308     mark = PORT_ArenaMark(p12ctxt->arena);
01309 
01310     /* retrieve the key based upon the type that it is and 
01311      * specify the type of safeBag to store the key in
01312      */          
01313     if(!shroudKey) {
01314 
01315        /* extract the key unencrypted.  this will most likely go away */
01316        SECKEYPrivateKeyInfo *pki = PK11_ExportPrivateKeyInfo(cert, 
01317                                                        p12ctxt->wincx);
01318        if(!pki) {
01319            PORT_ArenaRelease(p12ctxt->arena, mark);
01320            PORT_SetError(SEC_ERROR_PKCS12_UNABLE_TO_EXPORT_KEY);
01321            return SECFailure;
01322        }   
01323        keyItem = PORT_ArenaZAlloc(p12ctxt->arena, sizeof(SECKEYPrivateKeyInfo));
01324        if(!keyItem) {
01325            PORT_SetError(SEC_ERROR_NO_MEMORY);
01326            goto loser;
01327        }
01328        rv = SECKEY_CopyPrivateKeyInfo(p12ctxt->arena, 
01329                                    (SECKEYPrivateKeyInfo *)keyItem, pki);
01330        keyType = SEC_OID_PKCS12_V1_KEY_BAG_ID;
01331        SECKEY_DestroyPrivateKeyInfo(pki, PR_TRUE);
01332     } else {
01333 
01334        /* extract the key encrypted */
01335        SECKEYEncryptedPrivateKeyInfo *epki = NULL;
01336        PK11SlotInfo *slot = NULL;
01337 
01338        if(!sec_pkcs12_convert_item_to_unicode(p12ctxt->arena, &uniPwitem,
01339                              pwitem, PR_TRUE, PR_TRUE, PR_TRUE)) {
01340            PORT_SetError(SEC_ERROR_NO_MEMORY);
01341            goto loser;
01342        }
01343 
01344        /* we want to make sure to take the key out of the key slot */
01345        if(PK11_IsInternal(p12ctxt->slot)) {
01346            slot = PK11_GetInternalKeySlot();
01347        } else {
01348            slot = PK11_ReferenceSlot(p12ctxt->slot);
01349        }
01350 
01351        epki = PK11_ExportEncryptedPrivateKeyInfo(slot, algorithm, 
01352                                             &uniPwitem, cert, 1, 
01353                                             p12ctxt->wincx);
01354        PK11_FreeSlot(slot);
01355        
01356        keyItem = PORT_ArenaZAlloc(p12ctxt->arena, 
01357                               sizeof(SECKEYEncryptedPrivateKeyInfo));
01358        if(!keyItem) {
01359            PORT_SetError(SEC_ERROR_NO_MEMORY);
01360            goto loser;
01361        }
01362        if(!epki) {
01363            PORT_SetError(SEC_ERROR_PKCS12_UNABLE_TO_EXPORT_KEY);
01364            return SECFailure;
01365        }   
01366        rv = SECKEY_CopyEncryptedPrivateKeyInfo(p12ctxt->arena, 
01367                                    (SECKEYEncryptedPrivateKeyInfo *)keyItem,
01368                                    epki);
01369        keyType = SEC_OID_PKCS12_V1_PKCS8_SHROUDED_KEY_BAG_ID;
01370        SECKEY_DestroyEncryptedPrivateKeyInfo(epki, PR_TRUE);
01371     }
01372 
01373     if(rv != SECSuccess) {
01374        goto loser;
01375     }
01376        
01377     /* if no nickname specified, let's see if the certificate has a 
01378      * nickname.
01379      */                                     
01380     if(!nickName) {
01381        if(cert->nickname) {
01382            nickname.data = (unsigned char *)cert->nickname;
01383            nickname.len = PORT_Strlen(cert->nickname);
01384            nickName = &nickname;
01385        }
01386     }
01387 
01388     /* create the safe bag and set any attributes */
01389     returnBag = sec_PKCS12CreateSafeBag(p12ctxt, keyType, keyItem);
01390     if(!returnBag) {
01391        rv = SECFailure;
01392        goto loser;
01393     }
01394 
01395     if(nickName) {
01396        if(sec_PKCS12AddAttributeToBag(p12ctxt, returnBag, 
01397                                    SEC_OID_PKCS9_FRIENDLY_NAME, nickName) 
01398                                    != SECSuccess) {
01399            goto loser;
01400        }
01401     }
01402           
01403     if(keyId) {
01404        if(sec_PKCS12AddAttributeToBag(p12ctxt, returnBag, SEC_OID_PKCS9_LOCAL_KEY_ID,
01405                                    keyId) != SECSuccess) {
01406            goto loser;
01407        }
01408     }
01409 
01410     if(nestedDest) {
01411        rv = sec_pkcs12_append_bag_to_safe_contents(p12ctxt->arena,
01412                                      (sec_PKCS12SafeContents*)nestedDest, 
01413                                      returnBag);
01414     } else {
01415        rv = sec_pkcs12_append_bag(p12ctxt, safe, returnBag);
01416     }
01417 
01418 loser:
01419 
01420     if (rv != SECSuccess) {
01421        PORT_ArenaRelease(p12ctxt->arena, mark);
01422     } else {
01423        PORT_ArenaUnmark(p12ctxt->arena, mark);
01424     }
01425 
01426     return rv;
01427 }
01428 
01429 /* SEC_PKCS12AddCertAndEncryptedKey
01430  *     Add a certificate and key pair to be exported.
01431  *
01432  *     p12ctxt - the export context 
01433  *     certSafe - the safeInfo where the cert is stored
01434  *     certNestedDest - the nested safeContents to store the cert
01435  *     keySafe - the safeInfo where the key is stored
01436  *     keyNestedDest - the nested safeContents to store the key
01437  *     shroudKey - extract the private key encrypted?
01438  *     pwitem - the password with which the key is encrypted
01439  *     algorithm - the algorithm with which the key is encrypted
01440  */
01441 SECStatus
01442 SEC_PKCS12AddDERCertAndEncryptedKey(SEC_PKCS12ExportContext *p12ctxt, 
01443               void *certSafe, void *certNestedDest, 
01444               SECItem *derCert, void *keySafe, 
01445               void *keyNestedDest, SECKEYEncryptedPrivateKeyInfo *epki,
01446               char *nickname)
01447 {             
01448     SECStatus rv = SECFailure;
01449     SGNDigestInfo *digest = NULL;
01450     void *mark = NULL;
01451     CERTCertificate *cert;
01452     SECItem nick = {siBuffer, NULL,0}, *nickPtr = NULL; 
01453 
01454     if(!p12ctxt || !certSafe || !keySafe || !derCert) {
01455        return SECFailure;
01456     }
01457 
01458     mark = PORT_ArenaMark(p12ctxt->arena);
01459 
01460     cert = CERT_NewTempCertificate(CERT_GetDefaultCertDB(),
01461                                    derCert, NULL, PR_FALSE, PR_FALSE);
01462     if(!cert) {
01463        PORT_ArenaRelease(p12ctxt->arena, mark);
01464        PORT_SetError(SEC_ERROR_NO_MEMORY);
01465        return SECFailure;
01466     }
01467     cert->nickname = nickname;
01468 
01469     /* generate the thumbprint of the cert to use as a keyId */
01470     digest = sec_pkcs12_compute_thumbprint(&cert->derCert);
01471     if(!digest) {
01472        CERT_DestroyCertificate(cert);
01473        return SECFailure;
01474     }
01475 
01476     /* add the certificate */
01477     rv = SEC_PKCS12AddCert(p12ctxt, (SEC_PKCS12SafeInfo*)certSafe, 
01478                         certNestedDest, cert, NULL,
01479                         &digest->digest, PR_FALSE);
01480     if(rv != SECSuccess) {
01481        goto loser;
01482     }
01483 
01484     if(nickname) {
01485        nick.data = (unsigned char *)nickname;
01486        nick.len = PORT_Strlen(nickname);
01487        nickPtr = &nick;
01488     } else {
01489        nickPtr = NULL;
01490     }
01491 
01492     /* add the key */
01493     rv = SEC_PKCS12AddEncryptedKey(p12ctxt, epki, (SEC_PKCS12SafeInfo*)keySafe,
01494                                keyNestedDest, &digest->digest, nickPtr );
01495     if(rv != SECSuccess) {
01496        goto loser;
01497     }
01498 
01499     SGN_DestroyDigestInfo(digest);
01500 
01501     PORT_ArenaUnmark(p12ctxt->arena, mark);
01502     return SECSuccess;
01503 
01504 loser:
01505     SGN_DestroyDigestInfo(digest);
01506     CERT_DestroyCertificate(cert);
01507     PORT_ArenaRelease(p12ctxt->arena, mark);
01508     
01509     return SECFailure; 
01510 }
01511 
01512 /* SEC_PKCS12AddCertAndKey
01513  *     Add a certificate and key pair to be exported.
01514  *
01515  *     p12ctxt - the export context 
01516  *     certSafe - the safeInfo where the cert is stored
01517  *     certNestedDest - the nested safeContents to store the cert
01518  *     keySafe - the safeInfo where the key is stored
01519  *     keyNestedDest - the nested safeContents to store the key
01520  *     shroudKey - extract the private key encrypted?
01521  *     pwitem - the password with which the key is encrypted
01522  *     algorithm - the algorithm with which the key is encrypted
01523  */
01524 SECStatus
01525 SEC_PKCS12AddCertAndKey(SEC_PKCS12ExportContext *p12ctxt, 
01526                      void *certSafe, void *certNestedDest, 
01527                      CERTCertificate *cert, CERTCertDBHandle *certDb,
01528                      void *keySafe, void *keyNestedDest, 
01529                      PRBool shroudKey, SECItem *pwitem, SECOidTag algorithm)
01530 {             
01531     SECStatus rv = SECFailure;
01532     SGNDigestInfo *digest = NULL;
01533     void *mark = NULL;
01534 
01535     if(!p12ctxt || !certSafe || !keySafe || !cert) {
01536        return SECFailure;
01537     }
01538 
01539     mark = PORT_ArenaMark(p12ctxt->arena);
01540 
01541     /* generate the thumbprint of the cert to use as a keyId */
01542     digest = sec_pkcs12_compute_thumbprint(&cert->derCert);
01543     if(!digest) {
01544        PORT_ArenaRelease(p12ctxt->arena, mark);
01545        return SECFailure;
01546     }
01547 
01548     /* add the certificate */
01549     rv = SEC_PKCS12AddCert(p12ctxt, (SEC_PKCS12SafeInfo*)certSafe, 
01550                         (SEC_PKCS12SafeInfo*)certNestedDest, cert, certDb,
01551                         &digest->digest, PR_TRUE);
01552     if(rv != SECSuccess) {
01553        goto loser;
01554     }
01555 
01556     /* add the key */
01557     rv = SEC_PKCS12AddKeyForCert(p12ctxt, (SEC_PKCS12SafeInfo*)keySafe, 
01558                              keyNestedDest, cert, 
01559                              shroudKey, algorithm, pwitem, 
01560                              &digest->digest, NULL );
01561     if(rv != SECSuccess) {
01562        goto loser;
01563     }
01564 
01565     SGN_DestroyDigestInfo(digest);
01566 
01567     PORT_ArenaUnmark(p12ctxt->arena, mark);
01568     return SECSuccess;
01569 
01570 loser:
01571     SGN_DestroyDigestInfo(digest);
01572     PORT_ArenaRelease(p12ctxt->arena, mark);
01573     
01574     return SECFailure; 
01575 }
01576 
01577 /* SEC_PKCS12CreateNestedSafeContents
01578  *     Allows nesting of safe contents to be implemented.  No limit imposed on 
01579  *     depth.  
01580  *
01581  *     p12ctxt - the export context 
01582  *     baseSafe - the base safeInfo 
01583  *     nestedDest - a parent safeContents (?)
01584  */
01585 void *
01586 SEC_PKCS12CreateNestedSafeContents(SEC_PKCS12ExportContext *p12ctxt,
01587                                void *baseSafe, void *nestedDest)
01588 {
01589     sec_PKCS12SafeContents *newSafe;
01590     sec_PKCS12SafeBag *safeContentsBag;
01591     void *mark;
01592     SECStatus rv;
01593 
01594     if(!p12ctxt || !baseSafe) {
01595        return NULL;
01596     }
01597 
01598     mark = PORT_ArenaMark(p12ctxt->arena);
01599 
01600     newSafe = sec_PKCS12CreateSafeContents(p12ctxt->arena);
01601     if(!newSafe) {
01602        PORT_ArenaRelease(p12ctxt->arena, mark);
01603        PORT_SetError(SEC_ERROR_NO_MEMORY);
01604        return NULL;
01605     }
01606 
01607     /* create the safeContents safeBag */
01608     safeContentsBag = sec_PKCS12CreateSafeBag(p12ctxt, 
01609                                    SEC_OID_PKCS12_V1_SAFE_CONTENTS_BAG_ID,
01610                                    newSafe);
01611     if(!safeContentsBag) {
01612        goto loser;
01613     }
01614 
01615     /* append the safeContents to the appropriate area */
01616     if(nestedDest) {
01617        rv = sec_pkcs12_append_bag_to_safe_contents(p12ctxt->arena, 
01618                                       (sec_PKCS12SafeContents*)nestedDest,
01619                                       safeContentsBag);
01620     } else {
01621        rv = sec_pkcs12_append_bag(p12ctxt, (SEC_PKCS12SafeInfo*)baseSafe, 
01622                                safeContentsBag);
01623     }
01624     if(rv != SECSuccess) {
01625        goto loser;
01626     }
01627 
01628     PORT_ArenaUnmark(p12ctxt->arena, mark);
01629     return newSafe;
01630 
01631 loser:
01632     PORT_ArenaRelease(p12ctxt->arena, mark);
01633     return NULL;
01634 }
01635 
01636 /*********************************
01637  * Encoding routines
01638  *********************************/
01639 
01640 /* set up the encoder context based on information in the export context
01641  * and return the newly allocated enocoder context.  A return of NULL 
01642  * indicates an error occurred. 
01643  */
01644 sec_PKCS12EncoderContext *
01645 sec_pkcs12_encoder_start_context(SEC_PKCS12ExportContext *p12exp)
01646 {
01647     sec_PKCS12EncoderContext *p12enc = NULL;
01648     unsigned int i, nonEmptyCnt;
01649     SECStatus rv;
01650     SECItem ignore = {0};
01651     void *mark;
01652 
01653     if(!p12exp || !p12exp->safeInfos) {
01654        return NULL;
01655     }
01656 
01657     /* check for any empty safes and skip them */
01658     i = nonEmptyCnt = 0;
01659     while(p12exp->safeInfos[i]) {
01660        if(p12exp->safeInfos[i]->itemCount) {
01661            nonEmptyCnt++;
01662        }
01663        i++;
01664     }
01665     if(nonEmptyCnt == 0) {
01666        return NULL;
01667     }
01668     p12exp->authSafe.encodedSafes[nonEmptyCnt] = NULL;
01669 
01670     /* allocate the encoder context */
01671     mark = PORT_ArenaMark(p12exp->arena);
01672     p12enc = PORT_ArenaZNew(p12exp->arena, sec_PKCS12EncoderContext);
01673     if(!p12enc) {
01674        PORT_SetError(SEC_ERROR_NO_MEMORY);
01675        return NULL;
01676     }
01677 
01678     p12enc->arena = p12exp->arena;
01679     p12enc->p12exp = p12exp;
01680 
01681     /* set up the PFX version and information */
01682     PORT_Memset(&p12enc->pfx, 0, sizeof(sec_PKCS12PFXItem));
01683     if(!SEC_ASN1EncodeInteger(p12exp->arena, &(p12enc->pfx.version), 
01684                            SEC_PKCS12_VERSION) ) {
01685        PORT_SetError(SEC_ERROR_NO_MEMORY);
01686        goto loser;
01687     }
01688 
01689     /* set up the authenticated safe content info based on the 
01690      * type of integrity being used.  this should be changed to
01691      * enforce integrity mode, but will not be implemented until
01692      * it is confirmed that integrity must be in place
01693      */
01694     if(p12exp->integrityEnabled && !p12exp->pwdIntegrity) {
01695        SECStatus rv;
01696 
01697        /* create public key integrity mode */
01698        p12enc->aSafeCinfo = SEC_PKCS7CreateSignedData(
01699                             p12exp->integrityInfo.pubkeyInfo.cert,
01700                             certUsageEmailSigner,
01701                             p12exp->integrityInfo.pubkeyInfo.certDb,
01702                             p12exp->integrityInfo.pubkeyInfo.algorithm,
01703                             NULL,
01704                             p12exp->pwfn,
01705                             p12exp->pwfnarg);
01706        if(!p12enc->aSafeCinfo) {
01707            goto loser;
01708        }
01709        if(SEC_PKCS7IncludeCertChain(p12enc->aSafeCinfo,NULL) != SECSuccess) {
01710            goto loser;
01711        }
01712        rv = SEC_PKCS7AddSigningTime(p12enc->aSafeCinfo);
01713        PORT_Assert(rv == SECSuccess);
01714     } else {
01715        p12enc->aSafeCinfo = SEC_PKCS7CreateData();
01716 
01717        /* init password pased integrity mode */
01718        if(p12exp->integrityEnabled) {
01719            SECItem  pwd = {siBuffer,NULL, 0};
01720            SECItem *salt = sec_pkcs12_generate_salt();
01721            PK11SymKey *symKey;
01722            SECItem *params;
01723            CK_MECHANISM_TYPE integrityMech;
01724            CK_MECHANISM_TYPE hmacMech;
01725 
01726            /* zero out macData and set values */
01727            PORT_Memset(&p12enc->mac, 0, sizeof(sec_PKCS12MacData));
01728 
01729            if(!salt) {
01730               PORT_SetError(SEC_ERROR_NO_MEMORY);
01731               goto loser;
01732            }
01733            if(SECITEM_CopyItem(p12exp->arena, &(p12enc->mac.macSalt), salt) 
01734                      != SECSuccess) {
01735               PORT_SetError(SEC_ERROR_NO_MEMORY);
01736               goto loser;
01737            }   
01738 
01739            /* generate HMAC key */
01740            if(!sec_pkcs12_convert_item_to_unicode(NULL, &pwd, 
01741                      p12exp->integrityInfo.pwdInfo.password, PR_TRUE, 
01742                      PR_TRUE, PR_TRUE)) {
01743               goto loser;
01744            }
01745 
01746            params = PK11_CreatePBEParams(salt, &pwd, 1);
01747            SECITEM_ZfreeItem(salt, PR_TRUE);
01748            SECITEM_ZfreeItem(&pwd, PR_FALSE);
01749 
01750            switch (p12exp->integrityInfo.pwdInfo.algorithm) {
01751            case SEC_OID_SHA1:
01752               integrityMech = CKM_NETSCAPE_PBE_SHA1_HMAC_KEY_GEN; break;
01753            case SEC_OID_MD5:
01754               integrityMech = CKM_NETSCAPE_PBE_MD5_HMAC_KEY_GEN;  break;
01755            case SEC_OID_MD2:
01756               integrityMech = CKM_NETSCAPE_PBE_MD2_HMAC_KEY_GEN;  break;
01757            default:
01758               goto loser;
01759            }
01760 
01761            symKey = PK11_KeyGen(NULL, integrityMech, params, 20, NULL);
01762            PK11_DestroyPBEParams(params);
01763            if(!symKey) {
01764               goto loser;
01765            }
01766 
01767            /* initialize hmac */
01768            /* XXX NBB, why is this mech different than the one above? */
01769            hmacMech =  sec_pkcs12_algtag_to_mech( 
01770                                      p12exp->integrityInfo.pwdInfo.algorithm);
01771 
01772            p12enc->hmacCx = PK11_CreateContextBySymKey( hmacMech, CKA_SIGN, 
01773                                                         symKey, &ignore);
01774 
01775            PK11_FreeSymKey(symKey);
01776            if(!p12enc->hmacCx) {
01777               PORT_SetError(SEC_ERROR_NO_MEMORY);
01778               goto loser;
01779            }
01780            rv = PK11_DigestBegin(p12enc->hmacCx);
01781            if (rv != SECSuccess)
01782               goto loser;
01783        }
01784     }
01785 
01786     if(!p12enc->aSafeCinfo) {
01787        goto loser;
01788     }
01789 
01790     PORT_ArenaUnmark(p12exp->arena, mark);
01791 
01792     return p12enc;
01793 
01794 loser:
01795     if(p12enc) {
01796        if(p12enc->aSafeCinfo) {
01797            SEC_PKCS7DestroyContentInfo(p12enc->aSafeCinfo);
01798        }
01799        if(p12enc->hmacCx) {
01800            PK11_DestroyContext(p12enc->hmacCx, PR_TRUE);
01801        }
01802     }
01803     if (p12exp->arena != NULL)
01804        PORT_ArenaRelease(p12exp->arena, mark);
01805 
01806     return NULL;
01807 }
01808 
01809 /* The outermost ASN.1 encoder calls this function for output.
01810 ** This function calls back to the library caller's output routine,
01811 ** which typically writes to a PKCS12 file.
01812  */
01813 static void
01814 sec_P12A1OutputCB_Outer(void *arg, const char *buf, unsigned long len,
01815                      int depth, SEC_ASN1EncodingPart data_kind)
01816 {
01817     struct sec_pkcs12_encoder_output *output;
01818 
01819     output = (struct sec_pkcs12_encoder_output*)arg;
01820     (* output->outputfn)(output->outputarg, buf, len);
01821 }
01822 
01823 /* The "middle" and "inner" ASN.1 encoders call this function to output. 
01824 ** This function does HMACing, if appropriate, and then buffers the data.
01825 ** The buffered data is eventually passed down to the underlying PKCS7 encoder.
01826  */
01827 static void
01828 sec_P12A1OutputCB_HmacP7Update(void *arg, const char *buf,
01829                             unsigned long        len, 
01830                             int                  depth,
01831                             SEC_ASN1EncodingPart data_kind)
01832 {
01833     sec_pkcs12OutputBuffer *  bufcx = (sec_pkcs12OutputBuffer *)arg;
01834 
01835     if(!buf || !len) 
01836        return;
01837 
01838     if (bufcx->hmacCx) {
01839        PK11_DigestOp(bufcx->hmacCx, (unsigned char *)buf, len);
01840     }
01841 
01842     /* buffer */
01843     if (bufcx->numBytes > 0) {
01844        int toCopy;
01845        if (len + bufcx->numBytes <= bufcx->bufBytes) {
01846            memcpy(bufcx->buf + bufcx->numBytes, buf, len);
01847            bufcx->numBytes += len;
01848            if (bufcx->numBytes < bufcx->bufBytes) 
01849               return;
01850            SEC_PKCS7EncoderUpdate(bufcx->p7eCx, bufcx->buf, bufcx->bufBytes);
01851            bufcx->numBytes = 0;
01852            return;
01853        } 
01854        toCopy = bufcx->bufBytes - bufcx->numBytes;
01855        memcpy(bufcx->buf + bufcx->numBytes, buf, toCopy);
01856        SEC_PKCS7EncoderUpdate(bufcx->p7eCx, bufcx->buf, bufcx->bufBytes);
01857        bufcx->numBytes = 0;
01858        len -= toCopy;
01859        buf += toCopy;
01860     } 
01861     /* buffer is presently empty */
01862     if (len >= bufcx->bufBytes) {
01863        /* Just pass it through */
01864        SEC_PKCS7EncoderUpdate(bufcx->p7eCx, buf, len);
01865     } else {
01866        /* copy it all into the buffer, and return */
01867        memcpy(bufcx->buf, buf, len);
01868        bufcx->numBytes = len;
01869     }
01870 }
01871 
01872 void
01873 sec_FlushPkcs12OutputBuffer( sec_pkcs12OutputBuffer *  bufcx)
01874 {
01875     if (bufcx->numBytes > 0) {
01876        SEC_PKCS7EncoderUpdate(bufcx->p7eCx, bufcx->buf, bufcx->numBytes);
01877        bufcx->numBytes = 0;
01878     }
01879 }
01880 
01881 /* Feeds the output of a PKCS7 encoder into the next outward ASN.1 encoder.
01882 ** This function is used by both the inner and middle PCS7 encoders.
01883 */
01884 static void
01885 sec_P12P7OutputCB_CallA1Update(void *arg, const char *buf, unsigned long len)
01886 {
01887     SEC_ASN1EncoderContext *cx = (SEC_ASN1EncoderContext*)arg;
01888 
01889     if (!buf || !len) 
01890        return;
01891 
01892     SEC_ASN1EncoderUpdate(cx, buf, len);
01893 }
01894 
01895 
01896 /* this function encodes content infos which are part of the
01897  * sequence of content infos labeled AuthenticatedSafes 
01898  */
01899 static SECStatus 
01900 sec_pkcs12_encoder_asafe_process(sec_PKCS12EncoderContext *p12ecx)
01901 {
01902     SEC_PKCS7EncoderContext *innerP7ecx;
01903     SEC_PKCS7ContentInfo    *cinfo;
01904     PK11SymKey              *bulkKey      = NULL;
01905     SEC_ASN1EncoderContext  *innerA1ecx   = NULL;
01906     SECStatus                rv           = SECSuccess;
01907 
01908     if(p12ecx->currentSafe < p12ecx->p12exp->authSafe.safeCount) {
01909        SEC_PKCS12SafeInfo *safeInfo;
01910        SECOidTag cinfoType;
01911 
01912        safeInfo = p12ecx->p12exp->safeInfos[p12ecx->currentSafe];
01913 
01914        /* skip empty safes */
01915        if(safeInfo->itemCount == 0) {
01916            return SECSuccess;
01917        }
01918 
01919        cinfo = safeInfo->cinfo;
01920        cinfoType = SEC_PKCS7ContentType(cinfo);
01921 
01922        /* determine the safe type and set the appropriate argument */
01923        switch(cinfoType) {
01924            case SEC_OID_PKCS7_DATA:
01925            case SEC_OID_PKCS7_ENVELOPED_DATA:
01926               break;
01927            case SEC_OID_PKCS7_ENCRYPTED_DATA:
01928               bulkKey = safeInfo->encryptionKey;
01929               PK11_SetSymKeyUserData(bulkKey, &safeInfo->pwitem, NULL);
01930               break;
01931            default:
01932               return SECFailure;
01933 
01934        }
01935 
01936        /* start the PKCS7 encoder */
01937        innerP7ecx = SEC_PKCS7EncoderStart(cinfo, 
01938                               sec_P12P7OutputCB_CallA1Update,
01939                               p12ecx->middleA1ecx, bulkKey);
01940        if(!innerP7ecx) {
01941            goto loser;
01942        }
01943 
01944        /* encode safe contents */
01945        p12ecx->innerBuf.p7eCx    = innerP7ecx;
01946        p12ecx->innerBuf.hmacCx   = NULL;
01947        p12ecx->innerBuf.numBytes = 0;
01948        p12ecx->innerBuf.bufBytes = sizeof p12ecx->innerBuf.buf;
01949 
01950        innerA1ecx = SEC_ASN1EncoderStart(safeInfo->safe, 
01951                                   sec_PKCS12SafeContentsTemplate,
01952                                sec_P12A1OutputCB_HmacP7Update, 
01953                                &p12ecx->innerBuf);
01954        if(!innerA1ecx) {
01955            goto loser;
01956        }   
01957        rv = SEC_ASN1EncoderUpdate(innerA1ecx, NULL, 0);
01958        SEC_ASN1EncoderFinish(innerA1ecx);
01959        sec_FlushPkcs12OutputBuffer( &p12ecx->innerBuf);
01960        innerA1ecx = NULL;
01961        if(rv != SECSuccess) {
01962            goto loser;
01963        }
01964 
01965 
01966        /* finish up safe content info */
01967        rv = SEC_PKCS7EncoderFinish(innerP7ecx, p12ecx->p12exp->pwfn, 
01968                                 p12ecx->p12exp->pwfnarg);
01969     }
01970     memset(&p12ecx->innerBuf, 0, sizeof p12ecx->innerBuf);
01971     return SECSuccess;
01972 
01973 loser:
01974     if(innerP7ecx) {
01975        SEC_PKCS7EncoderFinish(innerP7ecx, p12ecx->p12exp->pwfn, 
01976                             p12ecx->p12exp->pwfnarg);
01977     }
01978 
01979     if(innerA1ecx) {
01980        SEC_ASN1EncoderFinish(innerA1ecx);
01981     }
01982     memset(&p12ecx->innerBuf, 0, sizeof p12ecx->innerBuf);
01983     return SECFailure;
01984 }
01985 
01986 /* finish the HMAC and encode the macData so that it can be
01987  * encoded.
01988  */
01989 static SECStatus
01990 sec_Pkcs12FinishMac(sec_PKCS12EncoderContext *p12ecx)
01991 {
01992     SECItem hmac = { siBuffer, NULL, 0 };
01993     SECStatus rv;
01994     SGNDigestInfo *di = NULL;
01995     void *dummy;
01996 
01997     if(!p12ecx) {
01998        return SECFailure;
01999     }
02000 
02001     /* make sure we are using password integrity mode */
02002     if(!p12ecx->p12exp->integrityEnabled) {
02003        return SECSuccess;
02004     }
02005 
02006     if(!p12ecx->p12exp->pwdIntegrity) {
02007        return SECSuccess;
02008     }
02009 
02010     /* finish the hmac */
02011     hmac.data = (unsigned char *)PORT_ZAlloc(SHA1_LENGTH);
02012     if(!hmac.data) {
02013        PORT_SetError(SEC_ERROR_NO_MEMORY);
02014        return SECFailure;
02015     }
02016 
02017     rv = PK11_DigestFinal(p12ecx->hmacCx, hmac.data, &hmac.len, SHA1_LENGTH);
02018 
02019     if(rv != SECSuccess) {
02020        PORT_SetError(SEC_ERROR_NO_MEMORY);
02021        goto loser;
02022     }
02023 
02024     /* create the digest info */
02025     di = SGN_CreateDigestInfo(p12ecx->p12exp->integrityInfo.pwdInfo.algorithm,
02026                            hmac.data, hmac.len);
02027     if(!di) {
02028        PORT_SetError(SEC_ERROR_NO_MEMORY);
02029        rv = SECFailure;
02030        goto loser;
02031     }
02032 
02033     rv = SGN_CopyDigestInfo(p12ecx->arena, &p12ecx->mac.safeMac, di);
02034     if(rv != SECSuccess) {
02035        PORT_SetError(SEC_ERROR_NO_MEMORY);
02036        goto loser;
02037     }
02038 
02039     /* encode the mac data */
02040     dummy = SEC_ASN1EncodeItem(p12ecx->arena, &p12ecx->pfx.encodedMacData, 
02041                          &p12ecx->mac, sec_PKCS12MacDataTemplate);
02042     if(!dummy) {
02043        PORT_SetError(SEC_ERROR_NO_MEMORY);
02044        rv = SECFailure;
02045     }
02046 
02047 loser:
02048     if(di) {
02049        SGN_DestroyDigestInfo(di);
02050     }
02051     if(hmac.data) {
02052        SECITEM_ZfreeItem(&hmac, PR_FALSE);
02053     }
02054     PK11_DestroyContext(p12ecx->hmacCx, PR_TRUE);
02055     p12ecx->hmacCx = NULL;
02056 
02057     return rv;
02058 }
02059 
02060 /* pfx notify function for ASN1 encoder.  
02061  * We want to stop encoding once we reach the authenticated safe.  
02062  * At that point, the encoder will be updated via streaming
02063  * as the authenticated safe is  encoded. 
02064  */
02065 static void
02066 sec_pkcs12_encoder_pfx_notify(void *arg, PRBool before, void *dest, int real_depth)
02067 {
02068     sec_PKCS12EncoderContext *p12ecx;
02069 
02070     if(!before) {
02071        return;
02072     }
02073 
02074     /* look for authenticated safe */
02075     p12ecx = (sec_PKCS12EncoderContext*)arg;
02076     if(dest != &p12ecx->pfx.encodedAuthSafe) {
02077        return;
02078     }
02079 
02080     SEC_ASN1EncoderSetTakeFromBuf(p12ecx->outerA1ecx);
02081     SEC_ASN1EncoderSetStreaming(p12ecx->outerA1ecx);
02082     SEC_ASN1EncoderClearNotifyProc(p12ecx->outerA1ecx);
02083 }
02084 
02085 /* SEC_PKCS12Encode
02086  *     Encodes the PFX item and returns it to the output function, via
02087  *     callback.  the output function must be capable of multiple updates.
02088  *     
02089  *     p12exp - the export context 
02090  *     output - the output function callback, will be called more than once,
02091  *             must be able to accept streaming data.
02092  *     outputarg - argument for the output callback.
02093  */
02094 SECStatus
02095 SEC_PKCS12Encode(SEC_PKCS12ExportContext *p12exp, 
02096                SEC_PKCS12EncoderOutputCallback output, void *outputarg)
02097 {
02098     sec_PKCS12EncoderContext *p12enc;
02099     struct sec_pkcs12_encoder_output outInfo;
02100     SECStatus rv;
02101 
02102     if(!p12exp || !output) {
02103        return SECFailure;
02104     }
02105 
02106     /* get the encoder context */
02107     p12enc = sec_pkcs12_encoder_start_context(p12exp);
02108     if(!p12enc) {
02109        return SECFailure;
02110     }
02111 
02112     outInfo.outputfn = output;
02113     outInfo.outputarg = outputarg;
02114 
02115     /* set up PFX encoder, the "outer" encoder.  Set it for streaming */
02116     p12enc->outerA1ecx = SEC_ASN1EncoderStart(&p12enc->pfx, 
02117                                        sec_PKCS12PFXItemTemplate,
02118                                    sec_P12A1OutputCB_Outer, 
02119                                    &outInfo);
02120     if(!p12enc->outerA1ecx) {
02121        PORT_SetError(SEC_ERROR_NO_MEMORY);
02122        rv = SECFailure;
02123        goto loser;
02124     }
02125     SEC_ASN1EncoderSetStreaming(p12enc->outerA1ecx);
02126     SEC_ASN1EncoderSetNotifyProc(p12enc->outerA1ecx, 
02127                                  sec_pkcs12_encoder_pfx_notify, p12enc);
02128     rv = SEC_ASN1EncoderUpdate(p12enc->outerA1ecx, NULL, 0);
02129     if(rv != SECSuccess) {
02130        rv = SECFailure;
02131        goto loser;
02132     }
02133 
02134     /* set up asafe cinfo - the output of the encoder feeds the PFX encoder */
02135     p12enc->middleP7ecx = SEC_PKCS7EncoderStart(p12enc->aSafeCinfo, 
02136                                    sec_P12P7OutputCB_CallA1Update,
02137                                    p12enc->outerA1ecx, NULL);
02138     if(!p12enc->middleP7ecx) {
02139        rv = SECFailure;
02140        goto loser;
02141     }
02142 
02143     /* encode asafe */
02144     p12enc->middleBuf.p7eCx    = p12enc->middleP7ecx;
02145     p12enc->middleBuf.hmacCx   = NULL;
02146     p12enc->middleBuf.numBytes = 0;
02147     p12enc->middleBuf.bufBytes = sizeof p12enc->middleBuf.buf;
02148 
02149     /* Setup the "inner ASN.1 encoder for Authenticated Safes.  */
02150     if(p12enc->p12exp->integrityEnabled && 
02151        p12enc->p12exp->pwdIntegrity) {
02152        p12enc->middleBuf.hmacCx = p12enc->hmacCx;
02153     }
02154     p12enc->middleA1ecx = SEC_ASN1EncoderStart(&p12enc->p12exp->authSafe,
02155                          sec_PKCS12AuthenticatedSafeTemplate,
02156                          sec_P12A1OutputCB_HmacP7Update,
02157                          &p12enc->middleBuf);
02158     if(!p12enc->middleA1ecx) {
02159        rv = SECFailure;
02160        goto loser;
02161     }
02162     SEC_ASN1EncoderSetStreaming(p12enc->middleA1ecx);
02163     SEC_ASN1EncoderSetTakeFromBuf(p12enc->middleA1ecx); 
02164        
02165     /* encode each of the safes */                
02166     while(p12enc->currentSafe != p12enc->p12exp->safeInfoCount) {
02167        sec_pkcs12_encoder_asafe_process(p12enc);
02168        p12enc->currentSafe++;
02169     }
02170     SEC_ASN1EncoderClearTakeFromBuf(p12enc->middleA1ecx);
02171     SEC_ASN1EncoderClearStreaming(p12enc->middleA1ecx);
02172     SEC_ASN1EncoderUpdate(p12enc->middleA1ecx, NULL, 0);
02173     SEC_ASN1EncoderFinish(p12enc->middleA1ecx);
02174 
02175     sec_FlushPkcs12OutputBuffer( &p12enc->middleBuf);
02176 
02177     /* finish the encoding of the authenticated safes */
02178     rv = SEC_PKCS7EncoderFinish(p12enc->middleP7ecx, p12exp->pwfn, 
02179                             p12exp->pwfnarg);
02180     if(rv != SECSuccess) {
02181        goto loser;
02182     }
02183 
02184     SEC_ASN1EncoderClearTakeFromBuf(p12enc->outerA1ecx);
02185     SEC_ASN1EncoderClearStreaming(p12enc->outerA1ecx);
02186 
02187     /* update the mac, if necessary */
02188     rv = sec_Pkcs12FinishMac(p12enc);
02189     if(rv != SECSuccess) {
02190        goto loser;
02191     }
02192    
02193     /* finish encoding the pfx */ 
02194     rv = SEC_ASN1EncoderUpdate(p12enc->outerA1ecx, NULL, 0);
02195 
02196     SEC_ASN1EncoderFinish(p12enc->outerA1ecx);
02197 
02198 loser:
02199     return rv;
02200 }
02201 
02202 void
02203 SEC_PKCS12DestroyExportContext(SEC_PKCS12ExportContext *p12ecx)
02204 {
02205     int i = 0;
02206 
02207     if(!p12ecx) {
02208        return;
02209     }
02210 
02211     if(p12ecx->safeInfos) {
02212        i = 0;
02213        while(p12ecx->safeInfos[i] != NULL) {
02214            if(p12ecx->safeInfos[i]->encryptionKey) {
02215               PK11_FreeSymKey(p12ecx->safeInfos[i]->encryptionKey);
02216            }
02217            if(p12ecx->safeInfos[i]->cinfo) {
02218               SEC_PKCS7DestroyContentInfo(p12ecx->safeInfos[i]->cinfo);
02219            }
02220            i++;
02221        }
02222     }
02223 
02224     PK11_FreeSlot(p12ecx->slot);
02225 
02226     PORT_FreeArena(p12ecx->arena, PR_TRUE);
02227 }
02228 
02229 
02230 /*********************************
02231  * All-in-one routines for exporting certificates 
02232  *********************************/
02233 struct inPlaceEncodeInfo {
02234     PRBool error;
02235     SECItem outItem;
02236 };
02237 
02238 static void 
02239 sec_pkcs12_in_place_encoder_output(void *arg, const char *buf, unsigned long len)
02240 {
02241     struct inPlaceEncodeInfo *outInfo = (struct inPlaceEncodeInfo*)arg;
02242 
02243     if(!outInfo || !len || outInfo->error) {
02244        return;
02245     }
02246 
02247     if(!outInfo->outItem.data) {
02248        outInfo->outItem.data = (unsigned char*)PORT_ZAlloc(len);
02249        outInfo->outItem.len = 0;
02250     } else {
02251        if(!PORT_Realloc(&(outInfo->outItem.data), (outInfo->outItem.len + len))) {
02252            SECITEM_ZfreeItem(&(outInfo->outItem), PR_FALSE);
02253            outInfo->outItem.data = NULL;
02254            PORT_SetError(SEC_ERROR_NO_MEMORY);
02255            outInfo->error = PR_TRUE;
02256            return;
02257        }
02258     }
02259 
02260     PORT_Memcpy(&(outInfo->outItem.data[outInfo->outItem.len]), buf, len);
02261     outInfo->outItem.len += len;
02262 
02263     return;
02264 }
02265 
02266 /*
02267  * SEC_PKCS12ExportCertifcateAndKeyUsingPassword
02268  *     Exports a certificate/key pair using password-based encryption and
02269  *     authentication.
02270  *
02271  * pwfn, pwfnarg - password function and argument for the key database
02272  * cert - the certificate to export
02273  * certDb - certificate database
02274  * pwitem - the password to use
02275  * shroudKey - encrypt the key externally, 
02276  * keyShroudAlg - encryption algorithm for key
02277  * encryptionAlg - the algorithm with which data is encrypted
02278  * integrityAlg - the algorithm for integrity
02279  */
02280 SECItem *
02281 SEC_PKCS12ExportCertificateAndKeyUsingPassword(
02282                             SECKEYGetPasswordKey pwfn, void *pwfnarg,
02283                             CERTCertificate *cert, PK11SlotInfo *slot,
02284                             CERTCertDBHandle *certDb, SECItem *pwitem,
02285                             PRBool shroudKey, SECOidTag shroudAlg,
02286                             PRBool encryptCert, SECOidTag certEncAlg,
02287                             SECOidTag integrityAlg, void *wincx)
02288 {
02289     struct inPlaceEncodeInfo outInfo;
02290     SEC_PKCS12ExportContext *p12ecx = NULL;
02291     SEC_PKCS12SafeInfo *keySafe, *certSafe;
02292     SECItem *returnItem = NULL;
02293 
02294     if(!cert || !pwitem || !slot) {
02295        return NULL;
02296     }
02297 
02298     outInfo.error = PR_FALSE;
02299     outInfo.outItem.data = NULL;
02300     outInfo.outItem.len = 0;
02301 
02302     p12ecx = SEC_PKCS12CreateExportContext(pwfn, pwfnarg, slot, wincx);
02303     if(!p12ecx) {
02304        return NULL;
02305     }
02306 
02307     /* set up cert safe */
02308     if(encryptCert) {
02309        certSafe = SEC_PKCS12CreatePasswordPrivSafe(p12ecx, pwitem, certEncAlg);
02310     } else {
02311        certSafe = SEC_PKCS12CreateUnencryptedSafe(p12ecx);
02312     }
02313     if(!certSafe) {
02314        goto loser;
02315     }
02316 
02317     /* set up key safe */
02318     if(shroudKey) {
02319        keySafe = SEC_PKCS12CreateUnencryptedSafe(p12ecx);
02320     } else {
02321        keySafe = certSafe;
02322     }
02323     if(!keySafe) {
02324        goto loser;
02325     }
02326 
02327     /* add integrity mode */
02328     if(SEC_PKCS12AddPasswordIntegrity(p12ecx, pwitem, integrityAlg) 
02329               != SECSuccess) {
02330        goto loser;
02331     }
02332 
02333     /* add cert and key pair */
02334     if(SEC_PKCS12AddCertAndKey(p12ecx, certSafe, NULL, cert, certDb, 
02335                             keySafe, NULL, shroudKey, pwitem, shroudAlg)
02336               != SECSuccess) {
02337        goto loser;
02338     }
02339 
02340     /* encode the puppy */
02341     if(SEC_PKCS12Encode(p12ecx, sec_pkcs12_in_place_encoder_output, &outInfo)
02342               != SECSuccess) {
02343        goto loser;
02344     }
02345     if(outInfo.error) {
02346        goto loser;
02347     }
02348 
02349     SEC_PKCS12DestroyExportContext(p12ecx);
02350        
02351     returnItem = SECITEM_DupItem(&outInfo.outItem);
02352     SECITEM_ZfreeItem(&outInfo.outItem, PR_FALSE);
02353 
02354     return returnItem;
02355 
02356 loser:
02357     if(outInfo.outItem.data) {
02358        SECITEM_ZfreeItem(&(outInfo.outItem), PR_TRUE);
02359     }
02360 
02361     if(p12ecx) {
02362        SEC_PKCS12DestroyExportContext(p12ecx);
02363     }
02364 
02365     return NULL;
02366 }
02367 
02368