Back to index

lightning-sunbird  0.9+nobinonly
cmscipher.c
Go to the documentation of this file.
00001 /* ***** BEGIN LICENSE BLOCK *****
00002  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00003  *
00004  * The contents of this file are subject to the Mozilla Public License Version
00005  * 1.1 (the "License"); you may not use this file except in compliance with
00006  * the License. You may obtain a copy of the License at
00007  * http://www.mozilla.org/MPL/
00008  *
00009  * Software distributed under the License is distributed on an "AS IS" basis,
00010  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00011  * for the specific language governing rights and limitations under the
00012  * License.
00013  *
00014  * The Original Code is the Netscape security libraries.
00015  *
00016  * The Initial Developer of the Original Code is
00017  * Netscape Communications Corporation.
00018  * Portions created by the Initial Developer are Copyright (C) 1994-2000
00019  * the Initial Developer. All Rights Reserved.
00020  *
00021  * Contributor(s):
00022  *
00023  * Alternatively, the contents of this file may be used under the terms of
00024  * either the GNU General Public License Version 2 or later (the "GPL"), or
00025  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00026  * in which case the provisions of the GPL or the LGPL are applicable instead
00027  * of those above. If you wish to allow use of your version of this file only
00028  * under the terms of either the GPL or the LGPL, and not to allow others to
00029  * use your version of this file under the terms of the MPL, indicate your
00030  * decision by deleting the provisions above and replace them with the notice
00031  * and other provisions required by the GPL or the LGPL. If you do not delete
00032  * the provisions above, a recipient may use your version of this file under
00033  * the terms of any one of the MPL, the GPL or the LGPL.
00034  *
00035  * ***** END LICENSE BLOCK ***** */
00036 
00037 /*
00038  * Encryption/decryption routines for CMS implementation, none of which are exported.
00039  *
00040  * $Id: cmscipher.c,v 1.8.2.1 2006/06/12 21:05:16 alexei.volkov.bugs%sun.com Exp $
00041  */
00042 
00043 #include "cmslocal.h"
00044 
00045 #include "secoid.h"
00046 #include "secitem.h"
00047 #include "pk11func.h"
00048 #include "secerr.h"
00049 #include "secpkcs5.h"
00050 
00051 /*
00052  * -------------------------------------------------------------------
00053  * Cipher stuff.
00054  */
00055 
00056 typedef SECStatus (*nss_cms_cipher_function) (void *, unsigned char *, unsigned int *,
00057                                    unsigned int, const unsigned char *, unsigned int);
00058 typedef SECStatus (*nss_cms_cipher_destroy) (void *, PRBool);
00059 
00060 #define BLOCK_SIZE 4096
00061 
00062 struct NSSCMSCipherContextStr {
00063     void *           cx;                  /* PK11 cipher context */
00064     nss_cms_cipher_function doit;
00065     nss_cms_cipher_destroy destroy;
00066     PRBool           encrypt;             /* encrypt / decrypt switch */
00067     int                     block_size;          /* block & pad sizes for cipher */
00068     int                     pad_size;
00069     int                     pending_count;              /* pending data (not yet en/decrypted */
00070     unsigned char    pending_buf[BLOCK_SIZE];/* because of blocking */
00071 };
00072 
00073 /*
00074  * NSS_CMSCipherContext_StartDecrypt - create a cipher context to do decryption
00075  * based on the given bulk * encryption key and algorithm identifier (which may include an iv).
00076  *
00077  * XXX Once both are working, it might be nice to combine this and the
00078  * function below (for starting up encryption) into one routine, and just
00079  * have two simple cover functions which call it. 
00080  */
00081 NSSCMSCipherContext *
00082 NSS_CMSCipherContext_StartDecrypt(PK11SymKey *key, SECAlgorithmID *algid)
00083 {
00084     NSSCMSCipherContext *cc;
00085     void *ciphercx;
00086     CK_MECHANISM_TYPE mechanism;
00087     SECItem *param;
00088     PK11SlotInfo *slot;
00089     SECOidTag algtag;
00090 
00091     algtag = SECOID_GetAlgorithmTag(algid);
00092 
00093     /* set param and mechanism */
00094     if (SEC_PKCS5IsAlgorithmPBEAlg(algid)) {
00095        CK_MECHANISM pbeMech, cryptoMech;
00096        SECItem *pbeParams, *pwitem;
00097 
00098        PORT_Memset(&pbeMech, 0, sizeof(CK_MECHANISM));
00099        PORT_Memset(&cryptoMech, 0, sizeof(CK_MECHANISM));
00100 
00101        pwitem = PK11_GetSymKeyUserData(key);
00102        if (!pwitem) 
00103            return NULL;
00104 
00105        /* find correct PK11 mechanism and parameters to initialize pbeMech */
00106        pbeMech.mechanism = PK11_AlgtagToMechanism(algtag);
00107        pbeParams = PK11_ParamFromAlgid(algid);
00108        if (!pbeParams)
00109            return NULL;
00110        pbeMech.pParameter = pbeParams->data;
00111        pbeMech.ulParameterLen = pbeParams->len;
00112 
00113        /* now map pbeMech to cryptoMech */
00114        if (PK11_MapPBEMechanismToCryptoMechanism(&pbeMech, &cryptoMech, pwitem,
00115                                             PR_FALSE) != CKR_OK) { 
00116            SECITEM_ZfreeItem(pbeParams, PR_TRUE);
00117            return NULL;
00118        }
00119        SECITEM_ZfreeItem(pbeParams, PR_TRUE);
00120 
00121        /* and use it to initialize param & mechanism */
00122        if ((param = (SECItem *)PORT_ZAlloc(sizeof(SECItem))) == NULL)
00123             return NULL;
00124 
00125        param->data = (unsigned char *)cryptoMech.pParameter;
00126        param->len = cryptoMech.ulParameterLen;
00127        mechanism = cryptoMech.mechanism;
00128     } else {
00129        mechanism = PK11_AlgtagToMechanism(algtag);
00130        if ((param = PK11_ParamFromAlgid(algid)) == NULL)
00131            return NULL;
00132     }
00133 
00134     cc = (NSSCMSCipherContext *)PORT_ZAlloc(sizeof(NSSCMSCipherContext));
00135     if (cc == NULL) {
00136        SECITEM_FreeItem(param,PR_TRUE);
00137        return NULL;
00138     }
00139 
00140     /* figure out pad and block sizes */
00141     cc->pad_size = PK11_GetBlockSize(mechanism, param);
00142     slot = PK11_GetSlotFromKey(key);
00143     cc->block_size = PK11_IsHW(slot) ? BLOCK_SIZE : cc->pad_size;
00144     PK11_FreeSlot(slot);
00145 
00146     /* create PK11 cipher context */
00147     ciphercx = PK11_CreateContextBySymKey(mechanism, CKA_DECRYPT, key, param);
00148     SECITEM_FreeItem(param, PR_TRUE);
00149     if (ciphercx == NULL) {
00150        PORT_Free (cc);
00151        return NULL;
00152     }
00153 
00154     cc->cx = ciphercx;
00155     cc->doit =  (nss_cms_cipher_function) PK11_CipherOp;
00156     cc->destroy = (nss_cms_cipher_destroy) PK11_DestroyContext;
00157     cc->encrypt = PR_FALSE;
00158     cc->pending_count = 0;
00159 
00160     return cc;
00161 }
00162 
00163 /*
00164  * NSS_CMSCipherContext_StartEncrypt - create a cipher object to do encryption,
00165  * based on the given bulk encryption key and algorithm tag.  Fill in the algorithm
00166  * identifier (which may include an iv) appropriately.
00167  *
00168  * XXX Once both are working, it might be nice to combine this and the
00169  * function above (for starting up decryption) into one routine, and just
00170  * have two simple cover functions which call it. 
00171  */
00172 NSSCMSCipherContext *
00173 NSS_CMSCipherContext_StartEncrypt(PRArenaPool *poolp, PK11SymKey *key, SECAlgorithmID *algid)
00174 {
00175     NSSCMSCipherContext *cc;
00176     void *ciphercx;
00177     SECItem *param;
00178     SECStatus rv;
00179     CK_MECHANISM_TYPE mechanism;
00180     PK11SlotInfo *slot;
00181     PRBool needToEncodeAlgid = PR_FALSE;
00182     SECOidTag algtag = SECOID_GetAlgorithmTag(algid);
00183 
00184     /* set param and mechanism */
00185     if (SEC_PKCS5IsAlgorithmPBEAlg(algid)) {
00186        CK_MECHANISM pbeMech, cryptoMech;
00187        SECItem *pbeParams, *pwitem;
00188 
00189        PORT_Memset(&pbeMech, 0, sizeof(CK_MECHANISM));
00190        PORT_Memset(&cryptoMech, 0, sizeof(CK_MECHANISM));
00191 
00192        pwitem = PK11_GetSymKeyUserData(key);
00193        if (!pwitem) 
00194            return NULL;
00195 
00196        /* find correct PK11 mechanism and parameters to initialize pbeMech */
00197        pbeMech.mechanism = PK11_AlgtagToMechanism(algtag);
00198        pbeParams = PK11_ParamFromAlgid(algid);
00199        if (!pbeParams)
00200            return NULL;
00201        pbeMech.pParameter = pbeParams->data;
00202        pbeMech.ulParameterLen = pbeParams->len;
00203 
00204        /* now map pbeMech to cryptoMech */
00205        if (PK11_MapPBEMechanismToCryptoMechanism(&pbeMech, &cryptoMech, pwitem,
00206                                             PR_FALSE) != CKR_OK) { 
00207            SECITEM_ZfreeItem(pbeParams, PR_TRUE);
00208            return NULL;
00209        }
00210        SECITEM_ZfreeItem(pbeParams, PR_TRUE);
00211 
00212        /* and use it to initialize param & mechanism */
00213        if ((param = (SECItem *)PORT_ZAlloc(sizeof(SECItem))) == NULL)
00214            return NULL;
00215 
00216        param->data = (unsigned char *)cryptoMech.pParameter;
00217        param->len = cryptoMech.ulParameterLen;
00218        mechanism = cryptoMech.mechanism;
00219     } else {
00220        mechanism = PK11_AlgtagToMechanism(algtag);
00221        if ((param = PK11_GenerateNewParam(mechanism, key)) == NULL)
00222            return NULL;
00223        needToEncodeAlgid = PR_TRUE;
00224     }
00225 
00226     cc = (NSSCMSCipherContext *)PORT_ZAlloc(sizeof(NSSCMSCipherContext));
00227     if (cc == NULL) {
00228        goto loser;
00229     }
00230 
00231     /* now find pad and block sizes for our mechanism */
00232     cc->pad_size = PK11_GetBlockSize(mechanism,param);
00233     slot = PK11_GetSlotFromKey(key);
00234     cc->block_size = PK11_IsHW(slot) ? BLOCK_SIZE : cc->pad_size;
00235     PK11_FreeSlot(slot);
00236 
00237     /* and here we go, creating a PK11 cipher context */
00238     ciphercx = PK11_CreateContextBySymKey(mechanism, CKA_ENCRYPT, key, param);
00239     if (ciphercx == NULL) {
00240        PORT_Free(cc);
00241        cc = NULL;
00242        goto loser;
00243     }
00244 
00245     /*
00246      * These are placed after the CreateContextBySymKey() because some
00247      * mechanisms have to generate their IVs from their card (i.e. FORTEZZA).
00248      * Don't move it from here.
00249      * XXX is that right? the purpose of this is to get the correct algid
00250      *     containing the IVs etc. for encoding. this means we need to set this up
00251      *     BEFORE encoding the algid in the contentInfo, right?
00252      */
00253     if (needToEncodeAlgid) {
00254        rv = PK11_ParamToAlgid(algtag, param, poolp, algid);
00255        if(rv != SECSuccess) {
00256            PORT_Free(cc);
00257            cc = NULL;
00258            goto loser;
00259        }
00260     }
00261 
00262     cc->cx = ciphercx;
00263     cc->doit = (nss_cms_cipher_function)PK11_CipherOp;
00264     cc->destroy = (nss_cms_cipher_destroy)PK11_DestroyContext;
00265     cc->encrypt = PR_TRUE;
00266     cc->pending_count = 0;
00267 
00268 loser:
00269     SECITEM_FreeItem(param, PR_TRUE);
00270 
00271     return cc;
00272 }
00273 
00274 void
00275 NSS_CMSCipherContext_Destroy(NSSCMSCipherContext *cc)
00276 {
00277     PORT_Assert(cc != NULL);
00278     if (cc == NULL)
00279        return;
00280     (*cc->destroy)(cc->cx, PR_TRUE);
00281     PORT_Free(cc);
00282 }
00283 
00284 /*
00285  * NSS_CMSCipherContext_DecryptLength - find the output length of the next call to decrypt.
00286  *
00287  * cc - the cipher context
00288  * input_len - number of bytes used as input
00289  * final - true if this is the final chunk of data
00290  *
00291  * Result can be used to perform memory allocations.  Note that the amount
00292  * is exactly accurate only when not doing a block cipher or when final
00293  * is false, otherwise it is an upper bound on the amount because until
00294  * we see the data we do not know how many padding bytes there are
00295  * (always between 1 and bsize).
00296  *
00297  * Note that this can return zero, which does not mean that the decrypt
00298  * operation can be skipped!  (It simply means that there are not enough
00299  * bytes to make up an entire block; the bytes will be reserved until
00300  * there are enough to encrypt/decrypt at least one block.)  However,
00301  * if zero is returned it *does* mean that no output buffer need be
00302  * passed in to the subsequent decrypt operation, as no output bytes
00303  * will be stored.
00304  */
00305 unsigned int
00306 NSS_CMSCipherContext_DecryptLength(NSSCMSCipherContext *cc, unsigned int input_len, PRBool final)
00307 {
00308     int blocks, block_size;
00309 
00310     PORT_Assert (! cc->encrypt);
00311 
00312     block_size = cc->block_size;
00313 
00314     /*
00315      * If this is not a block cipher, then we always have the same
00316      * number of output bytes as we had input bytes.
00317      */
00318     if (block_size == 0)
00319        return input_len;
00320 
00321     /*
00322      * On the final call, we will always use up all of the pending
00323      * bytes plus all of the input bytes, *but*, there will be padding
00324      * at the end and we cannot predict how many bytes of padding we
00325      * will end up removing.  The amount given here is actually known
00326      * to be at least 1 byte too long (because we know we will have
00327      * at least 1 byte of padding), but seemed clearer/better to me.
00328      */
00329     if (final)
00330        return cc->pending_count + input_len;
00331 
00332     /*
00333      * Okay, this amount is exactly what we will output on the
00334      * next cipher operation.  We will always hang onto the last
00335      * 1 - block_size bytes for non-final operations.  That is,
00336      * we will do as many complete blocks as we can *except* the
00337      * last block (complete or partial).  (This is because until
00338      * we know we are at the end, we cannot know when to interpret
00339      * and removing the padding byte(s), which are guaranteed to
00340      * be there.)
00341      */
00342     blocks = (cc->pending_count + input_len - 1) / block_size;
00343     return blocks * block_size;
00344 }
00345 
00346 /*
00347  * NSS_CMSCipherContext_EncryptLength - find the output length of the next call to encrypt.
00348  *
00349  * cc - the cipher context
00350  * input_len - number of bytes used as input
00351  * final - true if this is the final chunk of data
00352  *
00353  * Result can be used to perform memory allocations.
00354  *
00355  * Note that this can return zero, which does not mean that the encrypt
00356  * operation can be skipped!  (It simply means that there are not enough
00357  * bytes to make up an entire block; the bytes will be reserved until
00358  * there are enough to encrypt/decrypt at least one block.)  However,
00359  * if zero is returned it *does* mean that no output buffer need be
00360  * passed in to the subsequent encrypt operation, as no output bytes
00361  * will be stored.
00362  */
00363 unsigned int
00364 NSS_CMSCipherContext_EncryptLength(NSSCMSCipherContext *cc, unsigned int input_len, PRBool final)
00365 {
00366     int blocks, block_size;
00367     int pad_size;
00368 
00369     PORT_Assert (cc->encrypt);
00370 
00371     block_size = cc->block_size;
00372     pad_size = cc->pad_size;
00373 
00374     /*
00375      * If this is not a block cipher, then we always have the same
00376      * number of output bytes as we had input bytes.
00377      */
00378     if (block_size == 0)
00379        return input_len;
00380 
00381     /*
00382      * On the final call, we only send out what we need for
00383      * remaining bytes plus the padding.  (There is always padding,
00384      * so even if we have an exact number of blocks as input, we
00385      * will add another full block that is just padding.)
00386      */
00387     if (final) {
00388        if (pad_size == 0) {
00389            return cc->pending_count + input_len;
00390        } else {
00391            blocks = (cc->pending_count + input_len) / pad_size;
00392            blocks++;
00393            return blocks*pad_size;
00394        }
00395     }
00396 
00397     /*
00398      * Now, count the number of complete blocks of data we have.
00399      */
00400     blocks = (cc->pending_count + input_len) / block_size;
00401 
00402 
00403     return blocks * block_size;
00404 }
00405 
00406 
00407 /*
00408  * NSS_CMSCipherContext_Decrypt - do the decryption
00409  *
00410  * cc - the cipher context
00411  * output - buffer for decrypted result bytes
00412  * output_len_p - number of bytes in output
00413  * max_output_len - upper bound on bytes to put into output
00414  * input - pointer to input bytes
00415  * input_len - number of input bytes
00416  * final - true if this is the final chunk of data
00417  *
00418  * Decrypts a given length of input buffer (starting at "input" and
00419  * containing "input_len" bytes), placing the decrypted bytes in
00420  * "output" and storing the output length in "*output_len_p".
00421  * "cc" is the return value from NSS_CMSCipher_StartDecrypt.
00422  * When "final" is true, this is the last of the data to be decrypted.
00423  *
00424  * This is much more complicated than it sounds when the cipher is
00425  * a block-type, meaning that the decryption function will only
00426  * operate on whole blocks.  But our caller is operating stream-wise,
00427  * and can pass in any number of bytes.  So we need to keep track
00428  * of block boundaries.  We save excess bytes between calls in "cc".
00429  * We also need to determine which bytes are padding, and remove
00430  * them from the output.  We can only do this step when we know we
00431  * have the final block of data.  PKCS #7 specifies that the padding
00432  * used for a block cipher is a string of bytes, each of whose value is
00433  * the same as the length of the padding, and that all data is padded.
00434  * (Even data that starts out with an exact multiple of blocks gets
00435  * added to it another block, all of which is padding.)
00436  */ 
00437 SECStatus
00438 NSS_CMSCipherContext_Decrypt(NSSCMSCipherContext *cc, unsigned char *output,
00439                 unsigned int *output_len_p, unsigned int max_output_len,
00440                 const unsigned char *input, unsigned int input_len,
00441                 PRBool final)
00442 {
00443     int blocks, bsize, pcount, padsize;
00444     unsigned int max_needed, ifraglen, ofraglen, output_len;
00445     unsigned char *pbuf;
00446     SECStatus rv;
00447 
00448     PORT_Assert (! cc->encrypt);
00449 
00450     /*
00451      * Check that we have enough room for the output.  Our caller should
00452      * already handle this; failure is really an internal error (i.e. bug).
00453      */
00454     max_needed = NSS_CMSCipherContext_DecryptLength(cc, input_len, final);
00455     PORT_Assert (max_output_len >= max_needed);
00456     if (max_output_len < max_needed) {
00457        /* PORT_SetError (XXX); */
00458        return SECFailure;
00459     }
00460 
00461     /*
00462      * hardware encryption does not like small decryption sizes here, so we
00463      * allow both blocking and padding.
00464      */
00465     bsize = cc->block_size;
00466     padsize = cc->pad_size;
00467 
00468     /*
00469      * When no blocking or padding work to do, we can simply call the
00470      * cipher function and we are done.
00471      */
00472     if (bsize == 0) {
00473        return (* cc->doit) (cc->cx, output, output_len_p, max_output_len,
00474                            input, input_len);
00475     }
00476 
00477     pcount = cc->pending_count;
00478     pbuf = cc->pending_buf;
00479 
00480     output_len = 0;
00481 
00482     if (pcount) {
00483        /*
00484         * Try to fill in an entire block, starting with the bytes
00485         * we already have saved away.
00486         */
00487        while (input_len && pcount < bsize) {
00488            pbuf[pcount++] = *input++;
00489            input_len--;
00490        }
00491        /*
00492         * If we have at most a whole block and this is not our last call,
00493         * then we are done for now.  (We do not try to decrypt a lone
00494         * single block because we cannot interpret the padding bytes
00495         * until we know we are handling the very last block of all input.)
00496         */
00497        if (input_len == 0 && !final) {
00498            cc->pending_count = pcount;
00499            if (output_len_p)
00500               *output_len_p = 0;
00501            return SECSuccess;
00502        }
00503        /*
00504         * Given the logic above, we expect to have a full block by now.
00505         * If we do not, there is something wrong, either with our own
00506         * logic or with (length of) the data given to us.
00507         */
00508        if ((padsize != 0) && (pcount % padsize) != 0) {
00509            PORT_Assert (final);    
00510            PORT_SetError (SEC_ERROR_BAD_DATA);
00511            return SECFailure;
00512        }
00513        /*
00514         * Decrypt the block.
00515         */
00516        rv = (*cc->doit)(cc->cx, output, &ofraglen, max_output_len,
00517                          pbuf, pcount);
00518        if (rv != SECSuccess)
00519            return rv;
00520 
00521        /*
00522         * For now anyway, all of our ciphers have the same number of
00523         * bytes of output as they do input.  If this ever becomes untrue,
00524         * then NSS_CMSCipherContext_DecryptLength needs to be made smarter!
00525         */
00526        PORT_Assert(ofraglen == pcount);
00527 
00528        /*
00529         * Account for the bytes now in output.
00530         */
00531        max_output_len -= ofraglen;
00532        output_len += ofraglen;
00533        output += ofraglen;
00534     }
00535 
00536     /*
00537      * If this is our last call, we expect to have an exact number of
00538      * blocks left to be decrypted; we will decrypt them all.
00539      * 
00540      * If not our last call, we always save between 1 and bsize bytes
00541      * until next time.  (We must do this because we cannot be sure
00542      * that none of the decrypted bytes are padding bytes until we
00543      * have at least another whole block of data.  You cannot tell by
00544      * looking -- the data could be anything -- you can only tell by
00545      * context, knowing you are looking at the last block.)  We could
00546      * decrypt a whole block now but it is easier if we just treat it
00547      * the same way we treat partial block bytes.
00548      */
00549     if (final) {
00550        if (padsize) {
00551            blocks = input_len / padsize;
00552            ifraglen = blocks * padsize;
00553        } else ifraglen = input_len;
00554        PORT_Assert (ifraglen == input_len);
00555 
00556        if (ifraglen != input_len) {
00557            PORT_SetError(SEC_ERROR_BAD_DATA);
00558            return SECFailure;
00559        }
00560     } else {
00561        blocks = (input_len - 1) / bsize;
00562        ifraglen = blocks * bsize;
00563        PORT_Assert (ifraglen < input_len);
00564 
00565        pcount = input_len - ifraglen;
00566        PORT_Memcpy (pbuf, input + ifraglen, pcount);
00567        cc->pending_count = pcount;
00568     }
00569 
00570     if (ifraglen) {
00571        rv = (* cc->doit)(cc->cx, output, &ofraglen, max_output_len,
00572                          input, ifraglen);
00573        if (rv != SECSuccess)
00574            return rv;
00575 
00576        /*
00577         * For now anyway, all of our ciphers have the same number of
00578         * bytes of output as they do input.  If this ever becomes untrue,
00579         * then sec_PKCS7DecryptLength needs to be made smarter!
00580         */
00581        PORT_Assert (ifraglen == ofraglen);
00582        if (ifraglen != ofraglen) {
00583            PORT_SetError(SEC_ERROR_BAD_DATA);
00584            return SECFailure;
00585        }
00586 
00587        output_len += ofraglen;
00588     } else {
00589        ofraglen = 0;
00590     }
00591 
00592     /*
00593      * If we just did our very last block, "remove" the padding by
00594      * adjusting the output length.
00595      */
00596     if (final && (padsize != 0)) {
00597        unsigned int padlen = *(output + ofraglen - 1);
00598 
00599        if (padlen == 0 || padlen > padsize) {
00600            PORT_SetError(SEC_ERROR_BAD_DATA);
00601            return SECFailure;
00602        }
00603        output_len -= padlen;
00604     }
00605 
00606     PORT_Assert (output_len_p != NULL || output_len == 0);
00607     if (output_len_p != NULL)
00608        *output_len_p = output_len;
00609 
00610     return SECSuccess;
00611 }
00612 
00613 /*
00614  * NSS_CMSCipherContext_Encrypt - do the encryption
00615  *
00616  * cc - the cipher context
00617  * output - buffer for decrypted result bytes
00618  * output_len_p - number of bytes in output
00619  * max_output_len - upper bound on bytes to put into output
00620  * input - pointer to input bytes
00621  * input_len - number of input bytes
00622  * final - true if this is the final chunk of data
00623  *
00624  * Encrypts a given length of input buffer (starting at "input" and
00625  * containing "input_len" bytes), placing the encrypted bytes in
00626  * "output" and storing the output length in "*output_len_p".
00627  * "cc" is the return value from NSS_CMSCipher_StartEncrypt.
00628  * When "final" is true, this is the last of the data to be encrypted.
00629  *
00630  * This is much more complicated than it sounds when the cipher is
00631  * a block-type, meaning that the encryption function will only
00632  * operate on whole blocks.  But our caller is operating stream-wise,
00633  * and can pass in any number of bytes.  So we need to keep track
00634  * of block boundaries.  We save excess bytes between calls in "cc".
00635  * We also need to add padding bytes at the end.  PKCS #7 specifies
00636  * that the padding used for a block cipher is a string of bytes,
00637  * each of whose value is the same as the length of the padding,
00638  * and that all data is padded.  (Even data that starts out with
00639  * an exact multiple of blocks gets added to it another block,
00640  * all of which is padding.)
00641  *
00642  * XXX I would kind of like to combine this with the function above
00643  * which does decryption, since they have a lot in common.  But the
00644  * tricky parts about padding and filling blocks would be much
00645  * harder to read that way, so I left them separate.  At least for
00646  * now until it is clear that they are right.
00647  */ 
00648 SECStatus
00649 NSS_CMSCipherContext_Encrypt(NSSCMSCipherContext *cc, unsigned char *output,
00650                 unsigned int *output_len_p, unsigned int max_output_len,
00651                 const unsigned char *input, unsigned int input_len,
00652                 PRBool final)
00653 {
00654     int blocks, bsize, padlen, pcount, padsize;
00655     unsigned int max_needed, ifraglen, ofraglen, output_len;
00656     unsigned char *pbuf;
00657     SECStatus rv;
00658 
00659     PORT_Assert (cc->encrypt);
00660 
00661     /*
00662      * Check that we have enough room for the output.  Our caller should
00663      * already handle this; failure is really an internal error (i.e. bug).
00664      */
00665     max_needed = NSS_CMSCipherContext_EncryptLength (cc, input_len, final);
00666     PORT_Assert (max_output_len >= max_needed);
00667     if (max_output_len < max_needed) {
00668        /* PORT_SetError (XXX); */
00669        return SECFailure;
00670     }
00671 
00672     bsize = cc->block_size;
00673     padsize = cc->pad_size;
00674 
00675     /*
00676      * When no blocking and padding work to do, we can simply call the
00677      * cipher function and we are done.
00678      */
00679     if (bsize == 0) {
00680        return (*cc->doit)(cc->cx, output, output_len_p, max_output_len,
00681                            input, input_len);
00682     }
00683 
00684     pcount = cc->pending_count;
00685     pbuf = cc->pending_buf;
00686 
00687     output_len = 0;
00688 
00689     if (pcount) {
00690        /*
00691         * Try to fill in an entire block, starting with the bytes
00692         * we already have saved away.
00693         */
00694        while (input_len && pcount < bsize) {
00695            pbuf[pcount++] = *input++;
00696            input_len--;
00697        }
00698        /*
00699         * If we do not have a full block and we know we will be
00700         * called again, then we are done for now.
00701         */
00702        if (pcount < bsize && !final) {
00703            cc->pending_count = pcount;
00704            if (output_len_p != NULL)
00705               *output_len_p = 0;
00706            return SECSuccess;
00707        }
00708        /*
00709         * If we have a whole block available, encrypt it.
00710         */
00711        if ((padsize == 0) || (pcount % padsize) == 0) {
00712            rv = (* cc->doit) (cc->cx, output, &ofraglen, max_output_len,
00713                             pbuf, pcount);
00714            if (rv != SECSuccess)
00715               return rv;
00716 
00717            /*
00718             * For now anyway, all of our ciphers have the same number of
00719             * bytes of output as they do input.  If this ever becomes untrue,
00720             * then sec_PKCS7EncryptLength needs to be made smarter!
00721             */
00722            PORT_Assert (ofraglen == pcount);
00723 
00724            /*
00725             * Account for the bytes now in output.
00726             */
00727            max_output_len -= ofraglen;
00728            output_len += ofraglen;
00729            output += ofraglen;
00730 
00731            pcount = 0;
00732        }
00733     }
00734 
00735     if (input_len) {
00736        PORT_Assert (pcount == 0);
00737 
00738        blocks = input_len / bsize;
00739        ifraglen = blocks * bsize;
00740 
00741        if (ifraglen) {
00742            rv = (* cc->doit) (cc->cx, output, &ofraglen, max_output_len,
00743                             input, ifraglen);
00744            if (rv != SECSuccess)
00745               return rv;
00746 
00747            /*
00748             * For now anyway, all of our ciphers have the same number of
00749             * bytes of output as they do input.  If this ever becomes untrue,
00750             * then sec_PKCS7EncryptLength needs to be made smarter!
00751             */
00752            PORT_Assert (ifraglen == ofraglen);
00753 
00754            max_output_len -= ofraglen;
00755            output_len += ofraglen;
00756            output += ofraglen;
00757        }
00758 
00759        pcount = input_len - ifraglen;
00760        PORT_Assert (pcount < bsize);
00761        if (pcount)
00762            PORT_Memcpy (pbuf, input + ifraglen, pcount);
00763     }
00764 
00765     if (final) {
00766        padlen = padsize - (pcount % padsize);
00767        PORT_Memset (pbuf + pcount, padlen, padlen);
00768        rv = (* cc->doit) (cc->cx, output, &ofraglen, max_output_len,
00769                          pbuf, pcount+padlen);
00770        if (rv != SECSuccess)
00771            return rv;
00772 
00773        /*
00774         * For now anyway, all of our ciphers have the same number of
00775         * bytes of output as they do input.  If this ever becomes untrue,
00776         * then sec_PKCS7EncryptLength needs to be made smarter!
00777         */
00778        PORT_Assert (ofraglen == (pcount+padlen));
00779        output_len += ofraglen;
00780     } else {
00781        cc->pending_count = pcount;
00782     }
00783 
00784     PORT_Assert (output_len_p != NULL || output_len == 0);
00785     if (output_len_p != NULL)
00786        *output_len_p = output_len;
00787 
00788     return SECSuccess;
00789 }