p7decode.c
来自「支持SSL v2/v3, TLS, PKCS #5, PKCS #7, PKCS」· C语言 代码 · 共 2,088 行 · 第 1/4 页
C
2,088 行
/* * The contents of this file are subject to the Mozilla Public * License Version 1.1 (the "License"); you may not use this file * except in compliance with the License. You may obtain a copy of * the License at http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or * implied. See the License for the specific language governing * rights and limitations under the License. * * The Original Code is the Netscape security libraries. * * The Initial Developer of the Original Code is Netscape * Communications Corporation. Portions created by Netscape are * Copyright (C) 1994-2000 Netscape Communications Corporation. All * Rights Reserved. * * Contributor(s): * * Alternatively, the contents of this file may be used under the * terms of the GNU General Public License Version 2 or later (the * "GPL"), in which case the provisions of the GPL are applicable * instead of those above. If you wish to allow use of your * version of this file only under the terms of the GPL and not to * allow others to use your version of this file under the MPL, * indicate your decision by deleting the provisions above and * replace them with the notice and other provisions required by * the GPL. If you do not delete the provisions above, a recipient * may use your version of this file under either the MPL or the * GPL. *//* * PKCS7 decoding, verification. * * $Id: p7decode.c,v 1.1 2000/03/31 19:16:05 relyea%netscape.com Exp $ */#include "p7local.h"#include "cert.h" /* XXX do not want to have to include */#include "certdb.h" /* certdb.h -- the trust stuff needed by */ /* the add certificate code needs to get */ /* rewritten/abstracted and then this */ /* include should be removed! */#include "cdbhdl.h"#include "cryptohi.h"#include "key.h"#include "secasn1.h"#include "secitem.h"#include "secoid.h"#include "pk11func.h"#include "prtime.h"#include "secerr.h"struct sec_pkcs7_decoder_worker { int depth; int digcnt; void **digcxs; SECHashObject **digobjs; sec_PKCS7CipherObject *decryptobj; PRBool saw_contents;};struct SEC_PKCS7DecoderContextStr { SEC_ASN1DecoderContext *dcx; SEC_PKCS7ContentInfo *cinfo; SEC_PKCS7DecoderContentCallback cb; void *cb_arg; SECKEYGetPasswordKey pwfn; void *pwfn_arg; struct sec_pkcs7_decoder_worker worker; PRArenaPool *tmp_poolp; int error; SEC_PKCS7GetDecryptKeyCallback dkcb; void *dkcb_arg; SEC_PKCS7DecryptionAllowedCallback decrypt_allowed_cb;};/* * Handle one worker, decrypting and digesting the data as necessary. * * XXX If/when we support nested contents, this probably needs to be * revised somewhat to get passed the content-info (which unfortunately * can be two different types depending on whether it is encrypted or not) * corresponding to the given worker. */static voidsec_pkcs7_decoder_work_data (SEC_PKCS7DecoderContext *p7dcx, struct sec_pkcs7_decoder_worker *worker, const unsigned char *data, unsigned long len, PRBool final){ unsigned char *buf = NULL; SECStatus rv; int i; /* * We should really have data to process, or we should be trying * to finish/flush the last block. (This is an overly paranoid * check since all callers are in this file and simple inspection * proves they do it right. But it could find a bug in future * modifications/development, that is why it is here.) */ PORT_Assert ((data != NULL && len) || final); /* * Decrypt this chunk. * * XXX If we get an error, we do not want to do the digest or callback, * but we want to keep decoding. Or maybe we want to stop decoding * altogether if there is a callback, because obviously we are not * sending the data back and they want to know that. */ if (worker->decryptobj != NULL) { /* XXX the following lengths should all be longs? */ unsigned int inlen; /* length of data being decrypted */ unsigned int outlen; /* length of decrypted data */ unsigned int buflen; /* length available for decrypted data */ SECItem *plain; inlen = len; buflen = sec_PKCS7DecryptLength (worker->decryptobj, inlen, final); if (buflen == 0) { if (inlen == 0) /* no input and no output */ return; /* * No output is expected, but the input data may be buffered * so we still have to call Decrypt. */ rv = sec_PKCS7Decrypt (worker->decryptobj, NULL, NULL, 0, data, inlen, final); if (rv != SECSuccess) { p7dcx->error = PORT_GetError(); return; /* XXX indicate error? */ } return; } if (p7dcx->cb != NULL) { buf = (unsigned char *) PORT_Alloc (buflen); plain = NULL; } else { unsigned long oldlen; /* * XXX This assumes one level of content only. * See comment above about nested content types. * XXX Also, it should work for signedAndEnvelopedData, too! */ plain = &(p7dcx->cinfo-> content.envelopedData->encContentInfo.plainContent); oldlen = plain->len; if (oldlen == 0) { buf = (unsigned char*)PORT_ArenaAlloc (p7dcx->cinfo->poolp, buflen); } else { buf = (unsigned char*)PORT_ArenaGrow (p7dcx->cinfo->poolp, plain->data, oldlen, oldlen + buflen); if (buf != NULL) buf += oldlen; } plain->data = buf; } if (buf == NULL) { p7dcx->error = SEC_ERROR_NO_MEMORY; return; /* XXX indicate error? */ } rv = sec_PKCS7Decrypt (worker->decryptobj, buf, &outlen, buflen, data, inlen, final); if (rv != SECSuccess) { p7dcx->error = PORT_GetError(); return; /* XXX indicate error? */ } if (plain != NULL) { PORT_Assert (final || outlen == buflen); plain->len += outlen; } data = buf; len = outlen; } /* * Update the running digests. */ if (len) { for (i = 0; i < worker->digcnt; i++) { (* worker->digobjs[i]->update) (worker->digcxs[i], data, len); } } /* * Pass back the contents bytes, and free the temporary buffer. */ if (p7dcx->cb != NULL) { if (len) (* p7dcx->cb) (p7dcx->cb_arg, (const char *)data, len); if (worker->decryptobj != NULL) { PORT_Assert (buf != NULL); PORT_Free (buf); } }}static voidsec_pkcs7_decoder_filter (void *arg, const char *data, unsigned long len, int depth, SEC_ASN1EncodingPart data_kind){ SEC_PKCS7DecoderContext *p7dcx; struct sec_pkcs7_decoder_worker *worker; /* * Since we do not handle any nested contents, the only bytes we * are really interested in are the actual contents bytes (not * the identifier, length, or end-of-contents bytes). If we were * handling nested types we would probably need to do something * smarter based on depth and data_kind. */ if (data_kind != SEC_ASN1_Contents) return; /* * The ASN.1 decoder should not even call us with a length of 0. * Just being paranoid. */ PORT_Assert (len); if (len == 0) return; p7dcx = (SEC_PKCS7DecoderContext*)arg; /* * Handling nested contents would mean that there is a chain * of workers -- one per each level of content. The following * would start with the first worker and loop over them. */ worker = &(p7dcx->worker); worker->saw_contents = PR_TRUE; sec_pkcs7_decoder_work_data (p7dcx, worker, (const unsigned char *) data, len, PR_FALSE);}/* * Create digest contexts for each algorithm in "digestalgs". * No algorithms is not an error, we just do not do anything. * An error (like trouble allocating memory), marks the error * in "p7dcx" and returns SECFailure, which means that our caller * should just give up altogether. */static SECStatussec_pkcs7_decoder_start_digests (SEC_PKCS7DecoderContext *p7dcx, int depth, SECAlgorithmID **digestalgs){ SECAlgorithmID *algid; SECOidData *oiddata; SECHashObject *digobj; void *digcx; int i, digcnt; if (digestalgs == NULL) return SECSuccess; /* * Count the algorithms. */ digcnt = 0; while (digestalgs[digcnt] != NULL) digcnt++; /* * No algorithms means no work to do. * This is not expected, so cause an assert. * But if it does happen, just act as if there were * no algorithms specified. */ PORT_Assert (digcnt != 0); if (digcnt == 0) return SECSuccess; p7dcx->worker.digcxs = (void**)PORT_ArenaAlloc (p7dcx->tmp_poolp, digcnt * sizeof (void *)); p7dcx->worker.digobjs = (SECHashObject**)PORT_ArenaAlloc (p7dcx->tmp_poolp, digcnt * sizeof (SECHashObject *)); if (p7dcx->worker.digcxs == NULL || p7dcx->worker.digobjs == NULL) { p7dcx->error = SEC_ERROR_NO_MEMORY; return SECFailure; } p7dcx->worker.depth = depth; p7dcx->worker.digcnt = 0; /* * Create a digest context for each algorithm. */ for (i = 0; i < digcnt; i++) { algid = digestalgs[i]; oiddata = SECOID_FindOID(&(algid->algorithm)); if (oiddata == NULL) { digobj = NULL; } else { switch (oiddata->offset) { case SEC_OID_MD2: digobj = &SECHashObjects[HASH_AlgMD2]; break; case SEC_OID_MD5: digobj = &SECHashObjects[HASH_AlgMD5]; break; case SEC_OID_SHA1: digobj = &SECHashObjects[HASH_AlgSHA1]; break; default: digobj = NULL; break; } } /* * Skip any algorithm we do not even recognize; obviously, * this could be a problem, but if it is critical then the * result will just be that the signature does not verify. * We do not necessarily want to error out here, because * the particular algorithm may not actually be important, * but we cannot know that until later. */ if (digobj == NULL) { p7dcx->worker.digcnt--; continue; } digcx = (* digobj->create)(); if (digcx != NULL) { (* digobj->begin) (digcx); p7dcx->worker.digobjs[p7dcx->worker.digcnt] = digobj; p7dcx->worker.digcxs[p7dcx->worker.digcnt] = digcx; p7dcx->worker.digcnt++; } } if (p7dcx->worker.digcnt != 0) SEC_ASN1DecoderSetFilterProc (p7dcx->dcx, sec_pkcs7_decoder_filter, p7dcx, (PRBool)(p7dcx->cb != NULL)); return SECSuccess;}/* * Close out all of the digest contexts, storing the results in "digestsp". */static SECStatussec_pkcs7_decoder_finish_digests (SEC_PKCS7DecoderContext *p7dcx, PRArenaPool *poolp, SECItem ***digestsp){ struct sec_pkcs7_decoder_worker *worker; SECHashObject *digobj; void *digcx; SECItem **digests, *digest; int i; void *mark; /* * XXX Handling nested contents would mean that there is a chain * of workers -- one per each level of content. The following * would want to find the last worker in the chain. */ worker = &(p7dcx->worker); /* * If no digests, then we have nothing to do. */ if (worker->digcnt == 0) return SECSuccess; /* * No matter what happens after this, we want to stop filtering. * XXX If we handle nested contents, we only want to stop filtering * if we are finishing off the *last* worker. */ SEC_ASN1DecoderClearFilterProc (p7dcx->dcx); /* * If we ended up with no contents, just destroy each * digest context -- they are meaningless and potentially * confusing, because their presence would imply some content * was digested. */ if (! worker->saw_contents) { for (i = 0; i < worker->digcnt; i++) { digcx = worker->digcxs[i]; digobj = worker->digobjs[i]; (* digobj->destroy) (digcx, PR_TRUE); } return SECSuccess; } mark = PORT_ArenaMark (poolp); /* * Close out each digest context, saving digest away. */ digests = (SECItem**)PORT_ArenaAlloc (poolp,(worker->digcnt+1)*sizeof(SECItem *)); digest = (SECItem*)PORT_ArenaAlloc (poolp, worker->digcnt*sizeof(SECItem)); if (digests == NULL || digest == NULL) { p7dcx->error = PORT_GetError(); PORT_ArenaRelease (poolp, mark); return SECFailure; } for (i = 0; i < worker->digcnt; i++, digest++) { digcx = worker->digcxs[i]; digobj = worker->digobjs[i]; digest->data = (unsigned char*)PORT_ArenaAlloc (poolp, digobj->length); if (digest->data == NULL) { p7dcx->error = PORT_GetError(); PORT_ArenaRelease (poolp, mark); return SECFailure; } digest->len = digobj->length; (* digobj->end) (digcx, digest->data, &(digest->len), digest->len); (* digobj->destroy) (digcx, PR_TRUE); digests[i] = digest; } digests[i] = NULL; *digestsp = digests; PORT_ArenaUnmark (poolp, mark); return SECSuccess;}/* * XXX Need comment explaining following helper function (which is used * by sec_pkcs7_decoder_start_decrypt). */extern const SEC_ASN1Template SEC_SMIMEKEAParamTemplateAllParams[];static PK11SymKey *sec_pkcs7_decoder_get_recipient_key (SEC_PKCS7DecoderContext *p7dcx, SEC_PKCS7RecipientInfo **recipientinfos, SEC_PKCS7EncryptedContentInfo *enccinfo){ SEC_PKCS7RecipientInfo *ri; CERTCertificate *cert = NULL; SECKEYPrivateKey *privkey = NULL; PK11SymKey *bulkkey; SECOidTag keyalgtag, bulkalgtag, encalgtag; PK11SlotInfo *slot; int i, bulkLength = 0; if (recipientinfos == NULL || recipientinfos[0] == NULL) { p7dcx->error = SEC_ERROR_NOT_A_RECIPIENT; goto no_key_found; } cert = PK11_FindCertAndKeyByRecipientList(&slot,recipientinfos,&ri, &privkey, p7dcx->pwfn_arg); if (cert == NULL) { p7dcx->error = SEC_ERROR_NOT_A_RECIPIENT; goto no_key_found; } ri->cert = cert; /* so we can find it later */ PORT_Assert(privkey != NULL); keyalgtag = SECOID_GetAlgorithmTag(&(cert->subjectPublicKeyInfo.algorithm)); encalgtag = SECOID_GetAlgorithmTag (&(ri->keyEncAlg)); if ((encalgtag != SEC_OID_NETSCAPE_SMIME_KEA) && (keyalgtag != encalgtag)) { p7dcx->error = SEC_ERROR_PKCS7_KEYALG_MISMATCH; goto no_key_found; } bulkalgtag = SECOID_GetAlgorithmTag (&(enccinfo->contentEncAlg)); switch (encalgtag) { case SEC_OID_PKCS1_RSA_ENCRYPTION: bulkkey = PK11_PubUnwrapSymKey (privkey, &ri->encKey, PK11_AlgtagToMechanism (bulkalgtag), CKA_DECRYPT, 0); if (bulkkey == NULL) { p7dcx->error = PORT_GetError(); PORT_SetError(0); goto no_key_found; } break; /* ### mwelch -- KEA */ case SEC_OID_NETSCAPE_SMIME_KEA: { SECStatus err; CK_MECHANISM_TYPE bulkType; PK11SymKey *tek; SECKEYPublicKey *senderPubKey; SEC_PKCS7SMIMEKEAParameters keaParams; (void) memset(&keaParams, 0, sizeof(keaParams)); /* Decode the KEA algorithm parameters. */ err = SEC_ASN1DecodeItem(NULL, &keaParams, SEC_SMIMEKEAParamTemplateAllParams, &(ri->keyEncAlg.parameters)); if (err != SECSuccess) { p7dcx->error = err; PORT_SetError(0); goto no_key_found; } /* We just got key data, no key structure. So, we create one. */
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?