Blame nss/lib/smime/cmscipher.c

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