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

mercurial