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 "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 "pkcs12.h" michael@0: #include "p12local.h" michael@0: #include "secpkcs7.h" michael@0: #include "secasn1.h" michael@0: #include "secerr.h" michael@0: #include "p12plcy.h" michael@0: michael@0: /* release the memory taken up by the list of nicknames */ michael@0: static void michael@0: sec_pkcs12_destroy_nickname_list(SECItem **nicknames) michael@0: { michael@0: int i = 0; michael@0: michael@0: if(nicknames == NULL) { michael@0: return; michael@0: } michael@0: michael@0: while(nicknames[i] != NULL) { michael@0: SECITEM_FreeItem(nicknames[i], PR_FALSE); michael@0: i++; michael@0: } michael@0: michael@0: PORT_Free(nicknames); michael@0: } michael@0: michael@0: /* release the memory taken up by the list of certificates */ michael@0: static void michael@0: sec_pkcs12_destroy_certificate_list(CERTCertificate **ref_certs) michael@0: { michael@0: int i = 0; michael@0: michael@0: if(ref_certs == NULL) { michael@0: return; michael@0: } michael@0: michael@0: while(ref_certs[i] != NULL) { michael@0: CERT_DestroyCertificate(ref_certs[i]); michael@0: i++; michael@0: } michael@0: } michael@0: michael@0: static void michael@0: sec_pkcs12_destroy_cinfos_for_cert_bags(SEC_PKCS12CertAndCRLBag *certBag) michael@0: { michael@0: int j = 0; michael@0: j = 0; michael@0: while(certBag->certAndCRLs[j] != NULL) { michael@0: SECOidTag certType = SECOID_FindOIDTag(&certBag->certAndCRLs[j]->BagID); michael@0: if(certType == SEC_OID_PKCS12_X509_CERT_CRL_BAG) { michael@0: SEC_PKCS12X509CertCRL *x509; michael@0: x509 = certBag->certAndCRLs[j]->value.x509; michael@0: SEC_PKCS7DestroyContentInfo(&x509->certOrCRL); michael@0: } michael@0: j++; michael@0: } michael@0: } michael@0: michael@0: /* destroy all content infos since they were not allocated in common michael@0: * pool michael@0: */ michael@0: static void michael@0: sec_pkcs12_destroy_cert_content_infos(SEC_PKCS12SafeContents *safe, michael@0: SEC_PKCS12Baggage *baggage) michael@0: { michael@0: int i, j; michael@0: michael@0: if((safe != NULL) && (safe->contents != NULL)) { michael@0: i = 0; michael@0: while(safe->contents[i] != NULL) { michael@0: SECOidTag bagType = SECOID_FindOIDTag(&safe->contents[i]->safeBagType); michael@0: if(bagType == SEC_OID_PKCS12_CERT_AND_CRL_BAG_ID) { michael@0: SEC_PKCS12CertAndCRLBag *certBag; michael@0: certBag = safe->contents[i]->safeContent.certAndCRLBag; michael@0: sec_pkcs12_destroy_cinfos_for_cert_bags(certBag); michael@0: } michael@0: i++; michael@0: } michael@0: } michael@0: michael@0: if((baggage != NULL) && (baggage->bags != NULL)) { michael@0: i = 0; michael@0: while(baggage->bags[i] != NULL) { michael@0: if(baggage->bags[i]->unencSecrets != NULL) { michael@0: j = 0; michael@0: while(baggage->bags[i]->unencSecrets[j] != NULL) { michael@0: SECOidTag bagType; michael@0: bagType = SECOID_FindOIDTag(&baggage->bags[i]->unencSecrets[j]->safeBagType); michael@0: if(bagType == SEC_OID_PKCS12_CERT_AND_CRL_BAG_ID) { michael@0: SEC_PKCS12CertAndCRLBag *certBag; michael@0: certBag = baggage->bags[i]->unencSecrets[j]->safeContent.certAndCRLBag; michael@0: sec_pkcs12_destroy_cinfos_for_cert_bags(certBag); michael@0: } michael@0: j++; michael@0: } michael@0: } michael@0: i++; michael@0: } michael@0: } michael@0: } michael@0: michael@0: /* convert the nickname list from a NULL termincated Char list michael@0: * to a NULL terminated SECItem list michael@0: */ michael@0: static SECItem ** michael@0: sec_pkcs12_convert_nickname_list(char **nicknames) michael@0: { michael@0: SECItem **nicks; michael@0: int i, j; michael@0: PRBool error = PR_FALSE; michael@0: michael@0: if(nicknames == NULL) { michael@0: return NULL; michael@0: } michael@0: michael@0: i = j = 0; michael@0: while(nicknames[i] != NULL) { michael@0: i++; michael@0: } michael@0: michael@0: /* allocate the space and copy the data */ michael@0: nicks = (SECItem **)PORT_ZAlloc(sizeof(SECItem *) * (i + 1)); michael@0: if(nicks != NULL) { michael@0: for(j = 0; ((j < i) && (error == PR_FALSE)); j++) { michael@0: nicks[j] = (SECItem *)PORT_ZAlloc(sizeof(SECItem)); michael@0: if(nicks[j] != NULL) { michael@0: nicks[j]->data = michael@0: (unsigned char *)PORT_ZAlloc(PORT_Strlen(nicknames[j])+1); michael@0: if(nicks[j]->data != NULL) { michael@0: nicks[j]->len = PORT_Strlen(nicknames[j]); michael@0: PORT_Memcpy(nicks[j]->data, nicknames[j], nicks[j]->len); michael@0: nicks[j]->data[nicks[j]->len] = 0; michael@0: } else { michael@0: error = PR_TRUE; michael@0: } michael@0: } else { michael@0: error = PR_TRUE; michael@0: } michael@0: } michael@0: } michael@0: michael@0: if(error == PR_TRUE) { michael@0: for(i = 0; i < j; i++) { michael@0: SECITEM_FreeItem(nicks[i], PR_TRUE); michael@0: } michael@0: PORT_Free(nicks); michael@0: nicks = NULL; michael@0: } michael@0: michael@0: return nicks; michael@0: } michael@0: michael@0: /* package the certificate add_cert into PKCS12 structures, michael@0: * retrieve the certificate chain for the cert and return michael@0: * the packaged contents. michael@0: * poolp -- common memory pool; michael@0: * add_cert -- certificate to package up michael@0: * nickname for the certificate michael@0: * a return of NULL indicates an error michael@0: */ michael@0: static SEC_PKCS12CertAndCRL * michael@0: sec_pkcs12_get_cert(PLArenaPool *poolp, michael@0: CERTCertificate *add_cert, michael@0: SECItem *nickname) michael@0: { michael@0: SEC_PKCS12CertAndCRL *cert; michael@0: SEC_PKCS7ContentInfo *cinfo; michael@0: SGNDigestInfo *t_di; michael@0: void *mark; michael@0: SECStatus rv; michael@0: michael@0: if((poolp == NULL) || (add_cert == NULL) || (nickname == NULL)) { michael@0: return NULL; michael@0: } michael@0: mark = PORT_ArenaMark(poolp); michael@0: michael@0: cert = sec_pkcs12_new_cert_crl(poolp, SEC_OID_PKCS12_X509_CERT_CRL_BAG); michael@0: if(cert != NULL) { michael@0: michael@0: /* copy the nickname */ michael@0: rv = SECITEM_CopyItem(poolp, &cert->nickname, nickname); michael@0: if(rv != SECSuccess) { michael@0: PORT_SetError(SEC_ERROR_NO_MEMORY); michael@0: cert = NULL; michael@0: } else { michael@0: michael@0: /* package the certificate and cert chain into a NULL signer michael@0: * PKCS 7 SignedData content Info and prepare it for encoding michael@0: * since we cannot use DER_ANY_TEMPLATE michael@0: */ michael@0: cinfo = SEC_PKCS7CreateCertsOnly(add_cert, PR_TRUE, NULL); michael@0: rv = SEC_PKCS7PrepareForEncode(cinfo, NULL, NULL, NULL); michael@0: michael@0: /* thumbprint the certificate */ michael@0: if((cinfo != NULL) && (rv == SECSuccess)) michael@0: { michael@0: PORT_Memcpy(&cert->value.x509->certOrCRL, cinfo, sizeof(*cinfo)); michael@0: t_di = sec_pkcs12_compute_thumbprint(&add_cert->derCert); michael@0: if(t_di != NULL) michael@0: { michael@0: /* test */ michael@0: rv = SGN_CopyDigestInfo(poolp, &cert->value.x509->thumbprint, michael@0: t_di); michael@0: if(rv != SECSuccess) { michael@0: cert = NULL; michael@0: PORT_SetError(SEC_ERROR_NO_MEMORY); michael@0: } michael@0: SGN_DestroyDigestInfo(t_di); michael@0: } michael@0: else michael@0: cert = NULL; michael@0: } michael@0: } michael@0: } michael@0: michael@0: if (cert == NULL) { michael@0: PORT_ArenaRelease(poolp, mark); michael@0: } else { michael@0: PORT_ArenaUnmark(poolp, mark); michael@0: } michael@0: michael@0: return cert; michael@0: } michael@0: michael@0: /* package the private key associated with the certificate and michael@0: * return the appropriate PKCS 12 structure michael@0: * poolp common memory pool michael@0: * nickname key nickname michael@0: * cert -- cert to look up michael@0: * wincx -- window handle michael@0: * an error is indicated by a return of NULL michael@0: */ michael@0: static SEC_PKCS12PrivateKey * michael@0: sec_pkcs12_get_private_key(PLArenaPool *poolp, michael@0: SECItem *nickname, michael@0: CERTCertificate *cert, michael@0: void *wincx) michael@0: { michael@0: SECKEYPrivateKeyInfo *pki; michael@0: SEC_PKCS12PrivateKey *pk; michael@0: SECStatus rv; michael@0: void *mark; michael@0: michael@0: if((poolp == NULL) || (nickname == NULL)) { michael@0: return NULL; michael@0: } michael@0: michael@0: mark = PORT_ArenaMark(poolp); michael@0: michael@0: /* retrieve key from the data base */ michael@0: pki = PK11_ExportPrivateKeyInfo(nickname, cert, wincx); michael@0: if(pki == NULL) { michael@0: PORT_ArenaRelease(poolp, mark); michael@0: PORT_SetError(SEC_ERROR_PKCS12_UNABLE_TO_EXPORT_KEY); michael@0: return NULL; michael@0: } michael@0: michael@0: pk = (SEC_PKCS12PrivateKey *)PORT_ArenaZAlloc(poolp, michael@0: sizeof(SEC_PKCS12PrivateKey)); michael@0: if(pk != NULL) { michael@0: rv = sec_pkcs12_init_pvk_data(poolp, &pk->pvkData); michael@0: michael@0: if(rv == SECSuccess) { michael@0: /* copy the key into poolp memory space */ michael@0: rv = SECKEY_CopyPrivateKeyInfo(poolp, &pk->pkcs8data, pki); michael@0: if(rv == SECSuccess) { michael@0: rv = SECITEM_CopyItem(poolp, &pk->pvkData.nickname, nickname); michael@0: } michael@0: } michael@0: michael@0: if(rv != SECSuccess) { michael@0: PORT_SetError(SEC_ERROR_NO_MEMORY); michael@0: pk = NULL; michael@0: } michael@0: } else { michael@0: PORT_SetError(SEC_ERROR_NO_MEMORY); michael@0: } michael@0: michael@0: /* destroy private key, zeroing out data */ michael@0: SECKEY_DestroyPrivateKeyInfo(pki, PR_TRUE); michael@0: if (pk == NULL) { michael@0: PORT_ArenaRelease(poolp, mark); michael@0: } else { michael@0: PORT_ArenaUnmark(poolp, mark); michael@0: } michael@0: michael@0: return pk; michael@0: } michael@0: michael@0: /* get a shrouded key item associated with a certificate michael@0: * return the appropriate PKCS 12 structure michael@0: * poolp common memory pool michael@0: * nickname key nickname michael@0: * cert -- cert to look up michael@0: * wincx -- window handle michael@0: * an error is indicated by a return of NULL michael@0: */ michael@0: static SEC_PKCS12ESPVKItem * michael@0: sec_pkcs12_get_shrouded_key(PLArenaPool *poolp, michael@0: SECItem *nickname, michael@0: CERTCertificate *cert, michael@0: SECOidTag algorithm, michael@0: SECItem *pwitem, michael@0: PKCS12UnicodeConvertFunction unicodeFn, michael@0: void *wincx) michael@0: { michael@0: SECKEYEncryptedPrivateKeyInfo *epki; michael@0: SEC_PKCS12ESPVKItem *pk; michael@0: void *mark; michael@0: SECStatus rv; michael@0: PK11SlotInfo *slot = NULL; michael@0: PRBool swapUnicodeBytes = PR_FALSE; michael@0: michael@0: #ifdef IS_LITTLE_ENDIAN michael@0: swapUnicodeBytes = PR_TRUE; michael@0: #endif michael@0: michael@0: if((poolp == NULL) || (nickname == NULL)) michael@0: return NULL; michael@0: michael@0: mark = PORT_ArenaMark(poolp); michael@0: michael@0: /* use internal key slot */ michael@0: slot = PK11_GetInternalKeySlot(); michael@0: michael@0: /* retrieve encrypted prviate key */ michael@0: epki = PK11_ExportEncryptedPrivateKeyInfo(slot, algorithm, pwitem, michael@0: nickname, cert, 1, 0, NULL); michael@0: PK11_FreeSlot(slot); michael@0: if(epki == NULL) { michael@0: PORT_SetError(SEC_ERROR_PKCS12_UNABLE_TO_EXPORT_KEY); michael@0: PORT_ArenaRelease(poolp, mark); michael@0: return NULL; michael@0: } michael@0: michael@0: /* create a private key and store the data into the poolp memory space */ michael@0: pk = sec_pkcs12_create_espvk(poolp, SEC_OID_PKCS12_PKCS8_KEY_SHROUDING); michael@0: if(pk != NULL) { michael@0: rv = sec_pkcs12_init_pvk_data(poolp, &pk->espvkData); michael@0: rv = SECITEM_CopyItem(poolp, &pk->espvkData.nickname, nickname); michael@0: pk->espvkCipherText.pkcs8KeyShroud = michael@0: (SECKEYEncryptedPrivateKeyInfo *)PORT_ArenaZAlloc(poolp, michael@0: sizeof(SECKEYEncryptedPrivateKeyInfo)); michael@0: if((pk->espvkCipherText.pkcs8KeyShroud != NULL) && (rv == SECSuccess)) { michael@0: rv = SECKEY_CopyEncryptedPrivateKeyInfo(poolp, michael@0: pk->espvkCipherText.pkcs8KeyShroud, epki); michael@0: if(rv == SECSuccess) { michael@0: rv = (*unicodeFn)(poolp, &pk->espvkData.uniNickName, nickname, michael@0: PR_TRUE, swapUnicodeBytes); michael@0: } michael@0: } michael@0: michael@0: if(rv != SECSuccess) { michael@0: PORT_SetError(SEC_ERROR_NO_MEMORY); michael@0: pk = NULL; michael@0: } michael@0: } michael@0: michael@0: SECKEY_DestroyEncryptedPrivateKeyInfo(epki, PR_TRUE); michael@0: if(pk == NULL) { michael@0: PORT_ArenaRelease(poolp, mark); michael@0: } else { michael@0: PORT_ArenaUnmark(poolp, mark); michael@0: } michael@0: michael@0: return pk; michael@0: } michael@0: michael@0: /* add a thumbprint to a private key associated certs list michael@0: * pvk is the area where the list is stored michael@0: * thumb is the thumbprint to copy michael@0: * a return of SECFailure indicates an error michael@0: */ michael@0: static SECStatus michael@0: sec_pkcs12_add_thumbprint(SEC_PKCS12PVKSupportingData *pvk, michael@0: SGNDigestInfo *thumb) michael@0: { michael@0: SGNDigestInfo **thumb_list = NULL; michael@0: int nthumbs, size; michael@0: void *mark, *dummy; michael@0: SECStatus rv = SECFailure; michael@0: michael@0: if((pvk == NULL) || (thumb == NULL)) { michael@0: return SECFailure; michael@0: } michael@0: michael@0: mark = PORT_ArenaMark(pvk->poolp); michael@0: michael@0: thumb_list = pvk->assocCerts; michael@0: nthumbs = pvk->nThumbs; michael@0: michael@0: /* allocate list space needed -- either growing or allocating michael@0: * list must be NULL terminated michael@0: */ michael@0: size = sizeof(SGNDigestInfo *); michael@0: dummy = PORT_ArenaGrow(pvk->poolp, thumb_list, (size * (nthumbs + 1)), michael@0: (size * (nthumbs + 2))); michael@0: thumb_list = dummy; michael@0: if(dummy != NULL) { michael@0: thumb_list[nthumbs] = (SGNDigestInfo *)PORT_ArenaZAlloc(pvk->poolp, michael@0: sizeof(SGNDigestInfo)); michael@0: if(thumb_list[nthumbs] != NULL) { michael@0: SGN_CopyDigestInfo(pvk->poolp, thumb_list[nthumbs], thumb); michael@0: nthumbs += 1; michael@0: thumb_list[nthumbs] = 0; michael@0: } else { michael@0: dummy = NULL; michael@0: } michael@0: } michael@0: michael@0: if(dummy == NULL) { michael@0: PORT_ArenaRelease(pvk->poolp, mark); michael@0: return SECFailure; michael@0: } michael@0: michael@0: pvk->assocCerts = thumb_list; michael@0: pvk->nThumbs = nthumbs; michael@0: michael@0: PORT_ArenaUnmark(pvk->poolp, mark); michael@0: return SECSuccess; michael@0: } michael@0: michael@0: /* search the list of shrouded keys in the baggage for the desired michael@0: * name. return a pointer to the item. a return of NULL indicates michael@0: * that no match was present or that an error occurred. michael@0: */ michael@0: static SEC_PKCS12ESPVKItem * michael@0: sec_pkcs12_get_espvk_by_name(SEC_PKCS12Baggage *luggage, michael@0: SECItem *name) michael@0: { michael@0: PRBool found = PR_FALSE; michael@0: SEC_PKCS12ESPVKItem *espvk = NULL; michael@0: int i, j; michael@0: SECComparison rv = SECEqual; michael@0: SECItem *t_name; michael@0: SEC_PKCS12BaggageItem *bag; michael@0: michael@0: if((luggage == NULL) || (name == NULL)) { michael@0: return NULL; michael@0: } michael@0: michael@0: i = 0; michael@0: while((found == PR_FALSE) && (i < luggage->luggage_size)) { michael@0: j = 0; michael@0: bag = luggage->bags[i]; michael@0: while((found == PR_FALSE) && (j < bag->nEspvks)) { michael@0: espvk = bag->espvks[j]; michael@0: if(espvk->poolp == NULL) { michael@0: espvk->poolp = luggage->poolp; michael@0: } michael@0: t_name = SECITEM_DupItem(&espvk->espvkData.nickname); michael@0: if(t_name != NULL) { michael@0: rv = SECITEM_CompareItem(name, t_name); michael@0: if(rv == SECEqual) { michael@0: found = PR_TRUE; michael@0: } michael@0: SECITEM_FreeItem(t_name, PR_TRUE); michael@0: } else { michael@0: PORT_SetError(SEC_ERROR_NO_MEMORY); michael@0: return NULL; michael@0: } michael@0: j++; michael@0: } michael@0: i++; michael@0: } michael@0: michael@0: if(found != PR_TRUE) { michael@0: PORT_SetError(SEC_ERROR_PKCS12_UNABLE_TO_LOCATE_OBJECT_BY_NAME); michael@0: return NULL; michael@0: } michael@0: michael@0: return espvk; michael@0: } michael@0: michael@0: /* locates a certificate and copies the thumbprint to the michael@0: * appropriate private key michael@0: */ michael@0: static SECStatus michael@0: sec_pkcs12_propagate_thumbprints(SECItem **nicknames, michael@0: CERTCertificate **ref_certs, michael@0: SEC_PKCS12SafeContents *safe, michael@0: SEC_PKCS12Baggage *baggage) michael@0: { michael@0: SEC_PKCS12CertAndCRL *cert; michael@0: SEC_PKCS12PrivateKey *key; michael@0: SEC_PKCS12ESPVKItem *espvk; michael@0: int i; michael@0: PRBool error = PR_FALSE; michael@0: SECStatus rv = SECFailure; michael@0: michael@0: if((nicknames == NULL) || (safe == NULL)) { michael@0: return SECFailure; michael@0: } michael@0: michael@0: i = 0; michael@0: while((nicknames[i] != NULL) && (error == PR_FALSE)) { michael@0: /* process all certs */ michael@0: cert = (SEC_PKCS12CertAndCRL *)sec_pkcs12_find_object(safe, baggage, michael@0: SEC_OID_PKCS12_CERT_AND_CRL_BAG_ID, michael@0: nicknames[i], NULL); michael@0: if(cert != NULL) { michael@0: /* locate key and copy thumbprint */ michael@0: key = (SEC_PKCS12PrivateKey *)sec_pkcs12_find_object(safe, baggage, michael@0: SEC_OID_PKCS12_KEY_BAG_ID, michael@0: nicknames[i], NULL); michael@0: if(key != NULL) { michael@0: key->pvkData.poolp = key->poolp; michael@0: rv = sec_pkcs12_add_thumbprint(&key->pvkData, michael@0: &cert->value.x509->thumbprint); michael@0: if(rv == SECFailure) michael@0: error = PR_TRUE; /* XXX Set error? */ michael@0: } michael@0: michael@0: /* look in the baggage as well...*/ michael@0: if((baggage != NULL) && (error == PR_FALSE)) { michael@0: espvk = sec_pkcs12_get_espvk_by_name(baggage, nicknames[i]); michael@0: if(espvk != NULL) { michael@0: espvk->espvkData.poolp = espvk->poolp; michael@0: rv = sec_pkcs12_add_thumbprint(&espvk->espvkData, michael@0: &cert->value.x509->thumbprint); michael@0: if(rv == SECFailure) michael@0: error = PR_TRUE; /* XXX Set error? */ michael@0: } michael@0: } michael@0: } michael@0: i++; michael@0: } michael@0: michael@0: if(error == PR_TRUE) { michael@0: return SECFailure; michael@0: } michael@0: michael@0: return SECSuccess; michael@0: } michael@0: michael@0: /* append a safe bag to the end of the safe contents list */ michael@0: SECStatus michael@0: sec_pkcs12_append_safe_bag(SEC_PKCS12SafeContents *safe, michael@0: SEC_PKCS12SafeBag *bag) michael@0: { michael@0: int size; michael@0: void *mark = NULL, *dummy = NULL; michael@0: michael@0: if((bag == NULL) || (safe == NULL)) michael@0: return SECFailure; michael@0: michael@0: mark = PORT_ArenaMark(safe->poolp); michael@0: michael@0: size = (safe->safe_size * sizeof(SEC_PKCS12SafeBag *)); michael@0: michael@0: if(safe->safe_size > 0) { michael@0: dummy = (SEC_PKCS12SafeBag **)PORT_ArenaGrow(safe->poolp, michael@0: safe->contents, michael@0: size, michael@0: (size + sizeof(SEC_PKCS12SafeBag *))); michael@0: safe->contents = dummy; michael@0: } else { michael@0: safe->contents = (SEC_PKCS12SafeBag **)PORT_ArenaZAlloc(safe->poolp, michael@0: (2 * sizeof(SEC_PKCS12SafeBag *))); michael@0: dummy = safe->contents; michael@0: } michael@0: michael@0: if(dummy == NULL) { michael@0: PORT_SetError(SEC_ERROR_NO_MEMORY); michael@0: goto loser; michael@0: } michael@0: michael@0: safe->contents[safe->safe_size] = bag; michael@0: safe->safe_size++; michael@0: safe->contents[safe->safe_size] = NULL; michael@0: michael@0: PORT_ArenaUnmark(safe->poolp, mark); michael@0: return SECSuccess; michael@0: michael@0: loser: michael@0: PORT_ArenaRelease(safe->poolp, mark); michael@0: return SECFailure; michael@0: } michael@0: michael@0: /* append a certificate onto the end of a cert bag */ michael@0: static SECStatus michael@0: sec_pkcs12_append_cert_to_bag(PLArenaPool *arena, michael@0: SEC_PKCS12SafeBag *safebag, michael@0: CERTCertificate *cert, michael@0: SECItem *nickname) michael@0: { michael@0: int size; michael@0: void *dummy = NULL, *mark = NULL; michael@0: SEC_PKCS12CertAndCRL *p12cert; michael@0: SEC_PKCS12CertAndCRLBag *bag; michael@0: michael@0: if((arena == NULL) || (safebag == NULL) || michael@0: (cert == NULL) || (nickname == NULL)) { michael@0: return SECFailure; michael@0: } michael@0: michael@0: bag = safebag->safeContent.certAndCRLBag; michael@0: if(bag == NULL) { michael@0: return SECFailure; michael@0: } michael@0: michael@0: mark = PORT_ArenaMark(arena); michael@0: michael@0: p12cert = sec_pkcs12_get_cert(arena, cert, nickname); michael@0: if(p12cert == NULL) { michael@0: PORT_ArenaRelease(bag->poolp, mark); michael@0: return SECFailure; michael@0: } michael@0: michael@0: size = bag->bag_size * sizeof(SEC_PKCS12CertAndCRL *); michael@0: if(bag->bag_size > 0) { michael@0: dummy = (SEC_PKCS12CertAndCRL **)PORT_ArenaGrow(bag->poolp, michael@0: bag->certAndCRLs, size, size + sizeof(SEC_PKCS12CertAndCRL *)); michael@0: bag->certAndCRLs = dummy; michael@0: } else { michael@0: bag->certAndCRLs = (SEC_PKCS12CertAndCRL **)PORT_ArenaZAlloc(bag->poolp, michael@0: (2 * sizeof(SEC_PKCS12CertAndCRL *))); michael@0: dummy = bag->certAndCRLs; michael@0: } michael@0: michael@0: if(dummy == NULL) { michael@0: PORT_SetError(SEC_ERROR_NO_MEMORY); michael@0: goto loser; michael@0: } michael@0: michael@0: bag->certAndCRLs[bag->bag_size] = p12cert; michael@0: bag->bag_size++; michael@0: bag->certAndCRLs[bag->bag_size] = NULL; michael@0: michael@0: PORT_ArenaUnmark(bag->poolp, mark); michael@0: return SECSuccess; michael@0: michael@0: loser: michael@0: PORT_ArenaRelease(bag->poolp, mark); michael@0: return SECFailure; michael@0: } michael@0: michael@0: /* append a key onto the end of a list of keys in a key bag */ michael@0: SECStatus michael@0: sec_pkcs12_append_key_to_bag(SEC_PKCS12SafeBag *safebag, michael@0: SEC_PKCS12PrivateKey *pk) michael@0: { michael@0: void *mark, *dummy; michael@0: SEC_PKCS12PrivateKeyBag *bag; michael@0: int size; michael@0: michael@0: if((safebag == NULL) || (pk == NULL)) michael@0: return SECFailure; michael@0: michael@0: bag = safebag->safeContent.keyBag; michael@0: if(bag == NULL) { michael@0: return SECFailure; michael@0: } michael@0: michael@0: mark = PORT_ArenaMark(bag->poolp); michael@0: michael@0: size = (bag->bag_size * sizeof(SEC_PKCS12PrivateKey *)); michael@0: michael@0: if(bag->bag_size > 0) { michael@0: dummy = (SEC_PKCS12PrivateKey **)PORT_ArenaGrow(bag->poolp, michael@0: bag->privateKeys, michael@0: size, michael@0: size + sizeof(SEC_PKCS12PrivateKey *)); michael@0: bag->privateKeys = dummy; michael@0: } else { michael@0: bag->privateKeys = (SEC_PKCS12PrivateKey **)PORT_ArenaZAlloc(bag->poolp, michael@0: (2 * sizeof(SEC_PKCS12PrivateKey *))); michael@0: dummy = bag->privateKeys; michael@0: } michael@0: michael@0: if(dummy == NULL) { michael@0: PORT_SetError(SEC_ERROR_NO_MEMORY); michael@0: goto loser; michael@0: } michael@0: michael@0: bag->privateKeys[bag->bag_size] = pk; michael@0: bag->bag_size++; michael@0: bag->privateKeys[bag->bag_size] = NULL; michael@0: michael@0: PORT_ArenaUnmark(bag->poolp, mark); michael@0: return SECSuccess; michael@0: michael@0: loser: michael@0: /* XXX Free memory? */ michael@0: PORT_ArenaRelease(bag->poolp, mark); michael@0: return SECFailure; michael@0: } michael@0: michael@0: /* append a safe bag to the baggage area */ michael@0: static SECStatus michael@0: sec_pkcs12_append_unshrouded_bag(SEC_PKCS12BaggageItem *bag, michael@0: SEC_PKCS12SafeBag *u_bag) michael@0: { michael@0: int size; michael@0: void *mark = NULL, *dummy = NULL; michael@0: michael@0: if((bag == NULL) || (u_bag == NULL)) michael@0: return SECFailure; michael@0: michael@0: mark = PORT_ArenaMark(bag->poolp); michael@0: michael@0: /* dump things into the first bag */ michael@0: size = (bag->nSecrets + 1) * sizeof(SEC_PKCS12SafeBag *); michael@0: dummy = PORT_ArenaGrow(bag->poolp, michael@0: bag->unencSecrets, size, michael@0: size + sizeof(SEC_PKCS12SafeBag *)); michael@0: bag->unencSecrets = dummy; michael@0: if(dummy == NULL) { michael@0: PORT_SetError(SEC_ERROR_NO_MEMORY); michael@0: goto loser; michael@0: } michael@0: michael@0: bag->unencSecrets[bag->nSecrets] = u_bag; michael@0: bag->nSecrets++; michael@0: bag->unencSecrets[bag->nSecrets] = NULL; michael@0: michael@0: PORT_ArenaUnmark(bag->poolp, mark); michael@0: return SECSuccess; michael@0: michael@0: loser: michael@0: PORT_ArenaRelease(bag->poolp, mark); michael@0: return SECFailure; michael@0: } michael@0: michael@0: /* gather up all certificates and keys and package them up michael@0: * in the safe, baggage, or both. michael@0: * nicknames is the list of nicknames and corresponding certs in ref_certs michael@0: * ref_certs a null terminated list of certificates michael@0: * rSafe, rBaggage -- return areas for safe and baggage michael@0: * shroud_keys -- store keys externally michael@0: * pwitem -- password for computing integrity mac and encrypting contents michael@0: * wincx -- window handle michael@0: * michael@0: * if a failure occurs, an error is set and SECFailure returned. michael@0: */ michael@0: static SECStatus michael@0: sec_pkcs12_package_certs_and_keys(SECItem **nicknames, michael@0: CERTCertificate **ref_certs, michael@0: PRBool unencryptedCerts, michael@0: SEC_PKCS12SafeContents **rSafe, michael@0: SEC_PKCS12Baggage **rBaggage, michael@0: PRBool shroud_keys, michael@0: SECOidTag shroud_alg, michael@0: SECItem *pwitem, michael@0: PKCS12UnicodeConvertFunction unicodeFn, michael@0: void *wincx) michael@0: { michael@0: PLArenaPool *permArena; michael@0: SEC_PKCS12SafeContents *safe = NULL; michael@0: SEC_PKCS12Baggage *baggage = NULL; michael@0: michael@0: SECStatus rv = SECFailure; michael@0: PRBool problem = PR_FALSE; michael@0: michael@0: SEC_PKCS12ESPVKItem *espvk = NULL; michael@0: SEC_PKCS12PrivateKey *pk = NULL; michael@0: CERTCertificate *add_cert = NULL; michael@0: SEC_PKCS12SafeBag *certbag = NULL, *keybag = NULL; michael@0: SEC_PKCS12BaggageItem *external_bag = NULL; michael@0: int ncerts = 0, nkeys = 0; michael@0: int i; michael@0: michael@0: if((nicknames == NULL) || (rSafe == NULL) || (rBaggage == NULL)) { michael@0: return SECFailure; michael@0: } michael@0: michael@0: *rBaggage = baggage; michael@0: *rSafe = safe; michael@0: michael@0: permArena = PORT_NewArena(SEC_ASN1_DEFAULT_ARENA_SIZE); michael@0: if(permArena == NULL) { michael@0: PORT_SetError(SEC_ERROR_NO_MEMORY); michael@0: return SECFailure; michael@0: } michael@0: michael@0: /* allocate structures */ michael@0: safe = sec_pkcs12_create_safe_contents(permArena); michael@0: if(safe == NULL) { michael@0: PORT_SetError(SEC_ERROR_NO_MEMORY); michael@0: rv = SECFailure; michael@0: goto loser; michael@0: } michael@0: michael@0: certbag = sec_pkcs12_create_safe_bag(permArena, michael@0: SEC_OID_PKCS12_CERT_AND_CRL_BAG_ID); michael@0: if(certbag == NULL) { michael@0: rv = SECFailure; michael@0: goto loser; michael@0: } michael@0: michael@0: if(shroud_keys != PR_TRUE) { michael@0: keybag = sec_pkcs12_create_safe_bag(permArena, michael@0: SEC_OID_PKCS12_KEY_BAG_ID); michael@0: if(keybag == NULL) { michael@0: rv = SECFailure; michael@0: goto loser; michael@0: } michael@0: } michael@0: michael@0: if((shroud_keys == PR_TRUE) || (unencryptedCerts == PR_TRUE)) { michael@0: baggage = sec_pkcs12_create_baggage(permArena); michael@0: if(baggage == NULL) { michael@0: rv = SECFailure; michael@0: goto loser; michael@0: } michael@0: external_bag = sec_pkcs12_create_external_bag(baggage); michael@0: } michael@0: michael@0: /* package keys and certs */ michael@0: i = 0; michael@0: while((nicknames[i] != NULL) && (problem == PR_FALSE)) { michael@0: if(ref_certs[i] != NULL) { michael@0: /* append cert to bag o certs */ michael@0: rv = sec_pkcs12_append_cert_to_bag(permArena, certbag, michael@0: ref_certs[i], michael@0: nicknames[i]); michael@0: if(rv == SECFailure) { michael@0: problem = PR_FALSE; michael@0: } else { michael@0: ncerts++; michael@0: } michael@0: michael@0: if(rv == SECSuccess) { michael@0: /* package up them keys */ michael@0: if(shroud_keys == PR_TRUE) { michael@0: espvk = sec_pkcs12_get_shrouded_key(permArena, michael@0: nicknames[i], michael@0: ref_certs[i], michael@0: shroud_alg, michael@0: pwitem, unicodeFn, michael@0: wincx); michael@0: if(espvk != NULL) { michael@0: rv = sec_pkcs12_append_shrouded_key(external_bag, espvk); michael@0: SECITEM_CopyItem(permArena, &espvk->derCert, michael@0: &ref_certs[i]->derCert); michael@0: } else { michael@0: rv = SECFailure; michael@0: } michael@0: } else { michael@0: pk = sec_pkcs12_get_private_key(permArena, nicknames[i], michael@0: ref_certs[i], wincx); michael@0: if(pk != NULL) { michael@0: rv = sec_pkcs12_append_key_to_bag(keybag, pk); michael@0: SECITEM_CopyItem(permArena, &espvk->derCert, michael@0: &ref_certs[i]->derCert); michael@0: } else { michael@0: rv = SECFailure; michael@0: } michael@0: } michael@0: michael@0: if(rv == SECFailure) { michael@0: problem = PR_TRUE; michael@0: } else { michael@0: nkeys++; michael@0: } michael@0: } michael@0: } else { michael@0: /* handle only keys here ? */ michael@0: problem = PR_TRUE; michael@0: } michael@0: i++; michael@0: } michael@0: michael@0: /* let success fall through */ michael@0: loser: michael@0: if(problem == PR_FALSE) { michael@0: /* if we have certs, we want to append the cert bag to the michael@0: * appropriate area michael@0: */ michael@0: if(ncerts > 0) { michael@0: if(unencryptedCerts != PR_TRUE) { michael@0: rv = sec_pkcs12_append_safe_bag(safe, certbag); michael@0: } else { michael@0: rv = sec_pkcs12_append_unshrouded_bag(external_bag, certbag); michael@0: } michael@0: } else { michael@0: rv = SECSuccess; michael@0: } michael@0: michael@0: /* append key bag, if they are stored in safe contents */ michael@0: if((rv == SECSuccess) && (shroud_keys == PR_FALSE) && (nkeys > 0)) { michael@0: rv = sec_pkcs12_append_safe_bag(safe, keybag); michael@0: } michael@0: } else { michael@0: rv = SECFailure; michael@0: } michael@0: michael@0: /* if baggage not used, NULLify it */ michael@0: if((shroud_keys == PR_TRUE) || (unencryptedCerts == PR_TRUE)) { michael@0: if(((unencryptedCerts == PR_TRUE) && (ncerts == 0)) && michael@0: ((shroud_keys == PR_TRUE) && (nkeys == 0))) michael@0: baggage = NULL; michael@0: } else { michael@0: baggage = NULL; michael@0: } michael@0: michael@0: if((problem == PR_TRUE) || (rv == SECFailure)) { michael@0: PORT_FreeArena(permArena, PR_TRUE); michael@0: rv = SECFailure; michael@0: baggage = NULL; michael@0: safe = NULL; michael@0: } michael@0: michael@0: *rBaggage = baggage; michael@0: *rSafe = safe; michael@0: michael@0: return rv; michael@0: } michael@0: michael@0: /* DER encode the safe contents and return a SECItem. if an error michael@0: * occurs, NULL is returned. michael@0: */ michael@0: static SECItem * michael@0: sec_pkcs12_encode_safe_contents(SEC_PKCS12SafeContents *safe) michael@0: { michael@0: SECItem *dsafe = NULL, *tsafe; michael@0: void *dummy = NULL; michael@0: PLArenaPool *arena; michael@0: michael@0: if(safe == NULL) { michael@0: return NULL; michael@0: } michael@0: michael@0: /* rv = sec_pkcs12_prepare_for_der_code_safe(safe, PR_TRUE); michael@0: if(rv != SECSuccess) { michael@0: PORT_SetError(SEC_ERROR_NO_MEMORY); michael@0: return NULL; michael@0: }*/ michael@0: michael@0: arena = PORT_NewArena(SEC_ASN1_DEFAULT_ARENA_SIZE); michael@0: if(arena == NULL) { michael@0: PORT_SetError(SEC_ERROR_NO_MEMORY); michael@0: return NULL; michael@0: } michael@0: michael@0: tsafe = (SECItem *)PORT_ArenaZAlloc(arena, sizeof(SECItem)); michael@0: if(tsafe != NULL) { michael@0: dummy = SEC_ASN1EncodeItem(arena, tsafe, safe, michael@0: SEC_PKCS12SafeContentsTemplate); michael@0: if(dummy != NULL) { michael@0: dsafe = SECITEM_DupItem(tsafe); michael@0: } else { michael@0: PORT_SetError(SEC_ERROR_NO_MEMORY); michael@0: } michael@0: } else { michael@0: PORT_SetError(SEC_ERROR_NO_MEMORY); michael@0: } michael@0: michael@0: PORT_FreeArena(arena, PR_TRUE); michael@0: michael@0: return dsafe; michael@0: } michael@0: michael@0: /* prepare the authenicated safe for encoding and encode it. michael@0: * baggage is copied to the appropriate area, safe is encoded and michael@0: * encrypted. the version and transport mode are set on the asafe. michael@0: * the whole ball of wax is then der encoded and packaged up into michael@0: * data content info michael@0: * safe -- container of certs and keys, is encrypted. michael@0: * baggage -- container of certs and keys, keys assumed to be encrypted by michael@0: * another method, certs are in the clear michael@0: * algorithm -- algorithm by which to encrypt safe michael@0: * pwitem -- password for encryption michael@0: * wincx - window handle michael@0: * michael@0: * return of NULL is an error condition. michael@0: */ michael@0: static SEC_PKCS7ContentInfo * michael@0: sec_pkcs12_get_auth_safe(SEC_PKCS12SafeContents *safe, michael@0: SEC_PKCS12Baggage *baggage, michael@0: SECOidTag algorithm, michael@0: SECItem *pwitem, michael@0: PKCS12UnicodeConvertFunction unicodeFn, michael@0: void *wincx) michael@0: { michael@0: SECItem *src = NULL, *dest = NULL, *psalt = NULL; michael@0: PLArenaPool *poolp; michael@0: SEC_PKCS12AuthenticatedSafe *asafe; michael@0: SEC_PKCS7ContentInfo *safe_cinfo = NULL; michael@0: SEC_PKCS7ContentInfo *asafe_cinfo = NULL; michael@0: void *dummy; michael@0: SECStatus rv = SECSuccess; michael@0: PRBool swapUnicodeBytes = PR_FALSE; michael@0: michael@0: #ifdef IS_LITTLE_ENDIAN michael@0: swapUnicodeBytes = PR_TRUE; michael@0: #endif michael@0: michael@0: if(((safe != NULL) && (pwitem == NULL)) && (baggage == NULL)) michael@0: return NULL; michael@0: michael@0: poolp = PORT_NewArena(SEC_ASN1_DEFAULT_ARENA_SIZE); michael@0: if(poolp == NULL) { michael@0: PORT_SetError(SEC_ERROR_NO_MEMORY); michael@0: return NULL; michael@0: } michael@0: michael@0: /* prepare authenticated safe for encode */ michael@0: asafe = sec_pkcs12_new_asafe(poolp); michael@0: if(asafe != NULL) { michael@0: michael@0: /* set version */ michael@0: dummy = SEC_ASN1EncodeInteger(asafe->poolp, &asafe->version, michael@0: SEC_PKCS12_PFX_VERSION); michael@0: if(dummy == NULL) { michael@0: PORT_SetError(SEC_ERROR_NO_MEMORY); michael@0: rv = SECFailure; michael@0: goto loser; michael@0: } michael@0: michael@0: /* generate the privacy salt used to create virtual pwd */ michael@0: psalt = sec_pkcs12_generate_salt(); michael@0: if(psalt != NULL) { michael@0: rv = SECITEM_CopyItem(asafe->poolp, &asafe->privacySalt, michael@0: psalt); michael@0: if(rv == SECSuccess) { michael@0: asafe->privacySalt.len *= 8; michael@0: } michael@0: else { michael@0: SECITEM_ZfreeItem(psalt, PR_TRUE); michael@0: PORT_SetError(SEC_ERROR_NO_MEMORY); michael@0: goto loser; michael@0: } michael@0: } michael@0: michael@0: if((psalt == NULL) || (rv == SECFailure)) { michael@0: PORT_SetError(SEC_ERROR_NO_MEMORY); michael@0: rv = SECFailure; michael@0: goto loser; michael@0: } michael@0: michael@0: /* package up safe contents */ michael@0: if(safe != NULL) michael@0: { michael@0: safe_cinfo = SEC_PKCS7CreateEncryptedData(algorithm, NULL, wincx); michael@0: if((safe_cinfo != NULL) && (safe->safe_size > 0)) { michael@0: /* encode the safe and encrypt the contents of the michael@0: * content info michael@0: */ michael@0: src = sec_pkcs12_encode_safe_contents(safe); michael@0: michael@0: if(src != NULL) { michael@0: rv = SEC_PKCS7SetContent(safe_cinfo, (char *)src->data, src->len); michael@0: SECITEM_ZfreeItem(src, PR_TRUE); michael@0: if(rv == SECSuccess) { michael@0: SECItem *vpwd; michael@0: vpwd = sec_pkcs12_create_virtual_password(pwitem, psalt, michael@0: unicodeFn, swapUnicodeBytes); michael@0: if(vpwd != NULL) { michael@0: rv = SEC_PKCS7EncryptContents(NULL, safe_cinfo, michael@0: vpwd, wincx); michael@0: SECITEM_ZfreeItem(vpwd, PR_TRUE); michael@0: } else { michael@0: rv = SECFailure; michael@0: PORT_SetError(SEC_ERROR_NO_MEMORY); michael@0: } michael@0: } else { michael@0: PORT_SetError(SEC_ERROR_NO_MEMORY); michael@0: } michael@0: } else { michael@0: rv = SECFailure; michael@0: } michael@0: } else if(safe->safe_size > 0) { michael@0: PORT_SetError(SEC_ERROR_NO_MEMORY); michael@0: goto loser; michael@0: } else { michael@0: /* case where there is NULL content in the safe contents */ michael@0: rv = SEC_PKCS7SetContent(safe_cinfo, NULL, 0); michael@0: if(rv != SECFailure) { michael@0: PORT_SetError(SEC_ERROR_NO_MEMORY); michael@0: } michael@0: } michael@0: michael@0: if(rv != SECSuccess) { michael@0: SEC_PKCS7DestroyContentInfo(safe_cinfo); michael@0: safe_cinfo = NULL; michael@0: goto loser; michael@0: } michael@0: michael@0: asafe->safe = safe_cinfo; michael@0: /* michael@0: PORT_Memcpy(&asafe->safe, safe_cinfo, sizeof(*safe_cinfo)); michael@0: */ michael@0: } michael@0: michael@0: /* copy the baggage to the authenticated safe baggage if present */ michael@0: if(baggage != NULL) { michael@0: PORT_Memcpy(&asafe->baggage, baggage, sizeof(*baggage)); michael@0: } michael@0: michael@0: /* encode authenticated safe and store it in a Data content info */ michael@0: dest = (SECItem *)PORT_ArenaZAlloc(poolp, sizeof(SECItem)); michael@0: if(dest != NULL) { michael@0: dummy = SEC_ASN1EncodeItem(poolp, dest, asafe, michael@0: SEC_PKCS12AuthenticatedSafeTemplate); michael@0: if(dummy != NULL) { michael@0: asafe_cinfo = SEC_PKCS7CreateData(); michael@0: if(asafe_cinfo != NULL) { michael@0: rv = SEC_PKCS7SetContent(asafe_cinfo, michael@0: (char *)dest->data, michael@0: dest->len); michael@0: if(rv != SECSuccess) { michael@0: PORT_SetError(SEC_ERROR_NO_MEMORY); michael@0: SEC_PKCS7DestroyContentInfo(asafe_cinfo); michael@0: asafe_cinfo = NULL; michael@0: } michael@0: } michael@0: } else { michael@0: PORT_SetError(SEC_ERROR_NO_MEMORY); michael@0: rv = SECFailure; michael@0: } michael@0: } michael@0: } michael@0: michael@0: loser: michael@0: PORT_FreeArena(poolp, PR_TRUE); michael@0: if(safe_cinfo != NULL) { michael@0: SEC_PKCS7DestroyContentInfo(safe_cinfo); michael@0: } michael@0: if(psalt != NULL) { michael@0: SECITEM_ZfreeItem(psalt, PR_TRUE); michael@0: } michael@0: michael@0: if(rv == SECFailure) { michael@0: return NULL; michael@0: } michael@0: michael@0: return asafe_cinfo; michael@0: } michael@0: michael@0: /* generates the PFX and computes the mac on the authenticated safe michael@0: * NULL implies an error michael@0: */ michael@0: static SEC_PKCS12PFXItem * michael@0: sec_pkcs12_get_pfx(SEC_PKCS7ContentInfo *cinfo, michael@0: PRBool do_mac, michael@0: SECItem *pwitem, PKCS12UnicodeConvertFunction unicodeFn) michael@0: { michael@0: SECItem *dest = NULL, *mac = NULL, *salt = NULL, *key = NULL; michael@0: SEC_PKCS12PFXItem *pfx; michael@0: SECStatus rv = SECFailure; michael@0: SGNDigestInfo *di; michael@0: SECItem *vpwd; michael@0: PRBool swapUnicodeBytes = PR_FALSE; michael@0: michael@0: #ifdef IS_LITTLE_ENDIAN michael@0: swapUnicodeBytes = PR_TRUE; michael@0: #endif michael@0: michael@0: if((cinfo == NULL) || ((do_mac == PR_TRUE) && (pwitem == NULL))) { michael@0: return NULL; michael@0: } michael@0: michael@0: /* allocate new pfx structure */ michael@0: pfx = sec_pkcs12_new_pfx(); michael@0: if(pfx == NULL) { michael@0: PORT_SetError(SEC_ERROR_NO_MEMORY); michael@0: return NULL; michael@0: } michael@0: michael@0: PORT_Memcpy(&pfx->authSafe, cinfo, sizeof(*cinfo)); michael@0: if(do_mac == PR_TRUE) { michael@0: michael@0: /* salt for computing mac */ michael@0: salt = sec_pkcs12_generate_salt(); michael@0: if(salt != NULL) { michael@0: rv = SECITEM_CopyItem(pfx->poolp, &pfx->macData.macSalt, salt); michael@0: pfx->macData.macSalt.len *= 8; michael@0: michael@0: vpwd = sec_pkcs12_create_virtual_password(pwitem, salt, michael@0: unicodeFn, swapUnicodeBytes); michael@0: if(vpwd == NULL) { michael@0: rv = SECFailure; michael@0: key = NULL; michael@0: } else { michael@0: key = sec_pkcs12_generate_key_from_password(SEC_OID_SHA1, michael@0: salt, vpwd); michael@0: SECITEM_ZfreeItem(vpwd, PR_TRUE); michael@0: } michael@0: michael@0: if((key != NULL) && (rv == SECSuccess)) { michael@0: dest = SEC_PKCS7GetContent(cinfo); michael@0: if(dest != NULL) { michael@0: michael@0: /* compute mac on data -- for password integrity mode */ michael@0: mac = sec_pkcs12_generate_mac(key, dest, PR_FALSE); michael@0: if(mac != NULL) { michael@0: di = SGN_CreateDigestInfo(SEC_OID_SHA1, michael@0: mac->data, mac->len); michael@0: if(di != NULL) { michael@0: rv = SGN_CopyDigestInfo(pfx->poolp, michael@0: &pfx->macData.safeMac, di); michael@0: SGN_DestroyDigestInfo(di); michael@0: } else { michael@0: PORT_SetError(SEC_ERROR_NO_MEMORY); michael@0: } michael@0: SECITEM_ZfreeItem(mac, PR_TRUE); michael@0: } michael@0: } else { michael@0: rv = SECFailure; michael@0: } michael@0: } else { michael@0: PORT_SetError(SEC_ERROR_NO_MEMORY); michael@0: rv = SECFailure; michael@0: } michael@0: michael@0: if(key != NULL) { michael@0: SECITEM_ZfreeItem(key, PR_TRUE); michael@0: } michael@0: SECITEM_ZfreeItem(salt, PR_TRUE); michael@0: } michael@0: } michael@0: michael@0: if(rv == SECFailure) { michael@0: SEC_PKCS12DestroyPFX(pfx); michael@0: pfx = NULL; michael@0: } michael@0: michael@0: return pfx; michael@0: } michael@0: michael@0: /* der encode the pfx */ michael@0: static SECItem * michael@0: sec_pkcs12_encode_pfx(SEC_PKCS12PFXItem *pfx) michael@0: { michael@0: SECItem *dest; michael@0: void *dummy; michael@0: michael@0: if(pfx == NULL) { michael@0: return NULL; michael@0: } michael@0: michael@0: dest = (SECItem *)PORT_ZAlloc(sizeof(SECItem)); michael@0: if(dest == NULL) { michael@0: PORT_SetError(SEC_ERROR_NO_MEMORY); michael@0: return NULL; michael@0: } michael@0: michael@0: dummy = SEC_ASN1EncodeItem(NULL, dest, pfx, SEC_PKCS12PFXItemTemplate); michael@0: if(dummy == NULL) { michael@0: PORT_SetError(SEC_ERROR_NO_MEMORY); michael@0: SECITEM_ZfreeItem(dest, PR_TRUE); michael@0: dest = NULL; michael@0: } michael@0: michael@0: return dest; michael@0: } michael@0: michael@0: SECItem * michael@0: SEC_PKCS12GetPFX(char **nicknames, michael@0: CERTCertificate **ref_certs, michael@0: PRBool shroud_keys, michael@0: SEC_PKCS5GetPBEPassword pbef, michael@0: void *pbearg, michael@0: PKCS12UnicodeConvertFunction unicodeFn, michael@0: void *wincx) michael@0: { michael@0: SECItem **nicks = NULL; michael@0: SEC_PKCS12PFXItem *pfx = NULL; michael@0: SEC_PKCS12Baggage *baggage = NULL; michael@0: SEC_PKCS12SafeContents *safe = NULL; michael@0: SEC_PKCS7ContentInfo *cinfo = NULL; michael@0: SECStatus rv = SECFailure; michael@0: SECItem *dest = NULL, *pwitem = NULL; michael@0: PRBool problem = PR_FALSE; michael@0: PRBool unencryptedCerts; michael@0: SECOidTag shroud_alg, safe_alg; michael@0: michael@0: /* how should we encrypt certs ? */ michael@0: unencryptedCerts = !SEC_PKCS12IsEncryptionAllowed(); michael@0: if(!unencryptedCerts) { michael@0: safe_alg = SEC_PKCS12GetPreferredEncryptionAlgorithm(); michael@0: if(safe_alg == SEC_OID_UNKNOWN) { michael@0: safe_alg = SEC_PKCS12GetStrongestAllowedAlgorithm(); michael@0: } michael@0: if(safe_alg == SEC_OID_UNKNOWN) { michael@0: unencryptedCerts = PR_TRUE; michael@0: /* for export where no encryption is allowed, we still need michael@0: * to encrypt the NULL contents per the spec. encrypted info michael@0: * is known plaintext, so it shouldn't be a problem. michael@0: */ michael@0: safe_alg = SEC_OID_PKCS12_PBE_WITH_SHA1_AND_40_BIT_RC2_CBC; michael@0: } michael@0: } else { michael@0: /* for export where no encryption is allowed, we still need michael@0: * to encrypt the NULL contents per the spec. encrypted info michael@0: * is known plaintext, so it shouldn't be a problem. michael@0: */ michael@0: safe_alg = SEC_OID_PKCS12_PBE_WITH_SHA1_AND_40_BIT_RC2_CBC; michael@0: } michael@0: michael@0: /* keys are always stored with triple DES */ michael@0: shroud_alg = SEC_OID_PKCS12_PBE_WITH_SHA1_AND_TRIPLE_DES_CBC; michael@0: michael@0: /* check for FIPS, if so, do not encrypt certs */ michael@0: if(PK11_IsFIPS() && !unencryptedCerts) { michael@0: unencryptedCerts = PR_TRUE; michael@0: } michael@0: michael@0: if((nicknames == NULL) || (pbef == NULL) || (ref_certs == NULL)) { michael@0: problem = PR_TRUE; michael@0: goto loser; michael@0: } michael@0: michael@0: michael@0: /* get password */ michael@0: pwitem = (*pbef)(pbearg); michael@0: if(pwitem == NULL) { michael@0: problem = PR_TRUE; michael@0: goto loser; michael@0: } michael@0: nicks = sec_pkcs12_convert_nickname_list(nicknames); michael@0: michael@0: /* get safe and baggage */ michael@0: rv = sec_pkcs12_package_certs_and_keys(nicks, ref_certs, unencryptedCerts, michael@0: &safe, &baggage, shroud_keys, michael@0: shroud_alg, pwitem, unicodeFn, wincx); michael@0: if(rv == SECFailure) { michael@0: problem = PR_TRUE; michael@0: } michael@0: michael@0: if((safe != NULL) && (problem == PR_FALSE)) { michael@0: /* copy thumbprints */ michael@0: rv = sec_pkcs12_propagate_thumbprints(nicks, ref_certs, safe, baggage); michael@0: michael@0: /* package everything up into AuthenticatedSafe */ michael@0: cinfo = sec_pkcs12_get_auth_safe(safe, baggage, michael@0: safe_alg, pwitem, unicodeFn, wincx); michael@0: michael@0: sec_pkcs12_destroy_cert_content_infos(safe, baggage); michael@0: michael@0: /* get the pfx and mac it */ michael@0: if(cinfo != NULL) { michael@0: pfx = sec_pkcs12_get_pfx(cinfo, PR_TRUE, pwitem, unicodeFn); michael@0: if(pfx != NULL) { michael@0: dest = sec_pkcs12_encode_pfx(pfx); michael@0: SEC_PKCS12DestroyPFX(pfx); michael@0: } michael@0: SEC_PKCS7DestroyContentInfo(cinfo); michael@0: } michael@0: michael@0: if(safe != NULL) { michael@0: PORT_FreeArena(safe->poolp, PR_TRUE); michael@0: } michael@0: } else { michael@0: if(safe != NULL) { michael@0: PORT_FreeArena(safe->poolp, PR_TRUE); michael@0: } michael@0: } michael@0: michael@0: loser: michael@0: if(nicks != NULL) { michael@0: sec_pkcs12_destroy_nickname_list(nicks); michael@0: } michael@0: michael@0: if(ref_certs != NULL) { michael@0: sec_pkcs12_destroy_certificate_list(ref_certs); michael@0: } michael@0: michael@0: if(pwitem != NULL) { michael@0: SECITEM_ZfreeItem(pwitem, PR_TRUE); michael@0: } michael@0: michael@0: if(problem == PR_TRUE) { michael@0: dest = NULL; michael@0: } michael@0: michael@0: return dest; michael@0: }