security/nss/lib/pkcs7/p7decode.c

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

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

mercurial