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 + -
显示快捷键?