diff nss/lib/pkcs7/p7local.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/p7local.c	Mon Jul 28 10:47:06 2014 +0200
@@ -0,0 +1,1319 @@
+/* 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/. */
+
+/*
+ * Support routines for PKCS7 implementation, none of which are exported.
+ * This file should only contain things that are needed by both the
+ * encoding/creation side *and* the decoding/decryption side.  Anything
+ * else should be static routines in the appropriate file.
+ */
+
+#include "p7local.h"
+
+#include "cryptohi.h" 
+#include "secasn1.h"
+#include "secoid.h"
+#include "secitem.h"
+#include "pk11func.h"
+#include "secpkcs5.h"
+#include "secerr.h"
+
+/*
+ * -------------------------------------------------------------------
+ * Cipher stuff.
+ */
+
+typedef SECStatus (*sec_pkcs7_cipher_function) (void *,
+						unsigned char *,
+						unsigned *,
+						unsigned int,
+						const unsigned char *,
+						unsigned int);
+typedef SECStatus (*sec_pkcs7_cipher_destroy) (void *, PRBool);
+
+#define BLOCK_SIZE 4096
+
+struct sec_pkcs7_cipher_object {
+    void *cx;
+    sec_pkcs7_cipher_function doit;
+    sec_pkcs7_cipher_destroy destroy;
+    PRBool encrypt;
+    int block_size;
+    int pad_size;
+    int pending_count;
+    unsigned char pending_buf[BLOCK_SIZE];
+};
+
+SEC_ASN1_MKSUB(CERT_IssuerAndSNTemplate)
+SEC_ASN1_MKSUB(CERT_SetOfSignedCrlTemplate)
+SEC_ASN1_MKSUB(SECOID_AlgorithmIDTemplate)
+SEC_ASN1_MKSUB(SEC_OctetStringTemplate)
+SEC_ASN1_MKSUB(SEC_SetOfAnyTemplate)
+
+/*
+ * Create a cipher object to do decryption,  based on the given bulk
+ * encryption key and algorithm identifier (which may include an iv).
+ *
+ * XXX This interface, or one similar, would be really nice available
+ * in general...  I tried to keep the pkcs7-specific stuff (mostly
+ * having to do with padding) out of here.
+ *
+ * XXX Once both are working, it might be nice to combine this and the
+ * function below (for starting up encryption) into one routine, and just
+ * have two simple cover functions which call it. 
+ */
+sec_PKCS7CipherObject *
+sec_PKCS7CreateDecryptObject (PK11SymKey *key, SECAlgorithmID *algid)
+{
+    sec_PKCS7CipherObject *result;
+    SECOidTag algtag;
+    void *ciphercx;
+    CK_MECHANISM_TYPE cryptoMechType;
+    PK11SlotInfo *slot;
+    SECItem *param = NULL;
+
+    result = (struct sec_pkcs7_cipher_object*)
+      PORT_ZAlloc (sizeof(struct sec_pkcs7_cipher_object));
+    if (result == NULL)
+	return NULL;
+
+    ciphercx = NULL;
+    algtag = SECOID_GetAlgorithmTag (algid);
+
+    if (SEC_PKCS5IsAlgorithmPBEAlg(algid)) {
+	SECItem *pwitem;
+
+	pwitem = (SECItem *)PK11_GetSymKeyUserData(key);
+	if (!pwitem) {
+	    PORT_Free(result);
+	    return NULL;
+	}
+
+	cryptoMechType = PK11_GetPBECryptoMechanism(algid, &param, pwitem);
+	if (cryptoMechType == CKM_INVALID_MECHANISM) {
+	    PORT_Free(result);
+	    SECITEM_FreeItem(param,PR_TRUE);
+	    return NULL;
+	}
+    } else {
+	cryptoMechType = PK11_AlgtagToMechanism(algtag);
+	param = PK11_ParamFromAlgid(algid);
+	if (param == NULL) {
+	    PORT_Free(result);
+	    return NULL;
+	}
+    }
+
+    result->pad_size = PK11_GetBlockSize(cryptoMechType, param);
+    slot = PK11_GetSlotFromKey(key);
+    result->block_size = PK11_IsHW(slot) ? BLOCK_SIZE : result->pad_size;
+    PK11_FreeSlot(slot);
+    ciphercx = PK11_CreateContextBySymKey(cryptoMechType, CKA_DECRYPT, 
+					  key, param);
+    SECITEM_FreeItem(param,PR_TRUE);
+    if (ciphercx == NULL) {
+	PORT_Free (result);
+	return NULL;
+    }
+
+    result->cx = ciphercx;
+    result->doit =  (sec_pkcs7_cipher_function) PK11_CipherOp;
+    result->destroy = (sec_pkcs7_cipher_destroy) PK11_DestroyContext;
+    result->encrypt = PR_FALSE;
+    result->pending_count = 0;
+
+    return result;
+}
+
+/*
+ * Create a cipher object to do encryption, based on the given bulk
+ * encryption key and algorithm tag.  Fill in the algorithm identifier
+ * (which may include an iv) appropriately.
+ *
+ * XXX This interface, or one similar, would be really nice available
+ * in general...  I tried to keep the pkcs7-specific stuff (mostly
+ * having to do with padding) out of here.
+ *
+ * XXX Once both are working, it might be nice to combine this and the
+ * function above (for starting up decryption) into one routine, and just
+ * have two simple cover functions which call it. 
+ */
+sec_PKCS7CipherObject *
+sec_PKCS7CreateEncryptObject (PLArenaPool *poolp, PK11SymKey *key,
+			      SECOidTag algtag, SECAlgorithmID *algid)
+{
+    sec_PKCS7CipherObject *result;
+    void *ciphercx;
+    SECStatus rv;
+    CK_MECHANISM_TYPE cryptoMechType;
+    PK11SlotInfo *slot;
+    SECItem *param = NULL;
+    PRBool needToEncodeAlgid = PR_FALSE;
+
+    result = (struct sec_pkcs7_cipher_object*)
+	      PORT_ZAlloc (sizeof(struct sec_pkcs7_cipher_object));
+    if (result == NULL)
+	return NULL;
+
+    ciphercx = NULL;
+    if (SEC_PKCS5IsAlgorithmPBEAlg(algid)) {
+	SECItem *pwitem;
+
+	pwitem = (SECItem *)PK11_GetSymKeyUserData(key);
+	if (!pwitem) {
+	    PORT_Free(result);
+	    return NULL;
+	}
+
+	cryptoMechType = PK11_GetPBECryptoMechanism(algid, &param, pwitem);
+	if (cryptoMechType == CKM_INVALID_MECHANISM) {
+	    PORT_Free(result);
+	    SECITEM_FreeItem(param,PR_TRUE);
+	    return NULL;
+	}
+    } else {
+	cryptoMechType = PK11_AlgtagToMechanism(algtag);
+	param = PK11_GenerateNewParam(cryptoMechType, key);
+	if (param == NULL) {
+	    PORT_Free(result);
+	    return NULL;
+	}
+	needToEncodeAlgid = PR_TRUE;
+    }
+
+    result->pad_size = PK11_GetBlockSize(cryptoMechType,param);
+    slot = PK11_GetSlotFromKey(key);
+    result->block_size = PK11_IsHW(slot) ? BLOCK_SIZE : result->pad_size;
+    PK11_FreeSlot(slot);
+    ciphercx = PK11_CreateContextBySymKey(cryptoMechType, CKA_ENCRYPT, 
+    					  key, param);
+    if (ciphercx == NULL) {
+	PORT_Free (result);
+        SECITEM_FreeItem(param,PR_TRUE);
+	return NULL;
+    }
+
+    /*
+     * These are placed after the CreateContextBySymKey() because some
+     * mechanisms have to generate their IVs from their card (i.e. FORTEZZA).
+     * Don't move it from here.
+     */
+    if (needToEncodeAlgid) {
+	rv = PK11_ParamToAlgid(algtag,param,poolp,algid);
+	if(rv != SECSuccess) {
+	    PORT_Free (result);
+            SECITEM_FreeItem(param,PR_TRUE);
+	    return NULL;
+	}
+    }
+    SECITEM_FreeItem(param,PR_TRUE);
+
+    result->cx = ciphercx;
+    result->doit = (sec_pkcs7_cipher_function) PK11_CipherOp;
+    result->destroy = (sec_pkcs7_cipher_destroy) PK11_DestroyContext;
+    result->encrypt = PR_TRUE;
+    result->pending_count = 0;
+
+    return result;
+}
+
+
+/*
+ * Destroy the cipher object.
+ */
+static void
+sec_pkcs7_destroy_cipher (sec_PKCS7CipherObject *obj)
+{
+    (* obj->destroy) (obj->cx, PR_TRUE);
+    PORT_Free (obj);
+}
+
+void
+sec_PKCS7DestroyDecryptObject (sec_PKCS7CipherObject *obj)
+{
+    PORT_Assert (obj != NULL);
+    if (obj == NULL)
+	return;
+    PORT_Assert (! obj->encrypt);
+    sec_pkcs7_destroy_cipher (obj);
+}
+
+void
+sec_PKCS7DestroyEncryptObject (sec_PKCS7CipherObject *obj)
+{
+    PORT_Assert (obj != NULL);
+    if (obj == NULL)
+	return;
+    PORT_Assert (obj->encrypt);
+    sec_pkcs7_destroy_cipher (obj);
+}
+
+
+/*
+ * XXX I think all of the following lengths should be longs instead
+ * of ints, but our current crypto interface uses ints, so I did too.
+ */
+
+
+/*
+ * What will be the output length of the next call to decrypt?
+ * Result can be used to perform memory allocations.  Note that the amount
+ * is exactly accurate only when not doing a block cipher or when final
+ * is false, otherwise it is an upper bound on the amount because until
+ * we see the data we do not know how many padding bytes there are
+ * (always between 1 and bsize).
+ *
+ * Note that this can return zero, which does not mean that the decrypt
+ * operation can be skipped!  (It simply means that there are not enough
+ * bytes to make up an entire block; the bytes will be reserved until
+ * there are enough to encrypt/decrypt at least one block.)  However,
+ * if zero is returned it *does* mean that no output buffer need be
+ * passed in to the subsequent decrypt operation, as no output bytes
+ * will be stored.
+ */
+unsigned int
+sec_PKCS7DecryptLength (sec_PKCS7CipherObject *obj, unsigned int input_len,
+			PRBool final)
+{
+    int blocks, block_size;
+
+    PORT_Assert (! obj->encrypt);
+
+    block_size = obj->block_size;
+
+    /*
+     * If this is not a block cipher, then we always have the same
+     * number of output bytes as we had input bytes.
+     */
+    if (block_size == 0)
+	return input_len;
+
+    /*
+     * On the final call, we will always use up all of the pending
+     * bytes plus all of the input bytes, *but*, there will be padding
+     * at the end and we cannot predict how many bytes of padding we
+     * will end up removing.  The amount given here is actually known
+     * to be at least 1 byte too long (because we know we will have
+     * at least 1 byte of padding), but seemed clearer/better to me.
+     */
+    if (final)
+	return obj->pending_count + input_len;
+
+    /*
+     * Okay, this amount is exactly what we will output on the
+     * next cipher operation.  We will always hang onto the last
+     * 1 - block_size bytes for non-final operations.  That is,
+     * we will do as many complete blocks as we can *except* the
+     * last block (complete or partial).  (This is because until
+     * we know we are at the end, we cannot know when to interpret
+     * and removing the padding byte(s), which are guaranteed to
+     * be there.)
+     */
+    blocks = (obj->pending_count + input_len - 1) / block_size;
+    return blocks * block_size;
+}
+
+/*
+ * What will be the output length of the next call to encrypt?
+ * Result can be used to perform memory allocations.
+ *
+ * Note that this can return zero, which does not mean that the encrypt
+ * operation can be skipped!  (It simply means that there are not enough
+ * bytes to make up an entire block; the bytes will be reserved until
+ * there are enough to encrypt/decrypt at least one block.)  However,
+ * if zero is returned it *does* mean that no output buffer need be
+ * passed in to the subsequent encrypt operation, as no output bytes
+ * will be stored.
+ */
+unsigned int
+sec_PKCS7EncryptLength (sec_PKCS7CipherObject *obj, unsigned int input_len,
+			PRBool final)
+{
+    int blocks, block_size;
+    int pad_size;
+
+    PORT_Assert (obj->encrypt);
+
+    block_size = obj->block_size;
+    pad_size = obj->pad_size;
+
+    /*
+     * If this is not a block cipher, then we always have the same
+     * number of output bytes as we had input bytes.
+     */
+    if (block_size == 0)
+	return input_len;
+
+    /*
+     * On the final call, we only send out what we need for
+     * remaining bytes plus the padding.  (There is always padding,
+     * so even if we have an exact number of blocks as input, we
+     * will add another full block that is just padding.)
+     */
+    if (final) {
+	if (pad_size == 0) {
+    	    return obj->pending_count + input_len;
+	} else {
+    	    blocks = (obj->pending_count + input_len) / pad_size;
+	    blocks++;
+	    return blocks*pad_size;
+	}
+    }
+
+    /*
+     * Now, count the number of complete blocks of data we have.
+     */
+    blocks = (obj->pending_count + input_len) / block_size;
+
+
+    return blocks * block_size;
+}
+
+
+/*
+ * Decrypt a given length of input buffer (starting at "input" and
+ * containing "input_len" bytes), placing the decrypted bytes in
+ * "output" and storing the output length in "*output_len_p".
+ * "obj" is the return value from sec_PKCS7CreateDecryptObject.
+ * When "final" is true, this is the last of the data to be decrypted.
+ *
+ * This is much more complicated than it sounds when the cipher is
+ * a block-type, meaning that the decryption function will only
+ * operate on whole blocks.  But our caller is operating stream-wise,
+ * and can pass in any number of bytes.  So we need to keep track
+ * of block boundaries.  We save excess bytes between calls in "obj".
+ * We also need to determine which bytes are padding, and remove
+ * them from the output.  We can only do this step when we know we
+ * have the final block of data.  PKCS #7 specifies that the padding
+ * used for a block cipher is a string of bytes, each of whose value is
+ * the same as the length of the padding, and that all data is padded.
+ * (Even data that starts out with an exact multiple of blocks gets
+ * added to it another block, all of which is padding.)
+ */ 
+SECStatus
+sec_PKCS7Decrypt (sec_PKCS7CipherObject *obj, unsigned char *output,
+		  unsigned int *output_len_p, unsigned int max_output_len,
+		  const unsigned char *input, unsigned int input_len,
+		  PRBool final)
+{
+    int blocks, bsize, pcount, padsize;
+    unsigned int max_needed, ifraglen, ofraglen, output_len;
+    unsigned char *pbuf;
+    SECStatus rv;
+
+    PORT_Assert (! obj->encrypt);
+
+    /*
+     * Check that we have enough room for the output.  Our caller should
+     * already handle this; failure is really an internal error (i.e. bug).
+     */
+    max_needed = sec_PKCS7DecryptLength (obj, input_len, final);
+    PORT_Assert (max_output_len >= max_needed);
+    if (max_output_len < max_needed) {
+	/* PORT_SetError (XXX); */
+	return SECFailure;
+    }
+
+    /*
+     * hardware encryption does not like small decryption sizes here, so we
+     * allow both blocking and padding.
+     */
+    bsize = obj->block_size;
+    padsize = obj->pad_size;
+
+    /*
+     * When no blocking or padding work to do, we can simply call the
+     * cipher function and we are done.
+     */
+    if (bsize == 0) {
+	return (* obj->doit) (obj->cx, output, output_len_p, max_output_len,
+			      input, input_len);
+    }
+
+    pcount = obj->pending_count;
+    pbuf = obj->pending_buf;
+
+    output_len = 0;
+
+    if (pcount) {
+	/*
+	 * Try to fill in an entire block, starting with the bytes
+	 * we already have saved away.
+	 */
+	while (input_len && pcount < bsize) {
+	    pbuf[pcount++] = *input++;
+	    input_len--;
+	}
+	/*
+	 * If we have at most a whole block and this is not our last call,
+	 * then we are done for now.  (We do not try to decrypt a lone
+	 * single block because we cannot interpret the padding bytes
+	 * until we know we are handling the very last block of all input.)
+	 */
+	if (input_len == 0 && !final) {
+	    obj->pending_count = pcount;
+	    if (output_len_p)
+		*output_len_p = 0;
+	    return SECSuccess;
+	}
+	/*
+	 * Given the logic above, we expect to have a full block by now.
+	 * If we do not, there is something wrong, either with our own
+	 * logic or with (length of) the data given to us.
+	 */
+	PORT_Assert ((padsize == 0) || (pcount % padsize) == 0);
+	if ((padsize != 0) && (pcount % padsize) != 0) {
+	    PORT_Assert (final);	
+	    PORT_SetError (SEC_ERROR_BAD_DATA);
+	    return SECFailure;
+	}
+	/*
+	 * Decrypt the block.
+	 */
+	rv = (* obj->doit) (obj->cx, output, &ofraglen, max_output_len,
+			    pbuf, pcount);
+	if (rv != SECSuccess)
+	    return rv;
+
+	/*
+	 * For now anyway, all of our ciphers have the same number of
+	 * bytes of output as they do input.  If this ever becomes untrue,
+	 * then sec_PKCS7DecryptLength needs to be made smarter!
+	 */
+	PORT_Assert (ofraglen == pcount);
+
+	/*
+	 * Account for the bytes now in output.
+	 */
+	max_output_len -= ofraglen;
+	output_len += ofraglen;
+	output += ofraglen;
+    }
+
+    /*
+     * If this is our last call, we expect to have an exact number of
+     * blocks left to be decrypted; we will decrypt them all.
+     * 
+     * If not our last call, we always save between 1 and bsize bytes
+     * until next time.  (We must do this because we cannot be sure
+     * that none of the decrypted bytes are padding bytes until we
+     * have at least another whole block of data.  You cannot tell by
+     * looking -- the data could be anything -- you can only tell by
+     * context, knowing you are looking at the last block.)  We could
+     * decrypt a whole block now but it is easier if we just treat it
+     * the same way we treat partial block bytes.
+     */
+    if (final) {
+	if (padsize) {
+	    blocks = input_len / padsize;
+	    ifraglen = blocks * padsize;
+	} else ifraglen = input_len;
+	PORT_Assert (ifraglen == input_len);
+
+	if (ifraglen != input_len) {
+	    PORT_SetError (SEC_ERROR_BAD_DATA);
+	    return SECFailure;
+	}
+    } else {
+	blocks = (input_len - 1) / bsize;
+	ifraglen = blocks * bsize;
+	PORT_Assert (ifraglen < input_len);
+
+	pcount = input_len - ifraglen;
+	PORT_Memcpy (pbuf, input + ifraglen, pcount);
+	obj->pending_count = pcount;
+    }
+
+    if (ifraglen) {
+	rv = (* obj->doit) (obj->cx, output, &ofraglen, max_output_len,
+			    input, ifraglen);
+	if (rv != SECSuccess)
+	    return rv;
+
+	/*
+	 * For now anyway, all of our ciphers have the same number of
+	 * bytes of output as they do input.  If this ever becomes untrue,
+	 * then sec_PKCS7DecryptLength needs to be made smarter!
+	 */
+	PORT_Assert (ifraglen == ofraglen);
+	if (ifraglen != ofraglen) {
+	    PORT_SetError (SEC_ERROR_BAD_DATA);
+	    return SECFailure;
+	}
+
+	output_len += ofraglen;
+    } else {
+	ofraglen = 0;
+    }
+
+    /*
+     * If we just did our very last block, "remove" the padding by
+     * adjusting the output length.
+     */
+    if (final && (padsize != 0)) {
+	unsigned int padlen = *(output + ofraglen - 1);
+	if (padlen == 0 || padlen > padsize) {
+	    PORT_SetError (SEC_ERROR_BAD_DATA);
+	    return SECFailure;
+	}
+	output_len -= padlen;
+    }
+
+    PORT_Assert (output_len_p != NULL || output_len == 0);
+    if (output_len_p != NULL)
+	*output_len_p = output_len;
+
+    return SECSuccess;
+}
+
+/*
+ * Encrypt a given length of input buffer (starting at "input" and
+ * containing "input_len" bytes), placing the encrypted bytes in
+ * "output" and storing the output length in "*output_len_p".
+ * "obj" is the return value from sec_PKCS7CreateEncryptObject.
+ * When "final" is true, this is the last of the data to be encrypted.
+ *
+ * This is much more complicated than it sounds when the cipher is
+ * a block-type, meaning that the encryption function will only
+ * operate on whole blocks.  But our caller is operating stream-wise,
+ * and can pass in any number of bytes.  So we need to keep track
+ * of block boundaries.  We save excess bytes between calls in "obj".
+ * We also need to add padding bytes at the end.  PKCS #7 specifies
+ * that the padding used for a block cipher is a string of bytes,
+ * each of whose value is the same as the length of the padding,
+ * and that all data is padded.  (Even data that starts out with
+ * an exact multiple of blocks gets added to it another block,
+ * all of which is padding.)
+ *
+ * XXX I would kind of like to combine this with the function above
+ * which does decryption, since they have a lot in common.  But the
+ * tricky parts about padding and filling blocks would be much
+ * harder to read that way, so I left them separate.  At least for
+ * now until it is clear that they are right.
+ */ 
+SECStatus
+sec_PKCS7Encrypt (sec_PKCS7CipherObject *obj, unsigned char *output,
+		  unsigned int *output_len_p, unsigned int max_output_len,
+		  const unsigned char *input, unsigned int input_len,
+		  PRBool final)
+{
+    int blocks, bsize, padlen, pcount, padsize;
+    unsigned int max_needed, ifraglen, ofraglen, output_len;
+    unsigned char *pbuf;
+    SECStatus rv;
+
+    PORT_Assert (obj->encrypt);
+
+    /*
+     * Check that we have enough room for the output.  Our caller should
+     * already handle this; failure is really an internal error (i.e. bug).
+     */
+    max_needed = sec_PKCS7EncryptLength (obj, input_len, final);
+    PORT_Assert (max_output_len >= max_needed);
+    if (max_output_len < max_needed) {
+	/* PORT_SetError (XXX); */
+	return SECFailure;
+    }
+
+    bsize = obj->block_size;
+    padsize = obj->pad_size;
+
+    /*
+     * When no blocking and padding work to do, we can simply call the
+     * cipher function and we are done.
+     */
+    if (bsize == 0) {
+	return (* obj->doit) (obj->cx, output, output_len_p, max_output_len,
+			      input, input_len);
+    }
+
+    pcount = obj->pending_count;
+    pbuf = obj->pending_buf;
+
+    output_len = 0;
+
+    if (pcount) {
+	/*
+	 * Try to fill in an entire block, starting with the bytes
+	 * we already have saved away.
+	 */
+	while (input_len && pcount < bsize) {
+	    pbuf[pcount++] = *input++;
+	    input_len--;
+	}
+	/*
+	 * If we do not have a full block and we know we will be
+	 * called again, then we are done for now.
+	 */
+	if (pcount < bsize && !final) {
+	    obj->pending_count = pcount;
+	    if (output_len_p != NULL)
+		*output_len_p = 0;
+	    return SECSuccess;
+	}
+	/*
+	 * If we have a whole block available, encrypt it.
+	 */
+	if ((padsize == 0) || (pcount % padsize) == 0) {
+	    rv = (* obj->doit) (obj->cx, output, &ofraglen, max_output_len,
+				pbuf, pcount);
+	    if (rv != SECSuccess)
+		return rv;
+
+	    /*
+	     * For now anyway, all of our ciphers have the same number of
+	     * bytes of output as they do input.  If this ever becomes untrue,
+	     * then sec_PKCS7EncryptLength needs to be made smarter!
+	     */
+	    PORT_Assert (ofraglen == pcount);
+
+	    /*
+	     * Account for the bytes now in output.
+	     */
+	    max_output_len -= ofraglen;
+	    output_len += ofraglen;
+	    output += ofraglen;
+
+	    pcount = 0;
+	}
+    }
+
+    if (input_len) {
+	PORT_Assert (pcount == 0);
+
+	blocks = input_len / bsize;
+	ifraglen = blocks * bsize;
+
+	if (ifraglen) {
+	    rv = (* obj->doit) (obj->cx, output, &ofraglen, max_output_len,
+				input, ifraglen);
+	    if (rv != SECSuccess)
+		return rv;
+
+	    /*
+	     * For now anyway, all of our ciphers have the same number of
+	     * bytes of output as they do input.  If this ever becomes untrue,
+	     * then sec_PKCS7EncryptLength needs to be made smarter!
+	     */
+	    PORT_Assert (ifraglen == ofraglen);
+
+	    max_output_len -= ofraglen;
+	    output_len += ofraglen;
+	    output += ofraglen;
+	}
+
+	pcount = input_len - ifraglen;
+	PORT_Assert (pcount < bsize);
+	if (pcount)
+	    PORT_Memcpy (pbuf, input + ifraglen, pcount);
+    }
+
+    if (final) {
+	padlen = padsize - (pcount % padsize);
+	PORT_Memset (pbuf + pcount, padlen, padlen);
+	rv = (* obj->doit) (obj->cx, output, &ofraglen, max_output_len,
+			    pbuf, pcount+padlen);
+	if (rv != SECSuccess)
+	    return rv;
+
+	/*
+	 * For now anyway, all of our ciphers have the same number of
+	 * bytes of output as they do input.  If this ever becomes untrue,
+	 * then sec_PKCS7EncryptLength needs to be made smarter!
+	 */
+	PORT_Assert (ofraglen == (pcount+padlen));
+	output_len += ofraglen;
+    } else {
+	obj->pending_count = pcount;
+    }
+
+    PORT_Assert (output_len_p != NULL || output_len == 0);
+    if (output_len_p != NULL)
+	*output_len_p = output_len;
+
+    return SECSuccess;
+}
+
+/*
+ * End of cipher stuff.
+ * -------------------------------------------------------------------
+ */
+
+
+/*
+ * -------------------------------------------------------------------
+ * XXX The following Attribute stuff really belongs elsewhere.
+ * The Attribute type is *not* part of pkcs7 but rather X.501.
+ * But for now, since PKCS7 is the only customer of attributes,
+ * we define them here.  Once there is a use outside of PKCS7,
+ * then change the attribute types and functions from internal
+ * to external naming convention, and move them elsewhere!
+ */
+
+/*
+ * Look through a set of attributes and find one that matches the
+ * specified object ID.  If "only" is true, then make sure that
+ * there is not more than one attribute of the same type.  Otherwise,
+ * just return the first one found. (XXX Does anybody really want
+ * that first-found behavior?  It was like that when I found it...)
+ */
+SEC_PKCS7Attribute *
+sec_PKCS7FindAttribute (SEC_PKCS7Attribute **attrs, SECOidTag oidtag,
+			PRBool only)
+{
+    SECOidData *oid;
+    SEC_PKCS7Attribute *attr1, *attr2;
+
+    if (attrs == NULL)
+	return NULL;
+
+    oid = SECOID_FindOIDByTag(oidtag);
+    if (oid == NULL)
+	return NULL;
+
+    while ((attr1 = *attrs++) != NULL) {
+	if (attr1->type.len == oid->oid.len && PORT_Memcmp (attr1->type.data,
+							    oid->oid.data,
+							    oid->oid.len) == 0)
+	    break;
+    }
+
+    if (attr1 == NULL)
+	return NULL;
+
+    if (!only)
+	return attr1;
+
+    while ((attr2 = *attrs++) != NULL) {
+	if (attr2->type.len == oid->oid.len && PORT_Memcmp (attr2->type.data,
+							    oid->oid.data,
+							    oid->oid.len) == 0)
+	    break;
+    }
+
+    if (attr2 != NULL)
+	return NULL;
+
+    return attr1;
+}
+
+
+/*
+ * Return the single attribute value, doing some sanity checking first:
+ * - Multiple values are *not* expected.
+ * - Empty values are *not* expected.
+ */
+SECItem *
+sec_PKCS7AttributeValue(SEC_PKCS7Attribute *attr)
+{
+    SECItem *value;
+
+    if (attr == NULL)
+	return NULL;
+
+    value = attr->values[0];
+
+    if (value == NULL || value->data == NULL || value->len == 0)
+	return NULL;
+
+    if (attr->values[1] != NULL)
+	return NULL;
+
+    return value;
+}
+
+static const SEC_ASN1Template *
+sec_attr_choose_attr_value_template(void *src_or_dest, PRBool encoding)
+{
+    const SEC_ASN1Template *theTemplate;
+
+    SEC_PKCS7Attribute *attribute;
+    SECOidData *oiddata;
+    PRBool encoded;
+
+    PORT_Assert (src_or_dest != NULL);
+    if (src_or_dest == NULL)
+	return NULL;
+
+    attribute = (SEC_PKCS7Attribute*)src_or_dest;
+
+    if (encoding && attribute->encoded)
+	return SEC_ASN1_GET(SEC_AnyTemplate);
+
+    oiddata = attribute->typeTag;
+    if (oiddata == NULL) {
+	oiddata = SECOID_FindOID(&attribute->type);
+	attribute->typeTag = oiddata;
+    }
+
+    if (oiddata == NULL) {
+	encoded = PR_TRUE;
+	theTemplate = SEC_ASN1_GET(SEC_AnyTemplate);
+    } else {
+	switch (oiddata->offset) {
+	  default:
+	    encoded = PR_TRUE;
+	    theTemplate = SEC_ASN1_GET(SEC_AnyTemplate);
+	    break;
+	  case SEC_OID_PKCS9_EMAIL_ADDRESS:
+	  case SEC_OID_RFC1274_MAIL:
+	  case SEC_OID_PKCS9_UNSTRUCTURED_NAME:
+	    encoded = PR_FALSE;
+	    theTemplate = SEC_ASN1_GET(SEC_IA5StringTemplate);
+	    break;
+	  case SEC_OID_PKCS9_CONTENT_TYPE:
+	    encoded = PR_FALSE;
+	    theTemplate = SEC_ASN1_GET(SEC_ObjectIDTemplate);
+	    break;
+	  case SEC_OID_PKCS9_MESSAGE_DIGEST:
+	    encoded = PR_FALSE;
+	    theTemplate = SEC_ASN1_GET(SEC_OctetStringTemplate);
+	    break;
+	  case SEC_OID_PKCS9_SIGNING_TIME:
+	    encoded = PR_FALSE;
+            theTemplate = SEC_ASN1_GET(CERT_TimeChoiceTemplate);
+	    break;
+	  /* XXX Want other types here, too */
+	}
+    }
+
+    if (encoding) {
+	/*
+	 * If we are encoding and we think we have an already-encoded value,
+	 * then the code which initialized this attribute should have set
+	 * the "encoded" property to true (and we would have returned early,
+	 * up above).  No devastating error, but that code should be fixed.
+	 * (It could indicate that the resulting encoded bytes are wrong.)
+	 */
+	PORT_Assert (!encoded);
+    } else {
+	/*
+	 * We are decoding; record whether the resulting value is
+	 * still encoded or not.
+	 */
+	attribute->encoded = encoded;
+    }
+    return theTemplate;
+}
+
+static const SEC_ASN1TemplateChooserPtr sec_attr_chooser
+	= sec_attr_choose_attr_value_template;
+
+static const SEC_ASN1Template sec_pkcs7_attribute_template[] = {
+    { SEC_ASN1_SEQUENCE,
+	  0, NULL, sizeof(SEC_PKCS7Attribute) },
+    { SEC_ASN1_OBJECT_ID,
+	  offsetof(SEC_PKCS7Attribute,type) },
+    { SEC_ASN1_DYNAMIC | SEC_ASN1_SET_OF,
+	  offsetof(SEC_PKCS7Attribute,values),
+	  &sec_attr_chooser },
+    { 0 }
+};
+
+static const SEC_ASN1Template sec_pkcs7_set_of_attribute_template[] = {
+    { SEC_ASN1_SET_OF, 0, sec_pkcs7_attribute_template },
+};
+
+/*
+ * If you are wondering why this routine does not reorder the attributes
+ * first, and might be tempted to make it do so, see the comment by the
+ * call to ReorderAttributes in p7encode.c.  (Or, see who else calls this
+ * and think long and hard about the implications of making it always
+ * do the reordering.)
+ */
+SECItem *
+sec_PKCS7EncodeAttributes (PLArenaPool *poolp, SECItem *dest, void *src)
+{
+    return SEC_ASN1EncodeItem (poolp, dest, src,
+			       sec_pkcs7_set_of_attribute_template);
+}
+
+/*
+ * Make sure that the order of the attributes guarantees valid DER
+ * (which must be in lexigraphically ascending order for a SET OF);
+ * if reordering is necessary it will be done in place (in attrs).
+ */
+SECStatus
+sec_PKCS7ReorderAttributes (SEC_PKCS7Attribute **attrs)
+{
+    PLArenaPool *poolp;
+    int num_attrs, i, pass, besti;
+    unsigned int j;
+    SECItem **enc_attrs;
+    SEC_PKCS7Attribute **new_attrs;
+
+    /*
+     * I think we should not be called with NULL.  But if we are,
+     * call it a success anyway, because the order *is* okay.
+     */
+    PORT_Assert (attrs != NULL);
+    if (attrs == NULL)
+	return SECSuccess;
+
+    /*
+     * Count how many attributes we are dealing with here.
+     */
+    num_attrs = 0;
+    while (attrs[num_attrs] != NULL)
+	num_attrs++;
+
+    /*
+     * Again, I think we should have some attributes here.
+     * But if we do not, or if there is only one, then call it
+     * a success because it also already has a fine order.
+     */
+    PORT_Assert (num_attrs);
+    if (num_attrs == 0 || num_attrs == 1)
+	return SECSuccess;
+
+    /*
+     * Allocate an arena for us to work with, so it is easy to
+     * clean up all of the memory (fairly small pieces, really).
+     */
+    poolp = PORT_NewArena (1024);	/* XXX what is right value? */
+    if (poolp == NULL)
+	return SECFailure;		/* no memory; nothing we can do... */
+
+    /*
+     * Allocate arrays to hold the individual encodings which we will use
+     * for comparisons and the reordered attributes as they are sorted.
+     */
+    enc_attrs=(SECItem**)PORT_ArenaZAlloc(poolp, num_attrs*sizeof(SECItem *));
+    new_attrs = (SEC_PKCS7Attribute**)PORT_ArenaZAlloc (poolp,
+				  num_attrs * sizeof(SEC_PKCS7Attribute *));
+    if (enc_attrs == NULL || new_attrs == NULL) {
+	PORT_FreeArena (poolp, PR_FALSE);
+	return SECFailure;
+    }
+
+    /*
+     * DER encode each individual attribute.
+     */
+    for (i = 0; i < num_attrs; i++) {
+	enc_attrs[i] = SEC_ASN1EncodeItem (poolp, NULL, attrs[i],
+					   sec_pkcs7_attribute_template);
+	if (enc_attrs[i] == NULL) {
+	    PORT_FreeArena (poolp, PR_FALSE);
+	    return SECFailure;
+	}
+    }
+
+    /*
+     * Now compare and sort them; this is not the most efficient sorting
+     * method, but it is just fine for the problem at hand, because the
+     * number of attributes is (always) going to be small.
+     */
+    for (pass = 0; pass < num_attrs; pass++) {
+	/*
+	 * Find the first not-yet-accepted attribute.  (Once one is
+	 * sorted into the other array, it is cleared from enc_attrs.)
+	 */
+	for (i = 0; i < num_attrs; i++) {
+	    if (enc_attrs[i] != NULL)
+		break;
+	}
+	PORT_Assert (i < num_attrs);
+	besti = i;
+
+	/*
+	 * Find the lowest (lexigraphically) encoding.  One that is
+	 * shorter than all the rest is known to be "less" because each
+	 * attribute is of the same type (a SEQUENCE) and so thus the
+	 * first octet of each is the same, and the second octet is
+	 * the length (or the length of the length with the high bit
+	 * set, followed by the length, which also works out to always
+	 * order the shorter first).  Two (or more) that have the
+	 * same length need to be compared byte by byte until a mismatch
+	 * is found.
+	 */
+	for (i = besti + 1; i < num_attrs; i++) {
+	    if (enc_attrs[i] == NULL)	/* slot already handled */
+		continue;
+
+	    if (enc_attrs[i]->len != enc_attrs[besti]->len) {
+		if (enc_attrs[i]->len < enc_attrs[besti]->len)
+		    besti = i;
+		continue;
+	    }
+
+	    for (j = 0; j < enc_attrs[i]->len; j++) {
+		if (enc_attrs[i]->data[j] < enc_attrs[besti]->data[j]) {
+		    besti = i;
+		    break;
+		}
+	    }
+
+	    /*
+	     * For this not to be true, we would have to have encountered	
+	     * two *identical* attributes, which I think we should not see.
+	     * So assert if it happens, but even if it does, let it go
+	     * through; the ordering of the two does not matter.
+	     */
+	    PORT_Assert (j < enc_attrs[i]->len);
+	}
+
+	/*
+	 * Now we have found the next-lowest one; copy it over and
+	 * remove it from enc_attrs.
+	 */
+	new_attrs[pass] = attrs[besti];
+	enc_attrs[besti] = NULL;
+    }
+
+    /*
+     * Now new_attrs has the attributes in the order we want;
+     * copy them back into the attrs array we started with.
+     */
+    for (i = 0; i < num_attrs; i++)
+	attrs[i] = new_attrs[i];
+
+    PORT_FreeArena (poolp, PR_FALSE);
+    return SECSuccess;
+}
+
+/*
+ * End of attribute stuff.
+ * -------------------------------------------------------------------
+ */
+
+
+/*
+ * Templates and stuff.  Keep these at the end of the file.
+ */
+
+/* forward declaration */
+static const SEC_ASN1Template *
+sec_pkcs7_choose_content_template(void *src_or_dest, PRBool encoding);
+
+static const SEC_ASN1TemplateChooserPtr sec_pkcs7_chooser
+	= sec_pkcs7_choose_content_template;
+
+const SEC_ASN1Template sec_PKCS7ContentInfoTemplate[] = {
+    { SEC_ASN1_SEQUENCE | SEC_ASN1_MAY_STREAM,
+	  0, NULL, sizeof(SEC_PKCS7ContentInfo) },
+    { SEC_ASN1_OBJECT_ID,
+	  offsetof(SEC_PKCS7ContentInfo,contentType) },
+    { SEC_ASN1_OPTIONAL | SEC_ASN1_DYNAMIC | SEC_ASN1_MAY_STREAM
+     | SEC_ASN1_EXPLICIT | SEC_ASN1_CONSTRUCTED | SEC_ASN1_CONTEXT_SPECIFIC | 0,
+	  offsetof(SEC_PKCS7ContentInfo,content),
+	  &sec_pkcs7_chooser },
+    { 0 }
+};
+
+/* XXX These names should change from external to internal convention. */
+
+static const SEC_ASN1Template SEC_PKCS7SignerInfoTemplate[] = {
+    { SEC_ASN1_SEQUENCE,
+	  0, NULL, sizeof(SEC_PKCS7SignerInfo) },
+    { SEC_ASN1_INTEGER,
+	  offsetof(SEC_PKCS7SignerInfo,version) },
+    { SEC_ASN1_POINTER | SEC_ASN1_XTRN,
+	  offsetof(SEC_PKCS7SignerInfo,issuerAndSN),
+	  SEC_ASN1_SUB(CERT_IssuerAndSNTemplate) },
+    { SEC_ASN1_INLINE | SEC_ASN1_XTRN,
+	  offsetof(SEC_PKCS7SignerInfo,digestAlg),
+	  SEC_ASN1_SUB(SECOID_AlgorithmIDTemplate) },
+    { SEC_ASN1_OPTIONAL | SEC_ASN1_CONSTRUCTED | SEC_ASN1_CONTEXT_SPECIFIC | 0,
+	  offsetof(SEC_PKCS7SignerInfo,authAttr),
+	  sec_pkcs7_set_of_attribute_template },
+    { SEC_ASN1_INLINE | SEC_ASN1_XTRN,
+	  offsetof(SEC_PKCS7SignerInfo,digestEncAlg),
+	  SEC_ASN1_SUB(SECOID_AlgorithmIDTemplate) },
+    { SEC_ASN1_OCTET_STRING,
+	  offsetof(SEC_PKCS7SignerInfo,encDigest) },
+    { SEC_ASN1_OPTIONAL | SEC_ASN1_CONSTRUCTED | SEC_ASN1_CONTEXT_SPECIFIC | 1,
+	  offsetof(SEC_PKCS7SignerInfo,unAuthAttr),
+	  sec_pkcs7_set_of_attribute_template },
+    { 0 }
+};
+
+static const SEC_ASN1Template SEC_PKCS7SignedDataTemplate[] = {
+    { SEC_ASN1_SEQUENCE | SEC_ASN1_MAY_STREAM,
+	  0, NULL, sizeof(SEC_PKCS7SignedData) },
+    { SEC_ASN1_INTEGER,
+	  offsetof(SEC_PKCS7SignedData,version) },
+    { SEC_ASN1_SET_OF | SEC_ASN1_XTRN,
+	  offsetof(SEC_PKCS7SignedData,digestAlgorithms),
+	  SEC_ASN1_SUB(SECOID_AlgorithmIDTemplate) },
+    { SEC_ASN1_INLINE,
+	  offsetof(SEC_PKCS7SignedData,contentInfo),
+	  sec_PKCS7ContentInfoTemplate },
+    { SEC_ASN1_OPTIONAL | SEC_ASN1_CONSTRUCTED | SEC_ASN1_CONTEXT_SPECIFIC  |
+      SEC_ASN1_XTRN | 0,
+	  offsetof(SEC_PKCS7SignedData,rawCerts),
+	  SEC_ASN1_SUB(SEC_SetOfAnyTemplate) },
+    { SEC_ASN1_OPTIONAL | SEC_ASN1_CONSTRUCTED | SEC_ASN1_CONTEXT_SPECIFIC  |
+      SEC_ASN1_XTRN | 1,
+	  offsetof(SEC_PKCS7SignedData,crls),
+	  SEC_ASN1_SUB(CERT_SetOfSignedCrlTemplate) },
+    { SEC_ASN1_SET_OF,
+	  offsetof(SEC_PKCS7SignedData,signerInfos),
+	  SEC_PKCS7SignerInfoTemplate },
+    { 0 }
+};
+
+static const SEC_ASN1Template SEC_PointerToPKCS7SignedDataTemplate[] = {
+    { SEC_ASN1_POINTER, 0, SEC_PKCS7SignedDataTemplate }
+};
+
+static const SEC_ASN1Template SEC_PKCS7RecipientInfoTemplate[] = {
+    { SEC_ASN1_SEQUENCE,
+	  0, NULL, sizeof(SEC_PKCS7RecipientInfo) },
+    { SEC_ASN1_INTEGER,
+	  offsetof(SEC_PKCS7RecipientInfo,version) },
+    { SEC_ASN1_POINTER | SEC_ASN1_XTRN,
+	  offsetof(SEC_PKCS7RecipientInfo,issuerAndSN),
+	  SEC_ASN1_SUB(CERT_IssuerAndSNTemplate) },
+    { SEC_ASN1_INLINE | SEC_ASN1_XTRN,
+	  offsetof(SEC_PKCS7RecipientInfo,keyEncAlg),
+	  SEC_ASN1_SUB(SECOID_AlgorithmIDTemplate) },
+    { SEC_ASN1_OCTET_STRING,
+	  offsetof(SEC_PKCS7RecipientInfo,encKey) },
+    { 0 }
+};
+
+static const SEC_ASN1Template SEC_PKCS7EncryptedContentInfoTemplate[] = {
+    { SEC_ASN1_SEQUENCE | SEC_ASN1_MAY_STREAM,
+	  0, NULL, sizeof(SEC_PKCS7EncryptedContentInfo) },
+    { SEC_ASN1_OBJECT_ID,
+	  offsetof(SEC_PKCS7EncryptedContentInfo,contentType) },
+    { SEC_ASN1_INLINE | SEC_ASN1_XTRN,
+	  offsetof(SEC_PKCS7EncryptedContentInfo,contentEncAlg),
+	  SEC_ASN1_SUB(SECOID_AlgorithmIDTemplate) },
+    { SEC_ASN1_OPTIONAL | SEC_ASN1_MAY_STREAM | SEC_ASN1_CONTEXT_SPECIFIC |
+      SEC_ASN1_XTRN | 0,
+	  offsetof(SEC_PKCS7EncryptedContentInfo,encContent),
+	  SEC_ASN1_SUB(SEC_OctetStringTemplate) },
+    { 0 }
+};
+
+static const SEC_ASN1Template SEC_PKCS7EnvelopedDataTemplate[] = {
+    { SEC_ASN1_SEQUENCE | SEC_ASN1_MAY_STREAM,
+	  0, NULL, sizeof(SEC_PKCS7EnvelopedData) },
+    { SEC_ASN1_INTEGER,
+	  offsetof(SEC_PKCS7EnvelopedData,version) },
+    { SEC_ASN1_SET_OF,
+	  offsetof(SEC_PKCS7EnvelopedData,recipientInfos),
+	  SEC_PKCS7RecipientInfoTemplate },
+    { SEC_ASN1_INLINE,
+	  offsetof(SEC_PKCS7EnvelopedData,encContentInfo),
+	  SEC_PKCS7EncryptedContentInfoTemplate },
+    { 0 }
+};
+
+static const SEC_ASN1Template SEC_PointerToPKCS7EnvelopedDataTemplate[] = {
+    { SEC_ASN1_POINTER, 0, SEC_PKCS7EnvelopedDataTemplate }
+};
+
+static const SEC_ASN1Template SEC_PKCS7SignedAndEnvelopedDataTemplate[] = {
+    { SEC_ASN1_SEQUENCE | SEC_ASN1_MAY_STREAM,
+	  0, NULL, sizeof(SEC_PKCS7SignedAndEnvelopedData) },
+    { SEC_ASN1_INTEGER,
+	  offsetof(SEC_PKCS7SignedAndEnvelopedData,version) },
+    { SEC_ASN1_SET_OF,
+	  offsetof(SEC_PKCS7SignedAndEnvelopedData,recipientInfos),
+	  SEC_PKCS7RecipientInfoTemplate },
+    { SEC_ASN1_SET_OF | SEC_ASN1_XTRN,
+	  offsetof(SEC_PKCS7SignedAndEnvelopedData,digestAlgorithms),
+	  SEC_ASN1_SUB(SECOID_AlgorithmIDTemplate) },
+    { SEC_ASN1_INLINE,
+	  offsetof(SEC_PKCS7SignedAndEnvelopedData,encContentInfo),
+	  SEC_PKCS7EncryptedContentInfoTemplate },
+    { SEC_ASN1_OPTIONAL | SEC_ASN1_CONSTRUCTED | SEC_ASN1_CONTEXT_SPECIFIC |
+      SEC_ASN1_XTRN | 0,
+	  offsetof(SEC_PKCS7SignedAndEnvelopedData,rawCerts),
+	  SEC_ASN1_SUB(SEC_SetOfAnyTemplate) },
+    { SEC_ASN1_OPTIONAL | SEC_ASN1_CONSTRUCTED | SEC_ASN1_CONTEXT_SPECIFIC |
+      SEC_ASN1_XTRN | 1,
+	  offsetof(SEC_PKCS7SignedAndEnvelopedData,crls),
+	  SEC_ASN1_SUB(CERT_SetOfSignedCrlTemplate) },
+    { SEC_ASN1_SET_OF,
+	  offsetof(SEC_PKCS7SignedAndEnvelopedData,signerInfos),
+	  SEC_PKCS7SignerInfoTemplate },
+    { 0 }
+};
+
+static const SEC_ASN1Template
+SEC_PointerToPKCS7SignedAndEnvelopedDataTemplate[] = {
+    { SEC_ASN1_POINTER, 0, SEC_PKCS7SignedAndEnvelopedDataTemplate }
+};
+
+static const SEC_ASN1Template SEC_PKCS7DigestedDataTemplate[] = {
+    { SEC_ASN1_SEQUENCE | SEC_ASN1_MAY_STREAM,
+	  0, NULL, sizeof(SEC_PKCS7DigestedData) },
+    { SEC_ASN1_INTEGER,
+	  offsetof(SEC_PKCS7DigestedData,version) },
+    { SEC_ASN1_INLINE | SEC_ASN1_XTRN,
+	  offsetof(SEC_PKCS7DigestedData,digestAlg),
+	  SEC_ASN1_SUB(SECOID_AlgorithmIDTemplate) },
+    { SEC_ASN1_INLINE,
+	  offsetof(SEC_PKCS7DigestedData,contentInfo),
+	  sec_PKCS7ContentInfoTemplate },
+    { SEC_ASN1_OCTET_STRING,
+	  offsetof(SEC_PKCS7DigestedData,digest) },
+    { 0 }
+};
+
+static const SEC_ASN1Template SEC_PointerToPKCS7DigestedDataTemplate[] = {
+    { SEC_ASN1_POINTER, 0, SEC_PKCS7DigestedDataTemplate }
+};
+
+static const SEC_ASN1Template SEC_PKCS7EncryptedDataTemplate[] = {
+    { SEC_ASN1_SEQUENCE | SEC_ASN1_MAY_STREAM,
+	  0, NULL, sizeof(SEC_PKCS7EncryptedData) },
+    { SEC_ASN1_INTEGER,
+	  offsetof(SEC_PKCS7EncryptedData,version) },
+    { SEC_ASN1_INLINE,
+	  offsetof(SEC_PKCS7EncryptedData,encContentInfo),
+	  SEC_PKCS7EncryptedContentInfoTemplate },
+    { 0 }
+};
+
+static const SEC_ASN1Template SEC_PointerToPKCS7EncryptedDataTemplate[] = {
+    { SEC_ASN1_POINTER, 0, SEC_PKCS7EncryptedDataTemplate }
+};
+
+static const SEC_ASN1Template *
+sec_pkcs7_choose_content_template(void *src_or_dest, PRBool encoding)
+{
+    const SEC_ASN1Template *theTemplate;
+    SEC_PKCS7ContentInfo *cinfo;
+    SECOidTag kind;
+
+    PORT_Assert (src_or_dest != NULL);
+    if (src_or_dest == NULL)
+	return NULL;
+
+    cinfo = (SEC_PKCS7ContentInfo*)src_or_dest;
+    kind = SEC_PKCS7ContentType (cinfo);
+    switch (kind) {
+      default:
+	theTemplate = SEC_ASN1_GET(SEC_PointerToAnyTemplate);
+	break;
+      case SEC_OID_PKCS7_DATA:
+	theTemplate = SEC_ASN1_GET(SEC_PointerToOctetStringTemplate);
+	break;
+      case SEC_OID_PKCS7_SIGNED_DATA:
+	theTemplate = SEC_PointerToPKCS7SignedDataTemplate;
+	break;
+      case SEC_OID_PKCS7_ENVELOPED_DATA:
+	theTemplate = SEC_PointerToPKCS7EnvelopedDataTemplate;
+	break;
+      case SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA:
+	theTemplate = SEC_PointerToPKCS7SignedAndEnvelopedDataTemplate;
+	break;
+      case SEC_OID_PKCS7_DIGESTED_DATA:
+	theTemplate = SEC_PointerToPKCS7DigestedDataTemplate;
+	break;
+      case SEC_OID_PKCS7_ENCRYPTED_DATA:
+	theTemplate = SEC_PointerToPKCS7EncryptedDataTemplate;
+	break;
+    }
+    return theTemplate;
+}
+
+/*
+ * End of templates.  Do not add stuff after this; put new code
+ * up above the start of the template definitions.
+ */
This site is hosted by Intevation GmbH (Datenschutzerklärung und Impressum | Privacy Policy and Imprint)