p7decode.c

来自「支持SSL v2/v3, TLS, PKCS #5, PKCS #7, PKCS」· C语言 代码 · 共 2,088 行 · 第 1/4 页

C
2,088
字号
	 * XXX If the decryption key callback is set, we want to start	 * the decryption.  If the callback is not set, we will treat the	 * content as plain data, since we do not have the key.	 *	 * Is this the proper thing to do?	 */	if (before && dest == &(encd->encContentInfo.encContent)) {	    /*	     * Start the encryption process if the decryption key callback	     * is present.  Otherwise, treat the content like plain data.	     */	    rv = SECSuccess;	    if (p7dcx->dkcb != NULL) {		rv = sec_pkcs7_decoder_start_decrypt (p7dcx, depth, NULL,						      &(encd->encContentInfo),						      NULL);	    }	    if (rv != SECSuccess)		SEC_ASN1DecoderClearNotifyProc (p7dcx->dcx);			    break;	}	/*	 * Are we done?	 */	if (after && dest == &(encd->encContentInfo.encContent)) {	    /*	     * Close out the decryption context.  We ignore any error	     * because we are stopping anyway; the error status left	     * behind in p7dcx will be seen by outer functions.	     */	    (void) sec_pkcs7_decoder_finish_decrypt (p7dcx, cinfo->poolp,						     &(encd->encContentInfo));	    /*	     * Stop notify.	     */	    SEC_ASN1DecoderClearNotifyProc (p7dcx->dcx);	}	break;      case SEC_OID_PKCS7_DATA:	/*	 * If a output callback has been specified, we want to set the filter	 * to call the callback.  This is taken care of in 	 * sec_pkcs7_decoder_start_decrypt() or 	 * sec_pkcs7_decoder_start_digests() for the other content types.	 */ 		if (before && dest == &(cinfo->content.data)) {	    /* 	     * Set the filter proc up.	     */	    SEC_ASN1DecoderSetFilterProc (p7dcx->dcx,					  sec_pkcs7_decoder_filter,					  p7dcx,					  (PRBool)(p7dcx->cb != NULL));	    break;	}	if (after && dest == &(cinfo->content.data)) {	    /*	     * Time to clean up after ourself, stop the Notify and Filter	     * procedures.	     */	    SEC_ASN1DecoderClearNotifyProc (p7dcx->dcx);	    SEC_ASN1DecoderClearFilterProc (p7dcx->dcx);	}	break;      default:	SEC_ASN1DecoderClearNotifyProc (p7dcx->dcx);	break;    }}SEC_PKCS7DecoderContext *SEC_PKCS7DecoderStart(SEC_PKCS7DecoderContentCallback cb, void *cb_arg,		      SECKEYGetPasswordKey pwfn, void *pwfn_arg,		      SEC_PKCS7GetDecryptKeyCallback decrypt_key_cb, 		      void *decrypt_key_cb_arg,		      SEC_PKCS7DecryptionAllowedCallback decrypt_allowed_cb){    SEC_PKCS7DecoderContext *p7dcx;    SEC_ASN1DecoderContext *dcx;    SEC_PKCS7ContentInfo *cinfo;    PRArenaPool *poolp;    poolp = PORT_NewArena (1024);		/* XXX what is right value? */    if (poolp == NULL)	return NULL;    cinfo = (SEC_PKCS7ContentInfo*)PORT_ArenaZAlloc (poolp, sizeof(*cinfo));    if (cinfo == NULL) {	PORT_FreeArena (poolp, PR_FALSE);	return NULL;    }    cinfo->poolp = poolp;    cinfo->pwfn = pwfn;    cinfo->pwfn_arg = pwfn_arg;    cinfo->created = PR_FALSE;    cinfo->refCount = 1;    p7dcx =       (SEC_PKCS7DecoderContext*)PORT_ZAlloc (sizeof(SEC_PKCS7DecoderContext));    if (p7dcx == NULL) {	PORT_FreeArena (poolp, PR_FALSE);	return NULL;    }    p7dcx->tmp_poolp = PORT_NewArena (1024);	/* XXX what is right value? */    if (p7dcx->tmp_poolp == NULL) {	PORT_Free (p7dcx);	PORT_FreeArena (poolp, PR_FALSE);	return NULL;    }    dcx = SEC_ASN1DecoderStart (poolp, cinfo, sec_PKCS7ContentInfoTemplate);    if (dcx == NULL) {	PORT_FreeArena (p7dcx->tmp_poolp, PR_FALSE);	PORT_Free (p7dcx);	PORT_FreeArena (poolp, PR_FALSE);	return NULL;    }    SEC_ASN1DecoderSetNotifyProc (dcx, sec_pkcs7_decoder_notify, p7dcx);    p7dcx->dcx = dcx;    p7dcx->cinfo = cinfo;    p7dcx->cb = cb;    p7dcx->cb_arg = cb_arg;    p7dcx->pwfn = pwfn;    p7dcx->pwfn_arg = pwfn_arg;    p7dcx->dkcb = decrypt_key_cb;    p7dcx->dkcb_arg = decrypt_key_cb_arg;    p7dcx->decrypt_allowed_cb = decrypt_allowed_cb;    return p7dcx;}/* * Do the next chunk of PKCS7 decoding.  If there is a problem, set * an error and return a failure status.  Note that in the case of * an error, this routine is still prepared to be called again and * again in case that is the easiest route for our caller to take. * We simply detect it and do not do anything except keep setting * that error in case our caller has not noticed it yet... */SECStatusSEC_PKCS7DecoderUpdate(SEC_PKCS7DecoderContext *p7dcx,		       const char *buf, unsigned long len){    if (p7dcx->cinfo != NULL && p7dcx->dcx != NULL) { 	PORT_Assert (p7dcx->error == 0);	if (p7dcx->error == 0) {	    if (SEC_ASN1DecoderUpdate (p7dcx->dcx, buf, len) != SECSuccess) {		p7dcx->error = PORT_GetError();		PORT_Assert (p7dcx->error);		if (p7dcx->error == 0)		    p7dcx->error = -1;	    }	}    }    if (p7dcx->error) {	if (p7dcx->dcx != NULL) {	    (void) SEC_ASN1DecoderFinish (p7dcx->dcx);	    p7dcx->dcx = NULL;	}	if (p7dcx->cinfo != NULL) {	    SEC_PKCS7DestroyContentInfo (p7dcx->cinfo);	    p7dcx->cinfo = NULL;	}	PORT_SetError (p7dcx->error);	return SECFailure;    }    return SECSuccess;}SEC_PKCS7ContentInfo *SEC_PKCS7DecoderFinish(SEC_PKCS7DecoderContext *p7dcx){    SEC_PKCS7ContentInfo *cinfo;    cinfo = p7dcx->cinfo;    if (p7dcx->dcx != NULL) {	if (SEC_ASN1DecoderFinish (p7dcx->dcx) != SECSuccess) {	    SEC_PKCS7DestroyContentInfo (cinfo);	    cinfo = NULL;	}    }    PORT_FreeArena (p7dcx->tmp_poolp, PR_FALSE);    PORT_Free (p7dcx);    return cinfo;}SEC_PKCS7ContentInfo *SEC_PKCS7DecodeItem(SECItem *p7item,		    SEC_PKCS7DecoderContentCallback cb, void *cb_arg,		    SECKEYGetPasswordKey pwfn, void *pwfn_arg,		    SEC_PKCS7GetDecryptKeyCallback decrypt_key_cb, 		    void *decrypt_key_cb_arg,		    SEC_PKCS7DecryptionAllowedCallback decrypt_allowed_cb){    SEC_PKCS7DecoderContext *p7dcx;    p7dcx = SEC_PKCS7DecoderStart(cb, cb_arg, pwfn, pwfn_arg, decrypt_key_cb,				  decrypt_key_cb_arg, decrypt_allowed_cb);    (void) SEC_PKCS7DecoderUpdate(p7dcx, (char *) p7item->data, p7item->len);    return SEC_PKCS7DecoderFinish(p7dcx);}/* * If the thing contains any certs or crls return true; false otherwise. */PRBoolSEC_PKCS7ContainsCertsOrCrls(SEC_PKCS7ContentInfo *cinfo){    SECOidTag kind;    SECItem **certs;    CERTSignedCrl **crls;    kind = SEC_PKCS7ContentType (cinfo);    switch (kind) {      default:      case SEC_OID_PKCS7_DATA:      case SEC_OID_PKCS7_DIGESTED_DATA:      case SEC_OID_PKCS7_ENVELOPED_DATA:      case SEC_OID_PKCS7_ENCRYPTED_DATA:	return PR_FALSE;      case SEC_OID_PKCS7_SIGNED_DATA:	certs = cinfo->content.signedData->rawCerts;	crls = cinfo->content.signedData->crls;	break;      case SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA:	certs = cinfo->content.signedAndEnvelopedData->rawCerts;	crls = cinfo->content.signedAndEnvelopedData->crls;	break;    }    /*     * I know this could be collapsed, but I was in a mood to be explicit.     */    if (certs != NULL && certs[0] != NULL)	return PR_TRUE;    else if (crls != NULL && crls[0] != NULL)	return PR_TRUE;    else	return PR_FALSE;}/* return the content length...could use GetContent, however we * need the encrypted content length  */PRBoolSEC_PKCS7IsContentEmpty(SEC_PKCS7ContentInfo *cinfo, unsigned int minLen){    SECItem *item = NULL;    if(cinfo == NULL) {	return PR_TRUE;    }    switch(SEC_PKCS7ContentType(cinfo))     {	case SEC_OID_PKCS7_DATA:	    item = cinfo->content.data;	    break;	case SEC_OID_PKCS7_ENCRYPTED_DATA:	    item = &cinfo->content.encryptedData->encContentInfo.encContent;	    break;	default:	    /* add other types */	    return PR_FALSE;    }    if(!item) {	return PR_TRUE;    } else if(item->len <= minLen) {	return PR_TRUE;    }    return PR_FALSE;}PRBoolSEC_PKCS7ContentIsEncrypted(SEC_PKCS7ContentInfo *cinfo){    SECOidTag kind;    kind = SEC_PKCS7ContentType (cinfo);    switch (kind) {      default:      case SEC_OID_PKCS7_DATA:      case SEC_OID_PKCS7_DIGESTED_DATA:      case SEC_OID_PKCS7_SIGNED_DATA:	return PR_FALSE;      case SEC_OID_PKCS7_ENCRYPTED_DATA:      case SEC_OID_PKCS7_ENVELOPED_DATA:      case SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA:	return PR_TRUE;    }}/* * If the PKCS7 content has a signature (not just *could* have a signature) * return true; false otherwise.  This can/should be called before calling * VerifySignature, which will always indicate failure if no signature is * present, but that does not mean there even was a signature! * Note that the content itself can be empty (detached content was sent * another way); it is the presence of the signature that matters. */PRBoolSEC_PKCS7ContentIsSigned(SEC_PKCS7ContentInfo *cinfo){    SECOidTag kind;    SEC_PKCS7SignerInfo **signerinfos;    kind = SEC_PKCS7ContentType (cinfo);    switch (kind) {      default:      case SEC_OID_PKCS7_DATA:      case SEC_OID_PKCS7_DIGESTED_DATA:      case SEC_OID_PKCS7_ENVELOPED_DATA:      case SEC_OID_PKCS7_ENCRYPTED_DATA:	return PR_FALSE;      case SEC_OID_PKCS7_SIGNED_DATA:	signerinfos = cinfo->content.signedData->signerInfos;	break;      case SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA:	signerinfos = cinfo->content.signedAndEnvelopedData->signerInfos;	break;    }    /*     * I know this could be collapsed; but I kind of think it will get     * more complicated before I am finished, so...     */    if (signerinfos != NULL && signerinfos[0] != NULL)	return PR_TRUE;    else	return PR_FALSE;}/* * SEC_PKCS7ContentVerifySignature *	Look at a PKCS7 contentInfo and check if the signature is good. *	The digest was either calculated earlier (and is stored in the *	contentInfo itself) or is passed in via "detached_digest". * *	The verification checks that the signing cert is valid and trusted *	for the purpose specified by "certusage". * *	In addition, if "keepcerts" is true, add any new certificates found *	into our local database. * * XXX Each place which returns PR_FALSE should be sure to have a good * error set for inspection by the caller.  Alternatively, we could create * an enumeration of success and each type of failure and return that * instead of a boolean.  For now, the default in a bad situation is to * set the error to SEC_ERROR_PKCS7_BAD_SIGNATURE.  But this should be * reviewed; better (more specific) errors should be possible (to distinguish * a signature failure from a badly-formed pkcs7 signedData, for example). * Some of the errors should probably just be SEC_ERROR_BAD_SIGNATURE, * but that has a less helpful error string associated with it right now; * if/when that changes, review and change these as needed. * * XXX This is broken wrt signedAndEnvelopedData.  In that case, the * message digest is doubly encrypted -- first encrypted with the signer * private key but then again encrypted with the bulk encryption key used * to encrypt the content.  So before we can pass the digest to VerifyDigest, * we need to decrypt it with the bulk encryption key.  Also, in this case, * there should be NO authenticatedAttributes (signerinfo->authAttr should * be NULL). */static PRBoolsec_pkcs7_verify_signature(SEC_PKCS7ContentInfo *cinfo,			   SECCertUsage certusage,			   SECItem *detached_digest,			   HASH_HashType digest_type,			   PRBool keepcerts){    SECAlgorithmID **digestalgs, *bulkid;    SECItem *digest;    SECItem **digests;    SECItem **rawcerts;    CERTSignedCrl **crls;    SEC_PKCS7SignerInfo **signerinfos, *signerinfo;    CERTCertificate *cert, **certs;    PRBool goodsig;    CERTCertDBHandle local_certdb, *certdb, *defaultdb;    SECOidData *algiddata;    int i, certcount;    SECKEYPublicKey *publickey;    SECItem *content_type;    PK11SymKey *sigkey;    SECItem *utc_stime;    int64 stime;    SECStatus rv;    /*     * Everything needed in order to "goto done" safely.     */    goodsig = PR_FALSE;    certcount = 0;    cert = NULL;    certs = NULL;    certdb = NULL;    defaultdb = CERT_GetDefaultCertDB();    publickey = NULL;    if (! SEC_PKCS7ContentIsSigned(cinfo)) {	PORT_SetError (SEC_ERROR_PKCS7_BAD_SIGNATURE);	goto done;    }    PORT_Assert (cinfo->contentTypeTag != NULL);    switch (cinfo->contentTypeTag->offset) {      default:      case SEC_OID_PKCS7_DATA:      case SEC_OID_PKCS7_DIGESTED_DATA:      case SEC_OID_PKCS7_ENVELOPED_DATA:      case SEC_OID_PKCS7_ENCRYPTED_DATA:	/* Could only get here if SEC_PKCS7ContentIsSigned is broken. */	PORT_Assert (0);      case SEC_OID_PKCS7_SIGNED_DATA:	{	    SEC_PKCS7SignedData *sdp;	    sdp = cinfo->content.signedData;	    digestalgs = sdp->digestAlgorithms;	    digests = sdp->digests;	    rawcerts = sdp->rawCerts;	    crls = sdp->crls;	    signerinfos = sdp->signerInfos;	    content_type = &(sdp->contentInfo.contentType);	    sigkey = NULL;	    bulkid = NULL;	}	break;      case SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA:	{	    SEC_PKCS7SignedAndEnvelopedData *saedp;	    saedp = cinfo->content.signedAndEnvelopedData;	    digestalgs = saedp->digestAlgorithms;	    digests = saedp->digests;	    rawcerts = saedp->rawCerts;	    crls = saedp->crls;	    signerinfos = saedp->signerInfos;	    content_type = &(saedp->encContentInfo.contentType);	    sigkey = saedp->sigKey;	    bulkid = &(saedp->encContentInfo.contentEncAlg);	}	break;    }    if ((signerinfos == NULL) || (signerinfos[0] == NULL)) {	PORT_SetError (SEC_ERROR_PKCS7_BAD_SIGNATURE);	goto done;    }    /*     * XXX Need to handle multiple signatures; checking them is easy,     * but what should be the semantics here (like, return value)?     */    if (signerinfos[1] != NULL) {	PORT_SetError (SEC_ERROR_PKCS7_BAD_SIGNATURE);	goto done;    }    signerinfo = signerinfos[0];    /*     * XXX I would like to just pass the issuerAndSN, along with the rawcerts     * and crls, to some function that did all of this certificate stuff     * (open/close the database if necessary, verifying the certs, etc.)     * and gave me back a cert pointer if all was good.     */    certdb = defaultdb;    if (certdb == NULL) {	if (CERT_OpenCertDBFilename (&local_certdb, NULL,				     (PRBool)!keepcerts) != SECSuccess)	    goto done;	certdb = &local_certdb;    }    certcount = 0;    if (rawcerts != NULL) {	for (; rawcerts[certcount] != NULL; certcount++) {	    /* just counting */	}    }    /*     * Note that the result of this is that each cert in "certs"     * needs to be destroyed.     */    rv = CERT_ImportCerts(certdb, certusage, certcount, rawcerts, &certs,			  keepcerts, PR_FALSE, NULL);    if ( rv != SECSuccess ) {	goto done;    }    /*     * This cert will also need to be freed, but since we save it     * in signerinfo for later, we do not want to destroy it when     * we leave this function -- we let the clean-up of the entire

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?