security/nss/lib/smime/cmscipher.c

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

michael@0 1 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 2 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 4
michael@0 5 /*
michael@0 6 * Encryption/decryption routines for CMS implementation, none of which are exported.
michael@0 7 */
michael@0 8
michael@0 9 #include "cmslocal.h"
michael@0 10
michael@0 11 #include "secoid.h"
michael@0 12 #include "secitem.h"
michael@0 13 #include "pk11func.h"
michael@0 14 #include "secerr.h"
michael@0 15 #include "secpkcs5.h"
michael@0 16
michael@0 17 /*
michael@0 18 * -------------------------------------------------------------------
michael@0 19 * Cipher stuff.
michael@0 20 */
michael@0 21
michael@0 22 typedef SECStatus (*nss_cms_cipher_function) (void *, unsigned char *, unsigned int *,
michael@0 23 unsigned int, const unsigned char *, unsigned int);
michael@0 24 typedef SECStatus (*nss_cms_cipher_destroy) (void *, PRBool);
michael@0 25
michael@0 26 #define BLOCK_SIZE 4096
michael@0 27
michael@0 28 struct NSSCMSCipherContextStr {
michael@0 29 void * cx; /* PK11 cipher context */
michael@0 30 nss_cms_cipher_function doit;
michael@0 31 nss_cms_cipher_destroy destroy;
michael@0 32 PRBool encrypt; /* encrypt / decrypt switch */
michael@0 33 int block_size; /* block & pad sizes for cipher */
michael@0 34 int pad_size;
michael@0 35 int pending_count; /* pending data (not yet en/decrypted */
michael@0 36 unsigned char pending_buf[BLOCK_SIZE];/* because of blocking */
michael@0 37 };
michael@0 38
michael@0 39 /*
michael@0 40 * NSS_CMSCipherContext_StartDecrypt - create a cipher context to do decryption
michael@0 41 * based on the given bulk encryption key and algorithm identifier (which
michael@0 42 * may include an iv).
michael@0 43 *
michael@0 44 * XXX Once both are working, it might be nice to combine this and the
michael@0 45 * function below (for starting up encryption) into one routine, and just
michael@0 46 * have two simple cover functions which call it.
michael@0 47 */
michael@0 48 NSSCMSCipherContext *
michael@0 49 NSS_CMSCipherContext_StartDecrypt(PK11SymKey *key, SECAlgorithmID *algid)
michael@0 50 {
michael@0 51 NSSCMSCipherContext *cc;
michael@0 52 void *ciphercx;
michael@0 53 CK_MECHANISM_TYPE cryptoMechType;
michael@0 54 PK11SlotInfo *slot;
michael@0 55 SECOidTag algtag;
michael@0 56 SECItem *param = NULL;
michael@0 57
michael@0 58 algtag = SECOID_GetAlgorithmTag(algid);
michael@0 59
michael@0 60 /* set param and mechanism */
michael@0 61 if (SEC_PKCS5IsAlgorithmPBEAlg(algid)) {
michael@0 62 SECItem *pwitem;
michael@0 63
michael@0 64 pwitem = PK11_GetSymKeyUserData(key);
michael@0 65 if (!pwitem)
michael@0 66 return NULL;
michael@0 67
michael@0 68 cryptoMechType = PK11_GetPBECryptoMechanism(algid, &param, pwitem);
michael@0 69 if (cryptoMechType == CKM_INVALID_MECHANISM) {
michael@0 70 SECITEM_FreeItem(param,PR_TRUE);
michael@0 71 return NULL;
michael@0 72 }
michael@0 73
michael@0 74 } else {
michael@0 75 cryptoMechType = PK11_AlgtagToMechanism(algtag);
michael@0 76 if ((param = PK11_ParamFromAlgid(algid)) == NULL)
michael@0 77 return NULL;
michael@0 78 }
michael@0 79
michael@0 80 cc = (NSSCMSCipherContext *)PORT_ZAlloc(sizeof(NSSCMSCipherContext));
michael@0 81 if (cc == NULL) {
michael@0 82 SECITEM_FreeItem(param,PR_TRUE);
michael@0 83 return NULL;
michael@0 84 }
michael@0 85
michael@0 86 /* figure out pad and block sizes */
michael@0 87 cc->pad_size = PK11_GetBlockSize(cryptoMechType, param);
michael@0 88 slot = PK11_GetSlotFromKey(key);
michael@0 89 cc->block_size = PK11_IsHW(slot) ? BLOCK_SIZE : cc->pad_size;
michael@0 90 PK11_FreeSlot(slot);
michael@0 91
michael@0 92 /* create PK11 cipher context */
michael@0 93 ciphercx = PK11_CreateContextBySymKey(cryptoMechType, CKA_DECRYPT,
michael@0 94 key, param);
michael@0 95 SECITEM_FreeItem(param, PR_TRUE);
michael@0 96 if (ciphercx == NULL) {
michael@0 97 PORT_Free (cc);
michael@0 98 return NULL;
michael@0 99 }
michael@0 100
michael@0 101 cc->cx = ciphercx;
michael@0 102 cc->doit = (nss_cms_cipher_function) PK11_CipherOp;
michael@0 103 cc->destroy = (nss_cms_cipher_destroy) PK11_DestroyContext;
michael@0 104 cc->encrypt = PR_FALSE;
michael@0 105 cc->pending_count = 0;
michael@0 106
michael@0 107 return cc;
michael@0 108 }
michael@0 109
michael@0 110 /*
michael@0 111 * NSS_CMSCipherContext_StartEncrypt - create a cipher object to do encryption,
michael@0 112 * based on the given bulk encryption key and algorithm tag. Fill in the
michael@0 113 * algorithm identifier (which may include an iv) appropriately.
michael@0 114 *
michael@0 115 * XXX Once both are working, it might be nice to combine this and the
michael@0 116 * function above (for starting up decryption) into one routine, and just
michael@0 117 * have two simple cover functions which call it.
michael@0 118 */
michael@0 119 NSSCMSCipherContext *
michael@0 120 NSS_CMSCipherContext_StartEncrypt(PLArenaPool *poolp, PK11SymKey *key, SECAlgorithmID *algid)
michael@0 121 {
michael@0 122 NSSCMSCipherContext *cc;
michael@0 123 void *ciphercx;
michael@0 124 SECStatus rv;
michael@0 125 CK_MECHANISM_TYPE cryptoMechType;
michael@0 126 PK11SlotInfo *slot;
michael@0 127 SECItem *param = NULL;
michael@0 128 PRBool needToEncodeAlgid = PR_FALSE;
michael@0 129 SECOidTag algtag = SECOID_GetAlgorithmTag(algid);
michael@0 130
michael@0 131 /* set param and mechanism */
michael@0 132 if (SEC_PKCS5IsAlgorithmPBEAlg(algid)) {
michael@0 133 SECItem *pwitem;
michael@0 134
michael@0 135 pwitem = PK11_GetSymKeyUserData(key);
michael@0 136 if (!pwitem)
michael@0 137 return NULL;
michael@0 138
michael@0 139 cryptoMechType = PK11_GetPBECryptoMechanism(algid, &param, pwitem);
michael@0 140 if (cryptoMechType == CKM_INVALID_MECHANISM) {
michael@0 141 SECITEM_FreeItem(param,PR_TRUE);
michael@0 142 return NULL;
michael@0 143 }
michael@0 144 } else {
michael@0 145 cryptoMechType = PK11_AlgtagToMechanism(algtag);
michael@0 146 if ((param = PK11_GenerateNewParam(cryptoMechType, key)) == NULL)
michael@0 147 return NULL;
michael@0 148 needToEncodeAlgid = PR_TRUE;
michael@0 149 }
michael@0 150
michael@0 151 cc = (NSSCMSCipherContext *)PORT_ZAlloc(sizeof(NSSCMSCipherContext));
michael@0 152 if (cc == NULL) {
michael@0 153 goto loser;
michael@0 154 }
michael@0 155
michael@0 156 /* now find pad and block sizes for our mechanism */
michael@0 157 cc->pad_size = PK11_GetBlockSize(cryptoMechType, param);
michael@0 158 slot = PK11_GetSlotFromKey(key);
michael@0 159 cc->block_size = PK11_IsHW(slot) ? BLOCK_SIZE : cc->pad_size;
michael@0 160 PK11_FreeSlot(slot);
michael@0 161
michael@0 162 /* and here we go, creating a PK11 cipher context */
michael@0 163 ciphercx = PK11_CreateContextBySymKey(cryptoMechType, CKA_ENCRYPT,
michael@0 164 key, param);
michael@0 165 if (ciphercx == NULL) {
michael@0 166 PORT_Free(cc);
michael@0 167 cc = NULL;
michael@0 168 goto loser;
michael@0 169 }
michael@0 170
michael@0 171 /*
michael@0 172 * These are placed after the CreateContextBySymKey() because some
michael@0 173 * mechanisms have to generate their IVs from their card (i.e. FORTEZZA).
michael@0 174 * Don't move it from here.
michael@0 175 * XXX is that right? the purpose of this is to get the correct algid
michael@0 176 * containing the IVs etc. for encoding. this means we need to set this up
michael@0 177 * BEFORE encoding the algid in the contentInfo, right?
michael@0 178 */
michael@0 179 if (needToEncodeAlgid) {
michael@0 180 rv = PK11_ParamToAlgid(algtag, param, poolp, algid);
michael@0 181 if(rv != SECSuccess) {
michael@0 182 PORT_Free(cc);
michael@0 183 cc = NULL;
michael@0 184 goto loser;
michael@0 185 }
michael@0 186 }
michael@0 187
michael@0 188 cc->cx = ciphercx;
michael@0 189 cc->doit = (nss_cms_cipher_function)PK11_CipherOp;
michael@0 190 cc->destroy = (nss_cms_cipher_destroy)PK11_DestroyContext;
michael@0 191 cc->encrypt = PR_TRUE;
michael@0 192 cc->pending_count = 0;
michael@0 193
michael@0 194 loser:
michael@0 195 SECITEM_FreeItem(param, PR_TRUE);
michael@0 196
michael@0 197 return cc;
michael@0 198 }
michael@0 199
michael@0 200 void
michael@0 201 NSS_CMSCipherContext_Destroy(NSSCMSCipherContext *cc)
michael@0 202 {
michael@0 203 PORT_Assert(cc != NULL);
michael@0 204 if (cc == NULL)
michael@0 205 return;
michael@0 206 (*cc->destroy)(cc->cx, PR_TRUE);
michael@0 207 PORT_Free(cc);
michael@0 208 }
michael@0 209
michael@0 210 /*
michael@0 211 * NSS_CMSCipherContext_DecryptLength - find the output length of the next call to decrypt.
michael@0 212 *
michael@0 213 * cc - the cipher context
michael@0 214 * input_len - number of bytes used as input
michael@0 215 * final - true if this is the final chunk of data
michael@0 216 *
michael@0 217 * Result can be used to perform memory allocations. Note that the amount
michael@0 218 * is exactly accurate only when not doing a block cipher or when final
michael@0 219 * is false, otherwise it is an upper bound on the amount because until
michael@0 220 * we see the data we do not know how many padding bytes there are
michael@0 221 * (always between 1 and bsize).
michael@0 222 *
michael@0 223 * Note that this can return zero, which does not mean that the decrypt
michael@0 224 * operation can be skipped! (It simply means that there are not enough
michael@0 225 * bytes to make up an entire block; the bytes will be reserved until
michael@0 226 * there are enough to encrypt/decrypt at least one block.) However,
michael@0 227 * if zero is returned it *does* mean that no output buffer need be
michael@0 228 * passed in to the subsequent decrypt operation, as no output bytes
michael@0 229 * will be stored.
michael@0 230 */
michael@0 231 unsigned int
michael@0 232 NSS_CMSCipherContext_DecryptLength(NSSCMSCipherContext *cc, unsigned int input_len, PRBool final)
michael@0 233 {
michael@0 234 int blocks, block_size;
michael@0 235
michael@0 236 PORT_Assert (! cc->encrypt);
michael@0 237
michael@0 238 block_size = cc->block_size;
michael@0 239
michael@0 240 /*
michael@0 241 * If this is not a block cipher, then we always have the same
michael@0 242 * number of output bytes as we had input bytes.
michael@0 243 */
michael@0 244 if (block_size == 0)
michael@0 245 return input_len;
michael@0 246
michael@0 247 /*
michael@0 248 * On the final call, we will always use up all of the pending
michael@0 249 * bytes plus all of the input bytes, *but*, there will be padding
michael@0 250 * at the end and we cannot predict how many bytes of padding we
michael@0 251 * will end up removing. The amount given here is actually known
michael@0 252 * to be at least 1 byte too long (because we know we will have
michael@0 253 * at least 1 byte of padding), but seemed clearer/better to me.
michael@0 254 */
michael@0 255 if (final)
michael@0 256 return cc->pending_count + input_len;
michael@0 257
michael@0 258 /*
michael@0 259 * Okay, this amount is exactly what we will output on the
michael@0 260 * next cipher operation. We will always hang onto the last
michael@0 261 * 1 - block_size bytes for non-final operations. That is,
michael@0 262 * we will do as many complete blocks as we can *except* the
michael@0 263 * last block (complete or partial). (This is because until
michael@0 264 * we know we are at the end, we cannot know when to interpret
michael@0 265 * and removing the padding byte(s), which are guaranteed to
michael@0 266 * be there.)
michael@0 267 */
michael@0 268 blocks = (cc->pending_count + input_len - 1) / block_size;
michael@0 269 return blocks * block_size;
michael@0 270 }
michael@0 271
michael@0 272 /*
michael@0 273 * NSS_CMSCipherContext_EncryptLength - find the output length of the next call to encrypt.
michael@0 274 *
michael@0 275 * cc - the cipher context
michael@0 276 * input_len - number of bytes used as input
michael@0 277 * final - true if this is the final chunk of data
michael@0 278 *
michael@0 279 * Result can be used to perform memory allocations.
michael@0 280 *
michael@0 281 * Note that this can return zero, which does not mean that the encrypt
michael@0 282 * operation can be skipped! (It simply means that there are not enough
michael@0 283 * bytes to make up an entire block; the bytes will be reserved until
michael@0 284 * there are enough to encrypt/decrypt at least one block.) However,
michael@0 285 * if zero is returned it *does* mean that no output buffer need be
michael@0 286 * passed in to the subsequent encrypt operation, as no output bytes
michael@0 287 * will be stored.
michael@0 288 */
michael@0 289 unsigned int
michael@0 290 NSS_CMSCipherContext_EncryptLength(NSSCMSCipherContext *cc, unsigned int input_len, PRBool final)
michael@0 291 {
michael@0 292 int blocks, block_size;
michael@0 293 int pad_size;
michael@0 294
michael@0 295 PORT_Assert (cc->encrypt);
michael@0 296
michael@0 297 block_size = cc->block_size;
michael@0 298 pad_size = cc->pad_size;
michael@0 299
michael@0 300 /*
michael@0 301 * If this is not a block cipher, then we always have the same
michael@0 302 * number of output bytes as we had input bytes.
michael@0 303 */
michael@0 304 if (block_size == 0)
michael@0 305 return input_len;
michael@0 306
michael@0 307 /*
michael@0 308 * On the final call, we only send out what we need for
michael@0 309 * remaining bytes plus the padding. (There is always padding,
michael@0 310 * so even if we have an exact number of blocks as input, we
michael@0 311 * will add another full block that is just padding.)
michael@0 312 */
michael@0 313 if (final) {
michael@0 314 if (pad_size == 0) {
michael@0 315 return cc->pending_count + input_len;
michael@0 316 } else {
michael@0 317 blocks = (cc->pending_count + input_len) / pad_size;
michael@0 318 blocks++;
michael@0 319 return blocks*pad_size;
michael@0 320 }
michael@0 321 }
michael@0 322
michael@0 323 /*
michael@0 324 * Now, count the number of complete blocks of data we have.
michael@0 325 */
michael@0 326 blocks = (cc->pending_count + input_len) / block_size;
michael@0 327
michael@0 328
michael@0 329 return blocks * block_size;
michael@0 330 }
michael@0 331
michael@0 332
michael@0 333 /*
michael@0 334 * NSS_CMSCipherContext_Decrypt - do the decryption
michael@0 335 *
michael@0 336 * cc - the cipher context
michael@0 337 * output - buffer for decrypted result bytes
michael@0 338 * output_len_p - number of bytes in output
michael@0 339 * max_output_len - upper bound on bytes to put into output
michael@0 340 * input - pointer to input bytes
michael@0 341 * input_len - number of input bytes
michael@0 342 * final - true if this is the final chunk of data
michael@0 343 *
michael@0 344 * Decrypts a given length of input buffer (starting at "input" and
michael@0 345 * containing "input_len" bytes), placing the decrypted bytes in
michael@0 346 * "output" and storing the output length in "*output_len_p".
michael@0 347 * "cc" is the return value from NSS_CMSCipher_StartDecrypt.
michael@0 348 * When "final" is true, this is the last of the data to be decrypted.
michael@0 349 *
michael@0 350 * This is much more complicated than it sounds when the cipher is
michael@0 351 * a block-type, meaning that the decryption function will only
michael@0 352 * operate on whole blocks. But our caller is operating stream-wise,
michael@0 353 * and can pass in any number of bytes. So we need to keep track
michael@0 354 * of block boundaries. We save excess bytes between calls in "cc".
michael@0 355 * We also need to determine which bytes are padding, and remove
michael@0 356 * them from the output. We can only do this step when we know we
michael@0 357 * have the final block of data. PKCS #7 specifies that the padding
michael@0 358 * used for a block cipher is a string of bytes, each of whose value is
michael@0 359 * the same as the length of the padding, and that all data is padded.
michael@0 360 * (Even data that starts out with an exact multiple of blocks gets
michael@0 361 * added to it another block, all of which is padding.)
michael@0 362 */
michael@0 363 SECStatus
michael@0 364 NSS_CMSCipherContext_Decrypt(NSSCMSCipherContext *cc, unsigned char *output,
michael@0 365 unsigned int *output_len_p, unsigned int max_output_len,
michael@0 366 const unsigned char *input, unsigned int input_len,
michael@0 367 PRBool final)
michael@0 368 {
michael@0 369 int blocks, bsize, pcount, padsize;
michael@0 370 unsigned int max_needed, ifraglen, ofraglen, output_len;
michael@0 371 unsigned char *pbuf;
michael@0 372 SECStatus rv;
michael@0 373
michael@0 374 PORT_Assert (! cc->encrypt);
michael@0 375
michael@0 376 /*
michael@0 377 * Check that we have enough room for the output. Our caller should
michael@0 378 * already handle this; failure is really an internal error (i.e. bug).
michael@0 379 */
michael@0 380 max_needed = NSS_CMSCipherContext_DecryptLength(cc, input_len, final);
michael@0 381 PORT_Assert (max_output_len >= max_needed);
michael@0 382 if (max_output_len < max_needed) {
michael@0 383 /* PORT_SetError (XXX); */
michael@0 384 return SECFailure;
michael@0 385 }
michael@0 386
michael@0 387 /*
michael@0 388 * hardware encryption does not like small decryption sizes here, so we
michael@0 389 * allow both blocking and padding.
michael@0 390 */
michael@0 391 bsize = cc->block_size;
michael@0 392 padsize = cc->pad_size;
michael@0 393
michael@0 394 /*
michael@0 395 * When no blocking or padding work to do, we can simply call the
michael@0 396 * cipher function and we are done.
michael@0 397 */
michael@0 398 if (bsize == 0) {
michael@0 399 return (* cc->doit) (cc->cx, output, output_len_p, max_output_len,
michael@0 400 input, input_len);
michael@0 401 }
michael@0 402
michael@0 403 pcount = cc->pending_count;
michael@0 404 pbuf = cc->pending_buf;
michael@0 405
michael@0 406 output_len = 0;
michael@0 407
michael@0 408 if (pcount) {
michael@0 409 /*
michael@0 410 * Try to fill in an entire block, starting with the bytes
michael@0 411 * we already have saved away.
michael@0 412 */
michael@0 413 while (input_len && pcount < bsize) {
michael@0 414 pbuf[pcount++] = *input++;
michael@0 415 input_len--;
michael@0 416 }
michael@0 417 /*
michael@0 418 * If we have at most a whole block and this is not our last call,
michael@0 419 * then we are done for now. (We do not try to decrypt a lone
michael@0 420 * single block because we cannot interpret the padding bytes
michael@0 421 * until we know we are handling the very last block of all input.)
michael@0 422 */
michael@0 423 if (input_len == 0 && !final) {
michael@0 424 cc->pending_count = pcount;
michael@0 425 if (output_len_p)
michael@0 426 *output_len_p = 0;
michael@0 427 return SECSuccess;
michael@0 428 }
michael@0 429 /*
michael@0 430 * Given the logic above, we expect to have a full block by now.
michael@0 431 * If we do not, there is something wrong, either with our own
michael@0 432 * logic or with (length of) the data given to us.
michael@0 433 */
michael@0 434 if ((padsize != 0) && (pcount % padsize) != 0) {
michael@0 435 PORT_Assert (final);
michael@0 436 PORT_SetError (SEC_ERROR_BAD_DATA);
michael@0 437 return SECFailure;
michael@0 438 }
michael@0 439 /*
michael@0 440 * Decrypt the block.
michael@0 441 */
michael@0 442 rv = (*cc->doit)(cc->cx, output, &ofraglen, max_output_len,
michael@0 443 pbuf, pcount);
michael@0 444 if (rv != SECSuccess)
michael@0 445 return rv;
michael@0 446
michael@0 447 /*
michael@0 448 * For now anyway, all of our ciphers have the same number of
michael@0 449 * bytes of output as they do input. If this ever becomes untrue,
michael@0 450 * then NSS_CMSCipherContext_DecryptLength needs to be made smarter!
michael@0 451 */
michael@0 452 PORT_Assert(ofraglen == pcount);
michael@0 453
michael@0 454 /*
michael@0 455 * Account for the bytes now in output.
michael@0 456 */
michael@0 457 max_output_len -= ofraglen;
michael@0 458 output_len += ofraglen;
michael@0 459 output += ofraglen;
michael@0 460 }
michael@0 461
michael@0 462 /*
michael@0 463 * If this is our last call, we expect to have an exact number of
michael@0 464 * blocks left to be decrypted; we will decrypt them all.
michael@0 465 *
michael@0 466 * If not our last call, we always save between 1 and bsize bytes
michael@0 467 * until next time. (We must do this because we cannot be sure
michael@0 468 * that none of the decrypted bytes are padding bytes until we
michael@0 469 * have at least another whole block of data. You cannot tell by
michael@0 470 * looking -- the data could be anything -- you can only tell by
michael@0 471 * context, knowing you are looking at the last block.) We could
michael@0 472 * decrypt a whole block now but it is easier if we just treat it
michael@0 473 * the same way we treat partial block bytes.
michael@0 474 */
michael@0 475 if (final) {
michael@0 476 if (padsize) {
michael@0 477 blocks = input_len / padsize;
michael@0 478 ifraglen = blocks * padsize;
michael@0 479 } else ifraglen = input_len;
michael@0 480 PORT_Assert (ifraglen == input_len);
michael@0 481
michael@0 482 if (ifraglen != input_len) {
michael@0 483 PORT_SetError(SEC_ERROR_BAD_DATA);
michael@0 484 return SECFailure;
michael@0 485 }
michael@0 486 } else {
michael@0 487 blocks = (input_len - 1) / bsize;
michael@0 488 ifraglen = blocks * bsize;
michael@0 489 PORT_Assert (ifraglen < input_len);
michael@0 490
michael@0 491 pcount = input_len - ifraglen;
michael@0 492 PORT_Memcpy (pbuf, input + ifraglen, pcount);
michael@0 493 cc->pending_count = pcount;
michael@0 494 }
michael@0 495
michael@0 496 if (ifraglen) {
michael@0 497 rv = (* cc->doit)(cc->cx, output, &ofraglen, max_output_len,
michael@0 498 input, ifraglen);
michael@0 499 if (rv != SECSuccess)
michael@0 500 return rv;
michael@0 501
michael@0 502 /*
michael@0 503 * For now anyway, all of our ciphers have the same number of
michael@0 504 * bytes of output as they do input. If this ever becomes untrue,
michael@0 505 * then sec_PKCS7DecryptLength needs to be made smarter!
michael@0 506 */
michael@0 507 PORT_Assert (ifraglen == ofraglen);
michael@0 508 if (ifraglen != ofraglen) {
michael@0 509 PORT_SetError(SEC_ERROR_BAD_DATA);
michael@0 510 return SECFailure;
michael@0 511 }
michael@0 512
michael@0 513 output_len += ofraglen;
michael@0 514 } else {
michael@0 515 ofraglen = 0;
michael@0 516 }
michael@0 517
michael@0 518 /*
michael@0 519 * If we just did our very last block, "remove" the padding by
michael@0 520 * adjusting the output length.
michael@0 521 */
michael@0 522 if (final && (padsize != 0)) {
michael@0 523 unsigned int padlen = *(output + ofraglen - 1);
michael@0 524
michael@0 525 if (padlen == 0 || padlen > padsize) {
michael@0 526 PORT_SetError(SEC_ERROR_BAD_DATA);
michael@0 527 return SECFailure;
michael@0 528 }
michael@0 529 output_len -= padlen;
michael@0 530 }
michael@0 531
michael@0 532 PORT_Assert (output_len_p != NULL || output_len == 0);
michael@0 533 if (output_len_p != NULL)
michael@0 534 *output_len_p = output_len;
michael@0 535
michael@0 536 return SECSuccess;
michael@0 537 }
michael@0 538
michael@0 539 /*
michael@0 540 * NSS_CMSCipherContext_Encrypt - do the encryption
michael@0 541 *
michael@0 542 * cc - the cipher context
michael@0 543 * output - buffer for decrypted result bytes
michael@0 544 * output_len_p - number of bytes in output
michael@0 545 * max_output_len - upper bound on bytes to put into output
michael@0 546 * input - pointer to input bytes
michael@0 547 * input_len - number of input bytes
michael@0 548 * final - true if this is the final chunk of data
michael@0 549 *
michael@0 550 * Encrypts a given length of input buffer (starting at "input" and
michael@0 551 * containing "input_len" bytes), placing the encrypted bytes in
michael@0 552 * "output" and storing the output length in "*output_len_p".
michael@0 553 * "cc" is the return value from NSS_CMSCipher_StartEncrypt.
michael@0 554 * When "final" is true, this is the last of the data to be encrypted.
michael@0 555 *
michael@0 556 * This is much more complicated than it sounds when the cipher is
michael@0 557 * a block-type, meaning that the encryption function will only
michael@0 558 * operate on whole blocks. But our caller is operating stream-wise,
michael@0 559 * and can pass in any number of bytes. So we need to keep track
michael@0 560 * of block boundaries. We save excess bytes between calls in "cc".
michael@0 561 * We also need to add padding bytes at the end. PKCS #7 specifies
michael@0 562 * that the padding used for a block cipher is a string of bytes,
michael@0 563 * each of whose value is the same as the length of the padding,
michael@0 564 * and that all data is padded. (Even data that starts out with
michael@0 565 * an exact multiple of blocks gets added to it another block,
michael@0 566 * all of which is padding.)
michael@0 567 *
michael@0 568 * XXX I would kind of like to combine this with the function above
michael@0 569 * which does decryption, since they have a lot in common. But the
michael@0 570 * tricky parts about padding and filling blocks would be much
michael@0 571 * harder to read that way, so I left them separate. At least for
michael@0 572 * now until it is clear that they are right.
michael@0 573 */
michael@0 574 SECStatus
michael@0 575 NSS_CMSCipherContext_Encrypt(NSSCMSCipherContext *cc, unsigned char *output,
michael@0 576 unsigned int *output_len_p, unsigned int max_output_len,
michael@0 577 const unsigned char *input, unsigned int input_len,
michael@0 578 PRBool final)
michael@0 579 {
michael@0 580 int blocks, bsize, padlen, pcount, padsize;
michael@0 581 unsigned int max_needed, ifraglen, ofraglen, output_len;
michael@0 582 unsigned char *pbuf;
michael@0 583 SECStatus rv;
michael@0 584
michael@0 585 PORT_Assert (cc->encrypt);
michael@0 586
michael@0 587 /*
michael@0 588 * Check that we have enough room for the output. Our caller should
michael@0 589 * already handle this; failure is really an internal error (i.e. bug).
michael@0 590 */
michael@0 591 max_needed = NSS_CMSCipherContext_EncryptLength (cc, input_len, final);
michael@0 592 PORT_Assert (max_output_len >= max_needed);
michael@0 593 if (max_output_len < max_needed) {
michael@0 594 /* PORT_SetError (XXX); */
michael@0 595 return SECFailure;
michael@0 596 }
michael@0 597
michael@0 598 bsize = cc->block_size;
michael@0 599 padsize = cc->pad_size;
michael@0 600
michael@0 601 /*
michael@0 602 * When no blocking and padding work to do, we can simply call the
michael@0 603 * cipher function and we are done.
michael@0 604 */
michael@0 605 if (bsize == 0) {
michael@0 606 return (*cc->doit)(cc->cx, output, output_len_p, max_output_len,
michael@0 607 input, input_len);
michael@0 608 }
michael@0 609
michael@0 610 pcount = cc->pending_count;
michael@0 611 pbuf = cc->pending_buf;
michael@0 612
michael@0 613 output_len = 0;
michael@0 614
michael@0 615 if (pcount) {
michael@0 616 /*
michael@0 617 * Try to fill in an entire block, starting with the bytes
michael@0 618 * we already have saved away.
michael@0 619 */
michael@0 620 while (input_len && pcount < bsize) {
michael@0 621 pbuf[pcount++] = *input++;
michael@0 622 input_len--;
michael@0 623 }
michael@0 624 /*
michael@0 625 * If we do not have a full block and we know we will be
michael@0 626 * called again, then we are done for now.
michael@0 627 */
michael@0 628 if (pcount < bsize && !final) {
michael@0 629 cc->pending_count = pcount;
michael@0 630 if (output_len_p != NULL)
michael@0 631 *output_len_p = 0;
michael@0 632 return SECSuccess;
michael@0 633 }
michael@0 634 /*
michael@0 635 * If we have a whole block available, encrypt it.
michael@0 636 */
michael@0 637 if ((padsize == 0) || (pcount % padsize) == 0) {
michael@0 638 rv = (* cc->doit) (cc->cx, output, &ofraglen, max_output_len,
michael@0 639 pbuf, pcount);
michael@0 640 if (rv != SECSuccess)
michael@0 641 return rv;
michael@0 642
michael@0 643 /*
michael@0 644 * For now anyway, all of our ciphers have the same number of
michael@0 645 * bytes of output as they do input. If this ever becomes untrue,
michael@0 646 * then sec_PKCS7EncryptLength needs to be made smarter!
michael@0 647 */
michael@0 648 PORT_Assert (ofraglen == pcount);
michael@0 649
michael@0 650 /*
michael@0 651 * Account for the bytes now in output.
michael@0 652 */
michael@0 653 max_output_len -= ofraglen;
michael@0 654 output_len += ofraglen;
michael@0 655 output += ofraglen;
michael@0 656
michael@0 657 pcount = 0;
michael@0 658 }
michael@0 659 }
michael@0 660
michael@0 661 if (input_len) {
michael@0 662 PORT_Assert (pcount == 0);
michael@0 663
michael@0 664 blocks = input_len / bsize;
michael@0 665 ifraglen = blocks * bsize;
michael@0 666
michael@0 667 if (ifraglen) {
michael@0 668 rv = (* cc->doit) (cc->cx, output, &ofraglen, max_output_len,
michael@0 669 input, ifraglen);
michael@0 670 if (rv != SECSuccess)
michael@0 671 return rv;
michael@0 672
michael@0 673 /*
michael@0 674 * For now anyway, all of our ciphers have the same number of
michael@0 675 * bytes of output as they do input. If this ever becomes untrue,
michael@0 676 * then sec_PKCS7EncryptLength needs to be made smarter!
michael@0 677 */
michael@0 678 PORT_Assert (ifraglen == ofraglen);
michael@0 679
michael@0 680 max_output_len -= ofraglen;
michael@0 681 output_len += ofraglen;
michael@0 682 output += ofraglen;
michael@0 683 }
michael@0 684
michael@0 685 pcount = input_len - ifraglen;
michael@0 686 PORT_Assert (pcount < bsize);
michael@0 687 if (pcount)
michael@0 688 PORT_Memcpy (pbuf, input + ifraglen, pcount);
michael@0 689 }
michael@0 690
michael@0 691 if (final) {
michael@0 692 padlen = padsize - (pcount % padsize);
michael@0 693 PORT_Memset (pbuf + pcount, padlen, padlen);
michael@0 694 rv = (* cc->doit) (cc->cx, output, &ofraglen, max_output_len,
michael@0 695 pbuf, pcount+padlen);
michael@0 696 if (rv != SECSuccess)
michael@0 697 return rv;
michael@0 698
michael@0 699 /*
michael@0 700 * For now anyway, all of our ciphers have the same number of
michael@0 701 * bytes of output as they do input. If this ever becomes untrue,
michael@0 702 * then sec_PKCS7EncryptLength needs to be made smarter!
michael@0 703 */
michael@0 704 PORT_Assert (ofraglen == (pcount+padlen));
michael@0 705 output_len += ofraglen;
michael@0 706 } else {
michael@0 707 cc->pending_count = pcount;
michael@0 708 }
michael@0 709
michael@0 710 PORT_Assert (output_len_p != NULL || output_len == 0);
michael@0 711 if (output_len_p != NULL)
michael@0 712 *output_len_p = output_len;
michael@0 713
michael@0 714 return SECSuccess;
michael@0 715 }

mercurial