| /* 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, ¶m, 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, ¶m, 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); |
| PK11_DestroyContext(ciphercx, 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) |
| { |
| unsigned 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) { |
| if (padsize) { |
| padlen = padsize - (pcount % padsize); |
| PORT_Memset(pbuf + pcount, padlen, padlen); |
| } else { |
| padlen = 0; |
| } |
| 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. |
| */ |