| OLD | NEW |
| (Empty) |
| 1 /* This Source Code Form is subject to the terms of the Mozilla Public | |
| 2 * License, v. 2.0. If a copy of the MPL was not distributed with this | |
| 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ | |
| 4 | |
| 5 /* | |
| 6 * PKCS7 decoding, verification. | |
| 7 */ | |
| 8 | |
| 9 #include "p7local.h" | |
| 10 | |
| 11 #include "cert.h" | |
| 12 /* XXX do not want to have to include */ | |
| 13 #include "certdb.h" /* certdb.h -- the trust stuff needed by */ | |
| 14 /* the add certificate code needs to get */ | |
| 15 /* rewritten/abstracted and then this */ | |
| 16 /* include should be removed! */ | |
| 17 /*#include "cdbhdl.h" */ | |
| 18 #include "cryptohi.h" | |
| 19 #include "key.h" | |
| 20 #include "secasn1.h" | |
| 21 #include "secitem.h" | |
| 22 #include "secoid.h" | |
| 23 #include "pk11func.h" | |
| 24 #include "prtime.h" | |
| 25 #include "secerr.h" | |
| 26 #include "sechash.h" /* for HASH_GetHashObject() */ | |
| 27 #include "secder.h" | |
| 28 #include "secpkcs5.h" | |
| 29 | |
| 30 struct sec_pkcs7_decoder_worker { | |
| 31 int depth; | |
| 32 int digcnt; | |
| 33 void **digcxs; | |
| 34 const SECHashObject **digobjs; | |
| 35 sec_PKCS7CipherObject *decryptobj; | |
| 36 PRBool saw_contents; | |
| 37 }; | |
| 38 | |
| 39 struct SEC_PKCS7DecoderContextStr { | |
| 40 SEC_ASN1DecoderContext *dcx; | |
| 41 SEC_PKCS7ContentInfo *cinfo; | |
| 42 SEC_PKCS7DecoderContentCallback cb; | |
| 43 void *cb_arg; | |
| 44 SECKEYGetPasswordKey pwfn; | |
| 45 void *pwfn_arg; | |
| 46 struct sec_pkcs7_decoder_worker worker; | |
| 47 PLArenaPool *tmp_poolp; | |
| 48 int error; | |
| 49 SEC_PKCS7GetDecryptKeyCallback dkcb; | |
| 50 void *dkcb_arg; | |
| 51 SEC_PKCS7DecryptionAllowedCallback decrypt_allowed_cb; | |
| 52 }; | |
| 53 | |
| 54 /* | |
| 55 * Handle one worker, decrypting and digesting the data as necessary. | |
| 56 * | |
| 57 * XXX If/when we support nested contents, this probably needs to be | |
| 58 * revised somewhat to get passed the content-info (which unfortunately | |
| 59 * can be two different types depending on whether it is encrypted or not) | |
| 60 * corresponding to the given worker. | |
| 61 */ | |
| 62 static void | |
| 63 sec_pkcs7_decoder_work_data (SEC_PKCS7DecoderContext *p7dcx, | |
| 64 struct sec_pkcs7_decoder_worker *worker, | |
| 65 const unsigned char *data, unsigned long len, | |
| 66 PRBool final) | |
| 67 { | |
| 68 unsigned char *buf = NULL; | |
| 69 SECStatus rv; | |
| 70 int i; | |
| 71 | |
| 72 /* | |
| 73 * We should really have data to process, or we should be trying | |
| 74 * to finish/flush the last block. (This is an overly paranoid | |
| 75 * check since all callers are in this file and simple inspection | |
| 76 * proves they do it right. But it could find a bug in future | |
| 77 * modifications/development, that is why it is here.) | |
| 78 */ | |
| 79 PORT_Assert ((data != NULL && len) || final); | |
| 80 | |
| 81 /* | |
| 82 * Decrypt this chunk. | |
| 83 * | |
| 84 * XXX If we get an error, we do not want to do the digest or callback, | |
| 85 * but we want to keep decoding. Or maybe we want to stop decoding | |
| 86 * altogether if there is a callback, because obviously we are not | |
| 87 * sending the data back and they want to know that. | |
| 88 */ | |
| 89 if (worker->decryptobj != NULL) { | |
| 90 /* XXX the following lengths should all be longs? */ | |
| 91 unsigned int inlen; /* length of data being decrypted */ | |
| 92 unsigned int outlen; /* length of decrypted data */ | |
| 93 unsigned int buflen; /* length available for decrypted data */ | |
| 94 SECItem *plain; | |
| 95 | |
| 96 inlen = len; | |
| 97 buflen = sec_PKCS7DecryptLength (worker->decryptobj, inlen, final); | |
| 98 if (buflen == 0) { | |
| 99 if (inlen == 0) /* no input and no output */ | |
| 100 return; | |
| 101 /* | |
| 102 * No output is expected, but the input data may be buffered | |
| 103 * so we still have to call Decrypt. | |
| 104 */ | |
| 105 rv = sec_PKCS7Decrypt (worker->decryptobj, NULL, NULL, 0, | |
| 106 data, inlen, final); | |
| 107 if (rv != SECSuccess) { | |
| 108 p7dcx->error = PORT_GetError(); | |
| 109 return; /* XXX indicate error? */ | |
| 110 } | |
| 111 return; | |
| 112 } | |
| 113 | |
| 114 if (p7dcx->cb != NULL) { | |
| 115 buf = (unsigned char *) PORT_Alloc (buflen); | |
| 116 plain = NULL; | |
| 117 } else { | |
| 118 unsigned long oldlen; | |
| 119 | |
| 120 /* | |
| 121 * XXX This assumes one level of content only. | |
| 122 * See comment above about nested content types. | |
| 123 * XXX Also, it should work for signedAndEnvelopedData, too! | |
| 124 */ | |
| 125 plain = &(p7dcx->cinfo-> | |
| 126 content.envelopedData->encContentInfo.plainContent); | |
| 127 | |
| 128 oldlen = plain->len; | |
| 129 if (oldlen == 0) { | |
| 130 buf = (unsigned char*)PORT_ArenaAlloc (p7dcx->cinfo->poolp, | |
| 131 buflen); | |
| 132 } else { | |
| 133 buf = (unsigned char*)PORT_ArenaGrow (p7dcx->cinfo->poolp, | |
| 134 plain->data, | |
| 135 oldlen, oldlen + buflen); | |
| 136 if (buf != NULL) | |
| 137 buf += oldlen; | |
| 138 } | |
| 139 plain->data = buf; | |
| 140 } | |
| 141 if (buf == NULL) { | |
| 142 p7dcx->error = SEC_ERROR_NO_MEMORY; | |
| 143 return; /* XXX indicate error? */ | |
| 144 } | |
| 145 rv = sec_PKCS7Decrypt (worker->decryptobj, buf, &outlen, buflen, | |
| 146 data, inlen, final); | |
| 147 if (rv != SECSuccess) { | |
| 148 p7dcx->error = PORT_GetError(); | |
| 149 return; /* XXX indicate error? */ | |
| 150 } | |
| 151 if (plain != NULL) { | |
| 152 PORT_Assert (final || outlen == buflen); | |
| 153 plain->len += outlen; | |
| 154 } | |
| 155 data = buf; | |
| 156 len = outlen; | |
| 157 } | |
| 158 | |
| 159 /* | |
| 160 * Update the running digests. | |
| 161 */ | |
| 162 if (len) { | |
| 163 for (i = 0; i < worker->digcnt; i++) { | |
| 164 (* worker->digobjs[i]->update) (worker->digcxs[i], data, len); | |
| 165 } | |
| 166 } | |
| 167 | |
| 168 /* | |
| 169 * Pass back the contents bytes, and free the temporary buffer. | |
| 170 */ | |
| 171 if (p7dcx->cb != NULL) { | |
| 172 if (len) | |
| 173 (* p7dcx->cb) (p7dcx->cb_arg, (const char *)data, len); | |
| 174 if (worker->decryptobj != NULL) { | |
| 175 PORT_Assert (buf != NULL); | |
| 176 PORT_Free (buf); | |
| 177 } | |
| 178 } | |
| 179 } | |
| 180 | |
| 181 static void | |
| 182 sec_pkcs7_decoder_filter (void *arg, const char *data, unsigned long len, | |
| 183 int depth, SEC_ASN1EncodingPart data_kind) | |
| 184 { | |
| 185 SEC_PKCS7DecoderContext *p7dcx; | |
| 186 struct sec_pkcs7_decoder_worker *worker; | |
| 187 | |
| 188 /* | |
| 189 * Since we do not handle any nested contents, the only bytes we | |
| 190 * are really interested in are the actual contents bytes (not | |
| 191 * the identifier, length, or end-of-contents bytes). If we were | |
| 192 * handling nested types we would probably need to do something | |
| 193 * smarter based on depth and data_kind. | |
| 194 */ | |
| 195 if (data_kind != SEC_ASN1_Contents) | |
| 196 return; | |
| 197 | |
| 198 /* | |
| 199 * The ASN.1 decoder should not even call us with a length of 0. | |
| 200 * Just being paranoid. | |
| 201 */ | |
| 202 PORT_Assert (len); | |
| 203 if (len == 0) | |
| 204 return; | |
| 205 | |
| 206 p7dcx = (SEC_PKCS7DecoderContext*)arg; | |
| 207 | |
| 208 /* | |
| 209 * Handling nested contents would mean that there is a chain | |
| 210 * of workers -- one per each level of content. The following | |
| 211 * would start with the first worker and loop over them. | |
| 212 */ | |
| 213 worker = &(p7dcx->worker); | |
| 214 | |
| 215 worker->saw_contents = PR_TRUE; | |
| 216 | |
| 217 sec_pkcs7_decoder_work_data (p7dcx, worker, | |
| 218 (const unsigned char *) data, len, PR_FALSE); | |
| 219 } | |
| 220 | |
| 221 | |
| 222 /* | |
| 223 * Create digest contexts for each algorithm in "digestalgs". | |
| 224 * No algorithms is not an error, we just do not do anything. | |
| 225 * An error (like trouble allocating memory), marks the error | |
| 226 * in "p7dcx" and returns SECFailure, which means that our caller | |
| 227 * should just give up altogether. | |
| 228 */ | |
| 229 static SECStatus | |
| 230 sec_pkcs7_decoder_start_digests (SEC_PKCS7DecoderContext *p7dcx, int depth, | |
| 231 SECAlgorithmID **digestalgs) | |
| 232 { | |
| 233 int i, digcnt; | |
| 234 | |
| 235 if (digestalgs == NULL) | |
| 236 return SECSuccess; | |
| 237 | |
| 238 /* | |
| 239 * Count the algorithms. | |
| 240 */ | |
| 241 digcnt = 0; | |
| 242 while (digestalgs[digcnt] != NULL) | |
| 243 digcnt++; | |
| 244 | |
| 245 /* | |
| 246 * No algorithms means no work to do. | |
| 247 * Just act as if there were no algorithms specified. | |
| 248 */ | |
| 249 if (digcnt == 0) | |
| 250 return SECSuccess; | |
| 251 | |
| 252 p7dcx->worker.digcxs = (void**)PORT_ArenaAlloc (p7dcx->tmp_poolp, | |
| 253 digcnt * sizeof (void *)); | |
| 254 p7dcx->worker.digobjs = (const SECHashObject**)PORT_ArenaAlloc (p7dcx->tmp_p
oolp, | |
| 255 digcnt * sizeof (SECHashObject *)); | |
| 256 if (p7dcx->worker.digcxs == NULL || p7dcx->worker.digobjs == NULL) { | |
| 257 p7dcx->error = SEC_ERROR_NO_MEMORY; | |
| 258 return SECFailure; | |
| 259 } | |
| 260 | |
| 261 p7dcx->worker.depth = depth; | |
| 262 p7dcx->worker.digcnt = 0; | |
| 263 | |
| 264 /* | |
| 265 * Create a digest context for each algorithm. | |
| 266 */ | |
| 267 for (i = 0; i < digcnt; i++) { | |
| 268 SECAlgorithmID * algid = digestalgs[i]; | |
| 269 SECOidTag oidTag = SECOID_FindOIDTag(&(algid->algorithm)); | |
| 270 const SECHashObject *digobj = HASH_GetHashObjectByOidTag(oidTag); | |
| 271 void *digcx; | |
| 272 | |
| 273 /* | |
| 274 * Skip any algorithm we do not even recognize; obviously, | |
| 275 * this could be a problem, but if it is critical then the | |
| 276 * result will just be that the signature does not verify. | |
| 277 * We do not necessarily want to error out here, because | |
| 278 * the particular algorithm may not actually be important, | |
| 279 * but we cannot know that until later. | |
| 280 */ | |
| 281 if (digobj == NULL) { | |
| 282 p7dcx->worker.digcnt--; | |
| 283 continue; | |
| 284 } | |
| 285 | |
| 286 digcx = (* digobj->create)(); | |
| 287 if (digcx != NULL) { | |
| 288 (* digobj->begin) (digcx); | |
| 289 p7dcx->worker.digobjs[p7dcx->worker.digcnt] = digobj; | |
| 290 p7dcx->worker.digcxs[p7dcx->worker.digcnt] = digcx; | |
| 291 p7dcx->worker.digcnt++; | |
| 292 } | |
| 293 } | |
| 294 | |
| 295 if (p7dcx->worker.digcnt != 0) | |
| 296 SEC_ASN1DecoderSetFilterProc (p7dcx->dcx, | |
| 297 sec_pkcs7_decoder_filter, | |
| 298 p7dcx, | |
| 299 (PRBool)(p7dcx->cb != NULL)); | |
| 300 return SECSuccess; | |
| 301 } | |
| 302 | |
| 303 | |
| 304 /* | |
| 305 * Close out all of the digest contexts, storing the results in "digestsp". | |
| 306 */ | |
| 307 static SECStatus | |
| 308 sec_pkcs7_decoder_finish_digests (SEC_PKCS7DecoderContext *p7dcx, | |
| 309 PLArenaPool *poolp, | |
| 310 SECItem ***digestsp) | |
| 311 { | |
| 312 struct sec_pkcs7_decoder_worker *worker; | |
| 313 const SECHashObject *digobj; | |
| 314 void *digcx; | |
| 315 SECItem **digests, *digest; | |
| 316 int i; | |
| 317 void *mark; | |
| 318 | |
| 319 /* | |
| 320 * XXX Handling nested contents would mean that there is a chain | |
| 321 * of workers -- one per each level of content. The following | |
| 322 * would want to find the last worker in the chain. | |
| 323 */ | |
| 324 worker = &(p7dcx->worker); | |
| 325 | |
| 326 /* | |
| 327 * If no digests, then we have nothing to do. | |
| 328 */ | |
| 329 if (worker->digcnt == 0) | |
| 330 return SECSuccess; | |
| 331 | |
| 332 /* | |
| 333 * No matter what happens after this, we want to stop filtering. | |
| 334 * XXX If we handle nested contents, we only want to stop filtering | |
| 335 * if we are finishing off the *last* worker. | |
| 336 */ | |
| 337 SEC_ASN1DecoderClearFilterProc (p7dcx->dcx); | |
| 338 | |
| 339 /* | |
| 340 * If we ended up with no contents, just destroy each | |
| 341 * digest context -- they are meaningless and potentially | |
| 342 * confusing, because their presence would imply some content | |
| 343 * was digested. | |
| 344 */ | |
| 345 if (! worker->saw_contents) { | |
| 346 for (i = 0; i < worker->digcnt; i++) { | |
| 347 digcx = worker->digcxs[i]; | |
| 348 digobj = worker->digobjs[i]; | |
| 349 (* digobj->destroy) (digcx, PR_TRUE); | |
| 350 } | |
| 351 return SECSuccess; | |
| 352 } | |
| 353 | |
| 354 mark = PORT_ArenaMark (poolp); | |
| 355 | |
| 356 /* | |
| 357 * Close out each digest context, saving digest away. | |
| 358 */ | |
| 359 digests = | |
| 360 (SECItem**)PORT_ArenaAlloc (poolp,(worker->digcnt+1)*sizeof(SECItem *)); | |
| 361 digest = (SECItem*)PORT_ArenaAlloc (poolp, worker->digcnt*sizeof(SECItem)); | |
| 362 if (digests == NULL || digest == NULL) { | |
| 363 p7dcx->error = PORT_GetError(); | |
| 364 PORT_ArenaRelease (poolp, mark); | |
| 365 return SECFailure; | |
| 366 } | |
| 367 | |
| 368 for (i = 0; i < worker->digcnt; i++, digest++) { | |
| 369 digcx = worker->digcxs[i]; | |
| 370 digobj = worker->digobjs[i]; | |
| 371 | |
| 372 digest->data = (unsigned char*)PORT_ArenaAlloc (poolp, digobj->length); | |
| 373 if (digest->data == NULL) { | |
| 374 p7dcx->error = PORT_GetError(); | |
| 375 PORT_ArenaRelease (poolp, mark); | |
| 376 return SECFailure; | |
| 377 } | |
| 378 | |
| 379 digest->len = digobj->length; | |
| 380 (* digobj->end) (digcx, digest->data, &(digest->len), digest->len); | |
| 381 (* digobj->destroy) (digcx, PR_TRUE); | |
| 382 | |
| 383 digests[i] = digest; | |
| 384 } | |
| 385 digests[i] = NULL; | |
| 386 *digestsp = digests; | |
| 387 | |
| 388 PORT_ArenaUnmark (poolp, mark); | |
| 389 return SECSuccess; | |
| 390 } | |
| 391 | |
| 392 /* | |
| 393 * XXX Need comment explaining following helper function (which is used | |
| 394 * by sec_pkcs7_decoder_start_decrypt). | |
| 395 */ | |
| 396 | |
| 397 static PK11SymKey * | |
| 398 sec_pkcs7_decoder_get_recipient_key (SEC_PKCS7DecoderContext *p7dcx, | |
| 399 SEC_PKCS7RecipientInfo **recipientinfos, | |
| 400 SEC_PKCS7EncryptedContentInfo *enccinfo) | |
| 401 { | |
| 402 SEC_PKCS7RecipientInfo *ri; | |
| 403 CERTCertificate *cert = NULL; | |
| 404 SECKEYPrivateKey *privkey = NULL; | |
| 405 PK11SymKey *bulkkey = NULL; | |
| 406 SECOidTag keyalgtag, bulkalgtag, encalgtag; | |
| 407 PK11SlotInfo *slot = NULL; | |
| 408 | |
| 409 if (recipientinfos == NULL || recipientinfos[0] == NULL) { | |
| 410 p7dcx->error = SEC_ERROR_NOT_A_RECIPIENT; | |
| 411 goto no_key_found; | |
| 412 } | |
| 413 | |
| 414 cert = PK11_FindCertAndKeyByRecipientList(&slot,recipientinfos,&ri, | |
| 415 &privkey, p7dcx->pwfn_arg); | |
| 416 if (cert == NULL) { | |
| 417 p7dcx->error = SEC_ERROR_NOT_A_RECIPIENT; | |
| 418 goto no_key_found; | |
| 419 } | |
| 420 | |
| 421 ri->cert = cert; /* so we can find it later */ | |
| 422 PORT_Assert(privkey != NULL); | |
| 423 | |
| 424 keyalgtag = SECOID_GetAlgorithmTag(&(cert->subjectPublicKeyInfo.algorithm)); | |
| 425 encalgtag = SECOID_GetAlgorithmTag (&(ri->keyEncAlg)); | |
| 426 if (keyalgtag != encalgtag) { | |
| 427 p7dcx->error = SEC_ERROR_PKCS7_KEYALG_MISMATCH; | |
| 428 goto no_key_found; | |
| 429 } | |
| 430 bulkalgtag = SECOID_GetAlgorithmTag (&(enccinfo->contentEncAlg)); | |
| 431 | |
| 432 switch (encalgtag) { | |
| 433 case SEC_OID_PKCS1_RSA_ENCRYPTION: | |
| 434 bulkkey = PK11_PubUnwrapSymKey (privkey, &ri->encKey, | |
| 435 PK11_AlgtagToMechanism (bulkalgtag), | |
| 436 CKA_DECRYPT, 0); | |
| 437 if (bulkkey == NULL) { | |
| 438 p7dcx->error = PORT_GetError(); | |
| 439 PORT_SetError(0); | |
| 440 goto no_key_found; | |
| 441 } | |
| 442 break; | |
| 443 default: | |
| 444 p7dcx->error = SEC_ERROR_UNSUPPORTED_KEYALG; | |
| 445 break; | |
| 446 } | |
| 447 | |
| 448 no_key_found: | |
| 449 if (privkey != NULL) | |
| 450 SECKEY_DestroyPrivateKey (privkey); | |
| 451 if (slot != NULL) | |
| 452 PK11_FreeSlot(slot); | |
| 453 | |
| 454 return bulkkey; | |
| 455 } | |
| 456 | |
| 457 /* | |
| 458 * XXX The following comment is old -- the function used to only handle | |
| 459 * EnvelopedData or SignedAndEnvelopedData but now handles EncryptedData | |
| 460 * as well (and it had all of the code of the helper function above | |
| 461 * built into it), though the comment was left as is. Fix it... | |
| 462 * | |
| 463 * We are just about to decode the content of an EnvelopedData. | |
| 464 * Set up a decryption context so we can decrypt as we go. | |
| 465 * Presumably we are one of the recipients listed in "recipientinfos". | |
| 466 * (XXX And if we are not, or if we have trouble, what should we do? | |
| 467 * It would be nice to let the decoding still work. Maybe it should | |
| 468 * be an error if there is a content callback, but not an error otherwise?) | |
| 469 * The encryption key and related information can be found in "enccinfo". | |
| 470 */ | |
| 471 static SECStatus | |
| 472 sec_pkcs7_decoder_start_decrypt (SEC_PKCS7DecoderContext *p7dcx, int depth, | |
| 473 SEC_PKCS7RecipientInfo **recipientinfos, | |
| 474 SEC_PKCS7EncryptedContentInfo *enccinfo, | |
| 475 PK11SymKey **copy_key_for_signature) | |
| 476 { | |
| 477 PK11SymKey *bulkkey = NULL; | |
| 478 sec_PKCS7CipherObject *decryptobj; | |
| 479 | |
| 480 /* | |
| 481 * If a callback is supplied to retrieve the encryption key, | |
| 482 * for instance, for Encrypted Content infos, then retrieve | |
| 483 * the bulkkey from the callback. Otherwise, assume that | |
| 484 * we are processing Enveloped or SignedAndEnveloped data | |
| 485 * content infos. | |
| 486 * | |
| 487 * XXX Put an assert here? | |
| 488 */ | |
| 489 if (SEC_PKCS7ContentType(p7dcx->cinfo) == SEC_OID_PKCS7_ENCRYPTED_DATA) { | |
| 490 if (p7dcx->dkcb != NULL) { | |
| 491 bulkkey = (*p7dcx->dkcb)(p7dcx->dkcb_arg, | |
| 492 &(enccinfo->contentEncAlg)); | |
| 493 } | |
| 494 enccinfo->keysize = 0; | |
| 495 } else { | |
| 496 bulkkey = sec_pkcs7_decoder_get_recipient_key (p7dcx, recipientinfos, | |
| 497 enccinfo); | |
| 498 if (bulkkey == NULL) goto no_decryption; | |
| 499 enccinfo->keysize = PK11_GetKeyStrength(bulkkey, | |
| 500 &(enccinfo->contentEncAlg)); | |
| 501 | |
| 502 } | |
| 503 | |
| 504 /* | |
| 505 * XXX I think following should set error in p7dcx and clear set error | |
| 506 * (as used to be done here, or as is done in get_receipient_key above. | |
| 507 */ | |
| 508 if(bulkkey == NULL) { | |
| 509 goto no_decryption; | |
| 510 } | |
| 511 | |
| 512 /* | |
| 513 * We want to make sure decryption is allowed. This is done via | |
| 514 * a callback specified in SEC_PKCS7DecoderStart(). | |
| 515 */ | |
| 516 if (p7dcx->decrypt_allowed_cb) { | |
| 517 if ((*p7dcx->decrypt_allowed_cb) (&(enccinfo->contentEncAlg), | |
| 518 bulkkey) == PR_FALSE) { | |
| 519 p7dcx->error = SEC_ERROR_DECRYPTION_DISALLOWED; | |
| 520 goto no_decryption; | |
| 521 } | |
| 522 } else { | |
| 523 p7dcx->error = SEC_ERROR_DECRYPTION_DISALLOWED; | |
| 524 goto no_decryption; | |
| 525 } | |
| 526 | |
| 527 /* | |
| 528 * When decrypting a signedAndEnvelopedData, the signature also has | |
| 529 * to be decrypted with the bulk encryption key; to avoid having to | |
| 530 * get it all over again later (and do another potentially expensive | |
| 531 * RSA operation), copy it for later signature verification to use. | |
| 532 */ | |
| 533 if (copy_key_for_signature != NULL) | |
| 534 *copy_key_for_signature = PK11_ReferenceSymKey (bulkkey); | |
| 535 | |
| 536 /* | |
| 537 * Now we have the bulk encryption key (in bulkkey) and the | |
| 538 * the algorithm (in enccinfo->contentEncAlg). Using those, | |
| 539 * create a decryption context. | |
| 540 */ | |
| 541 decryptobj = sec_PKCS7CreateDecryptObject (bulkkey, | |
| 542 &(enccinfo->contentEncAlg)); | |
| 543 | |
| 544 /* | |
| 545 * We are done with (this) bulkkey now. | |
| 546 */ | |
| 547 PK11_FreeSymKey (bulkkey); | |
| 548 | |
| 549 if (decryptobj == NULL) { | |
| 550 p7dcx->error = PORT_GetError(); | |
| 551 PORT_SetError(0); | |
| 552 goto no_decryption; | |
| 553 } | |
| 554 | |
| 555 SEC_ASN1DecoderSetFilterProc (p7dcx->dcx, | |
| 556 sec_pkcs7_decoder_filter, | |
| 557 p7dcx, | |
| 558 (PRBool)(p7dcx->cb != NULL)); | |
| 559 | |
| 560 p7dcx->worker.depth = depth; | |
| 561 p7dcx->worker.decryptobj = decryptobj; | |
| 562 | |
| 563 return SECSuccess; | |
| 564 | |
| 565 no_decryption: | |
| 566 /* | |
| 567 * For some reason (error set already, if appropriate), we cannot | |
| 568 * decrypt the content. I am not sure what exactly is the right | |
| 569 * thing to do here; in some cases we want to just stop, and in | |
| 570 * others we want to let the decoding finish even though we cannot | |
| 571 * decrypt the content. My current thinking is that if the caller | |
| 572 * set up a content callback, then they are really interested in | |
| 573 * getting (decrypted) content, and if they cannot they will want | |
| 574 * to know about it. However, if no callback was specified, then | |
| 575 * maybe it is not important that the decryption failed. | |
| 576 */ | |
| 577 if (p7dcx->cb != NULL) | |
| 578 return SECFailure; | |
| 579 else | |
| 580 return SECSuccess; /* Let the decoding continue. */ | |
| 581 } | |
| 582 | |
| 583 | |
| 584 static SECStatus | |
| 585 sec_pkcs7_decoder_finish_decrypt (SEC_PKCS7DecoderContext *p7dcx, | |
| 586 PLArenaPool *poolp, | |
| 587 SEC_PKCS7EncryptedContentInfo *enccinfo) | |
| 588 { | |
| 589 struct sec_pkcs7_decoder_worker *worker; | |
| 590 | |
| 591 /* | |
| 592 * XXX Handling nested contents would mean that there is a chain | |
| 593 * of workers -- one per each level of content. The following | |
| 594 * would want to find the last worker in the chain. | |
| 595 */ | |
| 596 worker = &(p7dcx->worker); | |
| 597 | |
| 598 /* | |
| 599 * If no decryption context, then we have nothing to do. | |
| 600 */ | |
| 601 if (worker->decryptobj == NULL) | |
| 602 return SECSuccess; | |
| 603 | |
| 604 /* | |
| 605 * No matter what happens after this, we want to stop filtering. | |
| 606 * XXX If we handle nested contents, we only want to stop filtering | |
| 607 * if we are finishing off the *last* worker. | |
| 608 */ | |
| 609 SEC_ASN1DecoderClearFilterProc (p7dcx->dcx); | |
| 610 | |
| 611 /* | |
| 612 * Handle the last block. | |
| 613 */ | |
| 614 sec_pkcs7_decoder_work_data (p7dcx, worker, NULL, 0, PR_TRUE); | |
| 615 | |
| 616 /* | |
| 617 * All done, destroy it. | |
| 618 */ | |
| 619 sec_PKCS7DestroyDecryptObject (worker->decryptobj); | |
| 620 worker->decryptobj = NULL; | |
| 621 | |
| 622 return SECSuccess; | |
| 623 } | |
| 624 | |
| 625 | |
| 626 static void | |
| 627 sec_pkcs7_decoder_notify (void *arg, PRBool before, void *dest, int depth) | |
| 628 { | |
| 629 SEC_PKCS7DecoderContext *p7dcx; | |
| 630 SEC_PKCS7ContentInfo *cinfo; | |
| 631 SEC_PKCS7SignedData *sigd; | |
| 632 SEC_PKCS7EnvelopedData *envd; | |
| 633 SEC_PKCS7SignedAndEnvelopedData *saed; | |
| 634 SEC_PKCS7EncryptedData *encd; | |
| 635 SEC_PKCS7DigestedData *digd; | |
| 636 PRBool after; | |
| 637 SECStatus rv; | |
| 638 | |
| 639 /* | |
| 640 * Just to make the code easier to read, create an "after" variable | |
| 641 * that is equivalent to "not before". | |
| 642 * (This used to be just the statement "after = !before", but that | |
| 643 * causes a warning on the mac; to avoid that, we do it the long way.) | |
| 644 */ | |
| 645 if (before) | |
| 646 after = PR_FALSE; | |
| 647 else | |
| 648 after = PR_TRUE; | |
| 649 | |
| 650 p7dcx = (SEC_PKCS7DecoderContext*)arg; | |
| 651 cinfo = p7dcx->cinfo; | |
| 652 | |
| 653 if (cinfo->contentTypeTag == NULL) { | |
| 654 if (after && dest == &(cinfo->contentType)) | |
| 655 cinfo->contentTypeTag = SECOID_FindOID(&(cinfo->contentType)); | |
| 656 return; | |
| 657 } | |
| 658 | |
| 659 switch (cinfo->contentTypeTag->offset) { | |
| 660 case SEC_OID_PKCS7_SIGNED_DATA: | |
| 661 sigd = cinfo->content.signedData; | |
| 662 if (sigd == NULL) | |
| 663 break; | |
| 664 | |
| 665 if (sigd->contentInfo.contentTypeTag == NULL) { | |
| 666 if (after && dest == &(sigd->contentInfo.contentType)) | |
| 667 sigd->contentInfo.contentTypeTag = | |
| 668 SECOID_FindOID(&(sigd->contentInfo.contentType)); | |
| 669 break; | |
| 670 } | |
| 671 | |
| 672 /* | |
| 673 * We only set up a filtering digest if the content is | |
| 674 * plain DATA; anything else needs more work because a | |
| 675 * second pass is required to produce a DER encoding from | |
| 676 * an input that can be BER encoded. (This is a requirement | |
| 677 * of PKCS7 that is unfortunate, but there you have it.) | |
| 678 * | |
| 679 * XXX Also, since we stop here if this is not DATA, the | |
| 680 * inner content is not getting processed at all. Someday | |
| 681 * we may want to fix that. | |
| 682 */ | |
| 683 if (sigd->contentInfo.contentTypeTag->offset != SEC_OID_PKCS7_DATA) { | |
| 684 /* XXX Set an error in p7dcx->error */ | |
| 685 SEC_ASN1DecoderClearNotifyProc (p7dcx->dcx); | |
| 686 break; | |
| 687 } | |
| 688 | |
| 689 /* | |
| 690 * Just before the content, we want to set up a digest context | |
| 691 * for each digest algorithm listed, and start a filter which | |
| 692 * will run all of the contents bytes through that digest. | |
| 693 */ | |
| 694 if (before && dest == &(sigd->contentInfo.content)) { | |
| 695 rv = sec_pkcs7_decoder_start_digests (p7dcx, depth, | |
| 696 sigd->digestAlgorithms); | |
| 697 if (rv != SECSuccess) | |
| 698 SEC_ASN1DecoderClearNotifyProc (p7dcx->dcx); | |
| 699 | |
| 700 break; | |
| 701 } | |
| 702 | |
| 703 /* | |
| 704 * XXX To handle nested types, here is where we would want | |
| 705 * to check for inner boundaries that need handling. | |
| 706 */ | |
| 707 | |
| 708 /* | |
| 709 * Are we done? | |
| 710 */ | |
| 711 if (after && dest == &(sigd->contentInfo.content)) { | |
| 712 /* | |
| 713 * Close out the digest contexts. We ignore any error | |
| 714 * because we are stopping anyway; the error status left | |
| 715 * behind in p7dcx will be seen by outer functions. | |
| 716 */ | |
| 717 (void) sec_pkcs7_decoder_finish_digests (p7dcx, cinfo->poolp, | |
| 718 &(sigd->digests)); | |
| 719 | |
| 720 /* | |
| 721 * XXX To handle nested contents, we would need to remove | |
| 722 * the worker from the chain (and free it). | |
| 723 */ | |
| 724 | |
| 725 /* | |
| 726 * Stop notify. | |
| 727 */ | |
| 728 SEC_ASN1DecoderClearNotifyProc (p7dcx->dcx); | |
| 729 } | |
| 730 break; | |
| 731 | |
| 732 case SEC_OID_PKCS7_ENVELOPED_DATA: | |
| 733 envd = cinfo->content.envelopedData; | |
| 734 if (envd == NULL) | |
| 735 break; | |
| 736 | |
| 737 if (envd->encContentInfo.contentTypeTag == NULL) { | |
| 738 if (after && dest == &(envd->encContentInfo.contentType)) | |
| 739 envd->encContentInfo.contentTypeTag = | |
| 740 SECOID_FindOID(&(envd->encContentInfo.contentType)); | |
| 741 break; | |
| 742 } | |
| 743 | |
| 744 /* | |
| 745 * Just before the content, we want to set up a decryption | |
| 746 * context, and start a filter which will run all of the | |
| 747 * contents bytes through it to determine the plain content. | |
| 748 */ | |
| 749 if (before && dest == &(envd->encContentInfo.encContent)) { | |
| 750 rv = sec_pkcs7_decoder_start_decrypt (p7dcx, depth, | |
| 751 envd->recipientInfos, | |
| 752 &(envd->encContentInfo), | |
| 753 NULL); | |
| 754 if (rv != SECSuccess) | |
| 755 SEC_ASN1DecoderClearNotifyProc (p7dcx->dcx); | |
| 756 | |
| 757 break; | |
| 758 } | |
| 759 | |
| 760 /* | |
| 761 * Are we done? | |
| 762 */ | |
| 763 if (after && dest == &(envd->encContentInfo.encContent)) { | |
| 764 /* | |
| 765 * Close out the decryption context. We ignore any error | |
| 766 * because we are stopping anyway; the error status left | |
| 767 * behind in p7dcx will be seen by outer functions. | |
| 768 */ | |
| 769 (void) sec_pkcs7_decoder_finish_decrypt (p7dcx, cinfo->poolp, | |
| 770 &(envd->encContentInfo)); | |
| 771 | |
| 772 /* | |
| 773 * XXX To handle nested contents, we would need to remove | |
| 774 * the worker from the chain (and free it). | |
| 775 */ | |
| 776 | |
| 777 /* | |
| 778 * Stop notify. | |
| 779 */ | |
| 780 SEC_ASN1DecoderClearNotifyProc (p7dcx->dcx); | |
| 781 } | |
| 782 break; | |
| 783 | |
| 784 case SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA: | |
| 785 saed = cinfo->content.signedAndEnvelopedData; | |
| 786 if (saed == NULL) | |
| 787 break; | |
| 788 | |
| 789 if (saed->encContentInfo.contentTypeTag == NULL) { | |
| 790 if (after && dest == &(saed->encContentInfo.contentType)) | |
| 791 saed->encContentInfo.contentTypeTag = | |
| 792 SECOID_FindOID(&(saed->encContentInfo.contentType)); | |
| 793 break; | |
| 794 } | |
| 795 | |
| 796 /* | |
| 797 * Just before the content, we want to set up a decryption | |
| 798 * context *and* digest contexts, and start a filter which | |
| 799 * will run all of the contents bytes through both. | |
| 800 */ | |
| 801 if (before && dest == &(saed->encContentInfo.encContent)) { | |
| 802 rv = sec_pkcs7_decoder_start_decrypt (p7dcx, depth, | |
| 803 saed->recipientInfos, | |
| 804 &(saed->encContentInfo), | |
| 805 &(saed->sigKey)); | |
| 806 if (rv == SECSuccess) | |
| 807 rv = sec_pkcs7_decoder_start_digests (p7dcx, depth, | |
| 808 saed->digestAlgorithms); | |
| 809 if (rv != SECSuccess) | |
| 810 SEC_ASN1DecoderClearNotifyProc (p7dcx->dcx); | |
| 811 | |
| 812 break; | |
| 813 } | |
| 814 | |
| 815 /* | |
| 816 * Are we done? | |
| 817 */ | |
| 818 if (after && dest == &(saed->encContentInfo.encContent)) { | |
| 819 /* | |
| 820 * Close out the decryption and digests contexts. | |
| 821 * We ignore any errors because we are stopping anyway; | |
| 822 * the error status left behind in p7dcx will be seen by | |
| 823 * outer functions. | |
| 824 * | |
| 825 * Note that the decrypt stuff must be called first; | |
| 826 * it may have a last buffer to do which in turn has | |
| 827 * to be added to the digest. | |
| 828 */ | |
| 829 (void) sec_pkcs7_decoder_finish_decrypt (p7dcx, cinfo->poolp, | |
| 830 &(saed->encContentInfo)); | |
| 831 (void) sec_pkcs7_decoder_finish_digests (p7dcx, cinfo->poolp, | |
| 832 &(saed->digests)); | |
| 833 | |
| 834 /* | |
| 835 * XXX To handle nested contents, we would need to remove | |
| 836 * the worker from the chain (and free it). | |
| 837 */ | |
| 838 | |
| 839 /* | |
| 840 * Stop notify. | |
| 841 */ | |
| 842 SEC_ASN1DecoderClearNotifyProc (p7dcx->dcx); | |
| 843 } | |
| 844 break; | |
| 845 | |
| 846 case SEC_OID_PKCS7_DIGESTED_DATA: | |
| 847 digd = cinfo->content.digestedData; | |
| 848 | |
| 849 /* | |
| 850 * XXX Want to do the digest or not? Maybe future enhancement... | |
| 851 */ | |
| 852 if (before && dest == &(digd->contentInfo.content.data)) { | |
| 853 SEC_ASN1DecoderSetFilterProc (p7dcx->dcx, sec_pkcs7_decoder_filter, | |
| 854 p7dcx, | |
| 855 (PRBool)(p7dcx->cb != NULL)); | |
| 856 break; | |
| 857 } | |
| 858 | |
| 859 /* | |
| 860 * Are we done? | |
| 861 */ | |
| 862 if (after && dest == &(digd->contentInfo.content.data)) { | |
| 863 SEC_ASN1DecoderClearFilterProc (p7dcx->dcx); | |
| 864 } | |
| 865 break; | |
| 866 | |
| 867 case SEC_OID_PKCS7_ENCRYPTED_DATA: | |
| 868 encd = cinfo->content.encryptedData; | |
| 869 | |
| 870 /* | |
| 871 * XXX If the decryption key callback is set, we want to start | |
| 872 * the decryption. If the callback is not set, we will treat the | |
| 873 * content as plain data, since we do not have the key. | |
| 874 * | |
| 875 * Is this the proper thing to do? | |
| 876 */ | |
| 877 if (before && dest == &(encd->encContentInfo.encContent)) { | |
| 878 /* | |
| 879 * Start the encryption process if the decryption key callback | |
| 880 * is present. Otherwise, treat the content like plain data. | |
| 881 */ | |
| 882 rv = SECSuccess; | |
| 883 if (p7dcx->dkcb != NULL) { | |
| 884 rv = sec_pkcs7_decoder_start_decrypt (p7dcx, depth, NULL, | |
| 885 &(encd->encContentInfo), | |
| 886 NULL); | |
| 887 } | |
| 888 | |
| 889 if (rv != SECSuccess) | |
| 890 SEC_ASN1DecoderClearNotifyProc (p7dcx->dcx); | |
| 891 | |
| 892 break; | |
| 893 } | |
| 894 | |
| 895 /* | |
| 896 * Are we done? | |
| 897 */ | |
| 898 if (after && dest == &(encd->encContentInfo.encContent)) { | |
| 899 /* | |
| 900 * Close out the decryption context. We ignore any error | |
| 901 * because we are stopping anyway; the error status left | |
| 902 * behind in p7dcx will be seen by outer functions. | |
| 903 */ | |
| 904 (void) sec_pkcs7_decoder_finish_decrypt (p7dcx, cinfo->poolp, | |
| 905 &(encd->encContentInfo)); | |
| 906 | |
| 907 /* | |
| 908 * Stop notify. | |
| 909 */ | |
| 910 SEC_ASN1DecoderClearNotifyProc (p7dcx->dcx); | |
| 911 } | |
| 912 break; | |
| 913 | |
| 914 case SEC_OID_PKCS7_DATA: | |
| 915 /* | |
| 916 * If a output callback has been specified, we want to set the filter | |
| 917 * to call the callback. This is taken care of in | |
| 918 * sec_pkcs7_decoder_start_decrypt() or | |
| 919 * sec_pkcs7_decoder_start_digests() for the other content types. | |
| 920 */ | |
| 921 | |
| 922 if (before && dest == &(cinfo->content.data)) { | |
| 923 | |
| 924 /* | |
| 925 * Set the filter proc up. | |
| 926 */ | |
| 927 SEC_ASN1DecoderSetFilterProc (p7dcx->dcx, | |
| 928 sec_pkcs7_decoder_filter, | |
| 929 p7dcx, | |
| 930 (PRBool)(p7dcx->cb != NULL)); | |
| 931 break; | |
| 932 } | |
| 933 | |
| 934 if (after && dest == &(cinfo->content.data)) { | |
| 935 /* | |
| 936 * Time to clean up after ourself, stop the Notify and Filter | |
| 937 * procedures. | |
| 938 */ | |
| 939 SEC_ASN1DecoderClearNotifyProc (p7dcx->dcx); | |
| 940 SEC_ASN1DecoderClearFilterProc (p7dcx->dcx); | |
| 941 } | |
| 942 break; | |
| 943 | |
| 944 default: | |
| 945 SEC_ASN1DecoderClearNotifyProc (p7dcx->dcx); | |
| 946 break; | |
| 947 } | |
| 948 } | |
| 949 | |
| 950 | |
| 951 SEC_PKCS7DecoderContext * | |
| 952 SEC_PKCS7DecoderStart(SEC_PKCS7DecoderContentCallback cb, void *cb_arg, | |
| 953 SECKEYGetPasswordKey pwfn, void *pwfn_arg, | |
| 954 SEC_PKCS7GetDecryptKeyCallback decrypt_key_cb, | |
| 955 void *decrypt_key_cb_arg, | |
| 956 SEC_PKCS7DecryptionAllowedCallback decrypt_allowed_cb) | |
| 957 { | |
| 958 SEC_PKCS7DecoderContext *p7dcx; | |
| 959 SEC_ASN1DecoderContext *dcx; | |
| 960 SEC_PKCS7ContentInfo *cinfo; | |
| 961 PLArenaPool *poolp; | |
| 962 | |
| 963 poolp = PORT_NewArena (1024); /* XXX what is right value? */ | |
| 964 if (poolp == NULL) | |
| 965 return NULL; | |
| 966 | |
| 967 cinfo = (SEC_PKCS7ContentInfo*)PORT_ArenaZAlloc (poolp, sizeof(*cinfo)); | |
| 968 if (cinfo == NULL) { | |
| 969 PORT_FreeArena (poolp, PR_FALSE); | |
| 970 return NULL; | |
| 971 } | |
| 972 | |
| 973 cinfo->poolp = poolp; | |
| 974 cinfo->pwfn = pwfn; | |
| 975 cinfo->pwfn_arg = pwfn_arg; | |
| 976 cinfo->created = PR_FALSE; | |
| 977 cinfo->refCount = 1; | |
| 978 | |
| 979 p7dcx = | |
| 980 (SEC_PKCS7DecoderContext*)PORT_ZAlloc (sizeof(SEC_PKCS7DecoderContext)); | |
| 981 if (p7dcx == NULL) { | |
| 982 PORT_FreeArena (poolp, PR_FALSE); | |
| 983 return NULL; | |
| 984 } | |
| 985 | |
| 986 p7dcx->tmp_poolp = PORT_NewArena (1024); /* XXX what is right value? */ | |
| 987 if (p7dcx->tmp_poolp == NULL) { | |
| 988 PORT_Free (p7dcx); | |
| 989 PORT_FreeArena (poolp, PR_FALSE); | |
| 990 return NULL; | |
| 991 } | |
| 992 | |
| 993 dcx = SEC_ASN1DecoderStart (poolp, cinfo, sec_PKCS7ContentInfoTemplate); | |
| 994 if (dcx == NULL) { | |
| 995 PORT_FreeArena (p7dcx->tmp_poolp, PR_FALSE); | |
| 996 PORT_Free (p7dcx); | |
| 997 PORT_FreeArena (poolp, PR_FALSE); | |
| 998 return NULL; | |
| 999 } | |
| 1000 | |
| 1001 SEC_ASN1DecoderSetNotifyProc (dcx, sec_pkcs7_decoder_notify, p7dcx); | |
| 1002 | |
| 1003 p7dcx->dcx = dcx; | |
| 1004 p7dcx->cinfo = cinfo; | |
| 1005 p7dcx->cb = cb; | |
| 1006 p7dcx->cb_arg = cb_arg; | |
| 1007 p7dcx->pwfn = pwfn; | |
| 1008 p7dcx->pwfn_arg = pwfn_arg; | |
| 1009 p7dcx->dkcb = decrypt_key_cb; | |
| 1010 p7dcx->dkcb_arg = decrypt_key_cb_arg; | |
| 1011 p7dcx->decrypt_allowed_cb = decrypt_allowed_cb; | |
| 1012 | |
| 1013 return p7dcx; | |
| 1014 } | |
| 1015 | |
| 1016 | |
| 1017 /* | |
| 1018 * Do the next chunk of PKCS7 decoding. If there is a problem, set | |
| 1019 * an error and return a failure status. Note that in the case of | |
| 1020 * an error, this routine is still prepared to be called again and | |
| 1021 * again in case that is the easiest route for our caller to take. | |
| 1022 * We simply detect it and do not do anything except keep setting | |
| 1023 * that error in case our caller has not noticed it yet... | |
| 1024 */ | |
| 1025 SECStatus | |
| 1026 SEC_PKCS7DecoderUpdate(SEC_PKCS7DecoderContext *p7dcx, | |
| 1027 const char *buf, unsigned long len) | |
| 1028 { | |
| 1029 if (p7dcx->cinfo != NULL && p7dcx->dcx != NULL) { | |
| 1030 PORT_Assert (p7dcx->error == 0); | |
| 1031 if (p7dcx->error == 0) { | |
| 1032 if (SEC_ASN1DecoderUpdate (p7dcx->dcx, buf, len) != SECSuccess) { | |
| 1033 p7dcx->error = PORT_GetError(); | |
| 1034 PORT_Assert (p7dcx->error); | |
| 1035 if (p7dcx->error == 0) | |
| 1036 p7dcx->error = -1; | |
| 1037 } | |
| 1038 } | |
| 1039 } | |
| 1040 | |
| 1041 if (p7dcx->error) { | |
| 1042 if (p7dcx->dcx != NULL) { | |
| 1043 (void) SEC_ASN1DecoderFinish (p7dcx->dcx); | |
| 1044 p7dcx->dcx = NULL; | |
| 1045 } | |
| 1046 if (p7dcx->cinfo != NULL) { | |
| 1047 SEC_PKCS7DestroyContentInfo (p7dcx->cinfo); | |
| 1048 p7dcx->cinfo = NULL; | |
| 1049 } | |
| 1050 PORT_SetError (p7dcx->error); | |
| 1051 return SECFailure; | |
| 1052 } | |
| 1053 | |
| 1054 return SECSuccess; | |
| 1055 } | |
| 1056 | |
| 1057 | |
| 1058 SEC_PKCS7ContentInfo * | |
| 1059 SEC_PKCS7DecoderFinish(SEC_PKCS7DecoderContext *p7dcx) | |
| 1060 { | |
| 1061 SEC_PKCS7ContentInfo *cinfo; | |
| 1062 | |
| 1063 cinfo = p7dcx->cinfo; | |
| 1064 if (p7dcx->dcx != NULL) { | |
| 1065 if (SEC_ASN1DecoderFinish (p7dcx->dcx) != SECSuccess) { | |
| 1066 SEC_PKCS7DestroyContentInfo (cinfo); | |
| 1067 cinfo = NULL; | |
| 1068 } | |
| 1069 } | |
| 1070 /* free any NSS data structures */ | |
| 1071 if (p7dcx->worker.decryptobj) { | |
| 1072 sec_PKCS7DestroyDecryptObject (p7dcx->worker.decryptobj); | |
| 1073 } | |
| 1074 PORT_FreeArena (p7dcx->tmp_poolp, PR_FALSE); | |
| 1075 PORT_Free (p7dcx); | |
| 1076 return cinfo; | |
| 1077 } | |
| 1078 | |
| 1079 | |
| 1080 SEC_PKCS7ContentInfo * | |
| 1081 SEC_PKCS7DecodeItem(SECItem *p7item, | |
| 1082 SEC_PKCS7DecoderContentCallback cb, void *cb_arg, | |
| 1083 SECKEYGetPasswordKey pwfn, void *pwfn_arg, | |
| 1084 SEC_PKCS7GetDecryptKeyCallback decrypt_key_cb, | |
| 1085 void *decrypt_key_cb_arg, | |
| 1086 SEC_PKCS7DecryptionAllowedCallback decrypt_allowed_cb) | |
| 1087 { | |
| 1088 SEC_PKCS7DecoderContext *p7dcx; | |
| 1089 | |
| 1090 p7dcx = SEC_PKCS7DecoderStart(cb, cb_arg, pwfn, pwfn_arg, decrypt_key_cb, | |
| 1091 decrypt_key_cb_arg, decrypt_allowed_cb); | |
| 1092 if (!p7dcx) { | |
| 1093 /* error code is set */ | |
| 1094 return NULL; | |
| 1095 } | |
| 1096 (void) SEC_PKCS7DecoderUpdate(p7dcx, (char *) p7item->data, p7item->len); | |
| 1097 return SEC_PKCS7DecoderFinish(p7dcx); | |
| 1098 } | |
| 1099 | |
| 1100 /* | |
| 1101 * Abort the ASN.1 stream. Used by pkcs 12 | |
| 1102 */ | |
| 1103 void | |
| 1104 SEC_PKCS7DecoderAbort(SEC_PKCS7DecoderContext *p7dcx, int error) | |
| 1105 { | |
| 1106 PORT_Assert(p7dcx); | |
| 1107 SEC_ASN1DecoderAbort(p7dcx->dcx, error); | |
| 1108 } | |
| 1109 | |
| 1110 | |
| 1111 /* | |
| 1112 * If the thing contains any certs or crls return true; false otherwise. | |
| 1113 */ | |
| 1114 PRBool | |
| 1115 SEC_PKCS7ContainsCertsOrCrls(SEC_PKCS7ContentInfo *cinfo) | |
| 1116 { | |
| 1117 SECOidTag kind; | |
| 1118 SECItem **certs; | |
| 1119 CERTSignedCrl **crls; | |
| 1120 | |
| 1121 kind = SEC_PKCS7ContentType (cinfo); | |
| 1122 switch (kind) { | |
| 1123 default: | |
| 1124 case SEC_OID_PKCS7_DATA: | |
| 1125 case SEC_OID_PKCS7_DIGESTED_DATA: | |
| 1126 case SEC_OID_PKCS7_ENVELOPED_DATA: | |
| 1127 case SEC_OID_PKCS7_ENCRYPTED_DATA: | |
| 1128 return PR_FALSE; | |
| 1129 case SEC_OID_PKCS7_SIGNED_DATA: | |
| 1130 certs = cinfo->content.signedData->rawCerts; | |
| 1131 crls = cinfo->content.signedData->crls; | |
| 1132 break; | |
| 1133 case SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA: | |
| 1134 certs = cinfo->content.signedAndEnvelopedData->rawCerts; | |
| 1135 crls = cinfo->content.signedAndEnvelopedData->crls; | |
| 1136 break; | |
| 1137 } | |
| 1138 | |
| 1139 /* | |
| 1140 * I know this could be collapsed, but I was in a mood to be explicit. | |
| 1141 */ | |
| 1142 if (certs != NULL && certs[0] != NULL) | |
| 1143 return PR_TRUE; | |
| 1144 else if (crls != NULL && crls[0] != NULL) | |
| 1145 return PR_TRUE; | |
| 1146 else | |
| 1147 return PR_FALSE; | |
| 1148 } | |
| 1149 | |
| 1150 /* return the content length...could use GetContent, however we | |
| 1151 * need the encrypted content length | |
| 1152 */ | |
| 1153 PRBool | |
| 1154 SEC_PKCS7IsContentEmpty(SEC_PKCS7ContentInfo *cinfo, unsigned int minLen) | |
| 1155 { | |
| 1156 SECItem *item = NULL; | |
| 1157 | |
| 1158 if(cinfo == NULL) { | |
| 1159 return PR_TRUE; | |
| 1160 } | |
| 1161 | |
| 1162 switch(SEC_PKCS7ContentType(cinfo)) | |
| 1163 { | |
| 1164 case SEC_OID_PKCS7_DATA: | |
| 1165 item = cinfo->content.data; | |
| 1166 break; | |
| 1167 case SEC_OID_PKCS7_ENCRYPTED_DATA: | |
| 1168 item = &cinfo->content.encryptedData->encContentInfo.encContent; | |
| 1169 break; | |
| 1170 default: | |
| 1171 /* add other types */ | |
| 1172 return PR_FALSE; | |
| 1173 } | |
| 1174 | |
| 1175 if(!item) { | |
| 1176 return PR_TRUE; | |
| 1177 } else if(item->len <= minLen) { | |
| 1178 return PR_TRUE; | |
| 1179 } | |
| 1180 | |
| 1181 return PR_FALSE; | |
| 1182 } | |
| 1183 | |
| 1184 | |
| 1185 PRBool | |
| 1186 SEC_PKCS7ContentIsEncrypted(SEC_PKCS7ContentInfo *cinfo) | |
| 1187 { | |
| 1188 SECOidTag kind; | |
| 1189 | |
| 1190 kind = SEC_PKCS7ContentType (cinfo); | |
| 1191 switch (kind) { | |
| 1192 default: | |
| 1193 case SEC_OID_PKCS7_DATA: | |
| 1194 case SEC_OID_PKCS7_DIGESTED_DATA: | |
| 1195 case SEC_OID_PKCS7_SIGNED_DATA: | |
| 1196 return PR_FALSE; | |
| 1197 case SEC_OID_PKCS7_ENCRYPTED_DATA: | |
| 1198 case SEC_OID_PKCS7_ENVELOPED_DATA: | |
| 1199 case SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA: | |
| 1200 return PR_TRUE; | |
| 1201 } | |
| 1202 } | |
| 1203 | |
| 1204 | |
| 1205 /* | |
| 1206 * If the PKCS7 content has a signature (not just *could* have a signature) | |
| 1207 * return true; false otherwise. This can/should be called before calling | |
| 1208 * VerifySignature, which will always indicate failure if no signature is | |
| 1209 * present, but that does not mean there even was a signature! | |
| 1210 * Note that the content itself can be empty (detached content was sent | |
| 1211 * another way); it is the presence of the signature that matters. | |
| 1212 */ | |
| 1213 PRBool | |
| 1214 SEC_PKCS7ContentIsSigned(SEC_PKCS7ContentInfo *cinfo) | |
| 1215 { | |
| 1216 SECOidTag kind; | |
| 1217 SEC_PKCS7SignerInfo **signerinfos; | |
| 1218 | |
| 1219 kind = SEC_PKCS7ContentType (cinfo); | |
| 1220 switch (kind) { | |
| 1221 default: | |
| 1222 case SEC_OID_PKCS7_DATA: | |
| 1223 case SEC_OID_PKCS7_DIGESTED_DATA: | |
| 1224 case SEC_OID_PKCS7_ENVELOPED_DATA: | |
| 1225 case SEC_OID_PKCS7_ENCRYPTED_DATA: | |
| 1226 return PR_FALSE; | |
| 1227 case SEC_OID_PKCS7_SIGNED_DATA: | |
| 1228 signerinfos = cinfo->content.signedData->signerInfos; | |
| 1229 break; | |
| 1230 case SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA: | |
| 1231 signerinfos = cinfo->content.signedAndEnvelopedData->signerInfos; | |
| 1232 break; | |
| 1233 } | |
| 1234 | |
| 1235 /* | |
| 1236 * I know this could be collapsed; but I kind of think it will get | |
| 1237 * more complicated before I am finished, so... | |
| 1238 */ | |
| 1239 if (signerinfos != NULL && signerinfos[0] != NULL) | |
| 1240 return PR_TRUE; | |
| 1241 else | |
| 1242 return PR_FALSE; | |
| 1243 } | |
| 1244 | |
| 1245 | |
| 1246 /* | |
| 1247 * sec_pkcs7_verify_signature | |
| 1248 * | |
| 1249 * Look at a PKCS7 contentInfo and check if the signature is good. | |
| 1250 * The digest was either calculated earlier (and is stored in the | |
| 1251 * contentInfo itself) or is passed in via "detached_digest". | |
| 1252 * | |
| 1253 * The verification checks that the signing cert is valid and trusted | |
| 1254 * for the purpose specified by "certusage" at | |
| 1255 * - "*atTime" if "atTime" is not null, or | |
| 1256 * - the signing time if the signing time is available in "cinfo", or | |
| 1257 * - the current time (as returned by PR_Now). | |
| 1258 * | |
| 1259 * In addition, if "keepcerts" is true, add any new certificates found | |
| 1260 * into our local database. | |
| 1261 * | |
| 1262 * XXX Each place which returns PR_FALSE should be sure to have a good | |
| 1263 * error set for inspection by the caller. Alternatively, we could create | |
| 1264 * an enumeration of success and each type of failure and return that | |
| 1265 * instead of a boolean. For now, the default in a bad situation is to | |
| 1266 * set the error to SEC_ERROR_PKCS7_BAD_SIGNATURE. But this should be | |
| 1267 * reviewed; better (more specific) errors should be possible (to distinguish | |
| 1268 * a signature failure from a badly-formed pkcs7 signedData, for example). | |
| 1269 * Some of the errors should probably just be SEC_ERROR_BAD_SIGNATURE, | |
| 1270 * but that has a less helpful error string associated with it right now; | |
| 1271 * if/when that changes, review and change these as needed. | |
| 1272 * | |
| 1273 * XXX This is broken wrt signedAndEnvelopedData. In that case, the | |
| 1274 * message digest is doubly encrypted -- first encrypted with the signer | |
| 1275 * private key but then again encrypted with the bulk encryption key used | |
| 1276 * to encrypt the content. So before we can pass the digest to VerifyDigest, | |
| 1277 * we need to decrypt it with the bulk encryption key. Also, in this case, | |
| 1278 * there should be NO authenticatedAttributes (signerinfo->authAttr should | |
| 1279 * be NULL). | |
| 1280 */ | |
| 1281 static PRBool | |
| 1282 sec_pkcs7_verify_signature(SEC_PKCS7ContentInfo *cinfo, | |
| 1283 SECCertUsage certusage, | |
| 1284 const SECItem *detached_digest, | |
| 1285 HASH_HashType digest_type, | |
| 1286 PRBool keepcerts, | |
| 1287 const PRTime *atTime) | |
| 1288 { | |
| 1289 SECAlgorithmID **digestalgs, *bulkid; | |
| 1290 const SECItem *digest; | |
| 1291 SECItem **digests; | |
| 1292 SECItem **rawcerts; | |
| 1293 SEC_PKCS7SignerInfo **signerinfos, *signerinfo; | |
| 1294 CERTCertificate *cert, **certs; | |
| 1295 PRBool goodsig; | |
| 1296 CERTCertDBHandle *certdb, *defaultdb; | |
| 1297 SECOidTag encTag,digestTag; | |
| 1298 HASH_HashType found_type; | |
| 1299 int i, certcount; | |
| 1300 SECKEYPublicKey *publickey; | |
| 1301 SECItem *content_type; | |
| 1302 PK11SymKey *sigkey; | |
| 1303 SECItem *encoded_stime; | |
| 1304 PRTime stime; | |
| 1305 PRTime verificationTime; | |
| 1306 SECStatus rv; | |
| 1307 | |
| 1308 /* | |
| 1309 * Everything needed in order to "goto done" safely. | |
| 1310 */ | |
| 1311 goodsig = PR_FALSE; | |
| 1312 certcount = 0; | |
| 1313 cert = NULL; | |
| 1314 certs = NULL; | |
| 1315 certdb = NULL; | |
| 1316 defaultdb = CERT_GetDefaultCertDB(); | |
| 1317 publickey = NULL; | |
| 1318 | |
| 1319 if (! SEC_PKCS7ContentIsSigned(cinfo)) { | |
| 1320 PORT_SetError (SEC_ERROR_PKCS7_BAD_SIGNATURE); | |
| 1321 goto done; | |
| 1322 } | |
| 1323 | |
| 1324 PORT_Assert (cinfo->contentTypeTag != NULL); | |
| 1325 | |
| 1326 switch (cinfo->contentTypeTag->offset) { | |
| 1327 default: | |
| 1328 case SEC_OID_PKCS7_DATA: | |
| 1329 case SEC_OID_PKCS7_DIGESTED_DATA: | |
| 1330 case SEC_OID_PKCS7_ENVELOPED_DATA: | |
| 1331 case SEC_OID_PKCS7_ENCRYPTED_DATA: | |
| 1332 /* Could only get here if SEC_PKCS7ContentIsSigned is broken. */ | |
| 1333 PORT_Assert (0); | |
| 1334 case SEC_OID_PKCS7_SIGNED_DATA: | |
| 1335 { | |
| 1336 SEC_PKCS7SignedData *sdp; | |
| 1337 | |
| 1338 sdp = cinfo->content.signedData; | |
| 1339 digestalgs = sdp->digestAlgorithms; | |
| 1340 digests = sdp->digests; | |
| 1341 rawcerts = sdp->rawCerts; | |
| 1342 signerinfos = sdp->signerInfos; | |
| 1343 content_type = &(sdp->contentInfo.contentType); | |
| 1344 sigkey = NULL; | |
| 1345 bulkid = NULL; | |
| 1346 } | |
| 1347 break; | |
| 1348 case SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA: | |
| 1349 { | |
| 1350 SEC_PKCS7SignedAndEnvelopedData *saedp; | |
| 1351 | |
| 1352 saedp = cinfo->content.signedAndEnvelopedData; | |
| 1353 digestalgs = saedp->digestAlgorithms; | |
| 1354 digests = saedp->digests; | |
| 1355 rawcerts = saedp->rawCerts; | |
| 1356 signerinfos = saedp->signerInfos; | |
| 1357 content_type = &(saedp->encContentInfo.contentType); | |
| 1358 sigkey = saedp->sigKey; | |
| 1359 bulkid = &(saedp->encContentInfo.contentEncAlg); | |
| 1360 } | |
| 1361 break; | |
| 1362 } | |
| 1363 | |
| 1364 if ((signerinfos == NULL) || (signerinfos[0] == NULL)) { | |
| 1365 PORT_SetError (SEC_ERROR_PKCS7_BAD_SIGNATURE); | |
| 1366 goto done; | |
| 1367 } | |
| 1368 | |
| 1369 /* | |
| 1370 * XXX Need to handle multiple signatures; checking them is easy, | |
| 1371 * but what should be the semantics here (like, return value)? | |
| 1372 */ | |
| 1373 if (signerinfos[1] != NULL) { | |
| 1374 PORT_SetError (SEC_ERROR_PKCS7_BAD_SIGNATURE); | |
| 1375 goto done; | |
| 1376 } | |
| 1377 | |
| 1378 signerinfo = signerinfos[0]; | |
| 1379 | |
| 1380 /* | |
| 1381 * XXX I would like to just pass the issuerAndSN, along with the rawcerts | |
| 1382 * and crls, to some function that did all of this certificate stuff | |
| 1383 * (open/close the database if necessary, verifying the certs, etc.) | |
| 1384 * and gave me back a cert pointer if all was good. | |
| 1385 */ | |
| 1386 certdb = defaultdb; | |
| 1387 if (certdb == NULL) { | |
| 1388 goto done; | |
| 1389 } | |
| 1390 | |
| 1391 certcount = 0; | |
| 1392 if (rawcerts != NULL) { | |
| 1393 for (; rawcerts[certcount] != NULL; certcount++) { | |
| 1394 /* just counting */ | |
| 1395 } | |
| 1396 } | |
| 1397 | |
| 1398 /* | |
| 1399 * Note that the result of this is that each cert in "certs" | |
| 1400 * needs to be destroyed. | |
| 1401 */ | |
| 1402 rv = CERT_ImportCerts(certdb, certusage, certcount, rawcerts, &certs, | |
| 1403 keepcerts, PR_FALSE, NULL); | |
| 1404 if ( rv != SECSuccess ) { | |
| 1405 goto done; | |
| 1406 } | |
| 1407 | |
| 1408 /* | |
| 1409 * This cert will also need to be freed, but since we save it | |
| 1410 * in signerinfo for later, we do not want to destroy it when | |
| 1411 * we leave this function -- we let the clean-up of the entire | |
| 1412 * cinfo structure later do the destroy of this cert. | |
| 1413 */ | |
| 1414 cert = CERT_FindCertByIssuerAndSN(certdb, signerinfo->issuerAndSN); | |
| 1415 if (cert == NULL) { | |
| 1416 goto done; | |
| 1417 } | |
| 1418 | |
| 1419 signerinfo->cert = cert; | |
| 1420 | |
| 1421 /* | |
| 1422 * Get and convert the signing time; if available, it will be used | |
| 1423 * both on the cert verification and for importing the sender | |
| 1424 * email profile. | |
| 1425 */ | |
| 1426 encoded_stime = SEC_PKCS7GetSigningTime (cinfo); | |
| 1427 if (encoded_stime != NULL) { | |
| 1428 if (DER_DecodeTimeChoice (&stime, encoded_stime) != SECSuccess) | |
| 1429 encoded_stime = NULL; /* conversion failed, so pretend none */ | |
| 1430 } | |
| 1431 | |
| 1432 /* | |
| 1433 * XXX This uses the signing time, if available. Additionally, we | |
| 1434 * might want to, if there is no signing time, get the message time | |
| 1435 * from the mail header itself, and use that. That would require | |
| 1436 * a change to our interface though, and for S/MIME callers to pass | |
| 1437 * in a time (and for non-S/MIME callers to pass in nothing, or | |
| 1438 * maybe make them pass in the current time, always?). | |
| 1439 */ | |
| 1440 if (atTime) { | |
| 1441 verificationTime = *atTime; | |
| 1442 } else if (encoded_stime != NULL) { | |
| 1443 verificationTime = stime; | |
| 1444 } else { | |
| 1445 verificationTime = PR_Now(); | |
| 1446 } | |
| 1447 if (CERT_VerifyCert (certdb, cert, PR_TRUE, certusage, verificationTime, | |
| 1448 cinfo->pwfn_arg, NULL) != SECSuccess) | |
| 1449 { | |
| 1450 /* | |
| 1451 * XXX Give the user an option to check the signature anyway? | |
| 1452 * If we want to do this, need to give a way to leave and display | |
| 1453 * some dialog and get the answer and come back through (or do | |
| 1454 * the rest of what we do below elsewhere, maybe by putting it | |
| 1455 * in a function that we call below and could call from a dialog | |
| 1456 * finish handler). | |
| 1457 */ | |
| 1458 goto savecert; | |
| 1459 } | |
| 1460 | |
| 1461 publickey = CERT_ExtractPublicKey (cert); | |
| 1462 if (publickey == NULL) | |
| 1463 goto done; | |
| 1464 | |
| 1465 /* | |
| 1466 * XXX No! If digests is empty, see if we can create it now by | |
| 1467 * digesting the contents. This is necessary if we want to allow | |
| 1468 * somebody to do a simple decode (without filtering, etc.) and | |
| 1469 * then later call us here to do the verification. | |
| 1470 * OR, we can just specify that the interface to this routine | |
| 1471 * *requires* that the digest(s) be done before calling and either | |
| 1472 * stashed in the struct itself or passed in explicitly (as would | |
| 1473 * be done for detached contents). | |
| 1474 */ | |
| 1475 if ((digests == NULL || digests[0] == NULL) | |
| 1476 && (detached_digest == NULL || detached_digest->data == NULL)) | |
| 1477 goto done; | |
| 1478 | |
| 1479 /* | |
| 1480 * Find and confirm digest algorithm. | |
| 1481 */ | |
| 1482 digestTag = SECOID_FindOIDTag(&(signerinfo->digestAlg.algorithm)); | |
| 1483 | |
| 1484 /* make sure we understand the digest type first */ | |
| 1485 found_type = HASH_GetHashTypeByOidTag(digestTag); | |
| 1486 if ((digestTag == SEC_OID_UNKNOWN) || (found_type == HASH_AlgNULL)) { | |
| 1487 PORT_SetError (SEC_ERROR_PKCS7_BAD_SIGNATURE); | |
| 1488 goto done; | |
| 1489 } | |
| 1490 | |
| 1491 if (detached_digest != NULL) { | |
| 1492 unsigned int hashLen = HASH_ResultLen(found_type); | |
| 1493 | |
| 1494 if (digest_type != found_type || | |
| 1495 detached_digest->len != hashLen) { | |
| 1496 PORT_SetError (SEC_ERROR_PKCS7_BAD_SIGNATURE); | |
| 1497 goto done; | |
| 1498 } | |
| 1499 digest = detached_digest; | |
| 1500 } else { | |
| 1501 PORT_Assert (digestalgs != NULL && digestalgs[0] != NULL); | |
| 1502 if (digestalgs == NULL || digestalgs[0] == NULL) { | |
| 1503 PORT_SetError (SEC_ERROR_PKCS7_BAD_SIGNATURE); | |
| 1504 goto done; | |
| 1505 } | |
| 1506 | |
| 1507 /* | |
| 1508 * pick digest matching signerinfo->digestAlg from digests | |
| 1509 */ | |
| 1510 for (i = 0; digestalgs[i] != NULL; i++) { | |
| 1511 if (SECOID_FindOIDTag(&(digestalgs[i]->algorithm)) == digestTag) | |
| 1512 break; | |
| 1513 } | |
| 1514 if (digestalgs[i] == NULL) { | |
| 1515 PORT_SetError (SEC_ERROR_PKCS7_BAD_SIGNATURE); | |
| 1516 goto done; | |
| 1517 } | |
| 1518 | |
| 1519 digest = digests[i]; | |
| 1520 } | |
| 1521 | |
| 1522 encTag = SECOID_FindOIDTag(&(signerinfo->digestEncAlg.algorithm)); | |
| 1523 if (encTag == SEC_OID_UNKNOWN) { | |
| 1524 PORT_SetError (SEC_ERROR_PKCS7_BAD_SIGNATURE); | |
| 1525 goto done; | |
| 1526 } | |
| 1527 | |
| 1528 if (signerinfo->authAttr != NULL) { | |
| 1529 SEC_PKCS7Attribute *attr; | |
| 1530 SECItem *value; | |
| 1531 SECItem encoded_attrs; | |
| 1532 | |
| 1533 /* | |
| 1534 * We have a sigkey only for signedAndEnvelopedData, which is | |
| 1535 * not supposed to have any authenticated attributes. | |
| 1536 */ | |
| 1537 if (sigkey != NULL) { | |
| 1538 PORT_SetError (SEC_ERROR_PKCS7_BAD_SIGNATURE); | |
| 1539 goto done; | |
| 1540 } | |
| 1541 | |
| 1542 /* | |
| 1543 * PKCS #7 says that if there are any authenticated attributes, | |
| 1544 * then there must be one for content type which matches the | |
| 1545 * content type of the content being signed, and there must | |
| 1546 * be one for message digest which matches our message digest. | |
| 1547 * So check these things first. | |
| 1548 * XXX Might be nice to have a compare-attribute-value function | |
| 1549 * which could collapse the following nicely. | |
| 1550 */ | |
| 1551 attr = sec_PKCS7FindAttribute (signerinfo->authAttr, | |
| 1552 SEC_OID_PKCS9_CONTENT_TYPE, PR_TRUE); | |
| 1553 value = sec_PKCS7AttributeValue (attr); | |
| 1554 if (value == NULL || value->len != content_type->len) { | |
| 1555 PORT_SetError (SEC_ERROR_PKCS7_BAD_SIGNATURE); | |
| 1556 goto done; | |
| 1557 } | |
| 1558 if (PORT_Memcmp (value->data, content_type->data, value->len) != 0) { | |
| 1559 PORT_SetError (SEC_ERROR_PKCS7_BAD_SIGNATURE); | |
| 1560 goto done; | |
| 1561 } | |
| 1562 | |
| 1563 attr = sec_PKCS7FindAttribute (signerinfo->authAttr, | |
| 1564 SEC_OID_PKCS9_MESSAGE_DIGEST, PR_TRUE); | |
| 1565 value = sec_PKCS7AttributeValue (attr); | |
| 1566 if (value == NULL || value->len != digest->len) { | |
| 1567 PORT_SetError (SEC_ERROR_PKCS7_BAD_SIGNATURE); | |
| 1568 goto done; | |
| 1569 } | |
| 1570 if (PORT_Memcmp (value->data, digest->data, value->len) != 0) { | |
| 1571 PORT_SetError (SEC_ERROR_PKCS7_BAD_SIGNATURE); | |
| 1572 goto done; | |
| 1573 } | |
| 1574 | |
| 1575 /* | |
| 1576 * Okay, we met the constraints of the basic attributes. | |
| 1577 * Now check the signature, which is based on a digest of | |
| 1578 * the DER-encoded authenticated attributes. So, first we | |
| 1579 * encode and then we digest/verify. | |
| 1580 */ | |
| 1581 encoded_attrs.data = NULL; | |
| 1582 encoded_attrs.len = 0; | |
| 1583 if (sec_PKCS7EncodeAttributes (NULL, &encoded_attrs, | |
| 1584 &(signerinfo->authAttr)) == NULL) | |
| 1585 goto done; | |
| 1586 | |
| 1587 if (encoded_attrs.data == NULL || encoded_attrs.len == 0) { | |
| 1588 PORT_SetError (SEC_ERROR_PKCS7_BAD_SIGNATURE); | |
| 1589 goto done; | |
| 1590 } | |
| 1591 | |
| 1592 goodsig = (PRBool)(VFY_VerifyDataDirect(encoded_attrs.data, | |
| 1593 encoded_attrs.len, | |
| 1594 publickey, &(signerinfo->encDigest), | |
| 1595 encTag, digestTag, NULL, | |
| 1596 cinfo->pwfn_arg) == SECSuccess); | |
| 1597 PORT_Free (encoded_attrs.data); | |
| 1598 } else { | |
| 1599 SECItem *sig; | |
| 1600 SECItem holder; | |
| 1601 SECStatus rv; | |
| 1602 | |
| 1603 /* | |
| 1604 * No authenticated attributes. | |
| 1605 * The signature is based on the plain message digest. | |
| 1606 */ | |
| 1607 | |
| 1608 sig = &(signerinfo->encDigest); | |
| 1609 if (sig->len == 0) { /* bad signature */ | |
| 1610 PORT_SetError (SEC_ERROR_PKCS7_BAD_SIGNATURE); | |
| 1611 goto done; | |
| 1612 } | |
| 1613 | |
| 1614 if (sigkey != NULL) { | |
| 1615 sec_PKCS7CipherObject *decryptobj; | |
| 1616 unsigned int buflen; | |
| 1617 | |
| 1618 /* | |
| 1619 * For signedAndEnvelopedData, we first must decrypt the encrypted | |
| 1620 * digest with the bulk encryption key. The result is the normal | |
| 1621 * encrypted digest (aka the signature). | |
| 1622 */ | |
| 1623 decryptobj = sec_PKCS7CreateDecryptObject (sigkey, bulkid); | |
| 1624 if (decryptobj == NULL) | |
| 1625 goto done; | |
| 1626 | |
| 1627 buflen = sec_PKCS7DecryptLength (decryptobj, sig->len, PR_TRUE); | |
| 1628 PORT_Assert (buflen); | |
| 1629 if (buflen == 0) { /* something is wrong */ | |
| 1630 sec_PKCS7DestroyDecryptObject (decryptobj); | |
| 1631 goto done; | |
| 1632 } | |
| 1633 | |
| 1634 holder.data = (unsigned char*)PORT_Alloc (buflen); | |
| 1635 if (holder.data == NULL) { | |
| 1636 sec_PKCS7DestroyDecryptObject (decryptobj); | |
| 1637 goto done; | |
| 1638 } | |
| 1639 | |
| 1640 rv = sec_PKCS7Decrypt (decryptobj, holder.data, &holder.len, buflen, | |
| 1641 sig->data, sig->len, PR_TRUE); | |
| 1642 sec_PKCS7DestroyDecryptObject (decryptobj); | |
| 1643 if (rv != SECSuccess) { | |
| 1644 goto done; | |
| 1645 } | |
| 1646 | |
| 1647 sig = &holder; | |
| 1648 } | |
| 1649 | |
| 1650 goodsig = (PRBool)(VFY_VerifyDigestDirect(digest, publickey, sig, | |
| 1651 encTag, digestTag, cinfo->pwfn_arg) | |
| 1652 == SECSuccess); | |
| 1653 | |
| 1654 if (sigkey != NULL) { | |
| 1655 PORT_Assert (sig == &holder); | |
| 1656 PORT_ZFree (holder.data, holder.len); | |
| 1657 } | |
| 1658 } | |
| 1659 | |
| 1660 if (! goodsig) { | |
| 1661 /* | |
| 1662 * XXX Change the generic error into our specific one, because | |
| 1663 * in that case we get a better explanation out of the Security | |
| 1664 * Advisor. This is really a bug in our error strings (the | |
| 1665 * "generic" error has a lousy/wrong message associated with it | |
| 1666 * which assumes the signature verification was done for the | |
| 1667 * purposes of checking the issuer signature on a certificate) | |
| 1668 * but this is at least an easy workaround and/or in the | |
| 1669 * Security Advisor, which specifically checks for the error | |
| 1670 * SEC_ERROR_PKCS7_BAD_SIGNATURE and gives more explanation | |
| 1671 * in that case but does not similarly check for | |
| 1672 * SEC_ERROR_BAD_SIGNATURE. It probably should, but then would | |
| 1673 * probably say the wrong thing in the case that it *was* the | |
| 1674 * certificate signature check that failed during the cert | |
| 1675 * verification done above. Our error handling is really a mess. | |
| 1676 */ | |
| 1677 if (PORT_GetError() == SEC_ERROR_BAD_SIGNATURE) | |
| 1678 PORT_SetError (SEC_ERROR_PKCS7_BAD_SIGNATURE); | |
| 1679 } | |
| 1680 | |
| 1681 savecert: | |
| 1682 /* | |
| 1683 * Only save the smime profile if we are checking an email message and | |
| 1684 * the cert has an email address in it. | |
| 1685 */ | |
| 1686 if ( cert->emailAddr && cert->emailAddr[0] && | |
| 1687 ( ( certusage == certUsageEmailSigner ) || | |
| 1688 ( certusage == certUsageEmailRecipient ) ) ) { | |
| 1689 SECItem *profile = NULL; | |
| 1690 int save_error; | |
| 1691 | |
| 1692 /* | |
| 1693 * Remember the current error set because we do not care about | |
| 1694 * anything set by the functions we are about to call. | |
| 1695 */ | |
| 1696 save_error = PORT_GetError(); | |
| 1697 | |
| 1698 if (goodsig && (signerinfo->authAttr != NULL)) { | |
| 1699 /* | |
| 1700 * If the signature is good, then we can save the S/MIME profile, | |
| 1701 * if we have one. | |
| 1702 */ | |
| 1703 SEC_PKCS7Attribute *attr; | |
| 1704 | |
| 1705 attr = sec_PKCS7FindAttribute (signerinfo->authAttr, | |
| 1706 SEC_OID_PKCS9_SMIME_CAPABILITIES, | |
| 1707 PR_TRUE); | |
| 1708 profile = sec_PKCS7AttributeValue (attr); | |
| 1709 } | |
| 1710 | |
| 1711 rv = CERT_SaveSMimeProfile (cert, profile, encoded_stime); | |
| 1712 | |
| 1713 /* | |
| 1714 * Restore the saved error in case the calls above set a new | |
| 1715 * one that we do not actually care about. | |
| 1716 */ | |
| 1717 PORT_SetError (save_error); | |
| 1718 | |
| 1719 /* | |
| 1720 * XXX Failure is not indicated anywhere -- the signature | |
| 1721 * verification itself is unaffected by whether or not the | |
| 1722 * profile was successfully saved. | |
| 1723 */ | |
| 1724 } | |
| 1725 | |
| 1726 | |
| 1727 done: | |
| 1728 | |
| 1729 /* | |
| 1730 * See comment above about why we do not want to destroy cert | |
| 1731 * itself here. | |
| 1732 */ | |
| 1733 | |
| 1734 if (certs != NULL) | |
| 1735 CERT_DestroyCertArray (certs, certcount); | |
| 1736 | |
| 1737 if (publickey != NULL) | |
| 1738 SECKEY_DestroyPublicKey (publickey); | |
| 1739 | |
| 1740 return goodsig; | |
| 1741 } | |
| 1742 | |
| 1743 /* | |
| 1744 * SEC_PKCS7VerifySignature | |
| 1745 * Look at a PKCS7 contentInfo and check if the signature is good. | |
| 1746 * The verification checks that the signing cert is valid and trusted | |
| 1747 * for the purpose specified by "certusage". | |
| 1748 * | |
| 1749 * In addition, if "keepcerts" is true, add any new certificates found | |
| 1750 * into our local database. | |
| 1751 */ | |
| 1752 PRBool | |
| 1753 SEC_PKCS7VerifySignature(SEC_PKCS7ContentInfo *cinfo, | |
| 1754 SECCertUsage certusage, | |
| 1755 PRBool keepcerts) | |
| 1756 { | |
| 1757 return sec_pkcs7_verify_signature (cinfo, certusage, | |
| 1758 NULL, HASH_AlgNULL, keepcerts, NULL); | |
| 1759 } | |
| 1760 | |
| 1761 /* | |
| 1762 * SEC_PKCS7VerifyDetachedSignature | |
| 1763 * Look at a PKCS7 contentInfo and check if the signature matches | |
| 1764 * a passed-in digest (calculated, supposedly, from detached contents). | |
| 1765 * The verification checks that the signing cert is valid and trusted | |
| 1766 * for the purpose specified by "certusage". | |
| 1767 * | |
| 1768 * In addition, if "keepcerts" is true, add any new certificates found | |
| 1769 * into our local database. | |
| 1770 */ | |
| 1771 PRBool | |
| 1772 SEC_PKCS7VerifyDetachedSignature(SEC_PKCS7ContentInfo *cinfo, | |
| 1773 SECCertUsage certusage, | |
| 1774 const SECItem *detached_digest, | |
| 1775 HASH_HashType digest_type, | |
| 1776 PRBool keepcerts) | |
| 1777 { | |
| 1778 return sec_pkcs7_verify_signature (cinfo, certusage, | |
| 1779 detached_digest, digest_type, | |
| 1780 keepcerts, NULL); | |
| 1781 } | |
| 1782 | |
| 1783 /* | |
| 1784 * SEC_PKCS7VerifyDetachedSignatureAtTime | |
| 1785 * Look at a PKCS7 contentInfo and check if the signature matches | |
| 1786 * a passed-in digest (calculated, supposedly, from detached contents). | |
| 1787 * The verification checks that the signing cert is valid and trusted | |
| 1788 * for the purpose specified by "certusage" at time "atTime". | |
| 1789 * | |
| 1790 * In addition, if "keepcerts" is true, add any new certificates found | |
| 1791 * into our local database. | |
| 1792 */ | |
| 1793 PRBool | |
| 1794 SEC_PKCS7VerifyDetachedSignatureAtTime(SEC_PKCS7ContentInfo *cinfo, | |
| 1795 SECCertUsage certusage, | |
| 1796 const SECItem *detached_digest, | |
| 1797 HASH_HashType digest_type, | |
| 1798 PRBool keepcerts, | |
| 1799 PRTime atTime) | |
| 1800 { | |
| 1801 return sec_pkcs7_verify_signature (cinfo, certusage, | |
| 1802 detached_digest, digest_type, | |
| 1803 keepcerts, &atTime); | |
| 1804 } | |
| 1805 | |
| 1806 /* | |
| 1807 * Return the asked-for portion of the name of the signer of a PKCS7 | |
| 1808 * signed object. | |
| 1809 * | |
| 1810 * Returns a pointer to allocated memory, which must be freed. | |
| 1811 * A NULL return value is an error. | |
| 1812 */ | |
| 1813 | |
| 1814 #define sec_common_name 1 | |
| 1815 #define sec_email_address 2 | |
| 1816 | |
| 1817 static char * | |
| 1818 sec_pkcs7_get_signer_cert_info(SEC_PKCS7ContentInfo *cinfo, int selector) | |
| 1819 { | |
| 1820 SECOidTag kind; | |
| 1821 SEC_PKCS7SignerInfo **signerinfos; | |
| 1822 CERTCertificate *signercert; | |
| 1823 char *container; | |
| 1824 | |
| 1825 kind = SEC_PKCS7ContentType (cinfo); | |
| 1826 switch (kind) { | |
| 1827 default: | |
| 1828 case SEC_OID_PKCS7_DATA: | |
| 1829 case SEC_OID_PKCS7_DIGESTED_DATA: | |
| 1830 case SEC_OID_PKCS7_ENVELOPED_DATA: | |
| 1831 case SEC_OID_PKCS7_ENCRYPTED_DATA: | |
| 1832 PORT_Assert (0); | |
| 1833 return NULL; | |
| 1834 case SEC_OID_PKCS7_SIGNED_DATA: | |
| 1835 { | |
| 1836 SEC_PKCS7SignedData *sdp; | |
| 1837 | |
| 1838 sdp = cinfo->content.signedData; | |
| 1839 signerinfos = sdp->signerInfos; | |
| 1840 } | |
| 1841 break; | |
| 1842 case SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA: | |
| 1843 { | |
| 1844 SEC_PKCS7SignedAndEnvelopedData *saedp; | |
| 1845 | |
| 1846 saedp = cinfo->content.signedAndEnvelopedData; | |
| 1847 signerinfos = saedp->signerInfos; | |
| 1848 } | |
| 1849 break; | |
| 1850 } | |
| 1851 | |
| 1852 if (signerinfos == NULL || signerinfos[0] == NULL) | |
| 1853 return NULL; | |
| 1854 | |
| 1855 signercert = signerinfos[0]->cert; | |
| 1856 | |
| 1857 /* | |
| 1858 * No cert there; see if we can find one by calling verify ourselves. | |
| 1859 */ | |
| 1860 if (signercert == NULL) { | |
| 1861 /* | |
| 1862 * The cert usage does not matter in this case, because we do not | |
| 1863 * actually care about the verification itself, but we have to pick | |
| 1864 * some valid usage to pass in. | |
| 1865 */ | |
| 1866 (void) sec_pkcs7_verify_signature (cinfo, certUsageEmailSigner, | |
| 1867 NULL, HASH_AlgNULL, PR_FALSE, NULL); | |
| 1868 signercert = signerinfos[0]->cert; | |
| 1869 if (signercert == NULL) | |
| 1870 return NULL; | |
| 1871 } | |
| 1872 | |
| 1873 switch (selector) { | |
| 1874 case sec_common_name: | |
| 1875 container = CERT_GetCommonName (&signercert->subject); | |
| 1876 break; | |
| 1877 case sec_email_address: | |
| 1878 if(signercert->emailAddr && signercert->emailAddr[0]) { | |
| 1879 container = PORT_Strdup(signercert->emailAddr); | |
| 1880 } else { | |
| 1881 container = NULL; | |
| 1882 } | |
| 1883 break; | |
| 1884 default: | |
| 1885 PORT_Assert (0); | |
| 1886 container = NULL; | |
| 1887 break; | |
| 1888 } | |
| 1889 | |
| 1890 return container; | |
| 1891 } | |
| 1892 | |
| 1893 char * | |
| 1894 SEC_PKCS7GetSignerCommonName(SEC_PKCS7ContentInfo *cinfo) | |
| 1895 { | |
| 1896 return sec_pkcs7_get_signer_cert_info(cinfo, sec_common_name); | |
| 1897 } | |
| 1898 | |
| 1899 char * | |
| 1900 SEC_PKCS7GetSignerEmailAddress(SEC_PKCS7ContentInfo *cinfo) | |
| 1901 { | |
| 1902 return sec_pkcs7_get_signer_cert_info(cinfo, sec_email_address); | |
| 1903 } | |
| 1904 | |
| 1905 | |
| 1906 /* | |
| 1907 * Return the signing time, in UTCTime format, of a PKCS7 contentInfo. | |
| 1908 */ | |
| 1909 SECItem * | |
| 1910 SEC_PKCS7GetSigningTime(SEC_PKCS7ContentInfo *cinfo) | |
| 1911 { | |
| 1912 SEC_PKCS7SignerInfo **signerinfos; | |
| 1913 SEC_PKCS7Attribute *attr; | |
| 1914 | |
| 1915 if (SEC_PKCS7ContentType (cinfo) != SEC_OID_PKCS7_SIGNED_DATA) | |
| 1916 return NULL; | |
| 1917 | |
| 1918 signerinfos = cinfo->content.signedData->signerInfos; | |
| 1919 | |
| 1920 /* | |
| 1921 * No signature, or more than one, means no deal. | |
| 1922 */ | |
| 1923 if (signerinfos == NULL || signerinfos[0] == NULL || signerinfos[1] != NULL) | |
| 1924 return NULL; | |
| 1925 | |
| 1926 attr = sec_PKCS7FindAttribute (signerinfos[0]->authAttr, | |
| 1927 SEC_OID_PKCS9_SIGNING_TIME, PR_TRUE); | |
| 1928 return sec_PKCS7AttributeValue (attr); | |
| 1929 } | |
| OLD | NEW |