michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: /* michael@0: * Encryption/decryption routines for CMS implementation, none of which are exported. michael@0: */ michael@0: michael@0: #include "cmslocal.h" michael@0: michael@0: #include "secoid.h" michael@0: #include "secitem.h" michael@0: #include "pk11func.h" michael@0: #include "secerr.h" michael@0: #include "secpkcs5.h" michael@0: michael@0: /* michael@0: * ------------------------------------------------------------------- michael@0: * Cipher stuff. michael@0: */ michael@0: michael@0: typedef SECStatus (*nss_cms_cipher_function) (void *, unsigned char *, unsigned int *, michael@0: unsigned int, const unsigned char *, unsigned int); michael@0: typedef SECStatus (*nss_cms_cipher_destroy) (void *, PRBool); michael@0: michael@0: #define BLOCK_SIZE 4096 michael@0: michael@0: struct NSSCMSCipherContextStr { michael@0: void * cx; /* PK11 cipher context */ michael@0: nss_cms_cipher_function doit; michael@0: nss_cms_cipher_destroy destroy; michael@0: PRBool encrypt; /* encrypt / decrypt switch */ michael@0: int block_size; /* block & pad sizes for cipher */ michael@0: int pad_size; michael@0: int pending_count; /* pending data (not yet en/decrypted */ michael@0: unsigned char pending_buf[BLOCK_SIZE];/* because of blocking */ michael@0: }; michael@0: michael@0: /* michael@0: * NSS_CMSCipherContext_StartDecrypt - create a cipher context to do decryption michael@0: * based on the given bulk encryption key and algorithm identifier (which michael@0: * may include an iv). michael@0: * michael@0: * XXX Once both are working, it might be nice to combine this and the michael@0: * function below (for starting up encryption) into one routine, and just michael@0: * have two simple cover functions which call it. michael@0: */ michael@0: NSSCMSCipherContext * michael@0: NSS_CMSCipherContext_StartDecrypt(PK11SymKey *key, SECAlgorithmID *algid) michael@0: { michael@0: NSSCMSCipherContext *cc; michael@0: void *ciphercx; michael@0: CK_MECHANISM_TYPE cryptoMechType; michael@0: PK11SlotInfo *slot; michael@0: SECOidTag algtag; michael@0: SECItem *param = NULL; michael@0: michael@0: algtag = SECOID_GetAlgorithmTag(algid); michael@0: michael@0: /* set param and mechanism */ michael@0: if (SEC_PKCS5IsAlgorithmPBEAlg(algid)) { michael@0: SECItem *pwitem; michael@0: michael@0: pwitem = PK11_GetSymKeyUserData(key); michael@0: if (!pwitem) michael@0: return NULL; michael@0: michael@0: cryptoMechType = PK11_GetPBECryptoMechanism(algid, ¶m, pwitem); michael@0: if (cryptoMechType == CKM_INVALID_MECHANISM) { michael@0: SECITEM_FreeItem(param,PR_TRUE); michael@0: return NULL; michael@0: } michael@0: michael@0: } else { michael@0: cryptoMechType = PK11_AlgtagToMechanism(algtag); michael@0: if ((param = PK11_ParamFromAlgid(algid)) == NULL) michael@0: return NULL; michael@0: } michael@0: michael@0: cc = (NSSCMSCipherContext *)PORT_ZAlloc(sizeof(NSSCMSCipherContext)); michael@0: if (cc == NULL) { michael@0: SECITEM_FreeItem(param,PR_TRUE); michael@0: return NULL; michael@0: } michael@0: michael@0: /* figure out pad and block sizes */ michael@0: cc->pad_size = PK11_GetBlockSize(cryptoMechType, param); michael@0: slot = PK11_GetSlotFromKey(key); michael@0: cc->block_size = PK11_IsHW(slot) ? BLOCK_SIZE : cc->pad_size; michael@0: PK11_FreeSlot(slot); michael@0: michael@0: /* create PK11 cipher context */ michael@0: ciphercx = PK11_CreateContextBySymKey(cryptoMechType, CKA_DECRYPT, michael@0: key, param); michael@0: SECITEM_FreeItem(param, PR_TRUE); michael@0: if (ciphercx == NULL) { michael@0: PORT_Free (cc); michael@0: return NULL; michael@0: } michael@0: michael@0: cc->cx = ciphercx; michael@0: cc->doit = (nss_cms_cipher_function) PK11_CipherOp; michael@0: cc->destroy = (nss_cms_cipher_destroy) PK11_DestroyContext; michael@0: cc->encrypt = PR_FALSE; michael@0: cc->pending_count = 0; michael@0: michael@0: return cc; michael@0: } michael@0: michael@0: /* michael@0: * NSS_CMSCipherContext_StartEncrypt - create a cipher object to do encryption, michael@0: * based on the given bulk encryption key and algorithm tag. Fill in the michael@0: * algorithm identifier (which may include an iv) appropriately. michael@0: * michael@0: * XXX Once both are working, it might be nice to combine this and the michael@0: * function above (for starting up decryption) into one routine, and just michael@0: * have two simple cover functions which call it. michael@0: */ michael@0: NSSCMSCipherContext * michael@0: NSS_CMSCipherContext_StartEncrypt(PLArenaPool *poolp, PK11SymKey *key, SECAlgorithmID *algid) michael@0: { michael@0: NSSCMSCipherContext *cc; michael@0: void *ciphercx; michael@0: SECStatus rv; michael@0: CK_MECHANISM_TYPE cryptoMechType; michael@0: PK11SlotInfo *slot; michael@0: SECItem *param = NULL; michael@0: PRBool needToEncodeAlgid = PR_FALSE; michael@0: SECOidTag algtag = SECOID_GetAlgorithmTag(algid); michael@0: michael@0: /* set param and mechanism */ michael@0: if (SEC_PKCS5IsAlgorithmPBEAlg(algid)) { michael@0: SECItem *pwitem; michael@0: michael@0: pwitem = PK11_GetSymKeyUserData(key); michael@0: if (!pwitem) michael@0: return NULL; michael@0: michael@0: cryptoMechType = PK11_GetPBECryptoMechanism(algid, ¶m, pwitem); michael@0: if (cryptoMechType == CKM_INVALID_MECHANISM) { michael@0: SECITEM_FreeItem(param,PR_TRUE); michael@0: return NULL; michael@0: } michael@0: } else { michael@0: cryptoMechType = PK11_AlgtagToMechanism(algtag); michael@0: if ((param = PK11_GenerateNewParam(cryptoMechType, key)) == NULL) michael@0: return NULL; michael@0: needToEncodeAlgid = PR_TRUE; michael@0: } michael@0: michael@0: cc = (NSSCMSCipherContext *)PORT_ZAlloc(sizeof(NSSCMSCipherContext)); michael@0: if (cc == NULL) { michael@0: goto loser; michael@0: } michael@0: michael@0: /* now find pad and block sizes for our mechanism */ michael@0: cc->pad_size = PK11_GetBlockSize(cryptoMechType, param); michael@0: slot = PK11_GetSlotFromKey(key); michael@0: cc->block_size = PK11_IsHW(slot) ? BLOCK_SIZE : cc->pad_size; michael@0: PK11_FreeSlot(slot); michael@0: michael@0: /* and here we go, creating a PK11 cipher context */ michael@0: ciphercx = PK11_CreateContextBySymKey(cryptoMechType, CKA_ENCRYPT, michael@0: key, param); michael@0: if (ciphercx == NULL) { michael@0: PORT_Free(cc); michael@0: cc = NULL; michael@0: goto loser; michael@0: } michael@0: michael@0: /* michael@0: * These are placed after the CreateContextBySymKey() because some michael@0: * mechanisms have to generate their IVs from their card (i.e. FORTEZZA). michael@0: * Don't move it from here. michael@0: * XXX is that right? the purpose of this is to get the correct algid michael@0: * containing the IVs etc. for encoding. this means we need to set this up michael@0: * BEFORE encoding the algid in the contentInfo, right? michael@0: */ michael@0: if (needToEncodeAlgid) { michael@0: rv = PK11_ParamToAlgid(algtag, param, poolp, algid); michael@0: if(rv != SECSuccess) { michael@0: PORT_Free(cc); michael@0: cc = NULL; michael@0: goto loser; michael@0: } michael@0: } michael@0: michael@0: cc->cx = ciphercx; michael@0: cc->doit = (nss_cms_cipher_function)PK11_CipherOp; michael@0: cc->destroy = (nss_cms_cipher_destroy)PK11_DestroyContext; michael@0: cc->encrypt = PR_TRUE; michael@0: cc->pending_count = 0; michael@0: michael@0: loser: michael@0: SECITEM_FreeItem(param, PR_TRUE); michael@0: michael@0: return cc; michael@0: } michael@0: michael@0: void michael@0: NSS_CMSCipherContext_Destroy(NSSCMSCipherContext *cc) michael@0: { michael@0: PORT_Assert(cc != NULL); michael@0: if (cc == NULL) michael@0: return; michael@0: (*cc->destroy)(cc->cx, PR_TRUE); michael@0: PORT_Free(cc); michael@0: } michael@0: michael@0: /* michael@0: * NSS_CMSCipherContext_DecryptLength - find the output length of the next call to decrypt. michael@0: * michael@0: * cc - the cipher context michael@0: * input_len - number of bytes used as input michael@0: * final - true if this is the final chunk of data michael@0: * michael@0: * Result can be used to perform memory allocations. Note that the amount michael@0: * is exactly accurate only when not doing a block cipher or when final michael@0: * is false, otherwise it is an upper bound on the amount because until michael@0: * we see the data we do not know how many padding bytes there are michael@0: * (always between 1 and bsize). michael@0: * michael@0: * Note that this can return zero, which does not mean that the decrypt michael@0: * operation can be skipped! (It simply means that there are not enough michael@0: * bytes to make up an entire block; the bytes will be reserved until michael@0: * there are enough to encrypt/decrypt at least one block.) However, michael@0: * if zero is returned it *does* mean that no output buffer need be michael@0: * passed in to the subsequent decrypt operation, as no output bytes michael@0: * will be stored. michael@0: */ michael@0: unsigned int michael@0: NSS_CMSCipherContext_DecryptLength(NSSCMSCipherContext *cc, unsigned int input_len, PRBool final) michael@0: { michael@0: int blocks, block_size; michael@0: michael@0: PORT_Assert (! cc->encrypt); michael@0: michael@0: block_size = cc->block_size; michael@0: michael@0: /* michael@0: * If this is not a block cipher, then we always have the same michael@0: * number of output bytes as we had input bytes. michael@0: */ michael@0: if (block_size == 0) michael@0: return input_len; michael@0: michael@0: /* michael@0: * On the final call, we will always use up all of the pending michael@0: * bytes plus all of the input bytes, *but*, there will be padding michael@0: * at the end and we cannot predict how many bytes of padding we michael@0: * will end up removing. The amount given here is actually known michael@0: * to be at least 1 byte too long (because we know we will have michael@0: * at least 1 byte of padding), but seemed clearer/better to me. michael@0: */ michael@0: if (final) michael@0: return cc->pending_count + input_len; michael@0: michael@0: /* michael@0: * Okay, this amount is exactly what we will output on the michael@0: * next cipher operation. We will always hang onto the last michael@0: * 1 - block_size bytes for non-final operations. That is, michael@0: * we will do as many complete blocks as we can *except* the michael@0: * last block (complete or partial). (This is because until michael@0: * we know we are at the end, we cannot know when to interpret michael@0: * and removing the padding byte(s), which are guaranteed to michael@0: * be there.) michael@0: */ michael@0: blocks = (cc->pending_count + input_len - 1) / block_size; michael@0: return blocks * block_size; michael@0: } michael@0: michael@0: /* michael@0: * NSS_CMSCipherContext_EncryptLength - find the output length of the next call to encrypt. michael@0: * michael@0: * cc - the cipher context michael@0: * input_len - number of bytes used as input michael@0: * final - true if this is the final chunk of data michael@0: * michael@0: * Result can be used to perform memory allocations. michael@0: * michael@0: * Note that this can return zero, which does not mean that the encrypt michael@0: * operation can be skipped! (It simply means that there are not enough michael@0: * bytes to make up an entire block; the bytes will be reserved until michael@0: * there are enough to encrypt/decrypt at least one block.) However, michael@0: * if zero is returned it *does* mean that no output buffer need be michael@0: * passed in to the subsequent encrypt operation, as no output bytes michael@0: * will be stored. michael@0: */ michael@0: unsigned int michael@0: NSS_CMSCipherContext_EncryptLength(NSSCMSCipherContext *cc, unsigned int input_len, PRBool final) michael@0: { michael@0: int blocks, block_size; michael@0: int pad_size; michael@0: michael@0: PORT_Assert (cc->encrypt); michael@0: michael@0: block_size = cc->block_size; michael@0: pad_size = cc->pad_size; michael@0: michael@0: /* michael@0: * If this is not a block cipher, then we always have the same michael@0: * number of output bytes as we had input bytes. michael@0: */ michael@0: if (block_size == 0) michael@0: return input_len; michael@0: michael@0: /* michael@0: * On the final call, we only send out what we need for michael@0: * remaining bytes plus the padding. (There is always padding, michael@0: * so even if we have an exact number of blocks as input, we michael@0: * will add another full block that is just padding.) michael@0: */ michael@0: if (final) { michael@0: if (pad_size == 0) { michael@0: return cc->pending_count + input_len; michael@0: } else { michael@0: blocks = (cc->pending_count + input_len) / pad_size; michael@0: blocks++; michael@0: return blocks*pad_size; michael@0: } michael@0: } michael@0: michael@0: /* michael@0: * Now, count the number of complete blocks of data we have. michael@0: */ michael@0: blocks = (cc->pending_count + input_len) / block_size; michael@0: michael@0: michael@0: return blocks * block_size; michael@0: } michael@0: michael@0: michael@0: /* michael@0: * NSS_CMSCipherContext_Decrypt - do the decryption michael@0: * michael@0: * cc - the cipher context michael@0: * output - buffer for decrypted result bytes michael@0: * output_len_p - number of bytes in output michael@0: * max_output_len - upper bound on bytes to put into output michael@0: * input - pointer to input bytes michael@0: * input_len - number of input bytes michael@0: * final - true if this is the final chunk of data michael@0: * michael@0: * Decrypts a given length of input buffer (starting at "input" and michael@0: * containing "input_len" bytes), placing the decrypted bytes in michael@0: * "output" and storing the output length in "*output_len_p". michael@0: * "cc" is the return value from NSS_CMSCipher_StartDecrypt. michael@0: * When "final" is true, this is the last of the data to be decrypted. michael@0: * michael@0: * This is much more complicated than it sounds when the cipher is michael@0: * a block-type, meaning that the decryption function will only michael@0: * operate on whole blocks. But our caller is operating stream-wise, michael@0: * and can pass in any number of bytes. So we need to keep track michael@0: * of block boundaries. We save excess bytes between calls in "cc". michael@0: * We also need to determine which bytes are padding, and remove michael@0: * them from the output. We can only do this step when we know we michael@0: * have the final block of data. PKCS #7 specifies that the padding michael@0: * used for a block cipher is a string of bytes, each of whose value is michael@0: * the same as the length of the padding, and that all data is padded. michael@0: * (Even data that starts out with an exact multiple of blocks gets michael@0: * added to it another block, all of which is padding.) michael@0: */ michael@0: SECStatus michael@0: NSS_CMSCipherContext_Decrypt(NSSCMSCipherContext *cc, unsigned char *output, michael@0: unsigned int *output_len_p, unsigned int max_output_len, michael@0: const unsigned char *input, unsigned int input_len, michael@0: PRBool final) michael@0: { michael@0: int blocks, bsize, pcount, padsize; michael@0: unsigned int max_needed, ifraglen, ofraglen, output_len; michael@0: unsigned char *pbuf; michael@0: SECStatus rv; michael@0: michael@0: PORT_Assert (! cc->encrypt); michael@0: michael@0: /* michael@0: * Check that we have enough room for the output. Our caller should michael@0: * already handle this; failure is really an internal error (i.e. bug). michael@0: */ michael@0: max_needed = NSS_CMSCipherContext_DecryptLength(cc, input_len, final); michael@0: PORT_Assert (max_output_len >= max_needed); michael@0: if (max_output_len < max_needed) { michael@0: /* PORT_SetError (XXX); */ michael@0: return SECFailure; michael@0: } michael@0: michael@0: /* michael@0: * hardware encryption does not like small decryption sizes here, so we michael@0: * allow both blocking and padding. michael@0: */ michael@0: bsize = cc->block_size; michael@0: padsize = cc->pad_size; michael@0: michael@0: /* michael@0: * When no blocking or padding work to do, we can simply call the michael@0: * cipher function and we are done. michael@0: */ michael@0: if (bsize == 0) { michael@0: return (* cc->doit) (cc->cx, output, output_len_p, max_output_len, michael@0: input, input_len); michael@0: } michael@0: michael@0: pcount = cc->pending_count; michael@0: pbuf = cc->pending_buf; michael@0: michael@0: output_len = 0; michael@0: michael@0: if (pcount) { michael@0: /* michael@0: * Try to fill in an entire block, starting with the bytes michael@0: * we already have saved away. michael@0: */ michael@0: while (input_len && pcount < bsize) { michael@0: pbuf[pcount++] = *input++; michael@0: input_len--; michael@0: } michael@0: /* michael@0: * If we have at most a whole block and this is not our last call, michael@0: * then we are done for now. (We do not try to decrypt a lone michael@0: * single block because we cannot interpret the padding bytes michael@0: * until we know we are handling the very last block of all input.) michael@0: */ michael@0: if (input_len == 0 && !final) { michael@0: cc->pending_count = pcount; michael@0: if (output_len_p) michael@0: *output_len_p = 0; michael@0: return SECSuccess; michael@0: } michael@0: /* michael@0: * Given the logic above, we expect to have a full block by now. michael@0: * If we do not, there is something wrong, either with our own michael@0: * logic or with (length of) the data given to us. michael@0: */ michael@0: if ((padsize != 0) && (pcount % padsize) != 0) { michael@0: PORT_Assert (final); michael@0: PORT_SetError (SEC_ERROR_BAD_DATA); michael@0: return SECFailure; michael@0: } michael@0: /* michael@0: * Decrypt the block. michael@0: */ michael@0: rv = (*cc->doit)(cc->cx, output, &ofraglen, max_output_len, michael@0: pbuf, pcount); michael@0: if (rv != SECSuccess) michael@0: return rv; michael@0: michael@0: /* michael@0: * For now anyway, all of our ciphers have the same number of michael@0: * bytes of output as they do input. If this ever becomes untrue, michael@0: * then NSS_CMSCipherContext_DecryptLength needs to be made smarter! michael@0: */ michael@0: PORT_Assert(ofraglen == pcount); michael@0: michael@0: /* michael@0: * Account for the bytes now in output. michael@0: */ michael@0: max_output_len -= ofraglen; michael@0: output_len += ofraglen; michael@0: output += ofraglen; michael@0: } michael@0: michael@0: /* michael@0: * If this is our last call, we expect to have an exact number of michael@0: * blocks left to be decrypted; we will decrypt them all. michael@0: * michael@0: * If not our last call, we always save between 1 and bsize bytes michael@0: * until next time. (We must do this because we cannot be sure michael@0: * that none of the decrypted bytes are padding bytes until we michael@0: * have at least another whole block of data. You cannot tell by michael@0: * looking -- the data could be anything -- you can only tell by michael@0: * context, knowing you are looking at the last block.) We could michael@0: * decrypt a whole block now but it is easier if we just treat it michael@0: * the same way we treat partial block bytes. michael@0: */ michael@0: if (final) { michael@0: if (padsize) { michael@0: blocks = input_len / padsize; michael@0: ifraglen = blocks * padsize; michael@0: } else ifraglen = input_len; michael@0: PORT_Assert (ifraglen == input_len); michael@0: michael@0: if (ifraglen != input_len) { michael@0: PORT_SetError(SEC_ERROR_BAD_DATA); michael@0: return SECFailure; michael@0: } michael@0: } else { michael@0: blocks = (input_len - 1) / bsize; michael@0: ifraglen = blocks * bsize; michael@0: PORT_Assert (ifraglen < input_len); michael@0: michael@0: pcount = input_len - ifraglen; michael@0: PORT_Memcpy (pbuf, input + ifraglen, pcount); michael@0: cc->pending_count = pcount; michael@0: } michael@0: michael@0: if (ifraglen) { michael@0: rv = (* cc->doit)(cc->cx, output, &ofraglen, max_output_len, michael@0: input, ifraglen); michael@0: if (rv != SECSuccess) michael@0: return rv; michael@0: michael@0: /* michael@0: * For now anyway, all of our ciphers have the same number of michael@0: * bytes of output as they do input. If this ever becomes untrue, michael@0: * then sec_PKCS7DecryptLength needs to be made smarter! michael@0: */ michael@0: PORT_Assert (ifraglen == ofraglen); michael@0: if (ifraglen != ofraglen) { michael@0: PORT_SetError(SEC_ERROR_BAD_DATA); michael@0: return SECFailure; michael@0: } michael@0: michael@0: output_len += ofraglen; michael@0: } else { michael@0: ofraglen = 0; michael@0: } michael@0: michael@0: /* michael@0: * If we just did our very last block, "remove" the padding by michael@0: * adjusting the output length. michael@0: */ michael@0: if (final && (padsize != 0)) { michael@0: unsigned int padlen = *(output + ofraglen - 1); michael@0: michael@0: if (padlen == 0 || padlen > padsize) { michael@0: PORT_SetError(SEC_ERROR_BAD_DATA); michael@0: return SECFailure; michael@0: } michael@0: output_len -= padlen; michael@0: } michael@0: michael@0: PORT_Assert (output_len_p != NULL || output_len == 0); michael@0: if (output_len_p != NULL) michael@0: *output_len_p = output_len; michael@0: michael@0: return SECSuccess; michael@0: } michael@0: michael@0: /* michael@0: * NSS_CMSCipherContext_Encrypt - do the encryption michael@0: * michael@0: * cc - the cipher context michael@0: * output - buffer for decrypted result bytes michael@0: * output_len_p - number of bytes in output michael@0: * max_output_len - upper bound on bytes to put into output michael@0: * input - pointer to input bytes michael@0: * input_len - number of input bytes michael@0: * final - true if this is the final chunk of data michael@0: * michael@0: * Encrypts a given length of input buffer (starting at "input" and michael@0: * containing "input_len" bytes), placing the encrypted bytes in michael@0: * "output" and storing the output length in "*output_len_p". michael@0: * "cc" is the return value from NSS_CMSCipher_StartEncrypt. michael@0: * When "final" is true, this is the last of the data to be encrypted. michael@0: * michael@0: * This is much more complicated than it sounds when the cipher is michael@0: * a block-type, meaning that the encryption function will only michael@0: * operate on whole blocks. But our caller is operating stream-wise, michael@0: * and can pass in any number of bytes. So we need to keep track michael@0: * of block boundaries. We save excess bytes between calls in "cc". michael@0: * We also need to add padding bytes at the end. PKCS #7 specifies michael@0: * that the padding used for a block cipher is a string of bytes, michael@0: * each of whose value is the same as the length of the padding, michael@0: * and that all data is padded. (Even data that starts out with michael@0: * an exact multiple of blocks gets added to it another block, michael@0: * all of which is padding.) michael@0: * michael@0: * XXX I would kind of like to combine this with the function above michael@0: * which does decryption, since they have a lot in common. But the michael@0: * tricky parts about padding and filling blocks would be much michael@0: * harder to read that way, so I left them separate. At least for michael@0: * now until it is clear that they are right. michael@0: */ michael@0: SECStatus michael@0: NSS_CMSCipherContext_Encrypt(NSSCMSCipherContext *cc, unsigned char *output, michael@0: unsigned int *output_len_p, unsigned int max_output_len, michael@0: const unsigned char *input, unsigned int input_len, michael@0: PRBool final) michael@0: { michael@0: int blocks, bsize, padlen, pcount, padsize; michael@0: unsigned int max_needed, ifraglen, ofraglen, output_len; michael@0: unsigned char *pbuf; michael@0: SECStatus rv; michael@0: michael@0: PORT_Assert (cc->encrypt); michael@0: michael@0: /* michael@0: * Check that we have enough room for the output. Our caller should michael@0: * already handle this; failure is really an internal error (i.e. bug). michael@0: */ michael@0: max_needed = NSS_CMSCipherContext_EncryptLength (cc, input_len, final); michael@0: PORT_Assert (max_output_len >= max_needed); michael@0: if (max_output_len < max_needed) { michael@0: /* PORT_SetError (XXX); */ michael@0: return SECFailure; michael@0: } michael@0: michael@0: bsize = cc->block_size; michael@0: padsize = cc->pad_size; michael@0: michael@0: /* michael@0: * When no blocking and padding work to do, we can simply call the michael@0: * cipher function and we are done. michael@0: */ michael@0: if (bsize == 0) { michael@0: return (*cc->doit)(cc->cx, output, output_len_p, max_output_len, michael@0: input, input_len); michael@0: } michael@0: michael@0: pcount = cc->pending_count; michael@0: pbuf = cc->pending_buf; michael@0: michael@0: output_len = 0; michael@0: michael@0: if (pcount) { michael@0: /* michael@0: * Try to fill in an entire block, starting with the bytes michael@0: * we already have saved away. michael@0: */ michael@0: while (input_len && pcount < bsize) { michael@0: pbuf[pcount++] = *input++; michael@0: input_len--; michael@0: } michael@0: /* michael@0: * If we do not have a full block and we know we will be michael@0: * called again, then we are done for now. michael@0: */ michael@0: if (pcount < bsize && !final) { michael@0: cc->pending_count = pcount; michael@0: if (output_len_p != NULL) michael@0: *output_len_p = 0; michael@0: return SECSuccess; michael@0: } michael@0: /* michael@0: * If we have a whole block available, encrypt it. michael@0: */ michael@0: if ((padsize == 0) || (pcount % padsize) == 0) { michael@0: rv = (* cc->doit) (cc->cx, output, &ofraglen, max_output_len, michael@0: pbuf, pcount); michael@0: if (rv != SECSuccess) michael@0: return rv; michael@0: michael@0: /* michael@0: * For now anyway, all of our ciphers have the same number of michael@0: * bytes of output as they do input. If this ever becomes untrue, michael@0: * then sec_PKCS7EncryptLength needs to be made smarter! michael@0: */ michael@0: PORT_Assert (ofraglen == pcount); michael@0: michael@0: /* michael@0: * Account for the bytes now in output. michael@0: */ michael@0: max_output_len -= ofraglen; michael@0: output_len += ofraglen; michael@0: output += ofraglen; michael@0: michael@0: pcount = 0; michael@0: } michael@0: } michael@0: michael@0: if (input_len) { michael@0: PORT_Assert (pcount == 0); michael@0: michael@0: blocks = input_len / bsize; michael@0: ifraglen = blocks * bsize; michael@0: michael@0: if (ifraglen) { michael@0: rv = (* cc->doit) (cc->cx, output, &ofraglen, max_output_len, michael@0: input, ifraglen); michael@0: if (rv != SECSuccess) michael@0: return rv; michael@0: michael@0: /* michael@0: * For now anyway, all of our ciphers have the same number of michael@0: * bytes of output as they do input. If this ever becomes untrue, michael@0: * then sec_PKCS7EncryptLength needs to be made smarter! michael@0: */ michael@0: PORT_Assert (ifraglen == ofraglen); michael@0: michael@0: max_output_len -= ofraglen; michael@0: output_len += ofraglen; michael@0: output += ofraglen; michael@0: } michael@0: michael@0: pcount = input_len - ifraglen; michael@0: PORT_Assert (pcount < bsize); michael@0: if (pcount) michael@0: PORT_Memcpy (pbuf, input + ifraglen, pcount); michael@0: } michael@0: michael@0: if (final) { michael@0: padlen = padsize - (pcount % padsize); michael@0: PORT_Memset (pbuf + pcount, padlen, padlen); michael@0: rv = (* cc->doit) (cc->cx, output, &ofraglen, max_output_len, michael@0: pbuf, pcount+padlen); michael@0: if (rv != SECSuccess) michael@0: return rv; michael@0: michael@0: /* michael@0: * For now anyway, all of our ciphers have the same number of michael@0: * bytes of output as they do input. If this ever becomes untrue, michael@0: * then sec_PKCS7EncryptLength needs to be made smarter! michael@0: */ michael@0: PORT_Assert (ofraglen == (pcount+padlen)); michael@0: output_len += ofraglen; michael@0: } else { michael@0: cc->pending_count = pcount; michael@0: } michael@0: michael@0: PORT_Assert (output_len_p != NULL || output_len == 0); michael@0: if (output_len_p != NULL) michael@0: *output_len_p = output_len; michael@0: michael@0: return SECSuccess; michael@0: }