diff nss/lib/pkcs7/p7decode.c @ 0:1e5118fa0cb1

This is NSS with a Cmake Buildsyste To compile a static NSS library for Windows we've used the Chromium-NSS fork and added a Cmake buildsystem to compile it statically for Windows. See README.chromium for chromium changes and README.trustbridge for our modifications.
author Andre Heinecke <andre.heinecke@intevation.de>
date Mon, 28 Jul 2014 10:47:06 +0200
parents
children
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nss/lib/pkcs7/p7decode.c	Mon Jul 28 10:47:06 2014 +0200
@@ -0,0 +1,1932 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+ * PKCS7 decoding, verification.
+ */
+
+#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"
+#include "sechash.h"	/* for HASH_GetHashObject() */
+#include "secder.h"
+#include "secpkcs5.h"
+
+struct sec_pkcs7_decoder_worker {
+    int depth;
+    int digcnt;
+    void **digcxs;
+    const 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;
+    PLArenaPool *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 void
+sec_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 void
+sec_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 SECStatus
+sec_pkcs7_decoder_start_digests (SEC_PKCS7DecoderContext *p7dcx, int depth,
+				 SECAlgorithmID **digestalgs)
+{
+    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.
+     * Just act as if there were no algorithms specified.
+     */
+    if (digcnt == 0)
+	return SECSuccess;
+
+    p7dcx->worker.digcxs = (void**)PORT_ArenaAlloc (p7dcx->tmp_poolp,
+					    digcnt * sizeof (void *));
+    p7dcx->worker.digobjs = (const 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++) {
+	SECAlgorithmID *     algid  = digestalgs[i];
+	SECOidTag            oidTag = SECOID_FindOIDTag(&(algid->algorithm));
+	const SECHashObject *digobj = HASH_GetHashObjectByOidTag(oidTag);
+	void *digcx;
+
+	/*
+	 * 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 SECStatus
+sec_pkcs7_decoder_finish_digests (SEC_PKCS7DecoderContext *p7dcx,
+				  PLArenaPool *poolp,
+				  SECItem ***digestsp)
+{
+    struct sec_pkcs7_decoder_worker *worker;
+    const 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).
+ */
+
+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 = NULL;
+    SECOidTag keyalgtag, bulkalgtag, encalgtag;
+    PK11SlotInfo *slot = NULL;
+
+    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 (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;
+      default:
+	p7dcx->error = SEC_ERROR_UNSUPPORTED_KEYALG;
+	break;
+    }
+
+no_key_found:
+    if (privkey != NULL)
+	SECKEY_DestroyPrivateKey (privkey);
+    if (slot != NULL)
+	PK11_FreeSlot(slot);
+
+    return bulkkey;
+}
+ 
+/*
+ * XXX The following comment is old -- the function used to only handle
+ * EnvelopedData or SignedAndEnvelopedData but now handles EncryptedData
+ * as well (and it had all of the code of the helper function above
+ * built into it), though the comment was left as is.  Fix it...
+ *
+ * We are just about to decode the content of an EnvelopedData.
+ * Set up a decryption context so we can decrypt as we go.
+ * Presumably we are one of the recipients listed in "recipientinfos".
+ * (XXX And if we are not, or if we have trouble, what should we do?
+ *  It would be nice to let the decoding still work.  Maybe it should
+ *  be an error if there is a content callback, but not an error otherwise?)
+ * The encryption key and related information can be found in "enccinfo".
+ */
+static SECStatus
+sec_pkcs7_decoder_start_decrypt (SEC_PKCS7DecoderContext *p7dcx, int depth,
+				 SEC_PKCS7RecipientInfo **recipientinfos,
+				 SEC_PKCS7EncryptedContentInfo *enccinfo,
+				 PK11SymKey **copy_key_for_signature)
+{
+    PK11SymKey *bulkkey = NULL;
+    sec_PKCS7CipherObject *decryptobj;
+
+    /*
+     * If a callback is supplied to retrieve the encryption key, 
+     * for instance, for Encrypted Content infos, then retrieve
+     * the bulkkey from the callback.  Otherwise, assume that
+     * we are processing Enveloped or SignedAndEnveloped data
+     * content infos.
+     *
+     * XXX Put an assert here?
+     */
+    if (SEC_PKCS7ContentType(p7dcx->cinfo) == SEC_OID_PKCS7_ENCRYPTED_DATA) {
+	if (p7dcx->dkcb != NULL) {
+	    bulkkey = (*p7dcx->dkcb)(p7dcx->dkcb_arg, 
+				     &(enccinfo->contentEncAlg));
+	}
+	enccinfo->keysize = 0;
+    } else {
+	bulkkey = sec_pkcs7_decoder_get_recipient_key (p7dcx, recipientinfos, 
+						       enccinfo);
+	if (bulkkey == NULL) goto no_decryption;
+	enccinfo->keysize = PK11_GetKeyStrength(bulkkey, 
+						&(enccinfo->contentEncAlg));
+
+    }
+
+    /*
+     * XXX I think following should set error in p7dcx and clear set error
+     * (as used to be done here, or as is done in get_receipient_key above.
+     */
+    if(bulkkey == NULL) {
+	goto no_decryption;
+    }
+    
+    /* 
+     * We want to make sure decryption is allowed.  This is done via
+     * a callback specified in SEC_PKCS7DecoderStart().
+     */
+    if (p7dcx->decrypt_allowed_cb) {
+	if ((*p7dcx->decrypt_allowed_cb) (&(enccinfo->contentEncAlg), 
+					  bulkkey) == PR_FALSE) {
+	    p7dcx->error = SEC_ERROR_DECRYPTION_DISALLOWED;
+	    goto no_decryption;
+	}
+    } else {
+	    p7dcx->error = SEC_ERROR_DECRYPTION_DISALLOWED;
+	    goto no_decryption;
+    }
+
+    /*
+     * When decrypting a signedAndEnvelopedData, the signature also has
+     * to be decrypted with the bulk encryption key; to avoid having to
+     * get it all over again later (and do another potentially expensive
+     * RSA operation), copy it for later signature verification to use.
+     */
+    if (copy_key_for_signature != NULL)
+	*copy_key_for_signature = PK11_ReferenceSymKey (bulkkey);
+
+    /*
+     * Now we have the bulk encryption key (in bulkkey) and the
+     * the algorithm (in enccinfo->contentEncAlg).  Using those,
+     * create a decryption context.
+     */
+    decryptobj = sec_PKCS7CreateDecryptObject (bulkkey,
+					       &(enccinfo->contentEncAlg));
+
+    /*
+     * We are done with (this) bulkkey now.
+     */
+    PK11_FreeSymKey (bulkkey);
+
+    if (decryptobj == NULL) {
+	p7dcx->error = PORT_GetError();
+	PORT_SetError(0);
+	goto no_decryption;
+    }
+
+    SEC_ASN1DecoderSetFilterProc (p7dcx->dcx,
+				  sec_pkcs7_decoder_filter,
+				  p7dcx,
+				  (PRBool)(p7dcx->cb != NULL));
+
+    p7dcx->worker.depth = depth;
+    p7dcx->worker.decryptobj = decryptobj;
+
+    return SECSuccess;
+
+no_decryption:
+    /*
+     * For some reason (error set already, if appropriate), we cannot
+     * decrypt the content.  I am not sure what exactly is the right
+     * thing to do here; in some cases we want to just stop, and in
+     * others we want to let the decoding finish even though we cannot
+     * decrypt the content.  My current thinking is that if the caller
+     * set up a content callback, then they are really interested in
+     * getting (decrypted) content, and if they cannot they will want
+     * to know about it.  However, if no callback was specified, then
+     * maybe it is not important that the decryption failed.
+     */
+    if (p7dcx->cb != NULL)
+	return SECFailure;
+    else
+	return SECSuccess;	/* Let the decoding continue. */
+}
+
+
+static SECStatus
+sec_pkcs7_decoder_finish_decrypt (SEC_PKCS7DecoderContext *p7dcx,
+				  PLArenaPool *poolp,
+				  SEC_PKCS7EncryptedContentInfo *enccinfo)
+{
+    struct sec_pkcs7_decoder_worker *worker;
+
+    /*
+     * 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 decryption context, then we have nothing to do.
+     */
+    if (worker->decryptobj == NULL)
+	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);
+
+    /*
+     * Handle the last block.
+     */
+    sec_pkcs7_decoder_work_data (p7dcx, worker, NULL, 0, PR_TRUE);
+
+    /*
+     * All done, destroy it.
+     */
+    sec_PKCS7DestroyDecryptObject (worker->decryptobj);
+    worker->decryptobj = NULL;
+
+    return SECSuccess;
+}
+
+
+static void
+sec_pkcs7_decoder_notify (void *arg, PRBool before, void *dest, int depth)
+{
+    SEC_PKCS7DecoderContext *p7dcx;
+    SEC_PKCS7ContentInfo *cinfo;
+    SEC_PKCS7SignedData *sigd;
+    SEC_PKCS7EnvelopedData *envd;
+    SEC_PKCS7SignedAndEnvelopedData *saed;
+    SEC_PKCS7EncryptedData *encd;
+    SEC_PKCS7DigestedData *digd;
+    PRBool after;
+    SECStatus rv;
+
+    /*
+     * Just to make the code easier to read, create an "after" variable
+     * that is equivalent to "not before".
+     * (This used to be just the statement "after = !before", but that
+     * causes a warning on the mac; to avoid that, we do it the long way.)
+     */
+    if (before)
+	after = PR_FALSE;
+    else
+	after = PR_TRUE;
+
+    p7dcx = (SEC_PKCS7DecoderContext*)arg;
+    cinfo = p7dcx->cinfo;
+
+    if (cinfo->contentTypeTag == NULL) {
+	if (after && dest == &(cinfo->contentType))
+	    cinfo->contentTypeTag = SECOID_FindOID(&(cinfo->contentType));
+	return;
+    }
+
+    switch (cinfo->contentTypeTag->offset) {
+      case SEC_OID_PKCS7_SIGNED_DATA:
+	sigd = cinfo->content.signedData;
+	if (sigd == NULL)
+	    break;
+
+	if (sigd->contentInfo.contentTypeTag == NULL) {
+	    if (after && dest == &(sigd->contentInfo.contentType))
+		sigd->contentInfo.contentTypeTag =
+			SECOID_FindOID(&(sigd->contentInfo.contentType));
+	    break;
+	}
+
+	/*
+	 * We only set up a filtering digest if the content is
+	 * plain DATA; anything else needs more work because a
+	 * second pass is required to produce a DER encoding from
+	 * an input that can be BER encoded.  (This is a requirement
+	 * of PKCS7 that is unfortunate, but there you have it.)
+	 *
+	 * XXX Also, since we stop here if this is not DATA, the
+	 * inner content is not getting processed at all.  Someday
+	 * we may want to fix that.
+	 */
+	if (sigd->contentInfo.contentTypeTag->offset != SEC_OID_PKCS7_DATA) {
+	    /* XXX Set an error in p7dcx->error */
+	    SEC_ASN1DecoderClearNotifyProc (p7dcx->dcx);
+	    break;
+	}
+
+	/*
+	 * Just before the content, we want to set up a digest context
+	 * for each digest algorithm listed, and start a filter which
+	 * will run all of the contents bytes through that digest.
+	 */
+	if (before && dest == &(sigd->contentInfo.content)) {
+	    rv = sec_pkcs7_decoder_start_digests (p7dcx, depth,
+						  sigd->digestAlgorithms);
+	    if (rv != SECSuccess)
+		SEC_ASN1DecoderClearNotifyProc (p7dcx->dcx);
+
+	    break;
+	}
+
+	/*
+	 * XXX To handle nested types, here is where we would want
+	 * to check for inner boundaries that need handling.
+	 */
+
+	/*
+	 * Are we done?
+	 */
+	if (after && dest == &(sigd->contentInfo.content)) {
+	    /*
+	     * Close out the digest contexts.  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_digests (p7dcx, cinfo->poolp,
+						     &(sigd->digests));
+
+	    /*
+	     * XXX To handle nested contents, we would need to remove
+	     * the worker from the chain (and free it).
+	     */
+
+	    /*
+	     * Stop notify.
+	     */
+	    SEC_ASN1DecoderClearNotifyProc (p7dcx->dcx);
+	}
+	break;
+
+      case SEC_OID_PKCS7_ENVELOPED_DATA:
+	envd = cinfo->content.envelopedData;
+	if (envd == NULL)
+	    break;
+
+	if (envd->encContentInfo.contentTypeTag == NULL) {
+	    if (after && dest == &(envd->encContentInfo.contentType))
+		envd->encContentInfo.contentTypeTag =
+			SECOID_FindOID(&(envd->encContentInfo.contentType));
+	    break;
+	}
+
+	/*
+	 * Just before the content, we want to set up a decryption
+	 * context, and start a filter which will run all of the
+	 * contents bytes through it to determine the plain content.
+	 */
+	if (before && dest == &(envd->encContentInfo.encContent)) {
+	    rv = sec_pkcs7_decoder_start_decrypt (p7dcx, depth,
+						  envd->recipientInfos,
+						  &(envd->encContentInfo),
+						  NULL);
+	    if (rv != SECSuccess)
+		SEC_ASN1DecoderClearNotifyProc (p7dcx->dcx);
+
+	    break;
+	}
+
+	/*
+	 * Are we done?
+	 */
+	if (after && dest == &(envd->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,
+						     &(envd->encContentInfo));
+
+	    /*
+	     * XXX To handle nested contents, we would need to remove
+	     * the worker from the chain (and free it).
+	     */
+
+	    /*
+	     * Stop notify.
+	     */
+	    SEC_ASN1DecoderClearNotifyProc (p7dcx->dcx);
+	}
+	break;
+
+      case SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA:
+	saed = cinfo->content.signedAndEnvelopedData;
+	if (saed == NULL)
+	    break;
+
+	if (saed->encContentInfo.contentTypeTag == NULL) {
+	    if (after && dest == &(saed->encContentInfo.contentType))
+		saed->encContentInfo.contentTypeTag =
+			SECOID_FindOID(&(saed->encContentInfo.contentType));
+	    break;
+	}
+
+	/*
+	 * Just before the content, we want to set up a decryption
+	 * context *and* digest contexts, and start a filter which
+	 * will run all of the contents bytes through both.
+	 */
+	if (before && dest == &(saed->encContentInfo.encContent)) {
+	    rv = sec_pkcs7_decoder_start_decrypt (p7dcx, depth,
+						  saed->recipientInfos,
+						  &(saed->encContentInfo),
+						  &(saed->sigKey));
+	    if (rv == SECSuccess)
+		rv = sec_pkcs7_decoder_start_digests (p7dcx, depth,
+						      saed->digestAlgorithms);
+	    if (rv != SECSuccess)
+		SEC_ASN1DecoderClearNotifyProc (p7dcx->dcx);
+
+	    break;
+	}
+
+	/*
+	 * Are we done?
+	 */
+	if (after && dest == &(saed->encContentInfo.encContent)) {
+	    /*
+	     * Close out the decryption and digests contexts.
+	     * We ignore any errors because we are stopping anyway;
+	     * the error status left behind in p7dcx will be seen by
+	     * outer functions.
+	     *
+	     * Note that the decrypt stuff must be called first;
+	     * it may have a last buffer to do which in turn has
+	     * to be added to the digest.
+	     */
+	    (void) sec_pkcs7_decoder_finish_decrypt (p7dcx, cinfo->poolp,
+						     &(saed->encContentInfo));
+	    (void) sec_pkcs7_decoder_finish_digests (p7dcx, cinfo->poolp,
+						     &(saed->digests));
+
+	    /*
+	     * XXX To handle nested contents, we would need to remove
+	     * the worker from the chain (and free it).
+	     */
+
+	    /*
+	     * Stop notify.
+	     */
+	    SEC_ASN1DecoderClearNotifyProc (p7dcx->dcx);
+	}
+	break;
+
+      case SEC_OID_PKCS7_DIGESTED_DATA:
+	digd = cinfo->content.digestedData;
+	
+	/* 
+	 * XXX Want to do the digest or not?  Maybe future enhancement...
+	 */
+	if (before && dest == &(digd->contentInfo.content.data)) {
+	    SEC_ASN1DecoderSetFilterProc (p7dcx->dcx, sec_pkcs7_decoder_filter,
+					  p7dcx,
+					  (PRBool)(p7dcx->cb != NULL));
+	    break;
+	}
+
+	/*
+	 * Are we done?
+	 */
+	if (after && dest == &(digd->contentInfo.content.data)) {
+	    SEC_ASN1DecoderClearFilterProc (p7dcx->dcx);
+	}
+	break;
+
+      case SEC_OID_PKCS7_ENCRYPTED_DATA:
+	encd = cinfo->content.encryptedData;
+
+	/*
+	 * 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;
+    PLArenaPool *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...
+ */
+SECStatus
+SEC_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;
+	}
+    }
+    /* free any NSS data structures */
+    if (p7dcx->worker.decryptobj) {
+        sec_PKCS7DestroyDecryptObject (p7dcx->worker.decryptobj);
+    }
+    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);
+    if (!p7dcx) {
+        /* error code is set */
+        return NULL;
+    }
+    (void) SEC_PKCS7DecoderUpdate(p7dcx, (char *) p7item->data, p7item->len);
+    return SEC_PKCS7DecoderFinish(p7dcx);
+}
+
+/*
+ * Abort the ASN.1 stream. Used by pkcs 12
+ */
+void
+SEC_PKCS7DecoderAbort(SEC_PKCS7DecoderContext *p7dcx, int error)
+{
+    PORT_Assert(p7dcx);
+    SEC_ASN1DecoderAbort(p7dcx->dcx, error);
+}
+
+
+/*
+ * If the thing contains any certs or crls return true; false otherwise.
+ */
+PRBool
+SEC_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 
+ */
+PRBool
+SEC_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;
+}
+
+
+PRBool
+SEC_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.
+ */
+PRBool
+SEC_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_pkcs7_verify_signature
+ *
+ *	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" at
+ * 	- "*atTime" if "atTime" is not null, or
+ * 	- the signing time if the signing time is available in "cinfo", or
+ *	- the current time (as returned by PR_Now).
+ *
+ *	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 PRBool
+sec_pkcs7_verify_signature(SEC_PKCS7ContentInfo *cinfo,
+			   SECCertUsage certusage,
+			   const SECItem *detached_digest,
+			   HASH_HashType digest_type,
+			   PRBool keepcerts,
+			   const PRTime *atTime)
+{
+    SECAlgorithmID **digestalgs, *bulkid;
+    const SECItem *digest;
+    SECItem **digests;
+    SECItem **rawcerts;
+    CERTSignedCrl **crls;
+    SEC_PKCS7SignerInfo **signerinfos, *signerinfo;
+    CERTCertificate *cert, **certs;
+    PRBool goodsig;
+    CERTCertDBHandle *certdb, *defaultdb; 
+    SECOidTag encTag,digestTag;
+    HASH_HashType found_type;
+    int i, certcount;
+    SECKEYPublicKey *publickey;
+    SECItem *content_type;
+    PK11SymKey *sigkey;
+    SECItem *encoded_stime;
+    PRTime stime;
+    PRTime verificationTime;
+    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) {
+	goto done;
+    }
+
+    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
+     * cinfo structure later do the destroy of this cert.
+     */
+    cert = CERT_FindCertByIssuerAndSN(certdb, signerinfo->issuerAndSN);
+    if (cert == NULL) {
+	goto done;
+    }
+
+    signerinfo->cert = cert;
+
+    /*
+     * Get and convert the signing time; if available, it will be used
+     * both on the cert verification and for importing the sender
+     * email profile.
+     */
+    encoded_stime = SEC_PKCS7GetSigningTime (cinfo);
+    if (encoded_stime != NULL) {
+	if (DER_DecodeTimeChoice (&stime, encoded_stime) != SECSuccess)
+	    encoded_stime = NULL;	/* conversion failed, so pretend none */
+    }
+
+    /*
+     * XXX  This uses the signing time, if available.  Additionally, we
+     * might want to, if there is no signing time, get the message time
+     * from the mail header itself, and use that.  That would require
+     * a change to our interface though, and for S/MIME callers to pass
+     * in a time (and for non-S/MIME callers to pass in nothing, or
+     * maybe make them pass in the current time, always?).
+     */
+    if (atTime) {
+	verificationTime = *atTime;
+    } else if (encoded_stime != NULL) {
+	verificationTime = stime;
+    } else {
+	verificationTime = PR_Now();
+    }
+    if (CERT_VerifyCert (certdb, cert, PR_TRUE, certusage, verificationTime,
+			 cinfo->pwfn_arg, NULL) != SECSuccess)
+	{
+	/*
+	 * XXX Give the user an option to check the signature anyway?
+	 * If we want to do this, need to give a way to leave and display
+	 * some dialog and get the answer and come back through (or do
+	 * the rest of what we do below elsewhere, maybe by putting it
+	 * in a function that we call below and could call from a dialog
+	 * finish handler).
+	 */
+	goto savecert;
+    }
+
+    publickey = CERT_ExtractPublicKey (cert);
+    if (publickey == NULL)
+	goto done;
+
+    /*
+     * XXX No!  If digests is empty, see if we can create it now by
+     * digesting the contents.  This is necessary if we want to allow
+     * somebody to do a simple decode (without filtering, etc.) and
+     * then later call us here to do the verification.
+     * OR, we can just specify that the interface to this routine
+     * *requires* that the digest(s) be done before calling and either
+     * stashed in the struct itself or passed in explicitly (as would
+     * be done for detached contents).
+     */
+    if ((digests == NULL || digests[0] == NULL)
+	&& (detached_digest == NULL || detached_digest->data == NULL))
+	goto done;
+
+    /*
+     * Find and confirm digest algorithm.
+     */
+    digestTag = SECOID_FindOIDTag(&(signerinfo->digestAlg.algorithm));
+
+    /* make sure we understand the digest type first */
+    found_type = HASH_GetHashTypeByOidTag(digestTag);
+    if ((digestTag == SEC_OID_UNKNOWN) || (found_type == HASH_AlgNULL)) {
+	PORT_SetError (SEC_ERROR_PKCS7_BAD_SIGNATURE);
+	goto done;
+    }
+
+    if (detached_digest != NULL) {
+	unsigned int hashLen     = HASH_ResultLen(found_type);
+
+	if (digest_type != found_type || 
+	    detached_digest->len != hashLen) {
+	    PORT_SetError (SEC_ERROR_PKCS7_BAD_SIGNATURE);
+	    goto done;
+	}
+	digest = detached_digest;
+    } else {
+	PORT_Assert (digestalgs != NULL && digestalgs[0] != NULL);
+	if (digestalgs == NULL || digestalgs[0] == NULL) {
+	    PORT_SetError (SEC_ERROR_PKCS7_BAD_SIGNATURE);
+	    goto done;
+	}
+
+	/*
+	 * pick digest matching signerinfo->digestAlg from digests
+	 */
+	for (i = 0; digestalgs[i] != NULL; i++) {
+	    if (SECOID_FindOIDTag(&(digestalgs[i]->algorithm)) == digestTag)
+		break;
+	}
+	if (digestalgs[i] == NULL) {
+	    PORT_SetError (SEC_ERROR_PKCS7_BAD_SIGNATURE);
+	    goto done;
+	}
+
+	digest = digests[i];
+    }
+
+    encTag = SECOID_FindOIDTag(&(signerinfo->digestEncAlg.algorithm));
+    if (encTag == SEC_OID_UNKNOWN) {
+	PORT_SetError (SEC_ERROR_PKCS7_BAD_SIGNATURE);
+	goto done;
+    }
+
+    if (signerinfo->authAttr != NULL) {
+	SEC_PKCS7Attribute *attr;
+	SECItem *value;
+	SECItem encoded_attrs;
+
+	/*
+	 * We have a sigkey only for signedAndEnvelopedData, which is
+	 * not supposed to have any authenticated attributes.
+	 */
+	if (sigkey != NULL) {
+	    PORT_SetError (SEC_ERROR_PKCS7_BAD_SIGNATURE);
+	    goto done;
+	}
+
+	/*
+	 * PKCS #7 says that if there are any authenticated attributes,
+	 * then there must be one for content type which matches the
+	 * content type of the content being signed, and there must
+	 * be one for message digest which matches our message digest.
+	 * So check these things first.
+	 * XXX Might be nice to have a compare-attribute-value function
+	 * which could collapse the following nicely.
+	 */
+	attr = sec_PKCS7FindAttribute (signerinfo->authAttr,
+				       SEC_OID_PKCS9_CONTENT_TYPE, PR_TRUE);
+	value = sec_PKCS7AttributeValue (attr);
+	if (value == NULL || value->len != content_type->len) {
+	    PORT_SetError (SEC_ERROR_PKCS7_BAD_SIGNATURE);
+	    goto done;
+	}
+	if (PORT_Memcmp (value->data, content_type->data, value->len) != 0) {
+	    PORT_SetError (SEC_ERROR_PKCS7_BAD_SIGNATURE);
+	    goto done;
+	}
+
+	attr = sec_PKCS7FindAttribute (signerinfo->authAttr,
+				       SEC_OID_PKCS9_MESSAGE_DIGEST, PR_TRUE);
+	value = sec_PKCS7AttributeValue (attr);
+	if (value == NULL || value->len != digest->len) {
+	    PORT_SetError (SEC_ERROR_PKCS7_BAD_SIGNATURE);
+	    goto done;
+	}
+	if (PORT_Memcmp (value->data, digest->data, value->len) != 0) {
+	    PORT_SetError (SEC_ERROR_PKCS7_BAD_SIGNATURE);
+	    goto done;
+	}
+
+	/*
+	 * Okay, we met the constraints of the basic attributes.
+	 * Now check the signature, which is based on a digest of
+	 * the DER-encoded authenticated attributes.  So, first we
+	 * encode and then we digest/verify.
+	 */
+	encoded_attrs.data = NULL;
+	encoded_attrs.len = 0;
+	if (sec_PKCS7EncodeAttributes (NULL, &encoded_attrs,
+				       &(signerinfo->authAttr)) == NULL)
+	    goto done;
+
+	if (encoded_attrs.data == NULL || encoded_attrs.len == 0) {
+	    PORT_SetError (SEC_ERROR_PKCS7_BAD_SIGNATURE);
+	    goto done;
+	}
+
+	goodsig = (PRBool)(VFY_VerifyDataDirect(encoded_attrs.data, 
+				   encoded_attrs.len,
+				   publickey, &(signerinfo->encDigest),
+				   encTag, digestTag, NULL,
+				   cinfo->pwfn_arg) == SECSuccess);
+	PORT_Free (encoded_attrs.data);
+    } else {
+	SECItem *sig;
+	SECItem holder;
+	SECStatus rv;
+
+	/*
+	 * No authenticated attributes.
+	 * The signature is based on the plain message digest.
+	 */
+
+	sig = &(signerinfo->encDigest);
+	if (sig->len == 0) {		/* bad signature */
+	    PORT_SetError (SEC_ERROR_PKCS7_BAD_SIGNATURE);
+	    goto done;
+	}
+
+	if (sigkey != NULL) {
+	    sec_PKCS7CipherObject *decryptobj;
+	    unsigned int buflen;
+
+	    /*
+	     * For signedAndEnvelopedData, we first must decrypt the encrypted
+	     * digest with the bulk encryption key.  The result is the normal
+	     * encrypted digest (aka the signature).
+	     */
+	    decryptobj = sec_PKCS7CreateDecryptObject (sigkey, bulkid);
+	    if (decryptobj == NULL)
+		goto done;
+
+	    buflen = sec_PKCS7DecryptLength (decryptobj, sig->len, PR_TRUE);
+	    PORT_Assert (buflen);
+	    if (buflen == 0) {		/* something is wrong */
+		sec_PKCS7DestroyDecryptObject (decryptobj);
+		goto done;
+	    }
+
+	    holder.data = (unsigned char*)PORT_Alloc (buflen);
+	    if (holder.data == NULL) {
+		sec_PKCS7DestroyDecryptObject (decryptobj);
+		goto done;
+	    }
+
+	    rv = sec_PKCS7Decrypt (decryptobj, holder.data, &holder.len, buflen,
+				   sig->data, sig->len, PR_TRUE);
+	    sec_PKCS7DestroyDecryptObject (decryptobj);
+	    if (rv != SECSuccess) {
+		goto done;
+	    }
+
+	    sig = &holder;
+	}
+
+	goodsig = (PRBool)(VFY_VerifyDigestDirect(digest, publickey, sig,
+				     encTag, digestTag, cinfo->pwfn_arg)
+                            == SECSuccess);
+
+	if (sigkey != NULL) {
+	    PORT_Assert (sig == &holder);
+	    PORT_ZFree (holder.data, holder.len);
+	}
+    }
+
+    if (! goodsig) {
+	/*
+	 * XXX Change the generic error into our specific one, because
+	 * in that case we get a better explanation out of the Security
+	 * Advisor.  This is really a bug in our error strings (the
+	 * "generic" error has a lousy/wrong message associated with it
+	 * which assumes the signature verification was done for the
+	 * purposes of checking the issuer signature on a certificate)
+	 * but this is at least an easy workaround and/or in the
+	 * Security Advisor, which specifically checks for the error
+	 * SEC_ERROR_PKCS7_BAD_SIGNATURE and gives more explanation
+	 * in that case but does not similarly check for
+	 * SEC_ERROR_BAD_SIGNATURE.  It probably should, but then would
+	 * probably say the wrong thing in the case that it *was* the
+	 * certificate signature check that failed during the cert
+	 * verification done above.  Our error handling is really a mess.
+	 */
+	if (PORT_GetError() == SEC_ERROR_BAD_SIGNATURE)
+	    PORT_SetError (SEC_ERROR_PKCS7_BAD_SIGNATURE);
+    }
+
+savecert:
+    /*
+     * Only save the smime profile if we are checking an email message and
+     * the cert has an email address in it.
+     */
+    if ( cert->emailAddr && cert->emailAddr[0] &&
+	( ( certusage == certUsageEmailSigner ) ||
+	 ( certusage == certUsageEmailRecipient ) ) ) {
+	SECItem *profile = NULL;
+	int save_error;
+
+	/*
+	 * Remember the current error set because we do not care about
+	 * anything set by the functions we are about to call.
+	 */
+	save_error = PORT_GetError();
+
+	if (goodsig && (signerinfo->authAttr != NULL)) {
+	    /*
+	     * If the signature is good, then we can save the S/MIME profile,
+	     * if we have one.
+	     */
+	    SEC_PKCS7Attribute *attr;
+
+	    attr = sec_PKCS7FindAttribute (signerinfo->authAttr,
+					   SEC_OID_PKCS9_SMIME_CAPABILITIES,
+					   PR_TRUE);
+	    profile = sec_PKCS7AttributeValue (attr);
+	}
+
+	rv = CERT_SaveSMimeProfile (cert, profile, encoded_stime);
+
+	/*
+	 * Restore the saved error in case the calls above set a new
+	 * one that we do not actually care about.
+	 */
+	PORT_SetError (save_error);
+
+	/*
+	 * XXX Failure is not indicated anywhere -- the signature
+	 * verification itself is unaffected by whether or not the
+	 * profile was successfully saved.
+	 */
+    }
+	
+
+done:
+
+    /*
+     * See comment above about why we do not want to destroy cert
+     * itself here.
+     */
+
+    if (certs != NULL)
+	CERT_DestroyCertArray (certs, certcount);
+
+    if (publickey != NULL)
+	SECKEY_DestroyPublicKey (publickey);
+
+    return goodsig;
+}
+
+/*
+ * SEC_PKCS7VerifySignature
+ *	Look at a PKCS7 contentInfo and check if the signature is good.
+ *	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.
+ */
+PRBool
+SEC_PKCS7VerifySignature(SEC_PKCS7ContentInfo *cinfo,
+			 SECCertUsage certusage,
+			 PRBool keepcerts)
+{
+    return sec_pkcs7_verify_signature (cinfo, certusage,
+				       NULL, HASH_AlgNULL, keepcerts, NULL);
+}
+
+/*
+ * SEC_PKCS7VerifyDetachedSignature
+ *	Look at a PKCS7 contentInfo and check if the signature matches
+ *	a passed-in digest (calculated, supposedly, from detached contents).
+ *	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.
+ */
+PRBool
+SEC_PKCS7VerifyDetachedSignature(SEC_PKCS7ContentInfo *cinfo,
+				 SECCertUsage certusage,
+				 const SECItem *detached_digest,
+				 HASH_HashType digest_type,
+				 PRBool keepcerts)
+{
+    return sec_pkcs7_verify_signature (cinfo, certusage,
+				       detached_digest, digest_type,
+				       keepcerts, NULL);
+}
+
+/*
+ * SEC_PKCS7VerifyDetachedSignatureAtTime
+ *      Look at a PKCS7 contentInfo and check if the signature matches
+ *      a passed-in digest (calculated, supposedly, from detached contents).
+ *      The verification checks that the signing cert is valid and trusted
+ *      for the purpose specified by "certusage" at time "atTime".
+ *
+ *	In addition, if "keepcerts" is true, add any new certificates found
+ *	into our local database.
+ */
+PRBool
+SEC_PKCS7VerifyDetachedSignatureAtTime(SEC_PKCS7ContentInfo *cinfo,
+				       SECCertUsage certusage,
+				       const SECItem *detached_digest,
+				       HASH_HashType digest_type,
+				       PRBool keepcerts,
+				       PRTime atTime)
+{
+    return sec_pkcs7_verify_signature (cinfo, certusage,
+				       detached_digest, digest_type,
+				       keepcerts, &atTime);
+}
+
+/*
+ * Return the asked-for portion of the name of the signer of a PKCS7
+ * signed object.
+ *
+ * Returns a pointer to allocated memory, which must be freed.
+ * A NULL return value is an error.
+ */
+
+#define sec_common_name 1
+#define sec_email_address 2
+
+static char *
+sec_pkcs7_get_signer_cert_info(SEC_PKCS7ContentInfo *cinfo, int selector)
+{
+    SECOidTag kind;
+    SEC_PKCS7SignerInfo **signerinfos;
+    CERTCertificate *signercert;
+    char *container;
+
+    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:
+	PORT_Assert (0);
+	return NULL;
+      case SEC_OID_PKCS7_SIGNED_DATA:
+	{
+	    SEC_PKCS7SignedData *sdp;
+
+	    sdp = cinfo->content.signedData;
+	    signerinfos = sdp->signerInfos;
+	}
+	break;
+      case SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA:
+	{
+	    SEC_PKCS7SignedAndEnvelopedData *saedp;
+
+	    saedp = cinfo->content.signedAndEnvelopedData;
+	    signerinfos = saedp->signerInfos;
+	}
+	break;
+    }
+
+    if (signerinfos == NULL || signerinfos[0] == NULL)
+	return NULL;
+
+    signercert = signerinfos[0]->cert;
+
+    /*
+     * No cert there; see if we can find one by calling verify ourselves.
+     */
+    if (signercert == NULL) {
+	/*
+	 * The cert usage does not matter in this case, because we do not
+	 * actually care about the verification itself, but we have to pick
+	 * some valid usage to pass in.
+	 */
+	(void) sec_pkcs7_verify_signature (cinfo, certUsageEmailSigner,
+					   NULL, HASH_AlgNULL, PR_FALSE, NULL);
+	signercert = signerinfos[0]->cert;
+	if (signercert == NULL)
+	    return NULL;
+    }
+
+    switch (selector) {
+      case sec_common_name:
+	container = CERT_GetCommonName (&signercert->subject);
+	break;
+      case sec_email_address:
+	if(signercert->emailAddr && signercert->emailAddr[0]) {
+	    container = PORT_Strdup(signercert->emailAddr);
+	} else {
+	    container = NULL;
+	}
+	break;
+      default:
+	PORT_Assert (0);
+	container = NULL;
+	break;
+    }
+
+    return container;
+}
+
+char *
+SEC_PKCS7GetSignerCommonName(SEC_PKCS7ContentInfo *cinfo)
+{
+    return sec_pkcs7_get_signer_cert_info(cinfo, sec_common_name);
+}
+
+char *
+SEC_PKCS7GetSignerEmailAddress(SEC_PKCS7ContentInfo *cinfo)
+{
+    return sec_pkcs7_get_signer_cert_info(cinfo, sec_email_address);
+}
+
+
+/*
+ * Return the signing time, in UTCTime format, of a PKCS7 contentInfo.
+ */
+SECItem *
+SEC_PKCS7GetSigningTime(SEC_PKCS7ContentInfo *cinfo)
+{
+    SEC_PKCS7SignerInfo **signerinfos;
+    SEC_PKCS7Attribute *attr;
+
+    if (SEC_PKCS7ContentType (cinfo) != SEC_OID_PKCS7_SIGNED_DATA)
+	return NULL;
+
+    signerinfos = cinfo->content.signedData->signerInfos;
+
+    /*
+     * No signature, or more than one, means no deal.
+     */
+    if (signerinfos == NULL || signerinfos[0] == NULL || signerinfos[1] != NULL)
+	return NULL;
+
+    attr = sec_PKCS7FindAttribute (signerinfos[0]->authAttr,
+				   SEC_OID_PKCS9_SIGNING_TIME, PR_TRUE);
+    return sec_PKCS7AttributeValue (attr);
+}
This site is hosted by Intevation GmbH (Datenschutzerklärung und Impressum | Privacy Policy and Imprint)