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