security/nss/lib/pkcs12/p12exp.c

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

michael@0 1 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 2 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 4
michael@0 5 #include "plarena.h"
michael@0 6 #include "secitem.h"
michael@0 7 #include "secoid.h"
michael@0 8 #include "seccomon.h"
michael@0 9 #include "secport.h"
michael@0 10 #include "cert.h"
michael@0 11 #include "pkcs12.h"
michael@0 12 #include "p12local.h"
michael@0 13 #include "secpkcs7.h"
michael@0 14 #include "secasn1.h"
michael@0 15 #include "secerr.h"
michael@0 16 #include "p12plcy.h"
michael@0 17
michael@0 18 /* release the memory taken up by the list of nicknames */
michael@0 19 static void
michael@0 20 sec_pkcs12_destroy_nickname_list(SECItem **nicknames)
michael@0 21 {
michael@0 22 int i = 0;
michael@0 23
michael@0 24 if(nicknames == NULL) {
michael@0 25 return;
michael@0 26 }
michael@0 27
michael@0 28 while(nicknames[i] != NULL) {
michael@0 29 SECITEM_FreeItem(nicknames[i], PR_FALSE);
michael@0 30 i++;
michael@0 31 }
michael@0 32
michael@0 33 PORT_Free(nicknames);
michael@0 34 }
michael@0 35
michael@0 36 /* release the memory taken up by the list of certificates */
michael@0 37 static void
michael@0 38 sec_pkcs12_destroy_certificate_list(CERTCertificate **ref_certs)
michael@0 39 {
michael@0 40 int i = 0;
michael@0 41
michael@0 42 if(ref_certs == NULL) {
michael@0 43 return;
michael@0 44 }
michael@0 45
michael@0 46 while(ref_certs[i] != NULL) {
michael@0 47 CERT_DestroyCertificate(ref_certs[i]);
michael@0 48 i++;
michael@0 49 }
michael@0 50 }
michael@0 51
michael@0 52 static void
michael@0 53 sec_pkcs12_destroy_cinfos_for_cert_bags(SEC_PKCS12CertAndCRLBag *certBag)
michael@0 54 {
michael@0 55 int j = 0;
michael@0 56 j = 0;
michael@0 57 while(certBag->certAndCRLs[j] != NULL) {
michael@0 58 SECOidTag certType = SECOID_FindOIDTag(&certBag->certAndCRLs[j]->BagID);
michael@0 59 if(certType == SEC_OID_PKCS12_X509_CERT_CRL_BAG) {
michael@0 60 SEC_PKCS12X509CertCRL *x509;
michael@0 61 x509 = certBag->certAndCRLs[j]->value.x509;
michael@0 62 SEC_PKCS7DestroyContentInfo(&x509->certOrCRL);
michael@0 63 }
michael@0 64 j++;
michael@0 65 }
michael@0 66 }
michael@0 67
michael@0 68 /* destroy all content infos since they were not allocated in common
michael@0 69 * pool
michael@0 70 */
michael@0 71 static void
michael@0 72 sec_pkcs12_destroy_cert_content_infos(SEC_PKCS12SafeContents *safe,
michael@0 73 SEC_PKCS12Baggage *baggage)
michael@0 74 {
michael@0 75 int i, j;
michael@0 76
michael@0 77 if((safe != NULL) && (safe->contents != NULL)) {
michael@0 78 i = 0;
michael@0 79 while(safe->contents[i] != NULL) {
michael@0 80 SECOidTag bagType = SECOID_FindOIDTag(&safe->contents[i]->safeBagType);
michael@0 81 if(bagType == SEC_OID_PKCS12_CERT_AND_CRL_BAG_ID) {
michael@0 82 SEC_PKCS12CertAndCRLBag *certBag;
michael@0 83 certBag = safe->contents[i]->safeContent.certAndCRLBag;
michael@0 84 sec_pkcs12_destroy_cinfos_for_cert_bags(certBag);
michael@0 85 }
michael@0 86 i++;
michael@0 87 }
michael@0 88 }
michael@0 89
michael@0 90 if((baggage != NULL) && (baggage->bags != NULL)) {
michael@0 91 i = 0;
michael@0 92 while(baggage->bags[i] != NULL) {
michael@0 93 if(baggage->bags[i]->unencSecrets != NULL) {
michael@0 94 j = 0;
michael@0 95 while(baggage->bags[i]->unencSecrets[j] != NULL) {
michael@0 96 SECOidTag bagType;
michael@0 97 bagType = SECOID_FindOIDTag(&baggage->bags[i]->unencSecrets[j]->safeBagType);
michael@0 98 if(bagType == SEC_OID_PKCS12_CERT_AND_CRL_BAG_ID) {
michael@0 99 SEC_PKCS12CertAndCRLBag *certBag;
michael@0 100 certBag = baggage->bags[i]->unencSecrets[j]->safeContent.certAndCRLBag;
michael@0 101 sec_pkcs12_destroy_cinfos_for_cert_bags(certBag);
michael@0 102 }
michael@0 103 j++;
michael@0 104 }
michael@0 105 }
michael@0 106 i++;
michael@0 107 }
michael@0 108 }
michael@0 109 }
michael@0 110
michael@0 111 /* convert the nickname list from a NULL termincated Char list
michael@0 112 * to a NULL terminated SECItem list
michael@0 113 */
michael@0 114 static SECItem **
michael@0 115 sec_pkcs12_convert_nickname_list(char **nicknames)
michael@0 116 {
michael@0 117 SECItem **nicks;
michael@0 118 int i, j;
michael@0 119 PRBool error = PR_FALSE;
michael@0 120
michael@0 121 if(nicknames == NULL) {
michael@0 122 return NULL;
michael@0 123 }
michael@0 124
michael@0 125 i = j = 0;
michael@0 126 while(nicknames[i] != NULL) {
michael@0 127 i++;
michael@0 128 }
michael@0 129
michael@0 130 /* allocate the space and copy the data */
michael@0 131 nicks = (SECItem **)PORT_ZAlloc(sizeof(SECItem *) * (i + 1));
michael@0 132 if(nicks != NULL) {
michael@0 133 for(j = 0; ((j < i) && (error == PR_FALSE)); j++) {
michael@0 134 nicks[j] = (SECItem *)PORT_ZAlloc(sizeof(SECItem));
michael@0 135 if(nicks[j] != NULL) {
michael@0 136 nicks[j]->data =
michael@0 137 (unsigned char *)PORT_ZAlloc(PORT_Strlen(nicknames[j])+1);
michael@0 138 if(nicks[j]->data != NULL) {
michael@0 139 nicks[j]->len = PORT_Strlen(nicknames[j]);
michael@0 140 PORT_Memcpy(nicks[j]->data, nicknames[j], nicks[j]->len);
michael@0 141 nicks[j]->data[nicks[j]->len] = 0;
michael@0 142 } else {
michael@0 143 error = PR_TRUE;
michael@0 144 }
michael@0 145 } else {
michael@0 146 error = PR_TRUE;
michael@0 147 }
michael@0 148 }
michael@0 149 }
michael@0 150
michael@0 151 if(error == PR_TRUE) {
michael@0 152 for(i = 0; i < j; i++) {
michael@0 153 SECITEM_FreeItem(nicks[i], PR_TRUE);
michael@0 154 }
michael@0 155 PORT_Free(nicks);
michael@0 156 nicks = NULL;
michael@0 157 }
michael@0 158
michael@0 159 return nicks;
michael@0 160 }
michael@0 161
michael@0 162 /* package the certificate add_cert into PKCS12 structures,
michael@0 163 * retrieve the certificate chain for the cert and return
michael@0 164 * the packaged contents.
michael@0 165 * poolp -- common memory pool;
michael@0 166 * add_cert -- certificate to package up
michael@0 167 * nickname for the certificate
michael@0 168 * a return of NULL indicates an error
michael@0 169 */
michael@0 170 static SEC_PKCS12CertAndCRL *
michael@0 171 sec_pkcs12_get_cert(PLArenaPool *poolp,
michael@0 172 CERTCertificate *add_cert,
michael@0 173 SECItem *nickname)
michael@0 174 {
michael@0 175 SEC_PKCS12CertAndCRL *cert;
michael@0 176 SEC_PKCS7ContentInfo *cinfo;
michael@0 177 SGNDigestInfo *t_di;
michael@0 178 void *mark;
michael@0 179 SECStatus rv;
michael@0 180
michael@0 181 if((poolp == NULL) || (add_cert == NULL) || (nickname == NULL)) {
michael@0 182 return NULL;
michael@0 183 }
michael@0 184 mark = PORT_ArenaMark(poolp);
michael@0 185
michael@0 186 cert = sec_pkcs12_new_cert_crl(poolp, SEC_OID_PKCS12_X509_CERT_CRL_BAG);
michael@0 187 if(cert != NULL) {
michael@0 188
michael@0 189 /* copy the nickname */
michael@0 190 rv = SECITEM_CopyItem(poolp, &cert->nickname, nickname);
michael@0 191 if(rv != SECSuccess) {
michael@0 192 PORT_SetError(SEC_ERROR_NO_MEMORY);
michael@0 193 cert = NULL;
michael@0 194 } else {
michael@0 195
michael@0 196 /* package the certificate and cert chain into a NULL signer
michael@0 197 * PKCS 7 SignedData content Info and prepare it for encoding
michael@0 198 * since we cannot use DER_ANY_TEMPLATE
michael@0 199 */
michael@0 200 cinfo = SEC_PKCS7CreateCertsOnly(add_cert, PR_TRUE, NULL);
michael@0 201 rv = SEC_PKCS7PrepareForEncode(cinfo, NULL, NULL, NULL);
michael@0 202
michael@0 203 /* thumbprint the certificate */
michael@0 204 if((cinfo != NULL) && (rv == SECSuccess))
michael@0 205 {
michael@0 206 PORT_Memcpy(&cert->value.x509->certOrCRL, cinfo, sizeof(*cinfo));
michael@0 207 t_di = sec_pkcs12_compute_thumbprint(&add_cert->derCert);
michael@0 208 if(t_di != NULL)
michael@0 209 {
michael@0 210 /* test */
michael@0 211 rv = SGN_CopyDigestInfo(poolp, &cert->value.x509->thumbprint,
michael@0 212 t_di);
michael@0 213 if(rv != SECSuccess) {
michael@0 214 cert = NULL;
michael@0 215 PORT_SetError(SEC_ERROR_NO_MEMORY);
michael@0 216 }
michael@0 217 SGN_DestroyDigestInfo(t_di);
michael@0 218 }
michael@0 219 else
michael@0 220 cert = NULL;
michael@0 221 }
michael@0 222 }
michael@0 223 }
michael@0 224
michael@0 225 if (cert == NULL) {
michael@0 226 PORT_ArenaRelease(poolp, mark);
michael@0 227 } else {
michael@0 228 PORT_ArenaUnmark(poolp, mark);
michael@0 229 }
michael@0 230
michael@0 231 return cert;
michael@0 232 }
michael@0 233
michael@0 234 /* package the private key associated with the certificate and
michael@0 235 * return the appropriate PKCS 12 structure
michael@0 236 * poolp common memory pool
michael@0 237 * nickname key nickname
michael@0 238 * cert -- cert to look up
michael@0 239 * wincx -- window handle
michael@0 240 * an error is indicated by a return of NULL
michael@0 241 */
michael@0 242 static SEC_PKCS12PrivateKey *
michael@0 243 sec_pkcs12_get_private_key(PLArenaPool *poolp,
michael@0 244 SECItem *nickname,
michael@0 245 CERTCertificate *cert,
michael@0 246 void *wincx)
michael@0 247 {
michael@0 248 SECKEYPrivateKeyInfo *pki;
michael@0 249 SEC_PKCS12PrivateKey *pk;
michael@0 250 SECStatus rv;
michael@0 251 void *mark;
michael@0 252
michael@0 253 if((poolp == NULL) || (nickname == NULL)) {
michael@0 254 return NULL;
michael@0 255 }
michael@0 256
michael@0 257 mark = PORT_ArenaMark(poolp);
michael@0 258
michael@0 259 /* retrieve key from the data base */
michael@0 260 pki = PK11_ExportPrivateKeyInfo(nickname, cert, wincx);
michael@0 261 if(pki == NULL) {
michael@0 262 PORT_ArenaRelease(poolp, mark);
michael@0 263 PORT_SetError(SEC_ERROR_PKCS12_UNABLE_TO_EXPORT_KEY);
michael@0 264 return NULL;
michael@0 265 }
michael@0 266
michael@0 267 pk = (SEC_PKCS12PrivateKey *)PORT_ArenaZAlloc(poolp,
michael@0 268 sizeof(SEC_PKCS12PrivateKey));
michael@0 269 if(pk != NULL) {
michael@0 270 rv = sec_pkcs12_init_pvk_data(poolp, &pk->pvkData);
michael@0 271
michael@0 272 if(rv == SECSuccess) {
michael@0 273 /* copy the key into poolp memory space */
michael@0 274 rv = SECKEY_CopyPrivateKeyInfo(poolp, &pk->pkcs8data, pki);
michael@0 275 if(rv == SECSuccess) {
michael@0 276 rv = SECITEM_CopyItem(poolp, &pk->pvkData.nickname, nickname);
michael@0 277 }
michael@0 278 }
michael@0 279
michael@0 280 if(rv != SECSuccess) {
michael@0 281 PORT_SetError(SEC_ERROR_NO_MEMORY);
michael@0 282 pk = NULL;
michael@0 283 }
michael@0 284 } else {
michael@0 285 PORT_SetError(SEC_ERROR_NO_MEMORY);
michael@0 286 }
michael@0 287
michael@0 288 /* destroy private key, zeroing out data */
michael@0 289 SECKEY_DestroyPrivateKeyInfo(pki, PR_TRUE);
michael@0 290 if (pk == NULL) {
michael@0 291 PORT_ArenaRelease(poolp, mark);
michael@0 292 } else {
michael@0 293 PORT_ArenaUnmark(poolp, mark);
michael@0 294 }
michael@0 295
michael@0 296 return pk;
michael@0 297 }
michael@0 298
michael@0 299 /* get a shrouded key item associated with a certificate
michael@0 300 * return the appropriate PKCS 12 structure
michael@0 301 * poolp common memory pool
michael@0 302 * nickname key nickname
michael@0 303 * cert -- cert to look up
michael@0 304 * wincx -- window handle
michael@0 305 * an error is indicated by a return of NULL
michael@0 306 */
michael@0 307 static SEC_PKCS12ESPVKItem *
michael@0 308 sec_pkcs12_get_shrouded_key(PLArenaPool *poolp,
michael@0 309 SECItem *nickname,
michael@0 310 CERTCertificate *cert,
michael@0 311 SECOidTag algorithm,
michael@0 312 SECItem *pwitem,
michael@0 313 PKCS12UnicodeConvertFunction unicodeFn,
michael@0 314 void *wincx)
michael@0 315 {
michael@0 316 SECKEYEncryptedPrivateKeyInfo *epki;
michael@0 317 SEC_PKCS12ESPVKItem *pk;
michael@0 318 void *mark;
michael@0 319 SECStatus rv;
michael@0 320 PK11SlotInfo *slot = NULL;
michael@0 321 PRBool swapUnicodeBytes = PR_FALSE;
michael@0 322
michael@0 323 #ifdef IS_LITTLE_ENDIAN
michael@0 324 swapUnicodeBytes = PR_TRUE;
michael@0 325 #endif
michael@0 326
michael@0 327 if((poolp == NULL) || (nickname == NULL))
michael@0 328 return NULL;
michael@0 329
michael@0 330 mark = PORT_ArenaMark(poolp);
michael@0 331
michael@0 332 /* use internal key slot */
michael@0 333 slot = PK11_GetInternalKeySlot();
michael@0 334
michael@0 335 /* retrieve encrypted prviate key */
michael@0 336 epki = PK11_ExportEncryptedPrivateKeyInfo(slot, algorithm, pwitem,
michael@0 337 nickname, cert, 1, 0, NULL);
michael@0 338 PK11_FreeSlot(slot);
michael@0 339 if(epki == NULL) {
michael@0 340 PORT_SetError(SEC_ERROR_PKCS12_UNABLE_TO_EXPORT_KEY);
michael@0 341 PORT_ArenaRelease(poolp, mark);
michael@0 342 return NULL;
michael@0 343 }
michael@0 344
michael@0 345 /* create a private key and store the data into the poolp memory space */
michael@0 346 pk = sec_pkcs12_create_espvk(poolp, SEC_OID_PKCS12_PKCS8_KEY_SHROUDING);
michael@0 347 if(pk != NULL) {
michael@0 348 rv = sec_pkcs12_init_pvk_data(poolp, &pk->espvkData);
michael@0 349 rv = SECITEM_CopyItem(poolp, &pk->espvkData.nickname, nickname);
michael@0 350 pk->espvkCipherText.pkcs8KeyShroud =
michael@0 351 (SECKEYEncryptedPrivateKeyInfo *)PORT_ArenaZAlloc(poolp,
michael@0 352 sizeof(SECKEYEncryptedPrivateKeyInfo));
michael@0 353 if((pk->espvkCipherText.pkcs8KeyShroud != NULL) && (rv == SECSuccess)) {
michael@0 354 rv = SECKEY_CopyEncryptedPrivateKeyInfo(poolp,
michael@0 355 pk->espvkCipherText.pkcs8KeyShroud, epki);
michael@0 356 if(rv == SECSuccess) {
michael@0 357 rv = (*unicodeFn)(poolp, &pk->espvkData.uniNickName, nickname,
michael@0 358 PR_TRUE, swapUnicodeBytes);
michael@0 359 }
michael@0 360 }
michael@0 361
michael@0 362 if(rv != SECSuccess) {
michael@0 363 PORT_SetError(SEC_ERROR_NO_MEMORY);
michael@0 364 pk = NULL;
michael@0 365 }
michael@0 366 }
michael@0 367
michael@0 368 SECKEY_DestroyEncryptedPrivateKeyInfo(epki, PR_TRUE);
michael@0 369 if(pk == NULL) {
michael@0 370 PORT_ArenaRelease(poolp, mark);
michael@0 371 } else {
michael@0 372 PORT_ArenaUnmark(poolp, mark);
michael@0 373 }
michael@0 374
michael@0 375 return pk;
michael@0 376 }
michael@0 377
michael@0 378 /* add a thumbprint to a private key associated certs list
michael@0 379 * pvk is the area where the list is stored
michael@0 380 * thumb is the thumbprint to copy
michael@0 381 * a return of SECFailure indicates an error
michael@0 382 */
michael@0 383 static SECStatus
michael@0 384 sec_pkcs12_add_thumbprint(SEC_PKCS12PVKSupportingData *pvk,
michael@0 385 SGNDigestInfo *thumb)
michael@0 386 {
michael@0 387 SGNDigestInfo **thumb_list = NULL;
michael@0 388 int nthumbs, size;
michael@0 389 void *mark, *dummy;
michael@0 390 SECStatus rv = SECFailure;
michael@0 391
michael@0 392 if((pvk == NULL) || (thumb == NULL)) {
michael@0 393 return SECFailure;
michael@0 394 }
michael@0 395
michael@0 396 mark = PORT_ArenaMark(pvk->poolp);
michael@0 397
michael@0 398 thumb_list = pvk->assocCerts;
michael@0 399 nthumbs = pvk->nThumbs;
michael@0 400
michael@0 401 /* allocate list space needed -- either growing or allocating
michael@0 402 * list must be NULL terminated
michael@0 403 */
michael@0 404 size = sizeof(SGNDigestInfo *);
michael@0 405 dummy = PORT_ArenaGrow(pvk->poolp, thumb_list, (size * (nthumbs + 1)),
michael@0 406 (size * (nthumbs + 2)));
michael@0 407 thumb_list = dummy;
michael@0 408 if(dummy != NULL) {
michael@0 409 thumb_list[nthumbs] = (SGNDigestInfo *)PORT_ArenaZAlloc(pvk->poolp,
michael@0 410 sizeof(SGNDigestInfo));
michael@0 411 if(thumb_list[nthumbs] != NULL) {
michael@0 412 SGN_CopyDigestInfo(pvk->poolp, thumb_list[nthumbs], thumb);
michael@0 413 nthumbs += 1;
michael@0 414 thumb_list[nthumbs] = 0;
michael@0 415 } else {
michael@0 416 dummy = NULL;
michael@0 417 }
michael@0 418 }
michael@0 419
michael@0 420 if(dummy == NULL) {
michael@0 421 PORT_ArenaRelease(pvk->poolp, mark);
michael@0 422 return SECFailure;
michael@0 423 }
michael@0 424
michael@0 425 pvk->assocCerts = thumb_list;
michael@0 426 pvk->nThumbs = nthumbs;
michael@0 427
michael@0 428 PORT_ArenaUnmark(pvk->poolp, mark);
michael@0 429 return SECSuccess;
michael@0 430 }
michael@0 431
michael@0 432 /* search the list of shrouded keys in the baggage for the desired
michael@0 433 * name. return a pointer to the item. a return of NULL indicates
michael@0 434 * that no match was present or that an error occurred.
michael@0 435 */
michael@0 436 static SEC_PKCS12ESPVKItem *
michael@0 437 sec_pkcs12_get_espvk_by_name(SEC_PKCS12Baggage *luggage,
michael@0 438 SECItem *name)
michael@0 439 {
michael@0 440 PRBool found = PR_FALSE;
michael@0 441 SEC_PKCS12ESPVKItem *espvk = NULL;
michael@0 442 int i, j;
michael@0 443 SECComparison rv = SECEqual;
michael@0 444 SECItem *t_name;
michael@0 445 SEC_PKCS12BaggageItem *bag;
michael@0 446
michael@0 447 if((luggage == NULL) || (name == NULL)) {
michael@0 448 return NULL;
michael@0 449 }
michael@0 450
michael@0 451 i = 0;
michael@0 452 while((found == PR_FALSE) && (i < luggage->luggage_size)) {
michael@0 453 j = 0;
michael@0 454 bag = luggage->bags[i];
michael@0 455 while((found == PR_FALSE) && (j < bag->nEspvks)) {
michael@0 456 espvk = bag->espvks[j];
michael@0 457 if(espvk->poolp == NULL) {
michael@0 458 espvk->poolp = luggage->poolp;
michael@0 459 }
michael@0 460 t_name = SECITEM_DupItem(&espvk->espvkData.nickname);
michael@0 461 if(t_name != NULL) {
michael@0 462 rv = SECITEM_CompareItem(name, t_name);
michael@0 463 if(rv == SECEqual) {
michael@0 464 found = PR_TRUE;
michael@0 465 }
michael@0 466 SECITEM_FreeItem(t_name, PR_TRUE);
michael@0 467 } else {
michael@0 468 PORT_SetError(SEC_ERROR_NO_MEMORY);
michael@0 469 return NULL;
michael@0 470 }
michael@0 471 j++;
michael@0 472 }
michael@0 473 i++;
michael@0 474 }
michael@0 475
michael@0 476 if(found != PR_TRUE) {
michael@0 477 PORT_SetError(SEC_ERROR_PKCS12_UNABLE_TO_LOCATE_OBJECT_BY_NAME);
michael@0 478 return NULL;
michael@0 479 }
michael@0 480
michael@0 481 return espvk;
michael@0 482 }
michael@0 483
michael@0 484 /* locates a certificate and copies the thumbprint to the
michael@0 485 * appropriate private key
michael@0 486 */
michael@0 487 static SECStatus
michael@0 488 sec_pkcs12_propagate_thumbprints(SECItem **nicknames,
michael@0 489 CERTCertificate **ref_certs,
michael@0 490 SEC_PKCS12SafeContents *safe,
michael@0 491 SEC_PKCS12Baggage *baggage)
michael@0 492 {
michael@0 493 SEC_PKCS12CertAndCRL *cert;
michael@0 494 SEC_PKCS12PrivateKey *key;
michael@0 495 SEC_PKCS12ESPVKItem *espvk;
michael@0 496 int i;
michael@0 497 PRBool error = PR_FALSE;
michael@0 498 SECStatus rv = SECFailure;
michael@0 499
michael@0 500 if((nicknames == NULL) || (safe == NULL)) {
michael@0 501 return SECFailure;
michael@0 502 }
michael@0 503
michael@0 504 i = 0;
michael@0 505 while((nicknames[i] != NULL) && (error == PR_FALSE)) {
michael@0 506 /* process all certs */
michael@0 507 cert = (SEC_PKCS12CertAndCRL *)sec_pkcs12_find_object(safe, baggage,
michael@0 508 SEC_OID_PKCS12_CERT_AND_CRL_BAG_ID,
michael@0 509 nicknames[i], NULL);
michael@0 510 if(cert != NULL) {
michael@0 511 /* locate key and copy thumbprint */
michael@0 512 key = (SEC_PKCS12PrivateKey *)sec_pkcs12_find_object(safe, baggage,
michael@0 513 SEC_OID_PKCS12_KEY_BAG_ID,
michael@0 514 nicknames[i], NULL);
michael@0 515 if(key != NULL) {
michael@0 516 key->pvkData.poolp = key->poolp;
michael@0 517 rv = sec_pkcs12_add_thumbprint(&key->pvkData,
michael@0 518 &cert->value.x509->thumbprint);
michael@0 519 if(rv == SECFailure)
michael@0 520 error = PR_TRUE; /* XXX Set error? */
michael@0 521 }
michael@0 522
michael@0 523 /* look in the baggage as well...*/
michael@0 524 if((baggage != NULL) && (error == PR_FALSE)) {
michael@0 525 espvk = sec_pkcs12_get_espvk_by_name(baggage, nicknames[i]);
michael@0 526 if(espvk != NULL) {
michael@0 527 espvk->espvkData.poolp = espvk->poolp;
michael@0 528 rv = sec_pkcs12_add_thumbprint(&espvk->espvkData,
michael@0 529 &cert->value.x509->thumbprint);
michael@0 530 if(rv == SECFailure)
michael@0 531 error = PR_TRUE; /* XXX Set error? */
michael@0 532 }
michael@0 533 }
michael@0 534 }
michael@0 535 i++;
michael@0 536 }
michael@0 537
michael@0 538 if(error == PR_TRUE) {
michael@0 539 return SECFailure;
michael@0 540 }
michael@0 541
michael@0 542 return SECSuccess;
michael@0 543 }
michael@0 544
michael@0 545 /* append a safe bag to the end of the safe contents list */
michael@0 546 SECStatus
michael@0 547 sec_pkcs12_append_safe_bag(SEC_PKCS12SafeContents *safe,
michael@0 548 SEC_PKCS12SafeBag *bag)
michael@0 549 {
michael@0 550 int size;
michael@0 551 void *mark = NULL, *dummy = NULL;
michael@0 552
michael@0 553 if((bag == NULL) || (safe == NULL))
michael@0 554 return SECFailure;
michael@0 555
michael@0 556 mark = PORT_ArenaMark(safe->poolp);
michael@0 557
michael@0 558 size = (safe->safe_size * sizeof(SEC_PKCS12SafeBag *));
michael@0 559
michael@0 560 if(safe->safe_size > 0) {
michael@0 561 dummy = (SEC_PKCS12SafeBag **)PORT_ArenaGrow(safe->poolp,
michael@0 562 safe->contents,
michael@0 563 size,
michael@0 564 (size + sizeof(SEC_PKCS12SafeBag *)));
michael@0 565 safe->contents = dummy;
michael@0 566 } else {
michael@0 567 safe->contents = (SEC_PKCS12SafeBag **)PORT_ArenaZAlloc(safe->poolp,
michael@0 568 (2 * sizeof(SEC_PKCS12SafeBag *)));
michael@0 569 dummy = safe->contents;
michael@0 570 }
michael@0 571
michael@0 572 if(dummy == NULL) {
michael@0 573 PORT_SetError(SEC_ERROR_NO_MEMORY);
michael@0 574 goto loser;
michael@0 575 }
michael@0 576
michael@0 577 safe->contents[safe->safe_size] = bag;
michael@0 578 safe->safe_size++;
michael@0 579 safe->contents[safe->safe_size] = NULL;
michael@0 580
michael@0 581 PORT_ArenaUnmark(safe->poolp, mark);
michael@0 582 return SECSuccess;
michael@0 583
michael@0 584 loser:
michael@0 585 PORT_ArenaRelease(safe->poolp, mark);
michael@0 586 return SECFailure;
michael@0 587 }
michael@0 588
michael@0 589 /* append a certificate onto the end of a cert bag */
michael@0 590 static SECStatus
michael@0 591 sec_pkcs12_append_cert_to_bag(PLArenaPool *arena,
michael@0 592 SEC_PKCS12SafeBag *safebag,
michael@0 593 CERTCertificate *cert,
michael@0 594 SECItem *nickname)
michael@0 595 {
michael@0 596 int size;
michael@0 597 void *dummy = NULL, *mark = NULL;
michael@0 598 SEC_PKCS12CertAndCRL *p12cert;
michael@0 599 SEC_PKCS12CertAndCRLBag *bag;
michael@0 600
michael@0 601 if((arena == NULL) || (safebag == NULL) ||
michael@0 602 (cert == NULL) || (nickname == NULL)) {
michael@0 603 return SECFailure;
michael@0 604 }
michael@0 605
michael@0 606 bag = safebag->safeContent.certAndCRLBag;
michael@0 607 if(bag == NULL) {
michael@0 608 return SECFailure;
michael@0 609 }
michael@0 610
michael@0 611 mark = PORT_ArenaMark(arena);
michael@0 612
michael@0 613 p12cert = sec_pkcs12_get_cert(arena, cert, nickname);
michael@0 614 if(p12cert == NULL) {
michael@0 615 PORT_ArenaRelease(bag->poolp, mark);
michael@0 616 return SECFailure;
michael@0 617 }
michael@0 618
michael@0 619 size = bag->bag_size * sizeof(SEC_PKCS12CertAndCRL *);
michael@0 620 if(bag->bag_size > 0) {
michael@0 621 dummy = (SEC_PKCS12CertAndCRL **)PORT_ArenaGrow(bag->poolp,
michael@0 622 bag->certAndCRLs, size, size + sizeof(SEC_PKCS12CertAndCRL *));
michael@0 623 bag->certAndCRLs = dummy;
michael@0 624 } else {
michael@0 625 bag->certAndCRLs = (SEC_PKCS12CertAndCRL **)PORT_ArenaZAlloc(bag->poolp,
michael@0 626 (2 * sizeof(SEC_PKCS12CertAndCRL *)));
michael@0 627 dummy = bag->certAndCRLs;
michael@0 628 }
michael@0 629
michael@0 630 if(dummy == NULL) {
michael@0 631 PORT_SetError(SEC_ERROR_NO_MEMORY);
michael@0 632 goto loser;
michael@0 633 }
michael@0 634
michael@0 635 bag->certAndCRLs[bag->bag_size] = p12cert;
michael@0 636 bag->bag_size++;
michael@0 637 bag->certAndCRLs[bag->bag_size] = NULL;
michael@0 638
michael@0 639 PORT_ArenaUnmark(bag->poolp, mark);
michael@0 640 return SECSuccess;
michael@0 641
michael@0 642 loser:
michael@0 643 PORT_ArenaRelease(bag->poolp, mark);
michael@0 644 return SECFailure;
michael@0 645 }
michael@0 646
michael@0 647 /* append a key onto the end of a list of keys in a key bag */
michael@0 648 SECStatus
michael@0 649 sec_pkcs12_append_key_to_bag(SEC_PKCS12SafeBag *safebag,
michael@0 650 SEC_PKCS12PrivateKey *pk)
michael@0 651 {
michael@0 652 void *mark, *dummy;
michael@0 653 SEC_PKCS12PrivateKeyBag *bag;
michael@0 654 int size;
michael@0 655
michael@0 656 if((safebag == NULL) || (pk == NULL))
michael@0 657 return SECFailure;
michael@0 658
michael@0 659 bag = safebag->safeContent.keyBag;
michael@0 660 if(bag == NULL) {
michael@0 661 return SECFailure;
michael@0 662 }
michael@0 663
michael@0 664 mark = PORT_ArenaMark(bag->poolp);
michael@0 665
michael@0 666 size = (bag->bag_size * sizeof(SEC_PKCS12PrivateKey *));
michael@0 667
michael@0 668 if(bag->bag_size > 0) {
michael@0 669 dummy = (SEC_PKCS12PrivateKey **)PORT_ArenaGrow(bag->poolp,
michael@0 670 bag->privateKeys,
michael@0 671 size,
michael@0 672 size + sizeof(SEC_PKCS12PrivateKey *));
michael@0 673 bag->privateKeys = dummy;
michael@0 674 } else {
michael@0 675 bag->privateKeys = (SEC_PKCS12PrivateKey **)PORT_ArenaZAlloc(bag->poolp,
michael@0 676 (2 * sizeof(SEC_PKCS12PrivateKey *)));
michael@0 677 dummy = bag->privateKeys;
michael@0 678 }
michael@0 679
michael@0 680 if(dummy == NULL) {
michael@0 681 PORT_SetError(SEC_ERROR_NO_MEMORY);
michael@0 682 goto loser;
michael@0 683 }
michael@0 684
michael@0 685 bag->privateKeys[bag->bag_size] = pk;
michael@0 686 bag->bag_size++;
michael@0 687 bag->privateKeys[bag->bag_size] = NULL;
michael@0 688
michael@0 689 PORT_ArenaUnmark(bag->poolp, mark);
michael@0 690 return SECSuccess;
michael@0 691
michael@0 692 loser:
michael@0 693 /* XXX Free memory? */
michael@0 694 PORT_ArenaRelease(bag->poolp, mark);
michael@0 695 return SECFailure;
michael@0 696 }
michael@0 697
michael@0 698 /* append a safe bag to the baggage area */
michael@0 699 static SECStatus
michael@0 700 sec_pkcs12_append_unshrouded_bag(SEC_PKCS12BaggageItem *bag,
michael@0 701 SEC_PKCS12SafeBag *u_bag)
michael@0 702 {
michael@0 703 int size;
michael@0 704 void *mark = NULL, *dummy = NULL;
michael@0 705
michael@0 706 if((bag == NULL) || (u_bag == NULL))
michael@0 707 return SECFailure;
michael@0 708
michael@0 709 mark = PORT_ArenaMark(bag->poolp);
michael@0 710
michael@0 711 /* dump things into the first bag */
michael@0 712 size = (bag->nSecrets + 1) * sizeof(SEC_PKCS12SafeBag *);
michael@0 713 dummy = PORT_ArenaGrow(bag->poolp,
michael@0 714 bag->unencSecrets, size,
michael@0 715 size + sizeof(SEC_PKCS12SafeBag *));
michael@0 716 bag->unencSecrets = dummy;
michael@0 717 if(dummy == NULL) {
michael@0 718 PORT_SetError(SEC_ERROR_NO_MEMORY);
michael@0 719 goto loser;
michael@0 720 }
michael@0 721
michael@0 722 bag->unencSecrets[bag->nSecrets] = u_bag;
michael@0 723 bag->nSecrets++;
michael@0 724 bag->unencSecrets[bag->nSecrets] = NULL;
michael@0 725
michael@0 726 PORT_ArenaUnmark(bag->poolp, mark);
michael@0 727 return SECSuccess;
michael@0 728
michael@0 729 loser:
michael@0 730 PORT_ArenaRelease(bag->poolp, mark);
michael@0 731 return SECFailure;
michael@0 732 }
michael@0 733
michael@0 734 /* gather up all certificates and keys and package them up
michael@0 735 * in the safe, baggage, or both.
michael@0 736 * nicknames is the list of nicknames and corresponding certs in ref_certs
michael@0 737 * ref_certs a null terminated list of certificates
michael@0 738 * rSafe, rBaggage -- return areas for safe and baggage
michael@0 739 * shroud_keys -- store keys externally
michael@0 740 * pwitem -- password for computing integrity mac and encrypting contents
michael@0 741 * wincx -- window handle
michael@0 742 *
michael@0 743 * if a failure occurs, an error is set and SECFailure returned.
michael@0 744 */
michael@0 745 static SECStatus
michael@0 746 sec_pkcs12_package_certs_and_keys(SECItem **nicknames,
michael@0 747 CERTCertificate **ref_certs,
michael@0 748 PRBool unencryptedCerts,
michael@0 749 SEC_PKCS12SafeContents **rSafe,
michael@0 750 SEC_PKCS12Baggage **rBaggage,
michael@0 751 PRBool shroud_keys,
michael@0 752 SECOidTag shroud_alg,
michael@0 753 SECItem *pwitem,
michael@0 754 PKCS12UnicodeConvertFunction unicodeFn,
michael@0 755 void *wincx)
michael@0 756 {
michael@0 757 PLArenaPool *permArena;
michael@0 758 SEC_PKCS12SafeContents *safe = NULL;
michael@0 759 SEC_PKCS12Baggage *baggage = NULL;
michael@0 760
michael@0 761 SECStatus rv = SECFailure;
michael@0 762 PRBool problem = PR_FALSE;
michael@0 763
michael@0 764 SEC_PKCS12ESPVKItem *espvk = NULL;
michael@0 765 SEC_PKCS12PrivateKey *pk = NULL;
michael@0 766 CERTCertificate *add_cert = NULL;
michael@0 767 SEC_PKCS12SafeBag *certbag = NULL, *keybag = NULL;
michael@0 768 SEC_PKCS12BaggageItem *external_bag = NULL;
michael@0 769 int ncerts = 0, nkeys = 0;
michael@0 770 int i;
michael@0 771
michael@0 772 if((nicknames == NULL) || (rSafe == NULL) || (rBaggage == NULL)) {
michael@0 773 return SECFailure;
michael@0 774 }
michael@0 775
michael@0 776 *rBaggage = baggage;
michael@0 777 *rSafe = safe;
michael@0 778
michael@0 779 permArena = PORT_NewArena(SEC_ASN1_DEFAULT_ARENA_SIZE);
michael@0 780 if(permArena == NULL) {
michael@0 781 PORT_SetError(SEC_ERROR_NO_MEMORY);
michael@0 782 return SECFailure;
michael@0 783 }
michael@0 784
michael@0 785 /* allocate structures */
michael@0 786 safe = sec_pkcs12_create_safe_contents(permArena);
michael@0 787 if(safe == NULL) {
michael@0 788 PORT_SetError(SEC_ERROR_NO_MEMORY);
michael@0 789 rv = SECFailure;
michael@0 790 goto loser;
michael@0 791 }
michael@0 792
michael@0 793 certbag = sec_pkcs12_create_safe_bag(permArena,
michael@0 794 SEC_OID_PKCS12_CERT_AND_CRL_BAG_ID);
michael@0 795 if(certbag == NULL) {
michael@0 796 rv = SECFailure;
michael@0 797 goto loser;
michael@0 798 }
michael@0 799
michael@0 800 if(shroud_keys != PR_TRUE) {
michael@0 801 keybag = sec_pkcs12_create_safe_bag(permArena,
michael@0 802 SEC_OID_PKCS12_KEY_BAG_ID);
michael@0 803 if(keybag == NULL) {
michael@0 804 rv = SECFailure;
michael@0 805 goto loser;
michael@0 806 }
michael@0 807 }
michael@0 808
michael@0 809 if((shroud_keys == PR_TRUE) || (unencryptedCerts == PR_TRUE)) {
michael@0 810 baggage = sec_pkcs12_create_baggage(permArena);
michael@0 811 if(baggage == NULL) {
michael@0 812 rv = SECFailure;
michael@0 813 goto loser;
michael@0 814 }
michael@0 815 external_bag = sec_pkcs12_create_external_bag(baggage);
michael@0 816 }
michael@0 817
michael@0 818 /* package keys and certs */
michael@0 819 i = 0;
michael@0 820 while((nicknames[i] != NULL) && (problem == PR_FALSE)) {
michael@0 821 if(ref_certs[i] != NULL) {
michael@0 822 /* append cert to bag o certs */
michael@0 823 rv = sec_pkcs12_append_cert_to_bag(permArena, certbag,
michael@0 824 ref_certs[i],
michael@0 825 nicknames[i]);
michael@0 826 if(rv == SECFailure) {
michael@0 827 problem = PR_FALSE;
michael@0 828 } else {
michael@0 829 ncerts++;
michael@0 830 }
michael@0 831
michael@0 832 if(rv == SECSuccess) {
michael@0 833 /* package up them keys */
michael@0 834 if(shroud_keys == PR_TRUE) {
michael@0 835 espvk = sec_pkcs12_get_shrouded_key(permArena,
michael@0 836 nicknames[i],
michael@0 837 ref_certs[i],
michael@0 838 shroud_alg,
michael@0 839 pwitem, unicodeFn,
michael@0 840 wincx);
michael@0 841 if(espvk != NULL) {
michael@0 842 rv = sec_pkcs12_append_shrouded_key(external_bag, espvk);
michael@0 843 SECITEM_CopyItem(permArena, &espvk->derCert,
michael@0 844 &ref_certs[i]->derCert);
michael@0 845 } else {
michael@0 846 rv = SECFailure;
michael@0 847 }
michael@0 848 } else {
michael@0 849 pk = sec_pkcs12_get_private_key(permArena, nicknames[i],
michael@0 850 ref_certs[i], wincx);
michael@0 851 if(pk != NULL) {
michael@0 852 rv = sec_pkcs12_append_key_to_bag(keybag, pk);
michael@0 853 SECITEM_CopyItem(permArena, &espvk->derCert,
michael@0 854 &ref_certs[i]->derCert);
michael@0 855 } else {
michael@0 856 rv = SECFailure;
michael@0 857 }
michael@0 858 }
michael@0 859
michael@0 860 if(rv == SECFailure) {
michael@0 861 problem = PR_TRUE;
michael@0 862 } else {
michael@0 863 nkeys++;
michael@0 864 }
michael@0 865 }
michael@0 866 } else {
michael@0 867 /* handle only keys here ? */
michael@0 868 problem = PR_TRUE;
michael@0 869 }
michael@0 870 i++;
michael@0 871 }
michael@0 872
michael@0 873 /* let success fall through */
michael@0 874 loser:
michael@0 875 if(problem == PR_FALSE) {
michael@0 876 /* if we have certs, we want to append the cert bag to the
michael@0 877 * appropriate area
michael@0 878 */
michael@0 879 if(ncerts > 0) {
michael@0 880 if(unencryptedCerts != PR_TRUE) {
michael@0 881 rv = sec_pkcs12_append_safe_bag(safe, certbag);
michael@0 882 } else {
michael@0 883 rv = sec_pkcs12_append_unshrouded_bag(external_bag, certbag);
michael@0 884 }
michael@0 885 } else {
michael@0 886 rv = SECSuccess;
michael@0 887 }
michael@0 888
michael@0 889 /* append key bag, if they are stored in safe contents */
michael@0 890 if((rv == SECSuccess) && (shroud_keys == PR_FALSE) && (nkeys > 0)) {
michael@0 891 rv = sec_pkcs12_append_safe_bag(safe, keybag);
michael@0 892 }
michael@0 893 } else {
michael@0 894 rv = SECFailure;
michael@0 895 }
michael@0 896
michael@0 897 /* if baggage not used, NULLify it */
michael@0 898 if((shroud_keys == PR_TRUE) || (unencryptedCerts == PR_TRUE)) {
michael@0 899 if(((unencryptedCerts == PR_TRUE) && (ncerts == 0)) &&
michael@0 900 ((shroud_keys == PR_TRUE) && (nkeys == 0)))
michael@0 901 baggage = NULL;
michael@0 902 } else {
michael@0 903 baggage = NULL;
michael@0 904 }
michael@0 905
michael@0 906 if((problem == PR_TRUE) || (rv == SECFailure)) {
michael@0 907 PORT_FreeArena(permArena, PR_TRUE);
michael@0 908 rv = SECFailure;
michael@0 909 baggage = NULL;
michael@0 910 safe = NULL;
michael@0 911 }
michael@0 912
michael@0 913 *rBaggage = baggage;
michael@0 914 *rSafe = safe;
michael@0 915
michael@0 916 return rv;
michael@0 917 }
michael@0 918
michael@0 919 /* DER encode the safe contents and return a SECItem. if an error
michael@0 920 * occurs, NULL is returned.
michael@0 921 */
michael@0 922 static SECItem *
michael@0 923 sec_pkcs12_encode_safe_contents(SEC_PKCS12SafeContents *safe)
michael@0 924 {
michael@0 925 SECItem *dsafe = NULL, *tsafe;
michael@0 926 void *dummy = NULL;
michael@0 927 PLArenaPool *arena;
michael@0 928
michael@0 929 if(safe == NULL) {
michael@0 930 return NULL;
michael@0 931 }
michael@0 932
michael@0 933 /* rv = sec_pkcs12_prepare_for_der_code_safe(safe, PR_TRUE);
michael@0 934 if(rv != SECSuccess) {
michael@0 935 PORT_SetError(SEC_ERROR_NO_MEMORY);
michael@0 936 return NULL;
michael@0 937 }*/
michael@0 938
michael@0 939 arena = PORT_NewArena(SEC_ASN1_DEFAULT_ARENA_SIZE);
michael@0 940 if(arena == NULL) {
michael@0 941 PORT_SetError(SEC_ERROR_NO_MEMORY);
michael@0 942 return NULL;
michael@0 943 }
michael@0 944
michael@0 945 tsafe = (SECItem *)PORT_ArenaZAlloc(arena, sizeof(SECItem));
michael@0 946 if(tsafe != NULL) {
michael@0 947 dummy = SEC_ASN1EncodeItem(arena, tsafe, safe,
michael@0 948 SEC_PKCS12SafeContentsTemplate);
michael@0 949 if(dummy != NULL) {
michael@0 950 dsafe = SECITEM_DupItem(tsafe);
michael@0 951 } else {
michael@0 952 PORT_SetError(SEC_ERROR_NO_MEMORY);
michael@0 953 }
michael@0 954 } else {
michael@0 955 PORT_SetError(SEC_ERROR_NO_MEMORY);
michael@0 956 }
michael@0 957
michael@0 958 PORT_FreeArena(arena, PR_TRUE);
michael@0 959
michael@0 960 return dsafe;
michael@0 961 }
michael@0 962
michael@0 963 /* prepare the authenicated safe for encoding and encode it.
michael@0 964 * baggage is copied to the appropriate area, safe is encoded and
michael@0 965 * encrypted. the version and transport mode are set on the asafe.
michael@0 966 * the whole ball of wax is then der encoded and packaged up into
michael@0 967 * data content info
michael@0 968 * safe -- container of certs and keys, is encrypted.
michael@0 969 * baggage -- container of certs and keys, keys assumed to be encrypted by
michael@0 970 * another method, certs are in the clear
michael@0 971 * algorithm -- algorithm by which to encrypt safe
michael@0 972 * pwitem -- password for encryption
michael@0 973 * wincx - window handle
michael@0 974 *
michael@0 975 * return of NULL is an error condition.
michael@0 976 */
michael@0 977 static SEC_PKCS7ContentInfo *
michael@0 978 sec_pkcs12_get_auth_safe(SEC_PKCS12SafeContents *safe,
michael@0 979 SEC_PKCS12Baggage *baggage,
michael@0 980 SECOidTag algorithm,
michael@0 981 SECItem *pwitem,
michael@0 982 PKCS12UnicodeConvertFunction unicodeFn,
michael@0 983 void *wincx)
michael@0 984 {
michael@0 985 SECItem *src = NULL, *dest = NULL, *psalt = NULL;
michael@0 986 PLArenaPool *poolp;
michael@0 987 SEC_PKCS12AuthenticatedSafe *asafe;
michael@0 988 SEC_PKCS7ContentInfo *safe_cinfo = NULL;
michael@0 989 SEC_PKCS7ContentInfo *asafe_cinfo = NULL;
michael@0 990 void *dummy;
michael@0 991 SECStatus rv = SECSuccess;
michael@0 992 PRBool swapUnicodeBytes = PR_FALSE;
michael@0 993
michael@0 994 #ifdef IS_LITTLE_ENDIAN
michael@0 995 swapUnicodeBytes = PR_TRUE;
michael@0 996 #endif
michael@0 997
michael@0 998 if(((safe != NULL) && (pwitem == NULL)) && (baggage == NULL))
michael@0 999 return NULL;
michael@0 1000
michael@0 1001 poolp = PORT_NewArena(SEC_ASN1_DEFAULT_ARENA_SIZE);
michael@0 1002 if(poolp == NULL) {
michael@0 1003 PORT_SetError(SEC_ERROR_NO_MEMORY);
michael@0 1004 return NULL;
michael@0 1005 }
michael@0 1006
michael@0 1007 /* prepare authenticated safe for encode */
michael@0 1008 asafe = sec_pkcs12_new_asafe(poolp);
michael@0 1009 if(asafe != NULL) {
michael@0 1010
michael@0 1011 /* set version */
michael@0 1012 dummy = SEC_ASN1EncodeInteger(asafe->poolp, &asafe->version,
michael@0 1013 SEC_PKCS12_PFX_VERSION);
michael@0 1014 if(dummy == NULL) {
michael@0 1015 PORT_SetError(SEC_ERROR_NO_MEMORY);
michael@0 1016 rv = SECFailure;
michael@0 1017 goto loser;
michael@0 1018 }
michael@0 1019
michael@0 1020 /* generate the privacy salt used to create virtual pwd */
michael@0 1021 psalt = sec_pkcs12_generate_salt();
michael@0 1022 if(psalt != NULL) {
michael@0 1023 rv = SECITEM_CopyItem(asafe->poolp, &asafe->privacySalt,
michael@0 1024 psalt);
michael@0 1025 if(rv == SECSuccess) {
michael@0 1026 asafe->privacySalt.len *= 8;
michael@0 1027 }
michael@0 1028 else {
michael@0 1029 SECITEM_ZfreeItem(psalt, PR_TRUE);
michael@0 1030 PORT_SetError(SEC_ERROR_NO_MEMORY);
michael@0 1031 goto loser;
michael@0 1032 }
michael@0 1033 }
michael@0 1034
michael@0 1035 if((psalt == NULL) || (rv == SECFailure)) {
michael@0 1036 PORT_SetError(SEC_ERROR_NO_MEMORY);
michael@0 1037 rv = SECFailure;
michael@0 1038 goto loser;
michael@0 1039 }
michael@0 1040
michael@0 1041 /* package up safe contents */
michael@0 1042 if(safe != NULL)
michael@0 1043 {
michael@0 1044 safe_cinfo = SEC_PKCS7CreateEncryptedData(algorithm, NULL, wincx);
michael@0 1045 if((safe_cinfo != NULL) && (safe->safe_size > 0)) {
michael@0 1046 /* encode the safe and encrypt the contents of the
michael@0 1047 * content info
michael@0 1048 */
michael@0 1049 src = sec_pkcs12_encode_safe_contents(safe);
michael@0 1050
michael@0 1051 if(src != NULL) {
michael@0 1052 rv = SEC_PKCS7SetContent(safe_cinfo, (char *)src->data, src->len);
michael@0 1053 SECITEM_ZfreeItem(src, PR_TRUE);
michael@0 1054 if(rv == SECSuccess) {
michael@0 1055 SECItem *vpwd;
michael@0 1056 vpwd = sec_pkcs12_create_virtual_password(pwitem, psalt,
michael@0 1057 unicodeFn, swapUnicodeBytes);
michael@0 1058 if(vpwd != NULL) {
michael@0 1059 rv = SEC_PKCS7EncryptContents(NULL, safe_cinfo,
michael@0 1060 vpwd, wincx);
michael@0 1061 SECITEM_ZfreeItem(vpwd, PR_TRUE);
michael@0 1062 } else {
michael@0 1063 rv = SECFailure;
michael@0 1064 PORT_SetError(SEC_ERROR_NO_MEMORY);
michael@0 1065 }
michael@0 1066 } else {
michael@0 1067 PORT_SetError(SEC_ERROR_NO_MEMORY);
michael@0 1068 }
michael@0 1069 } else {
michael@0 1070 rv = SECFailure;
michael@0 1071 }
michael@0 1072 } else if(safe->safe_size > 0) {
michael@0 1073 PORT_SetError(SEC_ERROR_NO_MEMORY);
michael@0 1074 goto loser;
michael@0 1075 } else {
michael@0 1076 /* case where there is NULL content in the safe contents */
michael@0 1077 rv = SEC_PKCS7SetContent(safe_cinfo, NULL, 0);
michael@0 1078 if(rv != SECFailure) {
michael@0 1079 PORT_SetError(SEC_ERROR_NO_MEMORY);
michael@0 1080 }
michael@0 1081 }
michael@0 1082
michael@0 1083 if(rv != SECSuccess) {
michael@0 1084 SEC_PKCS7DestroyContentInfo(safe_cinfo);
michael@0 1085 safe_cinfo = NULL;
michael@0 1086 goto loser;
michael@0 1087 }
michael@0 1088
michael@0 1089 asafe->safe = safe_cinfo;
michael@0 1090 /*
michael@0 1091 PORT_Memcpy(&asafe->safe, safe_cinfo, sizeof(*safe_cinfo));
michael@0 1092 */
michael@0 1093 }
michael@0 1094
michael@0 1095 /* copy the baggage to the authenticated safe baggage if present */
michael@0 1096 if(baggage != NULL) {
michael@0 1097 PORT_Memcpy(&asafe->baggage, baggage, sizeof(*baggage));
michael@0 1098 }
michael@0 1099
michael@0 1100 /* encode authenticated safe and store it in a Data content info */
michael@0 1101 dest = (SECItem *)PORT_ArenaZAlloc(poolp, sizeof(SECItem));
michael@0 1102 if(dest != NULL) {
michael@0 1103 dummy = SEC_ASN1EncodeItem(poolp, dest, asafe,
michael@0 1104 SEC_PKCS12AuthenticatedSafeTemplate);
michael@0 1105 if(dummy != NULL) {
michael@0 1106 asafe_cinfo = SEC_PKCS7CreateData();
michael@0 1107 if(asafe_cinfo != NULL) {
michael@0 1108 rv = SEC_PKCS7SetContent(asafe_cinfo,
michael@0 1109 (char *)dest->data,
michael@0 1110 dest->len);
michael@0 1111 if(rv != SECSuccess) {
michael@0 1112 PORT_SetError(SEC_ERROR_NO_MEMORY);
michael@0 1113 SEC_PKCS7DestroyContentInfo(asafe_cinfo);
michael@0 1114 asafe_cinfo = NULL;
michael@0 1115 }
michael@0 1116 }
michael@0 1117 } else {
michael@0 1118 PORT_SetError(SEC_ERROR_NO_MEMORY);
michael@0 1119 rv = SECFailure;
michael@0 1120 }
michael@0 1121 }
michael@0 1122 }
michael@0 1123
michael@0 1124 loser:
michael@0 1125 PORT_FreeArena(poolp, PR_TRUE);
michael@0 1126 if(safe_cinfo != NULL) {
michael@0 1127 SEC_PKCS7DestroyContentInfo(safe_cinfo);
michael@0 1128 }
michael@0 1129 if(psalt != NULL) {
michael@0 1130 SECITEM_ZfreeItem(psalt, PR_TRUE);
michael@0 1131 }
michael@0 1132
michael@0 1133 if(rv == SECFailure) {
michael@0 1134 return NULL;
michael@0 1135 }
michael@0 1136
michael@0 1137 return asafe_cinfo;
michael@0 1138 }
michael@0 1139
michael@0 1140 /* generates the PFX and computes the mac on the authenticated safe
michael@0 1141 * NULL implies an error
michael@0 1142 */
michael@0 1143 static SEC_PKCS12PFXItem *
michael@0 1144 sec_pkcs12_get_pfx(SEC_PKCS7ContentInfo *cinfo,
michael@0 1145 PRBool do_mac,
michael@0 1146 SECItem *pwitem, PKCS12UnicodeConvertFunction unicodeFn)
michael@0 1147 {
michael@0 1148 SECItem *dest = NULL, *mac = NULL, *salt = NULL, *key = NULL;
michael@0 1149 SEC_PKCS12PFXItem *pfx;
michael@0 1150 SECStatus rv = SECFailure;
michael@0 1151 SGNDigestInfo *di;
michael@0 1152 SECItem *vpwd;
michael@0 1153 PRBool swapUnicodeBytes = PR_FALSE;
michael@0 1154
michael@0 1155 #ifdef IS_LITTLE_ENDIAN
michael@0 1156 swapUnicodeBytes = PR_TRUE;
michael@0 1157 #endif
michael@0 1158
michael@0 1159 if((cinfo == NULL) || ((do_mac == PR_TRUE) && (pwitem == NULL))) {
michael@0 1160 return NULL;
michael@0 1161 }
michael@0 1162
michael@0 1163 /* allocate new pfx structure */
michael@0 1164 pfx = sec_pkcs12_new_pfx();
michael@0 1165 if(pfx == NULL) {
michael@0 1166 PORT_SetError(SEC_ERROR_NO_MEMORY);
michael@0 1167 return NULL;
michael@0 1168 }
michael@0 1169
michael@0 1170 PORT_Memcpy(&pfx->authSafe, cinfo, sizeof(*cinfo));
michael@0 1171 if(do_mac == PR_TRUE) {
michael@0 1172
michael@0 1173 /* salt for computing mac */
michael@0 1174 salt = sec_pkcs12_generate_salt();
michael@0 1175 if(salt != NULL) {
michael@0 1176 rv = SECITEM_CopyItem(pfx->poolp, &pfx->macData.macSalt, salt);
michael@0 1177 pfx->macData.macSalt.len *= 8;
michael@0 1178
michael@0 1179 vpwd = sec_pkcs12_create_virtual_password(pwitem, salt,
michael@0 1180 unicodeFn, swapUnicodeBytes);
michael@0 1181 if(vpwd == NULL) {
michael@0 1182 rv = SECFailure;
michael@0 1183 key = NULL;
michael@0 1184 } else {
michael@0 1185 key = sec_pkcs12_generate_key_from_password(SEC_OID_SHA1,
michael@0 1186 salt, vpwd);
michael@0 1187 SECITEM_ZfreeItem(vpwd, PR_TRUE);
michael@0 1188 }
michael@0 1189
michael@0 1190 if((key != NULL) && (rv == SECSuccess)) {
michael@0 1191 dest = SEC_PKCS7GetContent(cinfo);
michael@0 1192 if(dest != NULL) {
michael@0 1193
michael@0 1194 /* compute mac on data -- for password integrity mode */
michael@0 1195 mac = sec_pkcs12_generate_mac(key, dest, PR_FALSE);
michael@0 1196 if(mac != NULL) {
michael@0 1197 di = SGN_CreateDigestInfo(SEC_OID_SHA1,
michael@0 1198 mac->data, mac->len);
michael@0 1199 if(di != NULL) {
michael@0 1200 rv = SGN_CopyDigestInfo(pfx->poolp,
michael@0 1201 &pfx->macData.safeMac, di);
michael@0 1202 SGN_DestroyDigestInfo(di);
michael@0 1203 } else {
michael@0 1204 PORT_SetError(SEC_ERROR_NO_MEMORY);
michael@0 1205 }
michael@0 1206 SECITEM_ZfreeItem(mac, PR_TRUE);
michael@0 1207 }
michael@0 1208 } else {
michael@0 1209 rv = SECFailure;
michael@0 1210 }
michael@0 1211 } else {
michael@0 1212 PORT_SetError(SEC_ERROR_NO_MEMORY);
michael@0 1213 rv = SECFailure;
michael@0 1214 }
michael@0 1215
michael@0 1216 if(key != NULL) {
michael@0 1217 SECITEM_ZfreeItem(key, PR_TRUE);
michael@0 1218 }
michael@0 1219 SECITEM_ZfreeItem(salt, PR_TRUE);
michael@0 1220 }
michael@0 1221 }
michael@0 1222
michael@0 1223 if(rv == SECFailure) {
michael@0 1224 SEC_PKCS12DestroyPFX(pfx);
michael@0 1225 pfx = NULL;
michael@0 1226 }
michael@0 1227
michael@0 1228 return pfx;
michael@0 1229 }
michael@0 1230
michael@0 1231 /* der encode the pfx */
michael@0 1232 static SECItem *
michael@0 1233 sec_pkcs12_encode_pfx(SEC_PKCS12PFXItem *pfx)
michael@0 1234 {
michael@0 1235 SECItem *dest;
michael@0 1236 void *dummy;
michael@0 1237
michael@0 1238 if(pfx == NULL) {
michael@0 1239 return NULL;
michael@0 1240 }
michael@0 1241
michael@0 1242 dest = (SECItem *)PORT_ZAlloc(sizeof(SECItem));
michael@0 1243 if(dest == NULL) {
michael@0 1244 PORT_SetError(SEC_ERROR_NO_MEMORY);
michael@0 1245 return NULL;
michael@0 1246 }
michael@0 1247
michael@0 1248 dummy = SEC_ASN1EncodeItem(NULL, dest, pfx, SEC_PKCS12PFXItemTemplate);
michael@0 1249 if(dummy == NULL) {
michael@0 1250 PORT_SetError(SEC_ERROR_NO_MEMORY);
michael@0 1251 SECITEM_ZfreeItem(dest, PR_TRUE);
michael@0 1252 dest = NULL;
michael@0 1253 }
michael@0 1254
michael@0 1255 return dest;
michael@0 1256 }
michael@0 1257
michael@0 1258 SECItem *
michael@0 1259 SEC_PKCS12GetPFX(char **nicknames,
michael@0 1260 CERTCertificate **ref_certs,
michael@0 1261 PRBool shroud_keys,
michael@0 1262 SEC_PKCS5GetPBEPassword pbef,
michael@0 1263 void *pbearg,
michael@0 1264 PKCS12UnicodeConvertFunction unicodeFn,
michael@0 1265 void *wincx)
michael@0 1266 {
michael@0 1267 SECItem **nicks = NULL;
michael@0 1268 SEC_PKCS12PFXItem *pfx = NULL;
michael@0 1269 SEC_PKCS12Baggage *baggage = NULL;
michael@0 1270 SEC_PKCS12SafeContents *safe = NULL;
michael@0 1271 SEC_PKCS7ContentInfo *cinfo = NULL;
michael@0 1272 SECStatus rv = SECFailure;
michael@0 1273 SECItem *dest = NULL, *pwitem = NULL;
michael@0 1274 PRBool problem = PR_FALSE;
michael@0 1275 PRBool unencryptedCerts;
michael@0 1276 SECOidTag shroud_alg, safe_alg;
michael@0 1277
michael@0 1278 /* how should we encrypt certs ? */
michael@0 1279 unencryptedCerts = !SEC_PKCS12IsEncryptionAllowed();
michael@0 1280 if(!unencryptedCerts) {
michael@0 1281 safe_alg = SEC_PKCS12GetPreferredEncryptionAlgorithm();
michael@0 1282 if(safe_alg == SEC_OID_UNKNOWN) {
michael@0 1283 safe_alg = SEC_PKCS12GetStrongestAllowedAlgorithm();
michael@0 1284 }
michael@0 1285 if(safe_alg == SEC_OID_UNKNOWN) {
michael@0 1286 unencryptedCerts = PR_TRUE;
michael@0 1287 /* for export where no encryption is allowed, we still need
michael@0 1288 * to encrypt the NULL contents per the spec. encrypted info
michael@0 1289 * is known plaintext, so it shouldn't be a problem.
michael@0 1290 */
michael@0 1291 safe_alg = SEC_OID_PKCS12_PBE_WITH_SHA1_AND_40_BIT_RC2_CBC;
michael@0 1292 }
michael@0 1293 } else {
michael@0 1294 /* for export where no encryption is allowed, we still need
michael@0 1295 * to encrypt the NULL contents per the spec. encrypted info
michael@0 1296 * is known plaintext, so it shouldn't be a problem.
michael@0 1297 */
michael@0 1298 safe_alg = SEC_OID_PKCS12_PBE_WITH_SHA1_AND_40_BIT_RC2_CBC;
michael@0 1299 }
michael@0 1300
michael@0 1301 /* keys are always stored with triple DES */
michael@0 1302 shroud_alg = SEC_OID_PKCS12_PBE_WITH_SHA1_AND_TRIPLE_DES_CBC;
michael@0 1303
michael@0 1304 /* check for FIPS, if so, do not encrypt certs */
michael@0 1305 if(PK11_IsFIPS() && !unencryptedCerts) {
michael@0 1306 unencryptedCerts = PR_TRUE;
michael@0 1307 }
michael@0 1308
michael@0 1309 if((nicknames == NULL) || (pbef == NULL) || (ref_certs == NULL)) {
michael@0 1310 problem = PR_TRUE;
michael@0 1311 goto loser;
michael@0 1312 }
michael@0 1313
michael@0 1314
michael@0 1315 /* get password */
michael@0 1316 pwitem = (*pbef)(pbearg);
michael@0 1317 if(pwitem == NULL) {
michael@0 1318 problem = PR_TRUE;
michael@0 1319 goto loser;
michael@0 1320 }
michael@0 1321 nicks = sec_pkcs12_convert_nickname_list(nicknames);
michael@0 1322
michael@0 1323 /* get safe and baggage */
michael@0 1324 rv = sec_pkcs12_package_certs_and_keys(nicks, ref_certs, unencryptedCerts,
michael@0 1325 &safe, &baggage, shroud_keys,
michael@0 1326 shroud_alg, pwitem, unicodeFn, wincx);
michael@0 1327 if(rv == SECFailure) {
michael@0 1328 problem = PR_TRUE;
michael@0 1329 }
michael@0 1330
michael@0 1331 if((safe != NULL) && (problem == PR_FALSE)) {
michael@0 1332 /* copy thumbprints */
michael@0 1333 rv = sec_pkcs12_propagate_thumbprints(nicks, ref_certs, safe, baggage);
michael@0 1334
michael@0 1335 /* package everything up into AuthenticatedSafe */
michael@0 1336 cinfo = sec_pkcs12_get_auth_safe(safe, baggage,
michael@0 1337 safe_alg, pwitem, unicodeFn, wincx);
michael@0 1338
michael@0 1339 sec_pkcs12_destroy_cert_content_infos(safe, baggage);
michael@0 1340
michael@0 1341 /* get the pfx and mac it */
michael@0 1342 if(cinfo != NULL) {
michael@0 1343 pfx = sec_pkcs12_get_pfx(cinfo, PR_TRUE, pwitem, unicodeFn);
michael@0 1344 if(pfx != NULL) {
michael@0 1345 dest = sec_pkcs12_encode_pfx(pfx);
michael@0 1346 SEC_PKCS12DestroyPFX(pfx);
michael@0 1347 }
michael@0 1348 SEC_PKCS7DestroyContentInfo(cinfo);
michael@0 1349 }
michael@0 1350
michael@0 1351 if(safe != NULL) {
michael@0 1352 PORT_FreeArena(safe->poolp, PR_TRUE);
michael@0 1353 }
michael@0 1354 } else {
michael@0 1355 if(safe != NULL) {
michael@0 1356 PORT_FreeArena(safe->poolp, PR_TRUE);
michael@0 1357 }
michael@0 1358 }
michael@0 1359
michael@0 1360 loser:
michael@0 1361 if(nicks != NULL) {
michael@0 1362 sec_pkcs12_destroy_nickname_list(nicks);
michael@0 1363 }
michael@0 1364
michael@0 1365 if(ref_certs != NULL) {
michael@0 1366 sec_pkcs12_destroy_certificate_list(ref_certs);
michael@0 1367 }
michael@0 1368
michael@0 1369 if(pwitem != NULL) {
michael@0 1370 SECITEM_ZfreeItem(pwitem, PR_TRUE);
michael@0 1371 }
michael@0 1372
michael@0 1373 if(problem == PR_TRUE) {
michael@0 1374 dest = NULL;
michael@0 1375 }
michael@0 1376
michael@0 1377 return dest;
michael@0 1378 }

mercurial