Wed, 31 Dec 2014 06:09:35 +0100
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 | } |