security/nss/lib/smime/cmscipher.c

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/security/nss/lib/smime/cmscipher.c	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,715 @@
     1.4 +/* This Source Code Form is subject to the terms of the Mozilla Public
     1.5 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.6 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.7 +
     1.8 +/*
     1.9 + * Encryption/decryption routines for CMS implementation, none of which are exported.
    1.10 + */
    1.11 +
    1.12 +#include "cmslocal.h"
    1.13 +
    1.14 +#include "secoid.h"
    1.15 +#include "secitem.h"
    1.16 +#include "pk11func.h"
    1.17 +#include "secerr.h"
    1.18 +#include "secpkcs5.h"
    1.19 +
    1.20 +/*
    1.21 + * -------------------------------------------------------------------
    1.22 + * Cipher stuff.
    1.23 + */
    1.24 +
    1.25 +typedef SECStatus (*nss_cms_cipher_function) (void *, unsigned char *, unsigned int *,
    1.26 +					unsigned int, const unsigned char *, unsigned int);
    1.27 +typedef SECStatus (*nss_cms_cipher_destroy) (void *, PRBool);
    1.28 +
    1.29 +#define BLOCK_SIZE 4096
    1.30 +
    1.31 +struct NSSCMSCipherContextStr {
    1.32 +    void *		cx;			/* PK11 cipher context */
    1.33 +    nss_cms_cipher_function doit;
    1.34 +    nss_cms_cipher_destroy destroy;
    1.35 +    PRBool		encrypt;		/* encrypt / decrypt switch */
    1.36 +    int			block_size;		/* block & pad sizes for cipher */
    1.37 +    int			pad_size;
    1.38 +    int			pending_count;		/* pending data (not yet en/decrypted */
    1.39 +    unsigned char	pending_buf[BLOCK_SIZE];/* because of blocking */
    1.40 +};
    1.41 +
    1.42 +/*
    1.43 + * NSS_CMSCipherContext_StartDecrypt - create a cipher context to do decryption
    1.44 + * based on the given bulk encryption key and algorithm identifier (which 
    1.45 + * may include an iv).
    1.46 + *
    1.47 + * XXX Once both are working, it might be nice to combine this and the
    1.48 + * function below (for starting up encryption) into one routine, and just
    1.49 + * have two simple cover functions which call it. 
    1.50 + */
    1.51 +NSSCMSCipherContext *
    1.52 +NSS_CMSCipherContext_StartDecrypt(PK11SymKey *key, SECAlgorithmID *algid)
    1.53 +{
    1.54 +    NSSCMSCipherContext *cc;
    1.55 +    void *ciphercx;
    1.56 +    CK_MECHANISM_TYPE cryptoMechType;
    1.57 +    PK11SlotInfo *slot;
    1.58 +    SECOidTag algtag;
    1.59 +    SECItem *param = NULL;
    1.60 +
    1.61 +    algtag = SECOID_GetAlgorithmTag(algid);
    1.62 +
    1.63 +    /* set param and mechanism */
    1.64 +    if (SEC_PKCS5IsAlgorithmPBEAlg(algid)) {
    1.65 +	SECItem *pwitem;
    1.66 +
    1.67 +	pwitem = PK11_GetSymKeyUserData(key);
    1.68 +	if (!pwitem) 
    1.69 +	    return NULL;
    1.70 +
    1.71 +	cryptoMechType = PK11_GetPBECryptoMechanism(algid, &param, pwitem);
    1.72 +	if (cryptoMechType == CKM_INVALID_MECHANISM) {
    1.73 +	    SECITEM_FreeItem(param,PR_TRUE);
    1.74 +	    return NULL;
    1.75 +	}
    1.76 +
    1.77 +    } else {
    1.78 +	cryptoMechType = PK11_AlgtagToMechanism(algtag);
    1.79 +	if ((param = PK11_ParamFromAlgid(algid)) == NULL)
    1.80 +	    return NULL;
    1.81 +    }
    1.82 +
    1.83 +    cc = (NSSCMSCipherContext *)PORT_ZAlloc(sizeof(NSSCMSCipherContext));
    1.84 +    if (cc == NULL) {
    1.85 +	SECITEM_FreeItem(param,PR_TRUE);
    1.86 +	return NULL;
    1.87 +    }
    1.88 +
    1.89 +    /* figure out pad and block sizes */
    1.90 +    cc->pad_size = PK11_GetBlockSize(cryptoMechType, param);
    1.91 +    slot = PK11_GetSlotFromKey(key);
    1.92 +    cc->block_size = PK11_IsHW(slot) ? BLOCK_SIZE : cc->pad_size;
    1.93 +    PK11_FreeSlot(slot);
    1.94 +
    1.95 +    /* create PK11 cipher context */
    1.96 +    ciphercx = PK11_CreateContextBySymKey(cryptoMechType, CKA_DECRYPT, 
    1.97 +					  key, param);
    1.98 +    SECITEM_FreeItem(param, PR_TRUE);
    1.99 +    if (ciphercx == NULL) {
   1.100 +	PORT_Free (cc);
   1.101 +	return NULL;
   1.102 +    }
   1.103 +
   1.104 +    cc->cx = ciphercx;
   1.105 +    cc->doit =  (nss_cms_cipher_function) PK11_CipherOp;
   1.106 +    cc->destroy = (nss_cms_cipher_destroy) PK11_DestroyContext;
   1.107 +    cc->encrypt = PR_FALSE;
   1.108 +    cc->pending_count = 0;
   1.109 +
   1.110 +    return cc;
   1.111 +}
   1.112 +
   1.113 +/*
   1.114 + * NSS_CMSCipherContext_StartEncrypt - create a cipher object to do encryption,
   1.115 + * based on the given bulk encryption key and algorithm tag.  Fill in the 
   1.116 + * algorithm identifier (which may include an iv) appropriately.
   1.117 + *
   1.118 + * XXX Once both are working, it might be nice to combine this and the
   1.119 + * function above (for starting up decryption) into one routine, and just
   1.120 + * have two simple cover functions which call it. 
   1.121 + */
   1.122 +NSSCMSCipherContext *
   1.123 +NSS_CMSCipherContext_StartEncrypt(PLArenaPool *poolp, PK11SymKey *key, SECAlgorithmID *algid)
   1.124 +{
   1.125 +    NSSCMSCipherContext *cc;
   1.126 +    void *ciphercx;
   1.127 +    SECStatus rv;
   1.128 +    CK_MECHANISM_TYPE cryptoMechType;
   1.129 +    PK11SlotInfo *slot;
   1.130 +    SECItem *param = NULL;
   1.131 +    PRBool needToEncodeAlgid = PR_FALSE;
   1.132 +    SECOidTag algtag = SECOID_GetAlgorithmTag(algid);
   1.133 +
   1.134 +    /* set param and mechanism */
   1.135 +    if (SEC_PKCS5IsAlgorithmPBEAlg(algid)) {
   1.136 +	SECItem *pwitem;
   1.137 +
   1.138 +	pwitem = PK11_GetSymKeyUserData(key);
   1.139 +	if (!pwitem) 
   1.140 +	    return NULL;
   1.141 +
   1.142 +	cryptoMechType = PK11_GetPBECryptoMechanism(algid, &param, pwitem);
   1.143 +	if (cryptoMechType == CKM_INVALID_MECHANISM) {
   1.144 +	    SECITEM_FreeItem(param,PR_TRUE);
   1.145 +	    return NULL;
   1.146 +	}
   1.147 +    } else {
   1.148 +	cryptoMechType = PK11_AlgtagToMechanism(algtag);
   1.149 +	if ((param = PK11_GenerateNewParam(cryptoMechType, key)) == NULL)
   1.150 +	    return NULL;
   1.151 +	needToEncodeAlgid = PR_TRUE;
   1.152 +    }
   1.153 +
   1.154 +    cc = (NSSCMSCipherContext *)PORT_ZAlloc(sizeof(NSSCMSCipherContext));
   1.155 +    if (cc == NULL) {
   1.156 +	goto loser;
   1.157 +    }
   1.158 +
   1.159 +    /* now find pad and block sizes for our mechanism */
   1.160 +    cc->pad_size = PK11_GetBlockSize(cryptoMechType, param);
   1.161 +    slot = PK11_GetSlotFromKey(key);
   1.162 +    cc->block_size = PK11_IsHW(slot) ? BLOCK_SIZE : cc->pad_size;
   1.163 +    PK11_FreeSlot(slot);
   1.164 +
   1.165 +    /* and here we go, creating a PK11 cipher context */
   1.166 +    ciphercx = PK11_CreateContextBySymKey(cryptoMechType, CKA_ENCRYPT, 
   1.167 +					  key, param);
   1.168 +    if (ciphercx == NULL) {
   1.169 +	PORT_Free(cc);
   1.170 +	cc = NULL;
   1.171 +	goto loser;
   1.172 +    }
   1.173 +
   1.174 +    /*
   1.175 +     * These are placed after the CreateContextBySymKey() because some
   1.176 +     * mechanisms have to generate their IVs from their card (i.e. FORTEZZA).
   1.177 +     * Don't move it from here.
   1.178 +     * XXX is that right? the purpose of this is to get the correct algid
   1.179 +     *     containing the IVs etc. for encoding. this means we need to set this up
   1.180 +     *     BEFORE encoding the algid in the contentInfo, right?
   1.181 +     */
   1.182 +    if (needToEncodeAlgid) {
   1.183 +	rv = PK11_ParamToAlgid(algtag, param, poolp, algid);
   1.184 +	if(rv != SECSuccess) {
   1.185 +	    PORT_Free(cc);
   1.186 +	    cc = NULL;
   1.187 +	    goto loser;
   1.188 +	}
   1.189 +    }
   1.190 +
   1.191 +    cc->cx = ciphercx;
   1.192 +    cc->doit = (nss_cms_cipher_function)PK11_CipherOp;
   1.193 +    cc->destroy = (nss_cms_cipher_destroy)PK11_DestroyContext;
   1.194 +    cc->encrypt = PR_TRUE;
   1.195 +    cc->pending_count = 0;
   1.196 +
   1.197 +loser:
   1.198 +    SECITEM_FreeItem(param, PR_TRUE);
   1.199 +
   1.200 +    return cc;
   1.201 +}
   1.202 +
   1.203 +void
   1.204 +NSS_CMSCipherContext_Destroy(NSSCMSCipherContext *cc)
   1.205 +{
   1.206 +    PORT_Assert(cc != NULL);
   1.207 +    if (cc == NULL)
   1.208 +	return;
   1.209 +    (*cc->destroy)(cc->cx, PR_TRUE);
   1.210 +    PORT_Free(cc);
   1.211 +}
   1.212 +
   1.213 +/*
   1.214 + * NSS_CMSCipherContext_DecryptLength - find the output length of the next call to decrypt.
   1.215 + *
   1.216 + * cc - the cipher context
   1.217 + * input_len - number of bytes used as input
   1.218 + * final - true if this is the final chunk of data
   1.219 + *
   1.220 + * Result can be used to perform memory allocations.  Note that the amount
   1.221 + * is exactly accurate only when not doing a block cipher or when final
   1.222 + * is false, otherwise it is an upper bound on the amount because until
   1.223 + * we see the data we do not know how many padding bytes there are
   1.224 + * (always between 1 and bsize).
   1.225 + *
   1.226 + * Note that this can return zero, which does not mean that the decrypt
   1.227 + * operation can be skipped!  (It simply means that there are not enough
   1.228 + * bytes to make up an entire block; the bytes will be reserved until
   1.229 + * there are enough to encrypt/decrypt at least one block.)  However,
   1.230 + * if zero is returned it *does* mean that no output buffer need be
   1.231 + * passed in to the subsequent decrypt operation, as no output bytes
   1.232 + * will be stored.
   1.233 + */
   1.234 +unsigned int
   1.235 +NSS_CMSCipherContext_DecryptLength(NSSCMSCipherContext *cc, unsigned int input_len, PRBool final)
   1.236 +{
   1.237 +    int blocks, block_size;
   1.238 +
   1.239 +    PORT_Assert (! cc->encrypt);
   1.240 +
   1.241 +    block_size = cc->block_size;
   1.242 +
   1.243 +    /*
   1.244 +     * If this is not a block cipher, then we always have the same
   1.245 +     * number of output bytes as we had input bytes.
   1.246 +     */
   1.247 +    if (block_size == 0)
   1.248 +	return input_len;
   1.249 +
   1.250 +    /*
   1.251 +     * On the final call, we will always use up all of the pending
   1.252 +     * bytes plus all of the input bytes, *but*, there will be padding
   1.253 +     * at the end and we cannot predict how many bytes of padding we
   1.254 +     * will end up removing.  The amount given here is actually known
   1.255 +     * to be at least 1 byte too long (because we know we will have
   1.256 +     * at least 1 byte of padding), but seemed clearer/better to me.
   1.257 +     */
   1.258 +    if (final)
   1.259 +	return cc->pending_count + input_len;
   1.260 +
   1.261 +    /*
   1.262 +     * Okay, this amount is exactly what we will output on the
   1.263 +     * next cipher operation.  We will always hang onto the last
   1.264 +     * 1 - block_size bytes for non-final operations.  That is,
   1.265 +     * we will do as many complete blocks as we can *except* the
   1.266 +     * last block (complete or partial).  (This is because until
   1.267 +     * we know we are at the end, we cannot know when to interpret
   1.268 +     * and removing the padding byte(s), which are guaranteed to
   1.269 +     * be there.)
   1.270 +     */
   1.271 +    blocks = (cc->pending_count + input_len - 1) / block_size;
   1.272 +    return blocks * block_size;
   1.273 +}
   1.274 +
   1.275 +/*
   1.276 + * NSS_CMSCipherContext_EncryptLength - find the output length of the next call to encrypt.
   1.277 + *
   1.278 + * cc - the cipher context
   1.279 + * input_len - number of bytes used as input
   1.280 + * final - true if this is the final chunk of data
   1.281 + *
   1.282 + * Result can be used to perform memory allocations.
   1.283 + *
   1.284 + * Note that this can return zero, which does not mean that the encrypt
   1.285 + * operation can be skipped!  (It simply means that there are not enough
   1.286 + * bytes to make up an entire block; the bytes will be reserved until
   1.287 + * there are enough to encrypt/decrypt at least one block.)  However,
   1.288 + * if zero is returned it *does* mean that no output buffer need be
   1.289 + * passed in to the subsequent encrypt operation, as no output bytes
   1.290 + * will be stored.
   1.291 + */
   1.292 +unsigned int
   1.293 +NSS_CMSCipherContext_EncryptLength(NSSCMSCipherContext *cc, unsigned int input_len, PRBool final)
   1.294 +{
   1.295 +    int blocks, block_size;
   1.296 +    int pad_size;
   1.297 +
   1.298 +    PORT_Assert (cc->encrypt);
   1.299 +
   1.300 +    block_size = cc->block_size;
   1.301 +    pad_size = cc->pad_size;
   1.302 +
   1.303 +    /*
   1.304 +     * If this is not a block cipher, then we always have the same
   1.305 +     * number of output bytes as we had input bytes.
   1.306 +     */
   1.307 +    if (block_size == 0)
   1.308 +	return input_len;
   1.309 +
   1.310 +    /*
   1.311 +     * On the final call, we only send out what we need for
   1.312 +     * remaining bytes plus the padding.  (There is always padding,
   1.313 +     * so even if we have an exact number of blocks as input, we
   1.314 +     * will add another full block that is just padding.)
   1.315 +     */
   1.316 +    if (final) {
   1.317 +	if (pad_size == 0) {
   1.318 +    	    return cc->pending_count + input_len;
   1.319 +	} else {
   1.320 +    	    blocks = (cc->pending_count + input_len) / pad_size;
   1.321 +	    blocks++;
   1.322 +	    return blocks*pad_size;
   1.323 +	}
   1.324 +    }
   1.325 +
   1.326 +    /*
   1.327 +     * Now, count the number of complete blocks of data we have.
   1.328 +     */
   1.329 +    blocks = (cc->pending_count + input_len) / block_size;
   1.330 +
   1.331 +
   1.332 +    return blocks * block_size;
   1.333 +}
   1.334 +
   1.335 +
   1.336 +/*
   1.337 + * NSS_CMSCipherContext_Decrypt - do the decryption
   1.338 + *
   1.339 + * cc - the cipher context
   1.340 + * output - buffer for decrypted result bytes
   1.341 + * output_len_p - number of bytes in output
   1.342 + * max_output_len - upper bound on bytes to put into output
   1.343 + * input - pointer to input bytes
   1.344 + * input_len - number of input bytes
   1.345 + * final - true if this is the final chunk of data
   1.346 + *
   1.347 + * Decrypts a given length of input buffer (starting at "input" and
   1.348 + * containing "input_len" bytes), placing the decrypted bytes in
   1.349 + * "output" and storing the output length in "*output_len_p".
   1.350 + * "cc" is the return value from NSS_CMSCipher_StartDecrypt.
   1.351 + * When "final" is true, this is the last of the data to be decrypted.
   1.352 + *
   1.353 + * This is much more complicated than it sounds when the cipher is
   1.354 + * a block-type, meaning that the decryption function will only
   1.355 + * operate on whole blocks.  But our caller is operating stream-wise,
   1.356 + * and can pass in any number of bytes.  So we need to keep track
   1.357 + * of block boundaries.  We save excess bytes between calls in "cc".
   1.358 + * We also need to determine which bytes are padding, and remove
   1.359 + * them from the output.  We can only do this step when we know we
   1.360 + * have the final block of data.  PKCS #7 specifies that the padding
   1.361 + * used for a block cipher is a string of bytes, each of whose value is
   1.362 + * the same as the length of the padding, and that all data is padded.
   1.363 + * (Even data that starts out with an exact multiple of blocks gets
   1.364 + * added to it another block, all of which is padding.)
   1.365 + */ 
   1.366 +SECStatus
   1.367 +NSS_CMSCipherContext_Decrypt(NSSCMSCipherContext *cc, unsigned char *output,
   1.368 +		  unsigned int *output_len_p, unsigned int max_output_len,
   1.369 +		  const unsigned char *input, unsigned int input_len,
   1.370 +		  PRBool final)
   1.371 +{
   1.372 +    int blocks, bsize, pcount, padsize;
   1.373 +    unsigned int max_needed, ifraglen, ofraglen, output_len;
   1.374 +    unsigned char *pbuf;
   1.375 +    SECStatus rv;
   1.376 +
   1.377 +    PORT_Assert (! cc->encrypt);
   1.378 +
   1.379 +    /*
   1.380 +     * Check that we have enough room for the output.  Our caller should
   1.381 +     * already handle this; failure is really an internal error (i.e. bug).
   1.382 +     */
   1.383 +    max_needed = NSS_CMSCipherContext_DecryptLength(cc, input_len, final);
   1.384 +    PORT_Assert (max_output_len >= max_needed);
   1.385 +    if (max_output_len < max_needed) {
   1.386 +	/* PORT_SetError (XXX); */
   1.387 +	return SECFailure;
   1.388 +    }
   1.389 +
   1.390 +    /*
   1.391 +     * hardware encryption does not like small decryption sizes here, so we
   1.392 +     * allow both blocking and padding.
   1.393 +     */
   1.394 +    bsize = cc->block_size;
   1.395 +    padsize = cc->pad_size;
   1.396 +
   1.397 +    /*
   1.398 +     * When no blocking or padding work to do, we can simply call the
   1.399 +     * cipher function and we are done.
   1.400 +     */
   1.401 +    if (bsize == 0) {
   1.402 +	return (* cc->doit) (cc->cx, output, output_len_p, max_output_len,
   1.403 +			      input, input_len);
   1.404 +    }
   1.405 +
   1.406 +    pcount = cc->pending_count;
   1.407 +    pbuf = cc->pending_buf;
   1.408 +
   1.409 +    output_len = 0;
   1.410 +
   1.411 +    if (pcount) {
   1.412 +	/*
   1.413 +	 * Try to fill in an entire block, starting with the bytes
   1.414 +	 * we already have saved away.
   1.415 +	 */
   1.416 +	while (input_len && pcount < bsize) {
   1.417 +	    pbuf[pcount++] = *input++;
   1.418 +	    input_len--;
   1.419 +	}
   1.420 +	/*
   1.421 +	 * If we have at most a whole block and this is not our last call,
   1.422 +	 * then we are done for now.  (We do not try to decrypt a lone
   1.423 +	 * single block because we cannot interpret the padding bytes
   1.424 +	 * until we know we are handling the very last block of all input.)
   1.425 +	 */
   1.426 +	if (input_len == 0 && !final) {
   1.427 +	    cc->pending_count = pcount;
   1.428 +	    if (output_len_p)
   1.429 +		*output_len_p = 0;
   1.430 +	    return SECSuccess;
   1.431 +	}
   1.432 +	/*
   1.433 +	 * Given the logic above, we expect to have a full block by now.
   1.434 +	 * If we do not, there is something wrong, either with our own
   1.435 +	 * logic or with (length of) the data given to us.
   1.436 +	 */
   1.437 +	if ((padsize != 0) && (pcount % padsize) != 0) {
   1.438 +	    PORT_Assert (final);	
   1.439 +	    PORT_SetError (SEC_ERROR_BAD_DATA);
   1.440 +	    return SECFailure;
   1.441 +	}
   1.442 +	/*
   1.443 +	 * Decrypt the block.
   1.444 +	 */
   1.445 +	rv = (*cc->doit)(cc->cx, output, &ofraglen, max_output_len,
   1.446 +			    pbuf, pcount);
   1.447 +	if (rv != SECSuccess)
   1.448 +	    return rv;
   1.449 +
   1.450 +	/*
   1.451 +	 * For now anyway, all of our ciphers have the same number of
   1.452 +	 * bytes of output as they do input.  If this ever becomes untrue,
   1.453 +	 * then NSS_CMSCipherContext_DecryptLength needs to be made smarter!
   1.454 +	 */
   1.455 +	PORT_Assert(ofraglen == pcount);
   1.456 +
   1.457 +	/*
   1.458 +	 * Account for the bytes now in output.
   1.459 +	 */
   1.460 +	max_output_len -= ofraglen;
   1.461 +	output_len += ofraglen;
   1.462 +	output += ofraglen;
   1.463 +    }
   1.464 +
   1.465 +    /*
   1.466 +     * If this is our last call, we expect to have an exact number of
   1.467 +     * blocks left to be decrypted; we will decrypt them all.
   1.468 +     * 
   1.469 +     * If not our last call, we always save between 1 and bsize bytes
   1.470 +     * until next time.  (We must do this because we cannot be sure
   1.471 +     * that none of the decrypted bytes are padding bytes until we
   1.472 +     * have at least another whole block of data.  You cannot tell by
   1.473 +     * looking -- the data could be anything -- you can only tell by
   1.474 +     * context, knowing you are looking at the last block.)  We could
   1.475 +     * decrypt a whole block now but it is easier if we just treat it
   1.476 +     * the same way we treat partial block bytes.
   1.477 +     */
   1.478 +    if (final) {
   1.479 +	if (padsize) {
   1.480 +	    blocks = input_len / padsize;
   1.481 +	    ifraglen = blocks * padsize;
   1.482 +	} else ifraglen = input_len;
   1.483 +	PORT_Assert (ifraglen == input_len);
   1.484 +
   1.485 +	if (ifraglen != input_len) {
   1.486 +	    PORT_SetError(SEC_ERROR_BAD_DATA);
   1.487 +	    return SECFailure;
   1.488 +	}
   1.489 +    } else {
   1.490 +	blocks = (input_len - 1) / bsize;
   1.491 +	ifraglen = blocks * bsize;
   1.492 +	PORT_Assert (ifraglen < input_len);
   1.493 +
   1.494 +	pcount = input_len - ifraglen;
   1.495 +	PORT_Memcpy (pbuf, input + ifraglen, pcount);
   1.496 +	cc->pending_count = pcount;
   1.497 +    }
   1.498 +
   1.499 +    if (ifraglen) {
   1.500 +	rv = (* cc->doit)(cc->cx, output, &ofraglen, max_output_len,
   1.501 +			    input, ifraglen);
   1.502 +	if (rv != SECSuccess)
   1.503 +	    return rv;
   1.504 +
   1.505 +	/*
   1.506 +	 * For now anyway, all of our ciphers have the same number of
   1.507 +	 * bytes of output as they do input.  If this ever becomes untrue,
   1.508 +	 * then sec_PKCS7DecryptLength needs to be made smarter!
   1.509 +	 */
   1.510 +	PORT_Assert (ifraglen == ofraglen);
   1.511 +	if (ifraglen != ofraglen) {
   1.512 +	    PORT_SetError(SEC_ERROR_BAD_DATA);
   1.513 +	    return SECFailure;
   1.514 +	}
   1.515 +
   1.516 +	output_len += ofraglen;
   1.517 +    } else {
   1.518 +	ofraglen = 0;
   1.519 +    }
   1.520 +
   1.521 +    /*
   1.522 +     * If we just did our very last block, "remove" the padding by
   1.523 +     * adjusting the output length.
   1.524 +     */
   1.525 +    if (final && (padsize != 0)) {
   1.526 +	unsigned int padlen = *(output + ofraglen - 1);
   1.527 +
   1.528 +	if (padlen == 0 || padlen > padsize) {
   1.529 +	    PORT_SetError(SEC_ERROR_BAD_DATA);
   1.530 +	    return SECFailure;
   1.531 +	}
   1.532 +	output_len -= padlen;
   1.533 +    }
   1.534 +
   1.535 +    PORT_Assert (output_len_p != NULL || output_len == 0);
   1.536 +    if (output_len_p != NULL)
   1.537 +	*output_len_p = output_len;
   1.538 +
   1.539 +    return SECSuccess;
   1.540 +}
   1.541 +
   1.542 +/*
   1.543 + * NSS_CMSCipherContext_Encrypt - do the encryption
   1.544 + *
   1.545 + * cc - the cipher context
   1.546 + * output - buffer for decrypted result bytes
   1.547 + * output_len_p - number of bytes in output
   1.548 + * max_output_len - upper bound on bytes to put into output
   1.549 + * input - pointer to input bytes
   1.550 + * input_len - number of input bytes
   1.551 + * final - true if this is the final chunk of data
   1.552 + *
   1.553 + * Encrypts a given length of input buffer (starting at "input" and
   1.554 + * containing "input_len" bytes), placing the encrypted bytes in
   1.555 + * "output" and storing the output length in "*output_len_p".
   1.556 + * "cc" is the return value from NSS_CMSCipher_StartEncrypt.
   1.557 + * When "final" is true, this is the last of the data to be encrypted.
   1.558 + *
   1.559 + * This is much more complicated than it sounds when the cipher is
   1.560 + * a block-type, meaning that the encryption function will only
   1.561 + * operate on whole blocks.  But our caller is operating stream-wise,
   1.562 + * and can pass in any number of bytes.  So we need to keep track
   1.563 + * of block boundaries.  We save excess bytes between calls in "cc".
   1.564 + * We also need to add padding bytes at the end.  PKCS #7 specifies
   1.565 + * that the padding used for a block cipher is a string of bytes,
   1.566 + * each of whose value is the same as the length of the padding,
   1.567 + * and that all data is padded.  (Even data that starts out with
   1.568 + * an exact multiple of blocks gets added to it another block,
   1.569 + * all of which is padding.)
   1.570 + *
   1.571 + * XXX I would kind of like to combine this with the function above
   1.572 + * which does decryption, since they have a lot in common.  But the
   1.573 + * tricky parts about padding and filling blocks would be much
   1.574 + * harder to read that way, so I left them separate.  At least for
   1.575 + * now until it is clear that they are right.
   1.576 + */ 
   1.577 +SECStatus
   1.578 +NSS_CMSCipherContext_Encrypt(NSSCMSCipherContext *cc, unsigned char *output,
   1.579 +		  unsigned int *output_len_p, unsigned int max_output_len,
   1.580 +		  const unsigned char *input, unsigned int input_len,
   1.581 +		  PRBool final)
   1.582 +{
   1.583 +    int blocks, bsize, padlen, pcount, padsize;
   1.584 +    unsigned int max_needed, ifraglen, ofraglen, output_len;
   1.585 +    unsigned char *pbuf;
   1.586 +    SECStatus rv;
   1.587 +
   1.588 +    PORT_Assert (cc->encrypt);
   1.589 +
   1.590 +    /*
   1.591 +     * Check that we have enough room for the output.  Our caller should
   1.592 +     * already handle this; failure is really an internal error (i.e. bug).
   1.593 +     */
   1.594 +    max_needed = NSS_CMSCipherContext_EncryptLength (cc, input_len, final);
   1.595 +    PORT_Assert (max_output_len >= max_needed);
   1.596 +    if (max_output_len < max_needed) {
   1.597 +	/* PORT_SetError (XXX); */
   1.598 +	return SECFailure;
   1.599 +    }
   1.600 +
   1.601 +    bsize = cc->block_size;
   1.602 +    padsize = cc->pad_size;
   1.603 +
   1.604 +    /*
   1.605 +     * When no blocking and padding work to do, we can simply call the
   1.606 +     * cipher function and we are done.
   1.607 +     */
   1.608 +    if (bsize == 0) {
   1.609 +	return (*cc->doit)(cc->cx, output, output_len_p, max_output_len,
   1.610 +			      input, input_len);
   1.611 +    }
   1.612 +
   1.613 +    pcount = cc->pending_count;
   1.614 +    pbuf = cc->pending_buf;
   1.615 +
   1.616 +    output_len = 0;
   1.617 +
   1.618 +    if (pcount) {
   1.619 +	/*
   1.620 +	 * Try to fill in an entire block, starting with the bytes
   1.621 +	 * we already have saved away.
   1.622 +	 */
   1.623 +	while (input_len && pcount < bsize) {
   1.624 +	    pbuf[pcount++] = *input++;
   1.625 +	    input_len--;
   1.626 +	}
   1.627 +	/*
   1.628 +	 * If we do not have a full block and we know we will be
   1.629 +	 * called again, then we are done for now.
   1.630 +	 */
   1.631 +	if (pcount < bsize && !final) {
   1.632 +	    cc->pending_count = pcount;
   1.633 +	    if (output_len_p != NULL)
   1.634 +		*output_len_p = 0;
   1.635 +	    return SECSuccess;
   1.636 +	}
   1.637 +	/*
   1.638 +	 * If we have a whole block available, encrypt it.
   1.639 +	 */
   1.640 +	if ((padsize == 0) || (pcount % padsize) == 0) {
   1.641 +	    rv = (* cc->doit) (cc->cx, output, &ofraglen, max_output_len,
   1.642 +				pbuf, pcount);
   1.643 +	    if (rv != SECSuccess)
   1.644 +		return rv;
   1.645 +
   1.646 +	    /*
   1.647 +	     * For now anyway, all of our ciphers have the same number of
   1.648 +	     * bytes of output as they do input.  If this ever becomes untrue,
   1.649 +	     * then sec_PKCS7EncryptLength needs to be made smarter!
   1.650 +	     */
   1.651 +	    PORT_Assert (ofraglen == pcount);
   1.652 +
   1.653 +	    /*
   1.654 +	     * Account for the bytes now in output.
   1.655 +	     */
   1.656 +	    max_output_len -= ofraglen;
   1.657 +	    output_len += ofraglen;
   1.658 +	    output += ofraglen;
   1.659 +
   1.660 +	    pcount = 0;
   1.661 +	}
   1.662 +    }
   1.663 +
   1.664 +    if (input_len) {
   1.665 +	PORT_Assert (pcount == 0);
   1.666 +
   1.667 +	blocks = input_len / bsize;
   1.668 +	ifraglen = blocks * bsize;
   1.669 +
   1.670 +	if (ifraglen) {
   1.671 +	    rv = (* cc->doit) (cc->cx, output, &ofraglen, max_output_len,
   1.672 +				input, ifraglen);
   1.673 +	    if (rv != SECSuccess)
   1.674 +		return rv;
   1.675 +
   1.676 +	    /*
   1.677 +	     * For now anyway, all of our ciphers have the same number of
   1.678 +	     * bytes of output as they do input.  If this ever becomes untrue,
   1.679 +	     * then sec_PKCS7EncryptLength needs to be made smarter!
   1.680 +	     */
   1.681 +	    PORT_Assert (ifraglen == ofraglen);
   1.682 +
   1.683 +	    max_output_len -= ofraglen;
   1.684 +	    output_len += ofraglen;
   1.685 +	    output += ofraglen;
   1.686 +	}
   1.687 +
   1.688 +	pcount = input_len - ifraglen;
   1.689 +	PORT_Assert (pcount < bsize);
   1.690 +	if (pcount)
   1.691 +	    PORT_Memcpy (pbuf, input + ifraglen, pcount);
   1.692 +    }
   1.693 +
   1.694 +    if (final) {
   1.695 +	padlen = padsize - (pcount % padsize);
   1.696 +	PORT_Memset (pbuf + pcount, padlen, padlen);
   1.697 +	rv = (* cc->doit) (cc->cx, output, &ofraglen, max_output_len,
   1.698 +			    pbuf, pcount+padlen);
   1.699 +	if (rv != SECSuccess)
   1.700 +	    return rv;
   1.701 +
   1.702 +	/*
   1.703 +	 * For now anyway, all of our ciphers have the same number of
   1.704 +	 * bytes of output as they do input.  If this ever becomes untrue,
   1.705 +	 * then sec_PKCS7EncryptLength needs to be made smarter!
   1.706 +	 */
   1.707 +	PORT_Assert (ofraglen == (pcount+padlen));
   1.708 +	output_len += ofraglen;
   1.709 +    } else {
   1.710 +	cc->pending_count = pcount;
   1.711 +    }
   1.712 +
   1.713 +    PORT_Assert (output_len_p != NULL || output_len == 0);
   1.714 +    if (output_len_p != NULL)
   1.715 +	*output_len_p = output_len;
   1.716 +
   1.717 +    return SECSuccess;
   1.718 +}

mercurial