michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "p12t.h" michael@0: #include "p12.h" michael@0: #include "plarena.h" michael@0: #include "secitem.h" michael@0: #include "secoid.h" michael@0: #include "seccomon.h" michael@0: #include "secport.h" michael@0: #include "cert.h" michael@0: #include "secpkcs7.h" michael@0: #include "secasn1.h" michael@0: #include "secerr.h" michael@0: #include "pk11func.h" michael@0: #include "p12plcy.h" michael@0: #include "p12local.h" michael@0: #include "prcpucfg.h" michael@0: michael@0: extern const int NSS_PBE_DEFAULT_ITERATION_COUNT; /* defined in p7create.c */ michael@0: michael@0: /* michael@0: ** This PKCS12 file encoder uses numerous nested ASN.1 and PKCS7 encoder michael@0: ** contexts. It can be difficult to keep straight. Here's a picture: michael@0: ** michael@0: ** "outer" ASN.1 encoder. The output goes to the library caller's CB. michael@0: ** "middle" PKCS7 encoder. Feeds the "outer" ASN.1 encoder. michael@0: ** "middle" ASN1 encoder. Encodes the encrypted aSafes. michael@0: ** Feeds the "middle" P7 encoder above. michael@0: ** "inner" PKCS7 encoder. Encrypts the "authenticated Safes" (aSafes) michael@0: ** Feeds the "middle" ASN.1 encoder above. michael@0: ** "inner" ASN.1 encoder. Encodes the unencrypted aSafes. michael@0: ** Feeds the "inner" P7 enocder above. michael@0: ** michael@0: ** Buffering has been added at each point where the output of an ASN.1 michael@0: ** encoder feeds the input of a PKCS7 encoder. michael@0: */ michael@0: michael@0: /********************************* michael@0: * Output buffer object, used to buffer output from ASN.1 encoder michael@0: * before passing data on down to the next PKCS7 encoder. michael@0: *********************************/ michael@0: michael@0: #define PK12_OUTPUT_BUFFER_SIZE 8192 michael@0: michael@0: struct sec_pkcs12OutputBufferStr { michael@0: SEC_PKCS7EncoderContext * p7eCx; michael@0: PK11Context * hmacCx; michael@0: unsigned int numBytes; michael@0: unsigned int bufBytes; michael@0: char buf[PK12_OUTPUT_BUFFER_SIZE]; michael@0: }; michael@0: typedef struct sec_pkcs12OutputBufferStr sec_pkcs12OutputBuffer; michael@0: michael@0: /********************************* michael@0: * Structures used in exporting the PKCS 12 blob michael@0: *********************************/ michael@0: michael@0: /* A SafeInfo is used for each ContentInfo which makes up the michael@0: * sequence of safes in the AuthenticatedSafe portion of the michael@0: * PFX structure. michael@0: */ michael@0: struct SEC_PKCS12SafeInfoStr { michael@0: PLArenaPool *arena; michael@0: michael@0: /* information for setting up password encryption */ michael@0: SECItem pwitem; michael@0: SECOidTag algorithm; michael@0: PK11SymKey *encryptionKey; michael@0: michael@0: /* how many items have been stored in this safe, michael@0: * we will skip any safe which does not contain any michael@0: * items michael@0: */ michael@0: unsigned int itemCount; michael@0: michael@0: /* the content info for the safe */ michael@0: SEC_PKCS7ContentInfo *cinfo; michael@0: michael@0: sec_PKCS12SafeContents *safe; michael@0: }; michael@0: michael@0: /* An opaque structure which contains information needed for exporting michael@0: * certificates and keys through PKCS 12. michael@0: */ michael@0: struct SEC_PKCS12ExportContextStr { michael@0: PLArenaPool *arena; michael@0: PK11SlotInfo *slot; michael@0: void *wincx; michael@0: michael@0: /* integrity information */ michael@0: PRBool integrityEnabled; michael@0: PRBool pwdIntegrity; michael@0: union { michael@0: struct sec_PKCS12PasswordModeInfo pwdInfo; michael@0: struct sec_PKCS12PublicKeyModeInfo pubkeyInfo; michael@0: } integrityInfo; michael@0: michael@0: /* helper functions */ michael@0: /* retrieve the password call back */ michael@0: SECKEYGetPasswordKey pwfn; michael@0: void *pwfnarg; michael@0: michael@0: /* safe contents bags */ michael@0: SEC_PKCS12SafeInfo **safeInfos; michael@0: unsigned int safeInfoCount; michael@0: michael@0: /* the sequence of safes */ michael@0: sec_PKCS12AuthenticatedSafe authSafe; michael@0: michael@0: /* information needing deletion */ michael@0: CERTCertificate **certList; michael@0: }; michael@0: michael@0: /* structures for passing information to encoder callbacks when processing michael@0: * data through the ASN1 engine. michael@0: */ michael@0: struct sec_pkcs12_encoder_output { michael@0: SEC_PKCS12EncoderOutputCallback outputfn; michael@0: void *outputarg; michael@0: }; michael@0: michael@0: struct sec_pkcs12_hmac_and_output_info { michael@0: void *arg; michael@0: struct sec_pkcs12_encoder_output output; michael@0: }; michael@0: michael@0: /* An encoder context which is used for the actual encoding michael@0: * portion of PKCS 12. michael@0: */ michael@0: typedef struct sec_PKCS12EncoderContextStr { michael@0: PLArenaPool *arena; michael@0: SEC_PKCS12ExportContext *p12exp; michael@0: michael@0: /* encoder information - this is set up based on whether michael@0: * password based or public key pased privacy is being used michael@0: */ michael@0: SEC_ASN1EncoderContext *outerA1ecx; michael@0: union { michael@0: struct sec_pkcs12_hmac_and_output_info hmacAndOutputInfo; michael@0: struct sec_pkcs12_encoder_output encOutput; michael@0: } output; michael@0: michael@0: /* structures for encoding of PFX and MAC */ michael@0: sec_PKCS12PFXItem pfx; michael@0: sec_PKCS12MacData mac; michael@0: michael@0: /* authenticated safe encoding tracking information */ michael@0: SEC_PKCS7ContentInfo *aSafeCinfo; michael@0: SEC_PKCS7EncoderContext *middleP7ecx; michael@0: SEC_ASN1EncoderContext *middleA1ecx; michael@0: unsigned int currentSafe; michael@0: michael@0: /* hmac context */ michael@0: PK11Context *hmacCx; michael@0: michael@0: /* output buffers */ michael@0: sec_pkcs12OutputBuffer middleBuf; michael@0: sec_pkcs12OutputBuffer innerBuf; michael@0: michael@0: } sec_PKCS12EncoderContext; michael@0: michael@0: michael@0: /********************************* michael@0: * Export setup routines michael@0: *********************************/ michael@0: michael@0: /* SEC_PKCS12CreateExportContext michael@0: * Creates an export context and sets the unicode and password retrieval michael@0: * callbacks. This is the first call which must be made when exporting michael@0: * a PKCS 12 blob. michael@0: * michael@0: * pwfn, pwfnarg - password retrieval callback and argument. these are michael@0: * required for password-authentication mode. michael@0: */ michael@0: SEC_PKCS12ExportContext * michael@0: SEC_PKCS12CreateExportContext(SECKEYGetPasswordKey pwfn, void *pwfnarg, michael@0: PK11SlotInfo *slot, void *wincx) michael@0: { michael@0: PLArenaPool *arena = NULL; michael@0: SEC_PKCS12ExportContext *p12ctxt = NULL; michael@0: michael@0: /* allocate the arena and create the context */ michael@0: arena = PORT_NewArena(4096); michael@0: if(!arena) { michael@0: PORT_SetError(SEC_ERROR_NO_MEMORY); michael@0: return NULL; michael@0: } michael@0: michael@0: p12ctxt = (SEC_PKCS12ExportContext *)PORT_ArenaZAlloc(arena, michael@0: sizeof(SEC_PKCS12ExportContext)); michael@0: if(!p12ctxt) { michael@0: PORT_SetError(SEC_ERROR_NO_MEMORY); michael@0: goto loser; michael@0: } michael@0: michael@0: /* password callback for key retrieval */ michael@0: p12ctxt->pwfn = pwfn; michael@0: p12ctxt->pwfnarg = pwfnarg; michael@0: michael@0: p12ctxt->integrityEnabled = PR_FALSE; michael@0: p12ctxt->arena = arena; michael@0: p12ctxt->wincx = wincx; michael@0: p12ctxt->slot = (slot) ? PK11_ReferenceSlot(slot) : PK11_GetInternalSlot(); michael@0: michael@0: return p12ctxt; michael@0: michael@0: loser: michael@0: if(arena) { michael@0: PORT_FreeArena(arena, PR_TRUE); michael@0: } michael@0: michael@0: return NULL; michael@0: } michael@0: michael@0: /* michael@0: * Adding integrity mode michael@0: */ michael@0: michael@0: /* SEC_PKCS12AddPasswordIntegrity michael@0: * Add password integrity to the exported data. If an integrity method michael@0: * has already been set, then return an error. michael@0: * michael@0: * p12ctxt - the export context michael@0: * pwitem - the password for integrity mode michael@0: * integAlg - the integrity algorithm to use for authentication. michael@0: */ michael@0: SECStatus michael@0: SEC_PKCS12AddPasswordIntegrity(SEC_PKCS12ExportContext *p12ctxt, michael@0: SECItem *pwitem, SECOidTag integAlg) michael@0: { michael@0: if(!p12ctxt || p12ctxt->integrityEnabled) { michael@0: return SECFailure; michael@0: } michael@0: michael@0: /* set up integrity information */ michael@0: p12ctxt->pwdIntegrity = PR_TRUE; michael@0: p12ctxt->integrityInfo.pwdInfo.password = michael@0: (SECItem*)PORT_ArenaZAlloc(p12ctxt->arena, sizeof(SECItem)); michael@0: if(!p12ctxt->integrityInfo.pwdInfo.password) { michael@0: PORT_SetError(SEC_ERROR_NO_MEMORY); michael@0: return SECFailure; michael@0: } michael@0: if(SECITEM_CopyItem(p12ctxt->arena, michael@0: p12ctxt->integrityInfo.pwdInfo.password, pwitem) michael@0: != SECSuccess) { michael@0: PORT_SetError(SEC_ERROR_NO_MEMORY); michael@0: return SECFailure; michael@0: } michael@0: p12ctxt->integrityInfo.pwdInfo.algorithm = integAlg; michael@0: p12ctxt->integrityEnabled = PR_TRUE; michael@0: michael@0: return SECSuccess; michael@0: } michael@0: michael@0: /* SEC_PKCS12AddPublicKeyIntegrity michael@0: * Add public key integrity to the exported data. If an integrity method michael@0: * has already been set, then return an error. The certificate must be michael@0: * allowed to be used as a signing cert. michael@0: * michael@0: * p12ctxt - the export context michael@0: * cert - signer certificate michael@0: * certDb - the certificate database michael@0: * algorithm - signing algorithm michael@0: * keySize - size of the signing key (?) michael@0: */ michael@0: SECStatus michael@0: SEC_PKCS12AddPublicKeyIntegrity(SEC_PKCS12ExportContext *p12ctxt, michael@0: CERTCertificate *cert, CERTCertDBHandle *certDb, michael@0: SECOidTag algorithm, int keySize) michael@0: { michael@0: if(!p12ctxt) { michael@0: return SECFailure; michael@0: } michael@0: michael@0: p12ctxt->integrityInfo.pubkeyInfo.cert = cert; michael@0: p12ctxt->integrityInfo.pubkeyInfo.certDb = certDb; michael@0: p12ctxt->integrityInfo.pubkeyInfo.algorithm = algorithm; michael@0: p12ctxt->integrityInfo.pubkeyInfo.keySize = keySize; michael@0: p12ctxt->integrityEnabled = PR_TRUE; michael@0: michael@0: return SECSuccess; michael@0: } michael@0: michael@0: michael@0: /* michael@0: * Adding safes - encrypted (password/public key) or unencrypted michael@0: * Each of the safe creation routines return an opaque pointer which michael@0: * are later passed into the routines for exporting certificates and michael@0: * keys. michael@0: */ michael@0: michael@0: /* append the newly created safeInfo to list of safeInfos in the export michael@0: * context. michael@0: */ michael@0: static SECStatus michael@0: sec_pkcs12_append_safe_info(SEC_PKCS12ExportContext *p12ctxt, SEC_PKCS12SafeInfo *info) michael@0: { michael@0: void *mark = NULL, *dummy1 = NULL, *dummy2 = NULL; michael@0: michael@0: if(!p12ctxt || !info) { michael@0: return SECFailure; michael@0: } michael@0: michael@0: mark = PORT_ArenaMark(p12ctxt->arena); michael@0: michael@0: /* if no safeInfos have been set, create the list, otherwise expand it. */ michael@0: if(!p12ctxt->safeInfoCount) { michael@0: p12ctxt->safeInfos = (SEC_PKCS12SafeInfo **)PORT_ArenaZAlloc(p12ctxt->arena, michael@0: 2 * sizeof(SEC_PKCS12SafeInfo *)); michael@0: dummy1 = p12ctxt->safeInfos; michael@0: p12ctxt->authSafe.encodedSafes = (SECItem **)PORT_ArenaZAlloc(p12ctxt->arena, michael@0: 2 * sizeof(SECItem *)); michael@0: dummy2 = p12ctxt->authSafe.encodedSafes; michael@0: } else { michael@0: dummy1 = PORT_ArenaGrow(p12ctxt->arena, p12ctxt->safeInfos, michael@0: (p12ctxt->safeInfoCount + 1) * sizeof(SEC_PKCS12SafeInfo *), michael@0: (p12ctxt->safeInfoCount + 2) * sizeof(SEC_PKCS12SafeInfo *)); michael@0: p12ctxt->safeInfos = (SEC_PKCS12SafeInfo **)dummy1; michael@0: dummy2 = PORT_ArenaGrow(p12ctxt->arena, p12ctxt->authSafe.encodedSafes, michael@0: (p12ctxt->authSafe.safeCount + 1) * sizeof(SECItem *), michael@0: (p12ctxt->authSafe.safeCount + 2) * sizeof(SECItem *)); michael@0: p12ctxt->authSafe.encodedSafes = (SECItem**)dummy2; michael@0: } michael@0: if(!dummy1 || !dummy2) { michael@0: PORT_SetError(SEC_ERROR_NO_MEMORY); michael@0: goto loser; michael@0: } michael@0: michael@0: /* append the new safeInfo and null terminate the list */ michael@0: p12ctxt->safeInfos[p12ctxt->safeInfoCount] = info; michael@0: p12ctxt->safeInfos[++p12ctxt->safeInfoCount] = NULL; michael@0: p12ctxt->authSafe.encodedSafes[p12ctxt->authSafe.safeCount] = michael@0: (SECItem*)PORT_ArenaZAlloc(p12ctxt->arena, sizeof(SECItem)); michael@0: if(!p12ctxt->authSafe.encodedSafes[p12ctxt->authSafe.safeCount]) { michael@0: PORT_SetError(SEC_ERROR_NO_MEMORY); michael@0: goto loser; michael@0: } michael@0: p12ctxt->authSafe.encodedSafes[++p12ctxt->authSafe.safeCount] = NULL; michael@0: michael@0: PORT_ArenaUnmark(p12ctxt->arena, mark); michael@0: return SECSuccess; michael@0: michael@0: loser: michael@0: PORT_ArenaRelease(p12ctxt->arena, mark); michael@0: return SECFailure; michael@0: } michael@0: michael@0: /* SEC_PKCS12CreatePasswordPrivSafe michael@0: * Create a password privacy safe to store exported information in. michael@0: * michael@0: * p12ctxt - export context michael@0: * pwitem - password for encryption michael@0: * privAlg - pbe algorithm through which encryption is done. michael@0: */ michael@0: SEC_PKCS12SafeInfo * michael@0: SEC_PKCS12CreatePasswordPrivSafe(SEC_PKCS12ExportContext *p12ctxt, michael@0: SECItem *pwitem, SECOidTag privAlg) michael@0: { michael@0: SEC_PKCS12SafeInfo *safeInfo = NULL; michael@0: void *mark = NULL; michael@0: PK11SlotInfo *slot = NULL; michael@0: SECAlgorithmID *algId; michael@0: SECItem uniPwitem = {siBuffer, NULL, 0}; michael@0: michael@0: if(!p12ctxt) { michael@0: return NULL; michael@0: } michael@0: michael@0: /* allocate the safe info */ michael@0: mark = PORT_ArenaMark(p12ctxt->arena); michael@0: safeInfo = (SEC_PKCS12SafeInfo *)PORT_ArenaZAlloc(p12ctxt->arena, michael@0: sizeof(SEC_PKCS12SafeInfo)); michael@0: if(!safeInfo) { michael@0: PORT_SetError(SEC_ERROR_NO_MEMORY); michael@0: PORT_ArenaRelease(p12ctxt->arena, mark); michael@0: return NULL; michael@0: } michael@0: michael@0: safeInfo->itemCount = 0; michael@0: michael@0: /* create the encrypted safe */ michael@0: safeInfo->cinfo = SEC_PKCS7CreateEncryptedData(privAlg, 0, p12ctxt->pwfn, michael@0: p12ctxt->pwfnarg); michael@0: if(!safeInfo->cinfo) { michael@0: PORT_SetError(SEC_ERROR_NO_MEMORY); michael@0: goto loser; michael@0: } michael@0: safeInfo->arena = p12ctxt->arena; michael@0: michael@0: /* convert the password to unicode */ michael@0: if(!sec_pkcs12_convert_item_to_unicode(NULL, &uniPwitem, pwitem, michael@0: PR_TRUE, PR_TRUE, PR_TRUE)) { michael@0: PORT_SetError(SEC_ERROR_NO_MEMORY); michael@0: goto loser; michael@0: } michael@0: if(SECITEM_CopyItem(p12ctxt->arena, &safeInfo->pwitem, &uniPwitem) != SECSuccess) { michael@0: PORT_SetError(SEC_ERROR_NO_MEMORY); michael@0: goto loser; michael@0: } michael@0: michael@0: /* generate the encryption key */ michael@0: slot = PK11_ReferenceSlot(p12ctxt->slot); michael@0: if(!slot) { michael@0: slot = PK11_GetInternalKeySlot(); michael@0: if(!slot) { michael@0: PORT_SetError(SEC_ERROR_NO_MEMORY); michael@0: goto loser; michael@0: } michael@0: } michael@0: michael@0: algId = SEC_PKCS7GetEncryptionAlgorithm(safeInfo->cinfo); michael@0: safeInfo->encryptionKey = PK11_PBEKeyGen(slot, algId, &uniPwitem, michael@0: PR_FALSE, p12ctxt->wincx); michael@0: if(!safeInfo->encryptionKey) { michael@0: goto loser; michael@0: } michael@0: michael@0: safeInfo->arena = p12ctxt->arena; michael@0: safeInfo->safe = NULL; michael@0: if(sec_pkcs12_append_safe_info(p12ctxt, safeInfo) != SECSuccess) { michael@0: goto loser; michael@0: } michael@0: michael@0: if(uniPwitem.data) { michael@0: SECITEM_ZfreeItem(&uniPwitem, PR_FALSE); michael@0: } michael@0: PORT_ArenaUnmark(p12ctxt->arena, mark); michael@0: michael@0: if (slot) { michael@0: PK11_FreeSlot(slot); michael@0: } michael@0: return safeInfo; michael@0: michael@0: loser: michael@0: if (slot) { michael@0: PK11_FreeSlot(slot); michael@0: } michael@0: if(safeInfo->cinfo) { michael@0: SEC_PKCS7DestroyContentInfo(safeInfo->cinfo); michael@0: } michael@0: michael@0: if(uniPwitem.data) { michael@0: SECITEM_ZfreeItem(&uniPwitem, PR_FALSE); michael@0: } michael@0: michael@0: PORT_ArenaRelease(p12ctxt->arena, mark); michael@0: return NULL; michael@0: } michael@0: michael@0: /* SEC_PKCS12CreateUnencryptedSafe michael@0: * Creates an unencrypted safe within the export context. michael@0: * michael@0: * p12ctxt - the export context michael@0: */ michael@0: SEC_PKCS12SafeInfo * michael@0: SEC_PKCS12CreateUnencryptedSafe(SEC_PKCS12ExportContext *p12ctxt) michael@0: { michael@0: SEC_PKCS12SafeInfo *safeInfo = NULL; michael@0: void *mark = NULL; michael@0: michael@0: if(!p12ctxt) { michael@0: return NULL; michael@0: } michael@0: michael@0: /* create the safe info */ michael@0: mark = PORT_ArenaMark(p12ctxt->arena); michael@0: safeInfo = (SEC_PKCS12SafeInfo *)PORT_ArenaZAlloc(p12ctxt->arena, michael@0: sizeof(SEC_PKCS12SafeInfo)); michael@0: if(!safeInfo) { michael@0: PORT_ArenaRelease(p12ctxt->arena, mark); michael@0: PORT_SetError(SEC_ERROR_NO_MEMORY); michael@0: return NULL; michael@0: } michael@0: michael@0: safeInfo->itemCount = 0; michael@0: michael@0: /* create the safe content */ michael@0: safeInfo->cinfo = SEC_PKCS7CreateData(); michael@0: if(!safeInfo->cinfo) { michael@0: PORT_SetError(SEC_ERROR_NO_MEMORY); michael@0: goto loser; michael@0: } michael@0: michael@0: if(sec_pkcs12_append_safe_info(p12ctxt, safeInfo) != SECSuccess) { michael@0: goto loser; michael@0: } michael@0: michael@0: PORT_ArenaUnmark(p12ctxt->arena, mark); michael@0: return safeInfo; michael@0: michael@0: loser: michael@0: if(safeInfo->cinfo) { michael@0: SEC_PKCS7DestroyContentInfo(safeInfo->cinfo); michael@0: } michael@0: michael@0: PORT_ArenaRelease(p12ctxt->arena, mark); michael@0: return NULL; michael@0: } michael@0: michael@0: /* SEC_PKCS12CreatePubKeyEncryptedSafe michael@0: * Creates a safe which is protected by public key encryption. michael@0: * michael@0: * p12ctxt - the export context michael@0: * certDb - the certificate database michael@0: * signer - the signer's certificate michael@0: * recipients - the list of recipient certificates. michael@0: * algorithm - the encryption algorithm to use michael@0: * keysize - the algorithms key size (?) michael@0: */ michael@0: SEC_PKCS12SafeInfo * michael@0: SEC_PKCS12CreatePubKeyEncryptedSafe(SEC_PKCS12ExportContext *p12ctxt, michael@0: CERTCertDBHandle *certDb, michael@0: CERTCertificate *signer, michael@0: CERTCertificate **recipients, michael@0: SECOidTag algorithm, int keysize) michael@0: { michael@0: SEC_PKCS12SafeInfo *safeInfo = NULL; michael@0: void *mark = NULL; michael@0: michael@0: if(!p12ctxt || !signer || !recipients || !(*recipients)) { michael@0: return NULL; michael@0: } michael@0: michael@0: /* allocate the safeInfo */ michael@0: mark = PORT_ArenaMark(p12ctxt->arena); michael@0: safeInfo = (SEC_PKCS12SafeInfo *)PORT_ArenaZAlloc(p12ctxt->arena, michael@0: sizeof(SEC_PKCS12SafeInfo)); michael@0: if(!safeInfo) { michael@0: PORT_ArenaRelease(p12ctxt->arena, mark); michael@0: PORT_SetError(SEC_ERROR_NO_MEMORY); michael@0: return NULL; michael@0: } michael@0: michael@0: safeInfo->itemCount = 0; michael@0: safeInfo->arena = p12ctxt->arena; michael@0: michael@0: /* create the enveloped content info using certUsageEmailSigner currently. michael@0: * XXX We need to eventually use something other than certUsageEmailSigner michael@0: */ michael@0: safeInfo->cinfo = SEC_PKCS7CreateEnvelopedData(signer, certUsageEmailSigner, michael@0: certDb, algorithm, keysize, michael@0: p12ctxt->pwfn, p12ctxt->pwfnarg); michael@0: if(!safeInfo->cinfo) { michael@0: PORT_SetError(SEC_ERROR_NO_MEMORY); michael@0: goto loser; michael@0: } michael@0: michael@0: /* add recipients */ michael@0: if(recipients) { michael@0: unsigned int i = 0; michael@0: while(recipients[i] != NULL) { michael@0: SECStatus rv = SEC_PKCS7AddRecipient(safeInfo->cinfo, recipients[i], michael@0: certUsageEmailRecipient, certDb); michael@0: if(rv != SECSuccess) { michael@0: goto loser; michael@0: } michael@0: i++; michael@0: } michael@0: } michael@0: michael@0: if(sec_pkcs12_append_safe_info(p12ctxt, safeInfo) != SECSuccess) { michael@0: goto loser; michael@0: } michael@0: michael@0: PORT_ArenaUnmark(p12ctxt->arena, mark); michael@0: return safeInfo; michael@0: michael@0: loser: michael@0: if(safeInfo->cinfo) { michael@0: SEC_PKCS7DestroyContentInfo(safeInfo->cinfo); michael@0: safeInfo->cinfo = NULL; michael@0: } michael@0: michael@0: PORT_ArenaRelease(p12ctxt->arena, mark); michael@0: return NULL; michael@0: } michael@0: michael@0: /********************************* michael@0: * Routines to handle the exporting of the keys and certificates michael@0: *********************************/ michael@0: michael@0: /* creates a safe contents which safeBags will be appended to */ michael@0: sec_PKCS12SafeContents * michael@0: sec_PKCS12CreateSafeContents(PLArenaPool *arena) michael@0: { michael@0: sec_PKCS12SafeContents *safeContents; michael@0: michael@0: if(arena == NULL) { michael@0: return NULL; michael@0: } michael@0: michael@0: /* create the safe contents */ michael@0: safeContents = (sec_PKCS12SafeContents *)PORT_ArenaZAlloc(arena, michael@0: sizeof(sec_PKCS12SafeContents)); michael@0: if(!safeContents) { michael@0: PORT_SetError(SEC_ERROR_NO_MEMORY); michael@0: goto loser; michael@0: } michael@0: michael@0: /* set up the internal contents info */ michael@0: safeContents->safeBags = NULL; michael@0: safeContents->arena = arena; michael@0: safeContents->bagCount = 0; michael@0: michael@0: return safeContents; michael@0: michael@0: loser: michael@0: return NULL; michael@0: } michael@0: michael@0: /* appends a safe bag to a safeContents using the specified arena. michael@0: */ michael@0: SECStatus michael@0: sec_pkcs12_append_bag_to_safe_contents(PLArenaPool *arena, michael@0: sec_PKCS12SafeContents *safeContents, michael@0: sec_PKCS12SafeBag *safeBag) michael@0: { michael@0: void *mark = NULL, *dummy = NULL; michael@0: michael@0: if(!arena || !safeBag || !safeContents) { michael@0: return SECFailure; michael@0: } michael@0: michael@0: mark = PORT_ArenaMark(arena); michael@0: if(!mark) { michael@0: PORT_SetError(SEC_ERROR_NO_MEMORY); michael@0: return SECFailure; michael@0: } michael@0: michael@0: /* allocate space for the list, or reallocate to increase space */ michael@0: if(!safeContents->safeBags) { michael@0: safeContents->safeBags = (sec_PKCS12SafeBag **)PORT_ArenaZAlloc(arena, michael@0: (2 * sizeof(sec_PKCS12SafeBag *))); michael@0: dummy = safeContents->safeBags; michael@0: safeContents->bagCount = 0; michael@0: } else { michael@0: dummy = PORT_ArenaGrow(arena, safeContents->safeBags, michael@0: (safeContents->bagCount + 1) * sizeof(sec_PKCS12SafeBag *), michael@0: (safeContents->bagCount + 2) * sizeof(sec_PKCS12SafeBag *)); michael@0: safeContents->safeBags = (sec_PKCS12SafeBag **)dummy; michael@0: } michael@0: michael@0: if(!dummy) { michael@0: PORT_ArenaRelease(arena, mark); michael@0: PORT_SetError(SEC_ERROR_NO_MEMORY); michael@0: return SECFailure; michael@0: } michael@0: michael@0: /* append the bag at the end and null terminate the list */ michael@0: safeContents->safeBags[safeContents->bagCount++] = safeBag; michael@0: safeContents->safeBags[safeContents->bagCount] = NULL; michael@0: michael@0: PORT_ArenaUnmark(arena, mark); michael@0: michael@0: return SECSuccess; michael@0: } michael@0: michael@0: /* appends a safeBag to a specific safeInfo. michael@0: */ michael@0: SECStatus michael@0: sec_pkcs12_append_bag(SEC_PKCS12ExportContext *p12ctxt, michael@0: SEC_PKCS12SafeInfo *safeInfo, sec_PKCS12SafeBag *safeBag) michael@0: { michael@0: sec_PKCS12SafeContents *dest; michael@0: SECStatus rv = SECFailure; michael@0: michael@0: if(!p12ctxt || !safeBag || !safeInfo) { michael@0: return SECFailure; michael@0: } michael@0: michael@0: if(!safeInfo->safe) { michael@0: safeInfo->safe = sec_PKCS12CreateSafeContents(p12ctxt->arena); michael@0: if(!safeInfo->safe) { michael@0: return SECFailure; michael@0: } michael@0: } michael@0: michael@0: dest = safeInfo->safe; michael@0: rv = sec_pkcs12_append_bag_to_safe_contents(p12ctxt->arena, dest, safeBag); michael@0: if(rv == SECSuccess) { michael@0: safeInfo->itemCount++; michael@0: } michael@0: michael@0: return rv; michael@0: } michael@0: michael@0: /* Creates a safeBag of the specified type, and if bagData is specified, michael@0: * the contents are set. The contents could be set later by the calling michael@0: * routine. michael@0: */ michael@0: sec_PKCS12SafeBag * michael@0: sec_PKCS12CreateSafeBag(SEC_PKCS12ExportContext *p12ctxt, SECOidTag bagType, michael@0: void *bagData) michael@0: { michael@0: sec_PKCS12SafeBag *safeBag; michael@0: PRBool setName = PR_TRUE; michael@0: void *mark = NULL; michael@0: SECStatus rv = SECSuccess; michael@0: SECOidData *oidData = NULL; michael@0: michael@0: if(!p12ctxt) { michael@0: return NULL; michael@0: } michael@0: michael@0: mark = PORT_ArenaMark(p12ctxt->arena); michael@0: if(!mark) { michael@0: PORT_SetError(SEC_ERROR_NO_MEMORY); michael@0: return NULL; michael@0: } michael@0: michael@0: safeBag = (sec_PKCS12SafeBag *)PORT_ArenaZAlloc(p12ctxt->arena, michael@0: sizeof(sec_PKCS12SafeBag)); michael@0: if(!safeBag) { michael@0: PORT_ArenaRelease(p12ctxt->arena, mark); michael@0: PORT_SetError(SEC_ERROR_NO_MEMORY); michael@0: return NULL; michael@0: } michael@0: michael@0: /* set the bags content based upon bag type */ michael@0: switch(bagType) { michael@0: case SEC_OID_PKCS12_V1_KEY_BAG_ID: michael@0: safeBag->safeBagContent.pkcs8KeyBag = michael@0: (SECKEYPrivateKeyInfo *)bagData; michael@0: break; michael@0: case SEC_OID_PKCS12_V1_CERT_BAG_ID: michael@0: safeBag->safeBagContent.certBag = (sec_PKCS12CertBag *)bagData; michael@0: break; michael@0: case SEC_OID_PKCS12_V1_CRL_BAG_ID: michael@0: safeBag->safeBagContent.crlBag = (sec_PKCS12CRLBag *)bagData; michael@0: break; michael@0: case SEC_OID_PKCS12_V1_SECRET_BAG_ID: michael@0: safeBag->safeBagContent.secretBag = (sec_PKCS12SecretBag *)bagData; michael@0: break; michael@0: case SEC_OID_PKCS12_V1_PKCS8_SHROUDED_KEY_BAG_ID: michael@0: safeBag->safeBagContent.pkcs8ShroudedKeyBag = michael@0: (SECKEYEncryptedPrivateKeyInfo *)bagData; michael@0: break; michael@0: case SEC_OID_PKCS12_V1_SAFE_CONTENTS_BAG_ID: michael@0: safeBag->safeBagContent.safeContents = michael@0: (sec_PKCS12SafeContents *)bagData; michael@0: setName = PR_FALSE; michael@0: break; michael@0: default: michael@0: goto loser; michael@0: } michael@0: michael@0: oidData = SECOID_FindOIDByTag(bagType); michael@0: if(oidData) { michael@0: rv = SECITEM_CopyItem(p12ctxt->arena, &safeBag->safeBagType, &oidData->oid); michael@0: if(rv != SECSuccess) { michael@0: PORT_SetError(SEC_ERROR_NO_MEMORY); michael@0: goto loser; michael@0: } michael@0: } else { michael@0: goto loser; michael@0: } michael@0: michael@0: safeBag->arena = p12ctxt->arena; michael@0: PORT_ArenaUnmark(p12ctxt->arena, mark); michael@0: michael@0: return safeBag; michael@0: michael@0: loser: michael@0: if(mark) { michael@0: PORT_ArenaRelease(p12ctxt->arena, mark); michael@0: } michael@0: michael@0: return NULL; michael@0: } michael@0: michael@0: /* Creates a new certificate bag and returns a pointer to it. If an error michael@0: * occurs NULL is returned. michael@0: */ michael@0: sec_PKCS12CertBag * michael@0: sec_PKCS12NewCertBag(PLArenaPool *arena, SECOidTag certType) michael@0: { michael@0: sec_PKCS12CertBag *certBag = NULL; michael@0: SECOidData *bagType = NULL; michael@0: SECStatus rv; michael@0: void *mark = NULL; michael@0: michael@0: if(!arena) { michael@0: return NULL; michael@0: } michael@0: michael@0: mark = PORT_ArenaMark(arena); michael@0: certBag = (sec_PKCS12CertBag *)PORT_ArenaZAlloc(arena, michael@0: sizeof(sec_PKCS12CertBag)); michael@0: if(!certBag) { michael@0: PORT_ArenaRelease(arena, mark); michael@0: PORT_SetError(SEC_ERROR_NO_MEMORY); michael@0: return NULL; michael@0: } michael@0: michael@0: bagType = SECOID_FindOIDByTag(certType); michael@0: if(!bagType) { michael@0: PORT_SetError(SEC_ERROR_NO_MEMORY); michael@0: goto loser; michael@0: } michael@0: michael@0: rv = SECITEM_CopyItem(arena, &certBag->bagID, &bagType->oid); michael@0: if(rv != SECSuccess) { michael@0: PORT_SetError(SEC_ERROR_NO_MEMORY); michael@0: goto loser; michael@0: } michael@0: michael@0: PORT_ArenaUnmark(arena, mark); michael@0: return certBag; michael@0: michael@0: loser: michael@0: PORT_ArenaRelease(arena, mark); michael@0: return NULL; michael@0: } michael@0: michael@0: /* Creates a new CRL bag and returns a pointer to it. If an error michael@0: * occurs NULL is returned. michael@0: */ michael@0: sec_PKCS12CRLBag * michael@0: sec_PKCS12NewCRLBag(PLArenaPool *arena, SECOidTag crlType) michael@0: { michael@0: sec_PKCS12CRLBag *crlBag = NULL; michael@0: SECOidData *bagType = NULL; michael@0: SECStatus rv; michael@0: void *mark = NULL; michael@0: michael@0: if(!arena) { michael@0: return NULL; michael@0: } michael@0: michael@0: mark = PORT_ArenaMark(arena); michael@0: crlBag = (sec_PKCS12CRLBag *)PORT_ArenaZAlloc(arena, michael@0: sizeof(sec_PKCS12CRLBag)); michael@0: if(!crlBag) { michael@0: PORT_ArenaRelease(arena, mark); michael@0: PORT_SetError(SEC_ERROR_NO_MEMORY); michael@0: return NULL; michael@0: } michael@0: michael@0: bagType = SECOID_FindOIDByTag(crlType); michael@0: if(!bagType) { michael@0: PORT_SetError(SEC_ERROR_NO_MEMORY); michael@0: goto loser; michael@0: } michael@0: michael@0: rv = SECITEM_CopyItem(arena, &crlBag->bagID, &bagType->oid); michael@0: if(rv != SECSuccess) { michael@0: PORT_SetError(SEC_ERROR_NO_MEMORY); michael@0: goto loser; michael@0: } michael@0: michael@0: PORT_ArenaUnmark(arena, mark); michael@0: return crlBag; michael@0: michael@0: loser: michael@0: PORT_ArenaRelease(arena, mark); michael@0: return NULL; michael@0: } michael@0: michael@0: /* sec_PKCS12AddAttributeToBag michael@0: * adds an attribute to a safeBag. currently, the only attributes supported michael@0: * are those which are specified within PKCS 12. michael@0: * michael@0: * p12ctxt - the export context michael@0: * safeBag - the safeBag to which attributes are appended michael@0: * attrType - the attribute type michael@0: * attrData - the attribute data michael@0: */ michael@0: SECStatus michael@0: sec_PKCS12AddAttributeToBag(SEC_PKCS12ExportContext *p12ctxt, michael@0: sec_PKCS12SafeBag *safeBag, SECOidTag attrType, michael@0: SECItem *attrData) michael@0: { michael@0: sec_PKCS12Attribute *attribute; michael@0: void *mark = NULL, *dummy = NULL; michael@0: SECOidData *oiddata = NULL; michael@0: SECItem unicodeName = { siBuffer, NULL, 0}; michael@0: void *src = NULL; michael@0: unsigned int nItems = 0; michael@0: SECStatus rv; michael@0: michael@0: if(!safeBag || !p12ctxt) { michael@0: return SECFailure; michael@0: } michael@0: michael@0: mark = PORT_ArenaMark(safeBag->arena); michael@0: michael@0: /* allocate the attribute */ michael@0: attribute = (sec_PKCS12Attribute *)PORT_ArenaZAlloc(safeBag->arena, michael@0: sizeof(sec_PKCS12Attribute)); michael@0: if(!attribute) { michael@0: PORT_SetError(SEC_ERROR_NO_MEMORY); michael@0: goto loser; michael@0: } michael@0: michael@0: /* set up the attribute */ michael@0: oiddata = SECOID_FindOIDByTag(attrType); michael@0: if(!oiddata) { michael@0: PORT_SetError(SEC_ERROR_NO_MEMORY); michael@0: goto loser; michael@0: } michael@0: if(SECITEM_CopyItem(p12ctxt->arena, &attribute->attrType, &oiddata->oid) != michael@0: SECSuccess) { michael@0: PORT_SetError(SEC_ERROR_NO_MEMORY); michael@0: goto loser; michael@0: } michael@0: michael@0: nItems = 1; michael@0: switch(attrType) { michael@0: case SEC_OID_PKCS9_LOCAL_KEY_ID: michael@0: { michael@0: src = attrData; michael@0: break; michael@0: } michael@0: case SEC_OID_PKCS9_FRIENDLY_NAME: michael@0: { michael@0: if(!sec_pkcs12_convert_item_to_unicode(p12ctxt->arena, michael@0: &unicodeName, attrData, PR_FALSE, michael@0: PR_FALSE, PR_TRUE)) { michael@0: goto loser; michael@0: } michael@0: src = &unicodeName; michael@0: break; michael@0: } michael@0: default: michael@0: goto loser; michael@0: } michael@0: michael@0: /* append the attribute to the attribute value list */ michael@0: attribute->attrValue = (SECItem **)PORT_ArenaZAlloc(p12ctxt->arena, michael@0: ((nItems + 1) * sizeof(SECItem *))); michael@0: if(!attribute->attrValue) { michael@0: PORT_SetError(SEC_ERROR_NO_MEMORY); michael@0: goto loser; michael@0: } michael@0: michael@0: /* XXX this will need to be changed if attributes requiring more than michael@0: * one element are ever used. michael@0: */ michael@0: attribute->attrValue[0] = (SECItem *)PORT_ArenaZAlloc(p12ctxt->arena, michael@0: sizeof(SECItem)); michael@0: if(!attribute->attrValue[0]) { michael@0: PORT_SetError(SEC_ERROR_NO_MEMORY); michael@0: goto loser; michael@0: } michael@0: attribute->attrValue[1] = NULL; michael@0: michael@0: rv = SECITEM_CopyItem(p12ctxt->arena, attribute->attrValue[0], michael@0: (SECItem*)src); michael@0: if(rv != SECSuccess) { michael@0: PORT_SetError(SEC_ERROR_NO_MEMORY); michael@0: goto loser; michael@0: } michael@0: michael@0: /* append the attribute to the safeBag attributes */ michael@0: if(safeBag->nAttribs) { michael@0: dummy = PORT_ArenaGrow(p12ctxt->arena, safeBag->attribs, michael@0: ((safeBag->nAttribs + 1) * sizeof(sec_PKCS12Attribute *)), michael@0: ((safeBag->nAttribs + 2) * sizeof(sec_PKCS12Attribute *))); michael@0: safeBag->attribs = (sec_PKCS12Attribute **)dummy; michael@0: } else { michael@0: safeBag->attribs = (sec_PKCS12Attribute **)PORT_ArenaZAlloc(p12ctxt->arena, michael@0: 2 * sizeof(sec_PKCS12Attribute *)); michael@0: dummy = safeBag->attribs; michael@0: } michael@0: if(!dummy) { michael@0: goto loser; michael@0: } michael@0: michael@0: safeBag->attribs[safeBag->nAttribs] = attribute; michael@0: safeBag->attribs[++safeBag->nAttribs] = NULL; michael@0: michael@0: PORT_ArenaUnmark(p12ctxt->arena, mark); michael@0: return SECSuccess; michael@0: michael@0: loser: michael@0: if(mark) { michael@0: PORT_ArenaRelease(p12ctxt->arena, mark); michael@0: } michael@0: michael@0: return SECFailure; michael@0: } michael@0: michael@0: /* SEC_PKCS12AddCert michael@0: * Adds a certificate to the data being exported. michael@0: * michael@0: * p12ctxt - the export context michael@0: * safe - the safeInfo to which the certificate is placed michael@0: * nestedDest - if the cert is to be placed within a nested safeContents then, michael@0: * this value is to be specified with the destination michael@0: * cert - the cert to export michael@0: * certDb - the certificate database handle michael@0: * keyId - a unique identifier to associate a certificate/key pair michael@0: * includeCertChain - PR_TRUE if the certificate chain is to be included. michael@0: */ michael@0: SECStatus michael@0: SEC_PKCS12AddCert(SEC_PKCS12ExportContext *p12ctxt, SEC_PKCS12SafeInfo *safe, michael@0: void *nestedDest, CERTCertificate *cert, michael@0: CERTCertDBHandle *certDb, SECItem *keyId, michael@0: PRBool includeCertChain) michael@0: { michael@0: sec_PKCS12CertBag *certBag; michael@0: sec_PKCS12SafeBag *safeBag; michael@0: void *mark; michael@0: SECStatus rv; michael@0: SECItem nick = {siBuffer, NULL,0}; michael@0: michael@0: if(!p12ctxt || !cert) { michael@0: return SECFailure; michael@0: } michael@0: mark = PORT_ArenaMark(p12ctxt->arena); michael@0: michael@0: /* allocate the cert bag */ michael@0: certBag = sec_PKCS12NewCertBag(p12ctxt->arena, michael@0: SEC_OID_PKCS9_X509_CERT); michael@0: if(!certBag) { michael@0: goto loser; michael@0: } michael@0: michael@0: if(SECITEM_CopyItem(p12ctxt->arena, &certBag->value.x509Cert, michael@0: &cert->derCert) != SECSuccess) { michael@0: PORT_SetError(SEC_ERROR_NO_MEMORY); michael@0: goto loser; michael@0: } michael@0: michael@0: /* if the cert chain is to be included, we should only be exporting michael@0: * the cert from our internal database. michael@0: */ michael@0: if(includeCertChain) { michael@0: CERTCertificateList *certList = CERT_CertChainFromCert(cert, michael@0: certUsageSSLClient, michael@0: PR_TRUE); michael@0: unsigned int count = 0; michael@0: if(!certList) { michael@0: PORT_SetError(SEC_ERROR_NO_MEMORY); michael@0: goto loser; michael@0: } michael@0: michael@0: /* add cert chain */ michael@0: for(count = 0; count < (unsigned int)certList->len; count++) { michael@0: if(SECITEM_CompareItem(&certList->certs[count], &cert->derCert) michael@0: != SECEqual) { michael@0: CERTCertificate *tempCert; michael@0: michael@0: /* decode the certificate */ michael@0: /* XXX michael@0: * This was rather silly. The chain is constructed above michael@0: * by finding all of the CERTCertificate's in the database. michael@0: * Then the chain is put into a CERTCertificateList, which only michael@0: * contains the DER. Finally, the DER was decoded, and the michael@0: * decoded cert was sent recursively back to this function. michael@0: * Beyond being inefficent, this causes data loss (specifically, michael@0: * the nickname). Instead, for 3.4, we'll do a lookup by the michael@0: * DER, which should return the cached entry. michael@0: */ michael@0: tempCert = CERT_FindCertByDERCert(CERT_GetDefaultCertDB(), michael@0: &certList->certs[count]); michael@0: if(!tempCert) { michael@0: CERT_DestroyCertificateList(certList); michael@0: goto loser; michael@0: } michael@0: michael@0: /* add the certificate */ michael@0: if(SEC_PKCS12AddCert(p12ctxt, safe, nestedDest, tempCert, michael@0: certDb, NULL, PR_FALSE) != SECSuccess) { michael@0: CERT_DestroyCertificate(tempCert); michael@0: CERT_DestroyCertificateList(certList); michael@0: goto loser; michael@0: } michael@0: CERT_DestroyCertificate(tempCert); michael@0: } michael@0: } michael@0: CERT_DestroyCertificateList(certList); michael@0: } michael@0: michael@0: /* if the certificate has a nickname, we will set the friendly name michael@0: * to that. michael@0: */ michael@0: if(cert->nickname) { michael@0: if (cert->slot && !PK11_IsInternal(cert->slot)) { michael@0: /* michael@0: * The cert is coming off of an external token, michael@0: * let's strip the token name from the nickname michael@0: * and only add what comes after the colon as the michael@0: * nickname. -javi michael@0: */ michael@0: char *delimit; michael@0: michael@0: delimit = PORT_Strchr(cert->nickname,':'); michael@0: if (delimit == NULL) { michael@0: nick.data = (unsigned char *)cert->nickname; michael@0: nick.len = PORT_Strlen(cert->nickname); michael@0: } else { michael@0: delimit++; michael@0: nick.data = (unsigned char *)PORT_ArenaStrdup(p12ctxt->arena, michael@0: delimit); michael@0: nick.len = PORT_Strlen(delimit); michael@0: } michael@0: } else { michael@0: nick.data = (unsigned char *)cert->nickname; michael@0: nick.len = PORT_Strlen(cert->nickname); michael@0: } michael@0: } michael@0: michael@0: safeBag = sec_PKCS12CreateSafeBag(p12ctxt, SEC_OID_PKCS12_V1_CERT_BAG_ID, michael@0: certBag); michael@0: if(!safeBag) { michael@0: goto loser; michael@0: } michael@0: michael@0: /* add the friendly name and keyId attributes, if necessary */ michael@0: if(nick.data) { michael@0: if(sec_PKCS12AddAttributeToBag(p12ctxt, safeBag, michael@0: SEC_OID_PKCS9_FRIENDLY_NAME, &nick) michael@0: != SECSuccess) { michael@0: goto loser; michael@0: } michael@0: } michael@0: michael@0: if(keyId) { michael@0: if(sec_PKCS12AddAttributeToBag(p12ctxt, safeBag, SEC_OID_PKCS9_LOCAL_KEY_ID, michael@0: keyId) != SECSuccess) { michael@0: goto loser; michael@0: } michael@0: } michael@0: michael@0: /* append the cert safeBag */ michael@0: if(nestedDest) { michael@0: rv = sec_pkcs12_append_bag_to_safe_contents(p12ctxt->arena, michael@0: (sec_PKCS12SafeContents*)nestedDest, michael@0: safeBag); michael@0: } else { michael@0: rv = sec_pkcs12_append_bag(p12ctxt, safe, safeBag); michael@0: } michael@0: michael@0: if(rv != SECSuccess) { michael@0: goto loser; michael@0: } michael@0: michael@0: PORT_ArenaUnmark(p12ctxt->arena, mark); michael@0: return SECSuccess; michael@0: michael@0: loser: michael@0: if(mark) { michael@0: PORT_ArenaRelease(p12ctxt->arena, mark); michael@0: } michael@0: michael@0: return SECFailure; michael@0: } michael@0: michael@0: /* SEC_PKCS12AddKeyForCert michael@0: * Extracts the key associated with a particular certificate and exports michael@0: * it. michael@0: * michael@0: * p12ctxt - the export context michael@0: * safe - the safeInfo to place the key in michael@0: * nestedDest - the nested safeContents to place a key michael@0: * cert - the certificate which the key belongs to michael@0: * shroudKey - encrypt the private key for export. This value should michael@0: * always be true. lower level code will not allow the export michael@0: * of unencrypted private keys. michael@0: * algorithm - the algorithm with which to encrypt the private key michael@0: * pwitem - the password to encrypt the private key with michael@0: * keyId - the keyID attribute michael@0: * nickName - the nickname attribute michael@0: */ michael@0: SECStatus michael@0: SEC_PKCS12AddKeyForCert(SEC_PKCS12ExportContext *p12ctxt, SEC_PKCS12SafeInfo *safe, michael@0: void *nestedDest, CERTCertificate *cert, michael@0: PRBool shroudKey, SECOidTag algorithm, SECItem *pwitem, michael@0: SECItem *keyId, SECItem *nickName) michael@0: { michael@0: void *mark; michael@0: void *keyItem; michael@0: SECOidTag keyType; michael@0: SECStatus rv = SECFailure; michael@0: SECItem nickname = {siBuffer,NULL,0}, uniPwitem = {siBuffer, NULL, 0}; michael@0: sec_PKCS12SafeBag *returnBag; michael@0: michael@0: if(!p12ctxt || !cert || !safe) { michael@0: return SECFailure; michael@0: } michael@0: michael@0: mark = PORT_ArenaMark(p12ctxt->arena); michael@0: michael@0: /* retrieve the key based upon the type that it is and michael@0: * specify the type of safeBag to store the key in michael@0: */ michael@0: if(!shroudKey) { michael@0: michael@0: /* extract the key unencrypted. this will most likely go away */ michael@0: SECKEYPrivateKeyInfo *pki = PK11_ExportPrivateKeyInfo(cert, michael@0: p12ctxt->wincx); michael@0: if(!pki) { michael@0: PORT_ArenaRelease(p12ctxt->arena, mark); michael@0: PORT_SetError(SEC_ERROR_PKCS12_UNABLE_TO_EXPORT_KEY); michael@0: return SECFailure; michael@0: } michael@0: keyItem = PORT_ArenaZAlloc(p12ctxt->arena, sizeof(SECKEYPrivateKeyInfo)); michael@0: if(!keyItem) { michael@0: PORT_SetError(SEC_ERROR_NO_MEMORY); michael@0: goto loser; michael@0: } michael@0: rv = SECKEY_CopyPrivateKeyInfo(p12ctxt->arena, michael@0: (SECKEYPrivateKeyInfo *)keyItem, pki); michael@0: keyType = SEC_OID_PKCS12_V1_KEY_BAG_ID; michael@0: SECKEY_DestroyPrivateKeyInfo(pki, PR_TRUE); michael@0: } else { michael@0: michael@0: /* extract the key encrypted */ michael@0: SECKEYEncryptedPrivateKeyInfo *epki = NULL; michael@0: PK11SlotInfo *slot = NULL; michael@0: michael@0: if(!sec_pkcs12_convert_item_to_unicode(p12ctxt->arena, &uniPwitem, michael@0: pwitem, PR_TRUE, PR_TRUE, PR_TRUE)) { michael@0: PORT_SetError(SEC_ERROR_NO_MEMORY); michael@0: goto loser; michael@0: } michael@0: michael@0: /* we want to make sure to take the key out of the key slot */ michael@0: if(PK11_IsInternal(p12ctxt->slot)) { michael@0: slot = PK11_GetInternalKeySlot(); michael@0: } else { michael@0: slot = PK11_ReferenceSlot(p12ctxt->slot); michael@0: } michael@0: michael@0: epki = PK11_ExportEncryptedPrivateKeyInfo(slot, algorithm, michael@0: &uniPwitem, cert, michael@0: NSS_PBE_DEFAULT_ITERATION_COUNT, michael@0: p12ctxt->wincx); michael@0: PK11_FreeSlot(slot); michael@0: if(!epki) { michael@0: PORT_SetError(SEC_ERROR_PKCS12_UNABLE_TO_EXPORT_KEY); michael@0: goto loser; michael@0: } michael@0: michael@0: keyItem = PORT_ArenaZAlloc(p12ctxt->arena, michael@0: sizeof(SECKEYEncryptedPrivateKeyInfo)); michael@0: if(!keyItem) { michael@0: PORT_SetError(SEC_ERROR_NO_MEMORY); michael@0: goto loser; michael@0: } michael@0: rv = SECKEY_CopyEncryptedPrivateKeyInfo(p12ctxt->arena, michael@0: (SECKEYEncryptedPrivateKeyInfo *)keyItem, michael@0: epki); michael@0: keyType = SEC_OID_PKCS12_V1_PKCS8_SHROUDED_KEY_BAG_ID; michael@0: SECKEY_DestroyEncryptedPrivateKeyInfo(epki, PR_TRUE); michael@0: } michael@0: michael@0: if(rv != SECSuccess) { michael@0: goto loser; michael@0: } michael@0: michael@0: /* if no nickname specified, let's see if the certificate has a michael@0: * nickname. michael@0: */ michael@0: if(!nickName) { michael@0: if(cert->nickname) { michael@0: nickname.data = (unsigned char *)cert->nickname; michael@0: nickname.len = PORT_Strlen(cert->nickname); michael@0: nickName = &nickname; michael@0: } michael@0: } michael@0: michael@0: /* create the safe bag and set any attributes */ michael@0: returnBag = sec_PKCS12CreateSafeBag(p12ctxt, keyType, keyItem); michael@0: if(!returnBag) { michael@0: rv = SECFailure; michael@0: goto loser; michael@0: } michael@0: michael@0: if(nickName) { michael@0: if(sec_PKCS12AddAttributeToBag(p12ctxt, returnBag, michael@0: SEC_OID_PKCS9_FRIENDLY_NAME, nickName) michael@0: != SECSuccess) { michael@0: goto loser; michael@0: } michael@0: } michael@0: michael@0: if(keyId) { michael@0: if(sec_PKCS12AddAttributeToBag(p12ctxt, returnBag, SEC_OID_PKCS9_LOCAL_KEY_ID, michael@0: keyId) != SECSuccess) { michael@0: goto loser; michael@0: } michael@0: } michael@0: michael@0: if(nestedDest) { michael@0: rv = sec_pkcs12_append_bag_to_safe_contents(p12ctxt->arena, michael@0: (sec_PKCS12SafeContents*)nestedDest, michael@0: returnBag); michael@0: } else { michael@0: rv = sec_pkcs12_append_bag(p12ctxt, safe, returnBag); michael@0: } michael@0: michael@0: loser: michael@0: michael@0: if (rv != SECSuccess) { michael@0: PORT_ArenaRelease(p12ctxt->arena, mark); michael@0: } else { michael@0: PORT_ArenaUnmark(p12ctxt->arena, mark); michael@0: } michael@0: michael@0: return rv; michael@0: } michael@0: michael@0: /* SEC_PKCS12AddCertOrChainAndKey michael@0: * Add a certificate and key pair to be exported. michael@0: * michael@0: * p12ctxt - the export context michael@0: * certSafe - the safeInfo where the cert is stored michael@0: * certNestedDest - the nested safeContents to store the cert michael@0: * keySafe - the safeInfo where the key is stored michael@0: * keyNestedDest - the nested safeContents to store the key michael@0: * shroudKey - extract the private key encrypted? michael@0: * pwitem - the password with which the key is encrypted michael@0: * algorithm - the algorithm with which the key is encrypted michael@0: * includeCertChain - also add certs from chain to bag. michael@0: */ michael@0: SECStatus michael@0: SEC_PKCS12AddCertOrChainAndKey(SEC_PKCS12ExportContext *p12ctxt, michael@0: void *certSafe, void *certNestedDest, michael@0: CERTCertificate *cert, CERTCertDBHandle *certDb, michael@0: void *keySafe, void *keyNestedDest, michael@0: PRBool shroudKey, SECItem *pwitem, michael@0: SECOidTag algorithm, PRBool includeCertChain) michael@0: { michael@0: SECStatus rv = SECFailure; michael@0: SGNDigestInfo *digest = NULL; michael@0: void *mark = NULL; michael@0: michael@0: if(!p12ctxt || !certSafe || !keySafe || !cert) { michael@0: return SECFailure; michael@0: } michael@0: michael@0: mark = PORT_ArenaMark(p12ctxt->arena); michael@0: michael@0: /* generate the thumbprint of the cert to use as a keyId */ michael@0: digest = sec_pkcs12_compute_thumbprint(&cert->derCert); michael@0: if(!digest) { michael@0: PORT_ArenaRelease(p12ctxt->arena, mark); michael@0: return SECFailure; michael@0: } michael@0: michael@0: /* add the certificate */ michael@0: rv = SEC_PKCS12AddCert(p12ctxt, (SEC_PKCS12SafeInfo*)certSafe, michael@0: (SEC_PKCS12SafeInfo*)certNestedDest, cert, certDb, michael@0: &digest->digest, includeCertChain); michael@0: if(rv != SECSuccess) { michael@0: goto loser; michael@0: } michael@0: michael@0: /* add the key */ michael@0: rv = SEC_PKCS12AddKeyForCert(p12ctxt, (SEC_PKCS12SafeInfo*)keySafe, michael@0: keyNestedDest, cert, michael@0: shroudKey, algorithm, pwitem, michael@0: &digest->digest, NULL ); michael@0: if(rv != SECSuccess) { michael@0: goto loser; michael@0: } michael@0: michael@0: SGN_DestroyDigestInfo(digest); michael@0: michael@0: PORT_ArenaUnmark(p12ctxt->arena, mark); michael@0: return SECSuccess; michael@0: michael@0: loser: michael@0: SGN_DestroyDigestInfo(digest); michael@0: PORT_ArenaRelease(p12ctxt->arena, mark); michael@0: michael@0: return SECFailure; michael@0: } michael@0: michael@0: /* like SEC_PKCS12AddCertOrChainAndKey, but always adds cert chain */ michael@0: SECStatus michael@0: SEC_PKCS12AddCertAndKey(SEC_PKCS12ExportContext *p12ctxt, michael@0: void *certSafe, void *certNestedDest, michael@0: CERTCertificate *cert, CERTCertDBHandle *certDb, michael@0: void *keySafe, void *keyNestedDest, michael@0: PRBool shroudKey, SECItem *pwItem, SECOidTag algorithm) michael@0: { michael@0: return SEC_PKCS12AddCertOrChainAndKey(p12ctxt, certSafe, certNestedDest, michael@0: cert, certDb, keySafe, keyNestedDest, shroudKey, pwItem, michael@0: algorithm, PR_TRUE); michael@0: } michael@0: michael@0: michael@0: /* SEC_PKCS12CreateNestedSafeContents michael@0: * Allows nesting of safe contents to be implemented. No limit imposed on michael@0: * depth. michael@0: * michael@0: * p12ctxt - the export context michael@0: * baseSafe - the base safeInfo michael@0: * nestedDest - a parent safeContents (?) michael@0: */ michael@0: void * michael@0: SEC_PKCS12CreateNestedSafeContents(SEC_PKCS12ExportContext *p12ctxt, michael@0: void *baseSafe, void *nestedDest) michael@0: { michael@0: sec_PKCS12SafeContents *newSafe; michael@0: sec_PKCS12SafeBag *safeContentsBag; michael@0: void *mark; michael@0: SECStatus rv; michael@0: michael@0: if(!p12ctxt || !baseSafe) { michael@0: return NULL; michael@0: } michael@0: michael@0: mark = PORT_ArenaMark(p12ctxt->arena); michael@0: michael@0: newSafe = sec_PKCS12CreateSafeContents(p12ctxt->arena); michael@0: if(!newSafe) { michael@0: PORT_ArenaRelease(p12ctxt->arena, mark); michael@0: PORT_SetError(SEC_ERROR_NO_MEMORY); michael@0: return NULL; michael@0: } michael@0: michael@0: /* create the safeContents safeBag */ michael@0: safeContentsBag = sec_PKCS12CreateSafeBag(p12ctxt, michael@0: SEC_OID_PKCS12_V1_SAFE_CONTENTS_BAG_ID, michael@0: newSafe); michael@0: if(!safeContentsBag) { michael@0: goto loser; michael@0: } michael@0: michael@0: /* append the safeContents to the appropriate area */ michael@0: if(nestedDest) { michael@0: rv = sec_pkcs12_append_bag_to_safe_contents(p12ctxt->arena, michael@0: (sec_PKCS12SafeContents*)nestedDest, michael@0: safeContentsBag); michael@0: } else { michael@0: rv = sec_pkcs12_append_bag(p12ctxt, (SEC_PKCS12SafeInfo*)baseSafe, michael@0: safeContentsBag); michael@0: } michael@0: if(rv != SECSuccess) { michael@0: goto loser; michael@0: } michael@0: michael@0: PORT_ArenaUnmark(p12ctxt->arena, mark); michael@0: return newSafe; michael@0: michael@0: loser: michael@0: PORT_ArenaRelease(p12ctxt->arena, mark); michael@0: return NULL; michael@0: } michael@0: michael@0: /********************************* michael@0: * Encoding routines michael@0: *********************************/ michael@0: michael@0: /* Clean up the resources allocated by a sec_PKCS12EncoderContext. */ michael@0: static void michael@0: sec_pkcs12_encoder_destroy_context(sec_PKCS12EncoderContext *p12enc) michael@0: { michael@0: if(p12enc) { michael@0: if(p12enc->outerA1ecx) { michael@0: SEC_ASN1EncoderFinish(p12enc->outerA1ecx); michael@0: p12enc->outerA1ecx = NULL; michael@0: } michael@0: if(p12enc->aSafeCinfo) { michael@0: SEC_PKCS7DestroyContentInfo(p12enc->aSafeCinfo); michael@0: p12enc->aSafeCinfo = NULL; michael@0: } michael@0: if(p12enc->middleP7ecx) { michael@0: SEC_PKCS7EncoderFinish(p12enc->middleP7ecx, p12enc->p12exp->pwfn, michael@0: p12enc->p12exp->pwfnarg); michael@0: p12enc->middleP7ecx = NULL; michael@0: } michael@0: if(p12enc->middleA1ecx) { michael@0: SEC_ASN1EncoderFinish(p12enc->middleA1ecx); michael@0: p12enc->middleA1ecx = NULL; michael@0: } michael@0: if(p12enc->hmacCx) { michael@0: PK11_DestroyContext(p12enc->hmacCx, PR_TRUE); michael@0: p12enc->hmacCx = NULL; michael@0: } michael@0: } michael@0: } michael@0: michael@0: /* set up the encoder context based on information in the export context michael@0: * and return the newly allocated enocoder context. A return of NULL michael@0: * indicates an error occurred. michael@0: */ michael@0: static sec_PKCS12EncoderContext * michael@0: sec_pkcs12_encoder_start_context(SEC_PKCS12ExportContext *p12exp) michael@0: { michael@0: sec_PKCS12EncoderContext *p12enc = NULL; michael@0: unsigned int i, nonEmptyCnt; michael@0: SECStatus rv; michael@0: SECItem ignore = {0}; michael@0: void *mark; michael@0: michael@0: if(!p12exp || !p12exp->safeInfos) { michael@0: return NULL; michael@0: } michael@0: michael@0: /* check for any empty safes and skip them */ michael@0: i = nonEmptyCnt = 0; michael@0: while(p12exp->safeInfos[i]) { michael@0: if(p12exp->safeInfos[i]->itemCount) { michael@0: nonEmptyCnt++; michael@0: } michael@0: i++; michael@0: } michael@0: if(nonEmptyCnt == 0) { michael@0: return NULL; michael@0: } michael@0: p12exp->authSafe.encodedSafes[nonEmptyCnt] = NULL; michael@0: michael@0: /* allocate the encoder context */ michael@0: mark = PORT_ArenaMark(p12exp->arena); michael@0: p12enc = PORT_ArenaZNew(p12exp->arena, sec_PKCS12EncoderContext); michael@0: if(!p12enc) { michael@0: PORT_SetError(SEC_ERROR_NO_MEMORY); michael@0: return NULL; michael@0: } michael@0: michael@0: p12enc->arena = p12exp->arena; michael@0: p12enc->p12exp = p12exp; michael@0: michael@0: /* set up the PFX version and information */ michael@0: PORT_Memset(&p12enc->pfx, 0, sizeof(sec_PKCS12PFXItem)); michael@0: if(!SEC_ASN1EncodeInteger(p12exp->arena, &(p12enc->pfx.version), michael@0: SEC_PKCS12_VERSION) ) { michael@0: PORT_SetError(SEC_ERROR_NO_MEMORY); michael@0: goto loser; michael@0: } michael@0: michael@0: /* set up the authenticated safe content info based on the michael@0: * type of integrity being used. this should be changed to michael@0: * enforce integrity mode, but will not be implemented until michael@0: * it is confirmed that integrity must be in place michael@0: */ michael@0: if(p12exp->integrityEnabled && !p12exp->pwdIntegrity) { michael@0: SECStatus rv; michael@0: michael@0: /* create public key integrity mode */ michael@0: p12enc->aSafeCinfo = SEC_PKCS7CreateSignedData( michael@0: p12exp->integrityInfo.pubkeyInfo.cert, michael@0: certUsageEmailSigner, michael@0: p12exp->integrityInfo.pubkeyInfo.certDb, michael@0: p12exp->integrityInfo.pubkeyInfo.algorithm, michael@0: NULL, michael@0: p12exp->pwfn, michael@0: p12exp->pwfnarg); michael@0: if(!p12enc->aSafeCinfo) { michael@0: goto loser; michael@0: } michael@0: if(SEC_PKCS7IncludeCertChain(p12enc->aSafeCinfo,NULL) != SECSuccess) { michael@0: goto loser; michael@0: } michael@0: rv = SEC_PKCS7AddSigningTime(p12enc->aSafeCinfo); michael@0: PORT_Assert(rv == SECSuccess); michael@0: } else { michael@0: p12enc->aSafeCinfo = SEC_PKCS7CreateData(); michael@0: michael@0: /* init password pased integrity mode */ michael@0: if(p12exp->integrityEnabled) { michael@0: SECItem pwd = {siBuffer,NULL, 0}; michael@0: SECItem *salt = sec_pkcs12_generate_salt(); michael@0: PK11SymKey *symKey; michael@0: SECItem *params; michael@0: CK_MECHANISM_TYPE integrityMechType; michael@0: CK_MECHANISM_TYPE hmacMechType; michael@0: michael@0: /* zero out macData and set values */ michael@0: PORT_Memset(&p12enc->mac, 0, sizeof(sec_PKCS12MacData)); michael@0: michael@0: if(!salt) { michael@0: PORT_SetError(SEC_ERROR_NO_MEMORY); michael@0: goto loser; michael@0: } michael@0: if(SECITEM_CopyItem(p12exp->arena, &(p12enc->mac.macSalt), salt) michael@0: != SECSuccess) { michael@0: /* XXX salt is leaked */ michael@0: PORT_SetError(SEC_ERROR_NO_MEMORY); michael@0: goto loser; michael@0: } michael@0: if (!SEC_ASN1EncodeInteger(p12exp->arena, &(p12enc->mac.iter), michael@0: NSS_PBE_DEFAULT_ITERATION_COUNT)) { michael@0: /* XXX salt is leaked */ michael@0: goto loser; michael@0: } michael@0: michael@0: /* generate HMAC key */ michael@0: if(!sec_pkcs12_convert_item_to_unicode(NULL, &pwd, michael@0: p12exp->integrityInfo.pwdInfo.password, PR_TRUE, michael@0: PR_TRUE, PR_TRUE)) { michael@0: /* XXX salt is leaked */ michael@0: goto loser; michael@0: } michael@0: /* michael@0: * This code only works with PKCS #12 Mac using PKCS #5 v1 michael@0: * PBA keygens. PKCS #5 v2 support will require a change to michael@0: * the PKCS #12 spec. michael@0: */ michael@0: params = PK11_CreatePBEParams(salt, &pwd, michael@0: NSS_PBE_DEFAULT_ITERATION_COUNT); michael@0: SECITEM_ZfreeItem(salt, PR_TRUE); michael@0: SECITEM_ZfreeItem(&pwd, PR_FALSE); michael@0: michael@0: /* get the PBA Mechanism to generate the key */ michael@0: switch (p12exp->integrityInfo.pwdInfo.algorithm) { michael@0: case SEC_OID_SHA1: michael@0: integrityMechType = CKM_PBA_SHA1_WITH_SHA1_HMAC; break; michael@0: case SEC_OID_MD5: michael@0: integrityMechType = CKM_NETSCAPE_PBE_MD5_HMAC_KEY_GEN; break; michael@0: case SEC_OID_MD2: michael@0: integrityMechType = CKM_NETSCAPE_PBE_MD2_HMAC_KEY_GEN; break; michael@0: default: michael@0: /* XXX params is leaked */ michael@0: goto loser; michael@0: } michael@0: michael@0: /* generate the key */ michael@0: symKey = PK11_KeyGen(NULL, integrityMechType, params, 20, NULL); michael@0: PK11_DestroyPBEParams(params); michael@0: if(!symKey) { michael@0: goto loser; michael@0: } michael@0: michael@0: /* initialize HMAC */ michael@0: /* Get the HMAC mechanism from the hash OID */ michael@0: hmacMechType= sec_pkcs12_algtag_to_mech( michael@0: p12exp->integrityInfo.pwdInfo.algorithm); michael@0: michael@0: p12enc->hmacCx = PK11_CreateContextBySymKey( hmacMechType, michael@0: CKA_SIGN, symKey, &ignore); michael@0: michael@0: PK11_FreeSymKey(symKey); michael@0: if(!p12enc->hmacCx) { michael@0: PORT_SetError(SEC_ERROR_NO_MEMORY); michael@0: goto loser; michael@0: } michael@0: rv = PK11_DigestBegin(p12enc->hmacCx); michael@0: if (rv != SECSuccess) michael@0: goto loser; michael@0: } michael@0: } michael@0: michael@0: if(!p12enc->aSafeCinfo) { michael@0: goto loser; michael@0: } michael@0: michael@0: PORT_ArenaUnmark(p12exp->arena, mark); michael@0: michael@0: return p12enc; michael@0: michael@0: loser: michael@0: sec_pkcs12_encoder_destroy_context(p12enc); michael@0: if (p12exp->arena != NULL) michael@0: PORT_ArenaRelease(p12exp->arena, mark); michael@0: michael@0: return NULL; michael@0: } michael@0: michael@0: /* The outermost ASN.1 encoder calls this function for output. michael@0: ** This function calls back to the library caller's output routine, michael@0: ** which typically writes to a PKCS12 file. michael@0: */ michael@0: static void michael@0: sec_P12A1OutputCB_Outer(void *arg, const char *buf, unsigned long len, michael@0: int depth, SEC_ASN1EncodingPart data_kind) michael@0: { michael@0: struct sec_pkcs12_encoder_output *output; michael@0: michael@0: output = (struct sec_pkcs12_encoder_output*)arg; michael@0: (* output->outputfn)(output->outputarg, buf, len); michael@0: } michael@0: michael@0: /* The "middle" and "inner" ASN.1 encoders call this function to output. michael@0: ** This function does HMACing, if appropriate, and then buffers the data. michael@0: ** The buffered data is eventually passed down to the underlying PKCS7 encoder. michael@0: */ michael@0: static void michael@0: sec_P12A1OutputCB_HmacP7Update(void *arg, const char *buf, michael@0: unsigned long len, michael@0: int depth, michael@0: SEC_ASN1EncodingPart data_kind) michael@0: { michael@0: sec_pkcs12OutputBuffer * bufcx = (sec_pkcs12OutputBuffer *)arg; michael@0: michael@0: if(!buf || !len) michael@0: return; michael@0: michael@0: if (bufcx->hmacCx) { michael@0: PK11_DigestOp(bufcx->hmacCx, (unsigned char *)buf, len); michael@0: } michael@0: michael@0: /* buffer */ michael@0: if (bufcx->numBytes > 0) { michael@0: int toCopy; michael@0: if (len + bufcx->numBytes <= bufcx->bufBytes) { michael@0: memcpy(bufcx->buf + bufcx->numBytes, buf, len); michael@0: bufcx->numBytes += len; michael@0: if (bufcx->numBytes < bufcx->bufBytes) michael@0: return; michael@0: SEC_PKCS7EncoderUpdate(bufcx->p7eCx, bufcx->buf, bufcx->bufBytes); michael@0: bufcx->numBytes = 0; michael@0: return; michael@0: } michael@0: toCopy = bufcx->bufBytes - bufcx->numBytes; michael@0: memcpy(bufcx->buf + bufcx->numBytes, buf, toCopy); michael@0: SEC_PKCS7EncoderUpdate(bufcx->p7eCx, bufcx->buf, bufcx->bufBytes); michael@0: bufcx->numBytes = 0; michael@0: len -= toCopy; michael@0: buf += toCopy; michael@0: } michael@0: /* buffer is presently empty */ michael@0: if (len >= bufcx->bufBytes) { michael@0: /* Just pass it through */ michael@0: SEC_PKCS7EncoderUpdate(bufcx->p7eCx, buf, len); michael@0: } else { michael@0: /* copy it all into the buffer, and return */ michael@0: memcpy(bufcx->buf, buf, len); michael@0: bufcx->numBytes = len; michael@0: } michael@0: } michael@0: michael@0: void michael@0: sec_FlushPkcs12OutputBuffer( sec_pkcs12OutputBuffer * bufcx) michael@0: { michael@0: if (bufcx->numBytes > 0) { michael@0: SEC_PKCS7EncoderUpdate(bufcx->p7eCx, bufcx->buf, bufcx->numBytes); michael@0: bufcx->numBytes = 0; michael@0: } michael@0: } michael@0: michael@0: /* Feeds the output of a PKCS7 encoder into the next outward ASN.1 encoder. michael@0: ** This function is used by both the inner and middle PCS7 encoders. michael@0: */ michael@0: static void michael@0: sec_P12P7OutputCB_CallA1Update(void *arg, const char *buf, unsigned long len) michael@0: { michael@0: SEC_ASN1EncoderContext *cx = (SEC_ASN1EncoderContext*)arg; michael@0: michael@0: if (!buf || !len) michael@0: return; michael@0: michael@0: SEC_ASN1EncoderUpdate(cx, buf, len); michael@0: } michael@0: michael@0: michael@0: /* this function encodes content infos which are part of the michael@0: * sequence of content infos labeled AuthenticatedSafes michael@0: */ michael@0: static SECStatus michael@0: sec_pkcs12_encoder_asafe_process(sec_PKCS12EncoderContext *p12ecx) michael@0: { michael@0: SEC_PKCS7EncoderContext *innerP7ecx; michael@0: SEC_PKCS7ContentInfo *cinfo; michael@0: PK11SymKey *bulkKey = NULL; michael@0: SEC_ASN1EncoderContext *innerA1ecx = NULL; michael@0: SECStatus rv = SECSuccess; michael@0: michael@0: if(p12ecx->currentSafe < p12ecx->p12exp->authSafe.safeCount) { michael@0: SEC_PKCS12SafeInfo *safeInfo; michael@0: SECOidTag cinfoType; michael@0: michael@0: safeInfo = p12ecx->p12exp->safeInfos[p12ecx->currentSafe]; michael@0: michael@0: /* skip empty safes */ michael@0: if(safeInfo->itemCount == 0) { michael@0: return SECSuccess; michael@0: } michael@0: michael@0: cinfo = safeInfo->cinfo; michael@0: cinfoType = SEC_PKCS7ContentType(cinfo); michael@0: michael@0: /* determine the safe type and set the appropriate argument */ michael@0: switch(cinfoType) { michael@0: case SEC_OID_PKCS7_DATA: michael@0: case SEC_OID_PKCS7_ENVELOPED_DATA: michael@0: break; michael@0: case SEC_OID_PKCS7_ENCRYPTED_DATA: michael@0: bulkKey = safeInfo->encryptionKey; michael@0: PK11_SetSymKeyUserData(bulkKey, &safeInfo->pwitem, NULL); michael@0: break; michael@0: default: michael@0: return SECFailure; michael@0: michael@0: } michael@0: michael@0: /* start the PKCS7 encoder */ michael@0: innerP7ecx = SEC_PKCS7EncoderStart(cinfo, michael@0: sec_P12P7OutputCB_CallA1Update, michael@0: p12ecx->middleA1ecx, bulkKey); michael@0: if(!innerP7ecx) { michael@0: goto loser; michael@0: } michael@0: michael@0: /* encode safe contents */ michael@0: p12ecx->innerBuf.p7eCx = innerP7ecx; michael@0: p12ecx->innerBuf.hmacCx = NULL; michael@0: p12ecx->innerBuf.numBytes = 0; michael@0: p12ecx->innerBuf.bufBytes = sizeof p12ecx->innerBuf.buf; michael@0: michael@0: innerA1ecx = SEC_ASN1EncoderStart(safeInfo->safe, michael@0: sec_PKCS12SafeContentsTemplate, michael@0: sec_P12A1OutputCB_HmacP7Update, michael@0: &p12ecx->innerBuf); michael@0: if(!innerA1ecx) { michael@0: goto loser; michael@0: } michael@0: rv = SEC_ASN1EncoderUpdate(innerA1ecx, NULL, 0); michael@0: SEC_ASN1EncoderFinish(innerA1ecx); michael@0: sec_FlushPkcs12OutputBuffer( &p12ecx->innerBuf); michael@0: innerA1ecx = NULL; michael@0: if(rv != SECSuccess) { michael@0: goto loser; michael@0: } michael@0: michael@0: michael@0: /* finish up safe content info */ michael@0: rv = SEC_PKCS7EncoderFinish(innerP7ecx, p12ecx->p12exp->pwfn, michael@0: p12ecx->p12exp->pwfnarg); michael@0: } michael@0: memset(&p12ecx->innerBuf, 0, sizeof p12ecx->innerBuf); michael@0: return SECSuccess; michael@0: michael@0: loser: michael@0: if(innerP7ecx) { michael@0: SEC_PKCS7EncoderFinish(innerP7ecx, p12ecx->p12exp->pwfn, michael@0: p12ecx->p12exp->pwfnarg); michael@0: } michael@0: michael@0: if(innerA1ecx) { michael@0: SEC_ASN1EncoderFinish(innerA1ecx); michael@0: } michael@0: memset(&p12ecx->innerBuf, 0, sizeof p12ecx->innerBuf); michael@0: return SECFailure; michael@0: } michael@0: michael@0: /* finish the HMAC and encode the macData so that it can be michael@0: * encoded. michael@0: */ michael@0: static SECStatus michael@0: sec_Pkcs12FinishMac(sec_PKCS12EncoderContext *p12ecx) michael@0: { michael@0: SECItem hmac = { siBuffer, NULL, 0 }; michael@0: SECStatus rv; michael@0: SGNDigestInfo *di = NULL; michael@0: void *dummy; michael@0: michael@0: if(!p12ecx) { michael@0: return SECFailure; michael@0: } michael@0: michael@0: /* make sure we are using password integrity mode */ michael@0: if(!p12ecx->p12exp->integrityEnabled) { michael@0: return SECSuccess; michael@0: } michael@0: michael@0: if(!p12ecx->p12exp->pwdIntegrity) { michael@0: return SECSuccess; michael@0: } michael@0: michael@0: /* finish the hmac */ michael@0: hmac.data = (unsigned char *)PORT_ZAlloc(SHA1_LENGTH); michael@0: if(!hmac.data) { michael@0: PORT_SetError(SEC_ERROR_NO_MEMORY); michael@0: return SECFailure; michael@0: } michael@0: michael@0: rv = PK11_DigestFinal(p12ecx->hmacCx, hmac.data, &hmac.len, SHA1_LENGTH); michael@0: michael@0: if(rv != SECSuccess) { michael@0: PORT_SetError(SEC_ERROR_NO_MEMORY); michael@0: goto loser; michael@0: } michael@0: michael@0: /* create the digest info */ michael@0: di = SGN_CreateDigestInfo(p12ecx->p12exp->integrityInfo.pwdInfo.algorithm, michael@0: hmac.data, hmac.len); michael@0: if(!di) { michael@0: PORT_SetError(SEC_ERROR_NO_MEMORY); michael@0: rv = SECFailure; michael@0: goto loser; michael@0: } michael@0: michael@0: rv = SGN_CopyDigestInfo(p12ecx->arena, &p12ecx->mac.safeMac, di); michael@0: if(rv != SECSuccess) { michael@0: PORT_SetError(SEC_ERROR_NO_MEMORY); michael@0: goto loser; michael@0: } michael@0: michael@0: /* encode the mac data */ michael@0: dummy = SEC_ASN1EncodeItem(p12ecx->arena, &p12ecx->pfx.encodedMacData, michael@0: &p12ecx->mac, sec_PKCS12MacDataTemplate); michael@0: if(!dummy) { michael@0: PORT_SetError(SEC_ERROR_NO_MEMORY); michael@0: rv = SECFailure; michael@0: } michael@0: michael@0: loser: michael@0: if(di) { michael@0: SGN_DestroyDigestInfo(di); michael@0: } michael@0: if(hmac.data) { michael@0: SECITEM_ZfreeItem(&hmac, PR_FALSE); michael@0: } michael@0: PK11_DestroyContext(p12ecx->hmacCx, PR_TRUE); michael@0: p12ecx->hmacCx = NULL; michael@0: michael@0: return rv; michael@0: } michael@0: michael@0: /* pfx notify function for ASN1 encoder. michael@0: * We want to stop encoding once we reach the authenticated safe. michael@0: * At that point, the encoder will be updated via streaming michael@0: * as the authenticated safe is encoded. michael@0: */ michael@0: static void michael@0: sec_pkcs12_encoder_pfx_notify(void *arg, PRBool before, void *dest, int real_depth) michael@0: { michael@0: sec_PKCS12EncoderContext *p12ecx; michael@0: michael@0: if(!before) { michael@0: return; michael@0: } michael@0: michael@0: /* look for authenticated safe */ michael@0: p12ecx = (sec_PKCS12EncoderContext*)arg; michael@0: if(dest != &p12ecx->pfx.encodedAuthSafe) { michael@0: return; michael@0: } michael@0: michael@0: SEC_ASN1EncoderSetTakeFromBuf(p12ecx->outerA1ecx); michael@0: SEC_ASN1EncoderSetStreaming(p12ecx->outerA1ecx); michael@0: SEC_ASN1EncoderClearNotifyProc(p12ecx->outerA1ecx); michael@0: } michael@0: michael@0: /* SEC_PKCS12Encode michael@0: * Encodes the PFX item and returns it to the output function, via michael@0: * callback. the output function must be capable of multiple updates. michael@0: * michael@0: * p12exp - the export context michael@0: * output - the output function callback, will be called more than once, michael@0: * must be able to accept streaming data. michael@0: * outputarg - argument for the output callback. michael@0: */ michael@0: SECStatus michael@0: SEC_PKCS12Encode(SEC_PKCS12ExportContext *p12exp, michael@0: SEC_PKCS12EncoderOutputCallback output, void *outputarg) michael@0: { michael@0: sec_PKCS12EncoderContext *p12enc; michael@0: struct sec_pkcs12_encoder_output outInfo; michael@0: SECStatus rv; michael@0: michael@0: if(!p12exp || !output) { michael@0: return SECFailure; michael@0: } michael@0: michael@0: /* get the encoder context */ michael@0: p12enc = sec_pkcs12_encoder_start_context(p12exp); michael@0: if(!p12enc) { michael@0: return SECFailure; michael@0: } michael@0: michael@0: outInfo.outputfn = output; michael@0: outInfo.outputarg = outputarg; michael@0: michael@0: /* set up PFX encoder, the "outer" encoder. Set it for streaming */ michael@0: p12enc->outerA1ecx = SEC_ASN1EncoderStart(&p12enc->pfx, michael@0: sec_PKCS12PFXItemTemplate, michael@0: sec_P12A1OutputCB_Outer, michael@0: &outInfo); michael@0: if(!p12enc->outerA1ecx) { michael@0: PORT_SetError(SEC_ERROR_NO_MEMORY); michael@0: rv = SECFailure; michael@0: goto loser; michael@0: } michael@0: SEC_ASN1EncoderSetStreaming(p12enc->outerA1ecx); michael@0: SEC_ASN1EncoderSetNotifyProc(p12enc->outerA1ecx, michael@0: sec_pkcs12_encoder_pfx_notify, p12enc); michael@0: rv = SEC_ASN1EncoderUpdate(p12enc->outerA1ecx, NULL, 0); michael@0: if(rv != SECSuccess) { michael@0: rv = SECFailure; michael@0: goto loser; michael@0: } michael@0: michael@0: /* set up asafe cinfo - the output of the encoder feeds the PFX encoder */ michael@0: p12enc->middleP7ecx = SEC_PKCS7EncoderStart(p12enc->aSafeCinfo, michael@0: sec_P12P7OutputCB_CallA1Update, michael@0: p12enc->outerA1ecx, NULL); michael@0: if(!p12enc->middleP7ecx) { michael@0: rv = SECFailure; michael@0: goto loser; michael@0: } michael@0: michael@0: /* encode asafe */ michael@0: p12enc->middleBuf.p7eCx = p12enc->middleP7ecx; michael@0: p12enc->middleBuf.hmacCx = NULL; michael@0: p12enc->middleBuf.numBytes = 0; michael@0: p12enc->middleBuf.bufBytes = sizeof p12enc->middleBuf.buf; michael@0: michael@0: /* Setup the "inner ASN.1 encoder for Authenticated Safes. */ michael@0: if(p12enc->p12exp->integrityEnabled && michael@0: p12enc->p12exp->pwdIntegrity) { michael@0: p12enc->middleBuf.hmacCx = p12enc->hmacCx; michael@0: } michael@0: p12enc->middleA1ecx = SEC_ASN1EncoderStart(&p12enc->p12exp->authSafe, michael@0: sec_PKCS12AuthenticatedSafeTemplate, michael@0: sec_P12A1OutputCB_HmacP7Update, michael@0: &p12enc->middleBuf); michael@0: if(!p12enc->middleA1ecx) { michael@0: rv = SECFailure; michael@0: goto loser; michael@0: } michael@0: SEC_ASN1EncoderSetStreaming(p12enc->middleA1ecx); michael@0: SEC_ASN1EncoderSetTakeFromBuf(p12enc->middleA1ecx); michael@0: michael@0: /* encode each of the safes */ michael@0: while(p12enc->currentSafe != p12enc->p12exp->safeInfoCount) { michael@0: sec_pkcs12_encoder_asafe_process(p12enc); michael@0: p12enc->currentSafe++; michael@0: } michael@0: SEC_ASN1EncoderClearTakeFromBuf(p12enc->middleA1ecx); michael@0: SEC_ASN1EncoderClearStreaming(p12enc->middleA1ecx); michael@0: SEC_ASN1EncoderUpdate(p12enc->middleA1ecx, NULL, 0); michael@0: SEC_ASN1EncoderFinish(p12enc->middleA1ecx); michael@0: p12enc->middleA1ecx = NULL; michael@0: michael@0: sec_FlushPkcs12OutputBuffer( &p12enc->middleBuf); michael@0: michael@0: /* finish the encoding of the authenticated safes */ michael@0: rv = SEC_PKCS7EncoderFinish(p12enc->middleP7ecx, p12exp->pwfn, michael@0: p12exp->pwfnarg); michael@0: p12enc->middleP7ecx = NULL; michael@0: if(rv != SECSuccess) { michael@0: goto loser; michael@0: } michael@0: michael@0: SEC_ASN1EncoderClearTakeFromBuf(p12enc->outerA1ecx); michael@0: SEC_ASN1EncoderClearStreaming(p12enc->outerA1ecx); michael@0: michael@0: /* update the mac, if necessary */ michael@0: rv = sec_Pkcs12FinishMac(p12enc); michael@0: if(rv != SECSuccess) { michael@0: goto loser; michael@0: } michael@0: michael@0: /* finish encoding the pfx */ michael@0: rv = SEC_ASN1EncoderUpdate(p12enc->outerA1ecx, NULL, 0); michael@0: michael@0: SEC_ASN1EncoderFinish(p12enc->outerA1ecx); michael@0: p12enc->outerA1ecx = NULL; michael@0: michael@0: loser: michael@0: sec_pkcs12_encoder_destroy_context(p12enc); michael@0: return rv; michael@0: } michael@0: michael@0: void michael@0: SEC_PKCS12DestroyExportContext(SEC_PKCS12ExportContext *p12ecx) michael@0: { michael@0: int i = 0; michael@0: michael@0: if(!p12ecx) { michael@0: return; michael@0: } michael@0: michael@0: if(p12ecx->safeInfos) { michael@0: i = 0; michael@0: while(p12ecx->safeInfos[i] != NULL) { michael@0: if(p12ecx->safeInfos[i]->encryptionKey) { michael@0: PK11_FreeSymKey(p12ecx->safeInfos[i]->encryptionKey); michael@0: } michael@0: if(p12ecx->safeInfos[i]->cinfo) { michael@0: SEC_PKCS7DestroyContentInfo(p12ecx->safeInfos[i]->cinfo); michael@0: } michael@0: i++; michael@0: } michael@0: } michael@0: michael@0: PK11_FreeSlot(p12ecx->slot); michael@0: michael@0: PORT_FreeArena(p12ecx->arena, PR_TRUE); michael@0: }