Back to index

lightning-sunbird  0.9+nobinonly
pk11cxt.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  * This file PK11Contexts which are  used in multipart hashing, 
00038  * encryption/decryption, and signing/verication operations.
00039  */
00040 
00041 #include "seccomon.h"
00042 #include "secmod.h"
00043 #include "nssilock.h"
00044 #include "secmodi.h"
00045 #include "secmodti.h"
00046 #include "pkcs11.h"
00047 #include "pk11func.h"
00048 #include "secitem.h"
00049 #include "secoid.h" 
00050 #include "sechash.h"
00051 #include "secerr.h"
00052 
00053 static const SECItem pk11_null_params = { 0 };
00054 
00055 /**********************************************************************
00056  *
00057  *                   Now Deal with Crypto Contexts
00058  *
00059  **********************************************************************/
00060 
00061 /*
00062  * the monitors...
00063  */
00064 void
00065 PK11_EnterContextMonitor(PK11Context *cx) {
00066     /* if we own the session and our slot is ThreadSafe, only monitor
00067      * the Context */
00068     if ((cx->ownSession) && (cx->slot->isThreadSafe)) {
00069        /* Should this use monitors instead? */
00070        PZ_Lock(cx->sessionLock);
00071     } else {
00072        PK11_EnterSlotMonitor(cx->slot);
00073     }
00074 }
00075 
00076 void
00077 PK11_ExitContextMonitor(PK11Context *cx) {
00078     /* if we own the session and our slot is ThreadSafe, only monitor
00079      * the Context */
00080     if ((cx->ownSession) && (cx->slot->isThreadSafe)) {
00081        /* Should this use monitors instead? */
00082        PZ_Unlock(cx->sessionLock);
00083     } else {
00084        PK11_ExitSlotMonitor(cx->slot);
00085     }
00086 }
00087 
00088 /*
00089  * Free up a Cipher Context
00090  */
00091 void
00092 PK11_DestroyContext(PK11Context *context, PRBool freeit)
00093 {
00094     pk11_CloseSession(context->slot,context->session,context->ownSession);
00095     /* initialize the critical fields of the context */
00096     if (context->savedData != NULL ) PORT_Free(context->savedData);
00097     if (context->key) PK11_FreeSymKey(context->key);
00098     if (context->param && context->param != &pk11_null_params)
00099        SECITEM_FreeItem(context->param, PR_TRUE);
00100     if (context->sessionLock) PZ_DestroyLock(context->sessionLock);
00101     PK11_FreeSlot(context->slot);
00102     if (freeit) PORT_Free(context);
00103 }
00104 
00105 /*
00106  * save the current context. Allocate Space if necessary.
00107  */
00108 static unsigned char *
00109 pk11_saveContextHelper(PK11Context *context, unsigned char *buffer, 
00110                        unsigned long *savedLength)
00111 {
00112     CK_RV crv;
00113 
00114     /* If buffer is NULL, this will get the length */
00115     crv = PK11_GETTAB(context->slot)->C_GetOperationState(context->session,
00116                                                           (CK_BYTE_PTR)buffer,
00117                                                           savedLength);
00118     if (!buffer || (crv == CKR_BUFFER_TOO_SMALL)) {
00119        /* the given buffer wasn't big enough (or was NULL), but we 
00120         * have the length, so try again with a new buffer and the 
00121         * correct length
00122         */
00123        unsigned long bufLen = *savedLength;
00124        buffer = PORT_Alloc(bufLen);
00125        if (buffer == NULL) {
00126            return (unsigned char *)NULL;
00127        }
00128        crv = PK11_GETTAB(context->slot)->C_GetOperationState(
00129                                                          context->session,
00130                                                           (CK_BYTE_PTR)buffer,
00131                                                           savedLength);
00132        if (crv != CKR_OK) {
00133            PORT_ZFree(buffer, bufLen);
00134        }
00135     }
00136     if (crv != CKR_OK) {
00137        PORT_SetError( PK11_MapError(crv) );
00138        return (unsigned char *)NULL;
00139     }
00140     return buffer;
00141 }
00142 
00143 void *
00144 pk11_saveContext(PK11Context *context, void *space, unsigned long *savedLength)
00145 {
00146     return pk11_saveContextHelper(context, 
00147                                   (unsigned char *)space, savedLength);
00148 }
00149 
00150 /*
00151  * restore the current context
00152  */
00153 SECStatus
00154 pk11_restoreContext(PK11Context *context,void *space, unsigned long savedLength)
00155 {
00156     CK_RV crv;
00157     CK_OBJECT_HANDLE objectID = (context->key) ? context->key->objectID:
00158                      CK_INVALID_HANDLE;
00159 
00160     PORT_Assert(space != NULL);
00161     if (space == NULL) {
00162        PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
00163        return SECFailure;
00164     }
00165     crv = PK11_GETTAB(context->slot)->C_SetOperationState(context->session,
00166         (CK_BYTE_PTR)space, savedLength, objectID, 0);
00167     if (crv != CKR_OK) {
00168        PORT_SetError( PK11_MapError(crv));
00169        return SECFailure;
00170    }
00171    return SECSuccess;
00172 }
00173 
00174 SECStatus pk11_Finalize(PK11Context *context);
00175 
00176 /*
00177  * Context initialization. Used by all flavors of CreateContext
00178  */
00179 static SECStatus 
00180 pk11_context_init(PK11Context *context, CK_MECHANISM *mech_info)
00181 {
00182     CK_RV crv;
00183     PK11SymKey *symKey = context->key;
00184     SECStatus rv = SECSuccess;
00185 
00186     switch (context->operation) {
00187     case CKA_ENCRYPT:
00188        crv=PK11_GETTAB(context->slot)->C_EncryptInit(context->session,
00189                             mech_info, symKey->objectID);
00190        break;
00191     case CKA_DECRYPT:
00192        if (context->fortezzaHack) {
00193            CK_ULONG count = 0;;
00194            /* generate the IV for fortezza */
00195            crv=PK11_GETTAB(context->slot)->C_EncryptInit(context->session,
00196                             mech_info, symKey->objectID);
00197            if (crv != CKR_OK) break;
00198            PK11_GETTAB(context->slot)->C_EncryptFinal(context->session,
00199                             NULL, &count);
00200        }
00201        crv=PK11_GETTAB(context->slot)->C_DecryptInit(context->session,
00202                             mech_info, symKey->objectID);
00203        break;
00204     case CKA_SIGN:
00205        crv=PK11_GETTAB(context->slot)->C_SignInit(context->session,
00206                             mech_info, symKey->objectID);
00207        break;
00208     case CKA_VERIFY:
00209        crv=PK11_GETTAB(context->slot)->C_SignInit(context->session,
00210                             mech_info, symKey->objectID);
00211        break;
00212     case CKA_DIGEST:
00213        crv=PK11_GETTAB(context->slot)->C_DigestInit(context->session,
00214                             mech_info);
00215        break;
00216     default:
00217        crv = CKR_OPERATION_NOT_INITIALIZED;
00218        break;
00219     }
00220 
00221     if (crv != CKR_OK) {
00222         PORT_SetError( PK11_MapError(crv) );
00223         return SECFailure;
00224     }
00225 
00226     /*
00227      * handle session starvation case.. use our last session to multiplex
00228      */
00229     if (!context->ownSession) {
00230        context->savedData = pk11_saveContext(context,context->savedData,
00231                             &context->savedLength);
00232        if (context->savedData == NULL) rv = SECFailure;
00233        /* clear out out session for others to use */
00234        pk11_Finalize(context);
00235     }
00236     return rv;
00237 }
00238 
00239 
00240 /*
00241  * Common Helper Function do come up with a new context.
00242  */
00243 static PK11Context *pk11_CreateNewContextInSlot(CK_MECHANISM_TYPE type,
00244      PK11SlotInfo *slot, CK_ATTRIBUTE_TYPE operation, PK11SymKey *symKey,
00245                                                       SECItem *param)
00246 {
00247     CK_MECHANISM mech_info;
00248     PK11Context *context;
00249     SECStatus rv;
00250        
00251     PORT_Assert(slot != NULL);
00252     if (!slot || (!symKey && operation != CKA_DIGEST)) {
00253        PORT_SetError(SEC_ERROR_INVALID_ARGS);
00254        return NULL;
00255     }
00256     context = (PK11Context *) PORT_Alloc(sizeof(PK11Context));
00257     if (context == NULL) {
00258        return NULL;
00259     }
00260 
00261     /* now deal with the fortezza hack... the fortezza hack is an attempt
00262      * to get around the issue of the card not allowing you to do a FORTEZZA
00263      * LoadIV/Encrypt, which was added because such a combination could be
00264      * use to circumvent the key escrow system. Unfortunately SSL needs to
00265      * do this kind of operation, so in SSL we do a loadIV (to verify it),
00266      * Then GenerateIV, and through away the first 8 bytes on either side
00267      * of the connection.*/
00268     context->fortezzaHack = PR_FALSE;
00269     if (type == CKM_SKIPJACK_CBC64) {
00270        if (symKey->origin == PK11_OriginFortezzaHack) {
00271            context->fortezzaHack = PR_TRUE;
00272        }
00273     }
00274 
00275     /* initialize the critical fields of the context */
00276     context->operation = operation;
00277     context->key = symKey ? PK11_ReferenceSymKey(symKey) : NULL;
00278     context->slot = PK11_ReferenceSlot(slot);
00279     context->session = pk11_GetNewSession(slot,&context->ownSession);
00280     context->cx = symKey ? symKey->cx : NULL;
00281     /* get our session */
00282     context->savedData = NULL;
00283 
00284     /* save the parameters so that some digesting stuff can do multiple
00285      * begins on a single context */
00286     context->type = type;
00287     if (param) {
00288        if (param->len > 0) {
00289            context->param = SECITEM_DupItem(param);
00290        } else {
00291            context->param = (SECItem *)&pk11_null_params;
00292        }
00293     } else {
00294        context->param = NULL;
00295     }
00296     context->init = PR_FALSE;
00297     context->sessionLock = PZ_NewLock(nssILockPK11cxt);
00298     if ((context->param == NULL) || (context->sessionLock == NULL)) {
00299        PK11_DestroyContext(context,PR_TRUE);
00300        return NULL;
00301     }
00302 
00303     mech_info.mechanism = type;
00304     mech_info.pParameter = param->data;
00305     mech_info.ulParameterLen = param->len;
00306     PK11_EnterContextMonitor(context);
00307     rv = pk11_context_init(context,&mech_info);
00308     PK11_ExitContextMonitor(context);
00309 
00310     if (rv != SECSuccess) {
00311        PK11_DestroyContext(context,PR_TRUE);
00312        return NULL;
00313     }
00314     context->init = PR_TRUE;
00315     return context;
00316 }
00317 
00318 
00319 /*
00320  * put together the various PK11_Create_Context calls used by different
00321  * parts of libsec.
00322  */
00323 PK11Context *
00324 __PK11_CreateContextByRawKey(PK11SlotInfo *slot, CK_MECHANISM_TYPE type,
00325      PK11Origin origin, CK_ATTRIBUTE_TYPE operation, SECItem *key, 
00326                                           SECItem *param, void *wincx)
00327 {
00328     PK11SymKey *symKey = NULL;
00329     PK11Context *context = NULL;
00330 
00331     /* first get a slot */
00332     if (slot == NULL) {
00333        slot = PK11_GetBestSlot(type,wincx);
00334        if (slot == NULL) {
00335            PORT_SetError( SEC_ERROR_NO_MODULE );
00336            goto loser;
00337        }
00338     } else {
00339        PK11_ReferenceSlot(slot);
00340     }
00341 
00342     /* now import the key */
00343     symKey = PK11_ImportSymKey(slot, type, origin, operation,  key, wincx);
00344     if (symKey == NULL) goto loser;
00345 
00346     context = PK11_CreateContextBySymKey(type, operation, symKey, param);
00347 
00348 loser:
00349     if (symKey) {
00350         PK11_FreeSymKey(symKey);
00351     }
00352     if (slot) {
00353         PK11_FreeSlot(slot);
00354     }
00355 
00356     return context;
00357 }
00358 
00359 PK11Context *
00360 PK11_CreateContextByRawKey(PK11SlotInfo *slot, CK_MECHANISM_TYPE type,
00361      PK11Origin origin, CK_ATTRIBUTE_TYPE operation, SECItem *key, 
00362                                           SECItem *param, void *wincx)
00363 {
00364     return __PK11_CreateContextByRawKey(slot, type, origin, operation,
00365                                         key, param, wincx);
00366 }
00367 
00368 
00369 /*
00370  * Create a context from a key. We really should make sure we aren't using
00371  * the same key in multiple session!
00372  */
00373 PK11Context *
00374 PK11_CreateContextBySymKey(CK_MECHANISM_TYPE type,CK_ATTRIBUTE_TYPE operation,
00375                      PK11SymKey *symKey, SECItem *param)
00376 {
00377     PK11SymKey *newKey;
00378     PK11Context *context;
00379 
00380     /* if this slot doesn't support the mechanism, go to a slot that does */
00381     newKey = pk11_ForceSlot(symKey,type,operation);
00382     if (newKey == NULL) {
00383        PK11_ReferenceSymKey(symKey);
00384     } else {
00385        symKey = newKey;
00386     }
00387 
00388 
00389     /* Context Adopts the symKey.... */
00390     context = pk11_CreateNewContextInSlot(type, symKey->slot, operation, symKey,
00391                                                       param);
00392     PK11_FreeSymKey(symKey);
00393     return context;
00394 }
00395 
00396 /*
00397  * Digest contexts don't need keys, but the do need to find a slot.
00398  * Macing should use PK11_CreateContextBySymKey.
00399  */
00400 PK11Context *
00401 PK11_CreateDigestContext(SECOidTag hashAlg)
00402 {
00403     /* digesting has to work without authentication to the slot */
00404     CK_MECHANISM_TYPE type;
00405     PK11SlotInfo *slot;
00406     PK11Context *context;
00407     SECItem param;
00408 
00409     type = PK11_AlgtagToMechanism(hashAlg);
00410     slot = PK11_GetBestSlot(type, NULL);
00411     if (slot == NULL) {
00412        PORT_SetError( SEC_ERROR_NO_MODULE );
00413        return NULL;
00414     }
00415 
00416     /* maybe should really be PK11_GenerateNewParam?? */
00417     param.data = NULL;
00418     param.len = 0;
00419     param.type = 0;
00420 
00421     context = pk11_CreateNewContextInSlot(type, slot, CKA_DIGEST, NULL, &param);
00422     PK11_FreeSlot(slot);
00423     return context;
00424 }
00425 
00426 /*
00427  * create a new context which is the clone of the state of old context.
00428  */
00429 PK11Context * PK11_CloneContext(PK11Context *old)
00430 {
00431      PK11Context *newcx;
00432      PRBool needFree = PR_FALSE;
00433      SECStatus rv = SECSuccess;
00434      void *data;
00435      unsigned long len;
00436 
00437      newcx = pk11_CreateNewContextInSlot(old->type, old->slot, old->operation,
00438                                           old->key, old->param);
00439      if (newcx == NULL) return NULL;
00440 
00441      /* now clone the save state. First we need to find the save state
00442       * of the old session. If the old context owns it's session,
00443       * the state needs to be saved, otherwise the state is in saveData. */
00444      if (old->ownSession) {
00445         PK11_EnterContextMonitor(old);
00446        data=pk11_saveContext(old,NULL,&len);
00447         PK11_ExitContextMonitor(old);
00448        needFree = PR_TRUE;
00449      } else {
00450        data = old->savedData;
00451        len = old->savedLength;
00452      }
00453 
00454      if (data == NULL) {
00455        PK11_DestroyContext(newcx,PR_TRUE);
00456        return NULL;
00457      }
00458 
00459      /* now copy that state into our new context. Again we have different
00460       * work if the new context owns it's own session. If it does, we
00461       * restore the state gathered above. If it doesn't, we copy the
00462       * saveData pointer... */
00463      if (newcx->ownSession) {
00464         PK11_EnterContextMonitor(newcx);
00465        rv = pk11_restoreContext(newcx,data,len);
00466         PK11_ExitContextMonitor(newcx);
00467      } else {
00468        PORT_Assert(newcx->savedData != NULL);
00469        if ((newcx->savedData == NULL) || (newcx->savedLength < len)) {
00470            PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
00471            rv = SECFailure;
00472        } else {
00473            PORT_Memcpy(newcx->savedData,data,len);
00474            newcx->savedLength = len;
00475        }
00476     }
00477 
00478     if (needFree) PORT_Free(data);
00479 
00480     if (rv != SECSuccess) {
00481        PK11_DestroyContext(newcx,PR_TRUE);
00482        return NULL;
00483     }
00484     return newcx;
00485 }
00486 
00487 /*
00488  * save the current context state into a variable. Required to make FORTEZZA
00489  * work.
00490  */
00491 SECStatus
00492 PK11_SaveContext(PK11Context *cx,unsigned char *save,int *len, int saveLength)
00493 {
00494     unsigned char * data = NULL;
00495     CK_ULONG length = saveLength;
00496 
00497     if (cx->ownSession) {
00498         PK11_EnterContextMonitor(cx);
00499        data = pk11_saveContextHelper(cx, save, &length);
00500         PK11_ExitContextMonitor(cx);
00501        if (data) *len = length;
00502     } else if ((unsigned) saveLength >= cx->savedLength) {
00503        data = (unsigned char*)cx->savedData;
00504        if (cx->savedData) {
00505            PORT_Memcpy(save,cx->savedData,cx->savedLength);
00506        }
00507        *len = cx->savedLength;
00508     }
00509     if (data != NULL) {
00510        if (cx->ownSession) {
00511            PORT_ZFree(data, length);
00512        }
00513        return SECSuccess;
00514     } else {
00515        return SECFailure;
00516     }
00517 }
00518 
00519 /* same as above, but may allocate the return buffer. */
00520 unsigned char *
00521 PK11_SaveContextAlloc(PK11Context *cx,
00522                       unsigned char *preAllocBuf, unsigned int pabLen,
00523                       unsigned int *stateLen)
00524 {
00525     unsigned char *stateBuf = NULL;
00526     unsigned long length = (unsigned long)pabLen;
00527 
00528     if (cx->ownSession) {
00529         PK11_EnterContextMonitor(cx);
00530        stateBuf = pk11_saveContextHelper(cx, preAllocBuf, &length);
00531         PK11_ExitContextMonitor(cx);
00532        *stateLen = (stateBuf != NULL) ? length : 0;
00533     } else {
00534        if (pabLen < cx->savedLength) {
00535            stateBuf = (unsigned char *)PORT_Alloc(cx->savedLength);
00536            if (!stateBuf) {
00537               return (unsigned char *)NULL;
00538            }
00539        } else {
00540            stateBuf = preAllocBuf;
00541        }
00542        if (cx->savedData) {
00543            PORT_Memcpy(stateBuf, cx->savedData, cx->savedLength);
00544        }
00545        *stateLen = cx->savedLength;
00546     }
00547     return stateBuf;
00548 }
00549 
00550 /*
00551  * restore the context state into a new running context. Also required for
00552  * FORTEZZA .
00553  */
00554 SECStatus
00555 PK11_RestoreContext(PK11Context *cx,unsigned char *save,int len)
00556 {
00557     SECStatus rv = SECSuccess;
00558     if (cx->ownSession) {
00559         PK11_EnterContextMonitor(cx);
00560        pk11_Finalize(cx);
00561        rv = pk11_restoreContext(cx,save,len);
00562         PK11_ExitContextMonitor(cx);
00563     } else {
00564        PORT_Assert(cx->savedData != NULL);
00565        if ((cx->savedData == NULL) || (cx->savedLength < (unsigned) len)) {
00566            PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
00567            rv = SECFailure;
00568        } else {
00569            PORT_Memcpy(cx->savedData,save,len);
00570            cx->savedLength = len;
00571        }
00572     }
00573     return rv;
00574 }
00575 
00576 /*
00577  * This is  to get FIPS compliance until we can convert
00578  * libjar to use PK11_ hashing functions. It returns PR_FALSE
00579  * if we can't get a PK11 Context.
00580  */
00581 PRBool
00582 PK11_HashOK(SECOidTag algID) {
00583     PK11Context *cx;
00584 
00585     cx = PK11_CreateDigestContext(algID);
00586     if (cx == NULL) return PR_FALSE;
00587     PK11_DestroyContext(cx, PR_TRUE);
00588     return PR_TRUE;
00589 }
00590 
00591 
00592 
00593 /*
00594  * start a new digesting or Mac'ing operation on this context
00595  */
00596 SECStatus PK11_DigestBegin(PK11Context *cx)
00597 {
00598     CK_MECHANISM mech_info;
00599     SECStatus rv;
00600 
00601     if (cx->init == PR_TRUE) {
00602        return SECSuccess;
00603     }
00604 
00605     /*
00606      * make sure the old context is clear first
00607      */
00608     PK11_EnterContextMonitor(cx);
00609     pk11_Finalize(cx);
00610 
00611     mech_info.mechanism = cx->type;
00612     mech_info.pParameter = cx->param->data;
00613     mech_info.ulParameterLen = cx->param->len;
00614     rv = pk11_context_init(cx,&mech_info);
00615     PK11_ExitContextMonitor(cx);
00616 
00617     if (rv != SECSuccess) {
00618        return SECFailure;
00619     }
00620     cx->init = PR_TRUE;
00621     return SECSuccess;
00622 }
00623 
00624 SECStatus
00625 PK11_HashBuf(SECOidTag hashAlg, unsigned char *out, unsigned char *in, 
00626                                                         int32 len) {
00627     PK11Context *context;
00628     unsigned int max_length;
00629     unsigned int out_length;
00630     SECStatus rv;
00631 
00632     /* len will be passed to PK11_DigestOp as unsigned. */
00633     if (len < 0) {
00634         PORT_SetError(SEC_ERROR_INVALID_ARGS);
00635         return SECFailure;
00636     }
00637 
00638     context = PK11_CreateDigestContext(hashAlg);
00639     if (context == NULL) return SECFailure;
00640 
00641     rv = PK11_DigestBegin(context);
00642     if (rv != SECSuccess) {
00643        PK11_DestroyContext(context, PR_TRUE);
00644        return rv;
00645     }
00646 
00647     rv = PK11_DigestOp(context, in, len);
00648     if (rv != SECSuccess) {
00649        PK11_DestroyContext(context, PR_TRUE);
00650        return rv;
00651     }
00652 
00653     /* XXX This really should have been an argument to this function! */
00654     max_length = HASH_ResultLenByOidTag(hashAlg);
00655     PORT_Assert(max_length);
00656     if (!max_length)
00657        max_length = HASH_LENGTH_MAX;
00658 
00659     rv = PK11_DigestFinal(context,out,&out_length,max_length);
00660     PK11_DestroyContext(context, PR_TRUE);
00661     return rv;
00662 }
00663 
00664 
00665 /*
00666  * execute a bulk encryption operation
00667  */
00668 SECStatus
00669 PK11_CipherOp(PK11Context *context, unsigned char * out, int *outlen, 
00670                             int maxout, unsigned char *in, int inlen)
00671 {
00672     CK_RV crv = CKR_OK;
00673     CK_ULONG length = maxout;
00674     CK_ULONG offset =0;
00675     SECStatus rv = SECSuccess;
00676     unsigned char *saveOut = out;
00677     unsigned char *allocOut = NULL;
00678 
00679     /* if we ran out of session, we need to restore our previously stored
00680      * state.
00681      */
00682     PK11_EnterContextMonitor(context);
00683     if (!context->ownSession) {
00684         rv = pk11_restoreContext(context,context->savedData,
00685                                                  context->savedLength);
00686        if (rv != SECSuccess) {
00687            PK11_ExitContextMonitor(context);
00688            return rv;
00689        }
00690     }
00691 
00692     /*
00693      * The fortezza hack is to send 8 extra bytes on the first encrypted and
00694      * loose them on the first decrypt.
00695      */
00696     if (context->fortezzaHack) {
00697        unsigned char random[8];
00698        if (context->operation == CKA_ENCRYPT) {
00699            PK11_ExitContextMonitor(context);
00700            rv = PK11_GenerateRandom(random,sizeof(random));
00701            PK11_EnterContextMonitor(context);
00702 
00703            /* since we are offseting the output, we can't encrypt back into
00704             * the same buffer... allocate a temporary buffer just for this
00705             * call. */
00706            allocOut = out = (unsigned char*)PORT_Alloc(maxout);
00707            if (out == NULL) {
00708               PK11_ExitContextMonitor(context);
00709               return SECFailure;
00710            }
00711            crv = PK11_GETTAB(context->slot)->C_EncryptUpdate(context->session,
00712               random,sizeof(random),out,&length);
00713 
00714            out += length;
00715            maxout -= length;
00716            offset = length;
00717        } else if (context->operation == CKA_DECRYPT) {
00718            length = sizeof(random);
00719            crv = PK11_GETTAB(context->slot)->C_DecryptUpdate(context->session,
00720               in,sizeof(random),random,&length);
00721            inlen -= length;
00722            in += length;
00723            context->fortezzaHack = PR_FALSE;
00724        }
00725     }
00726 
00727     switch (context->operation) {
00728     case CKA_ENCRYPT:
00729        length = maxout;
00730        crv=PK11_GETTAB(context->slot)->C_EncryptUpdate(context->session,
00731                                           in, inlen, out, &length);
00732        length += offset;
00733        break;
00734     case CKA_DECRYPT:
00735        length = maxout;
00736        crv=PK11_GETTAB(context->slot)->C_DecryptUpdate(context->session,
00737                                           in, inlen, out, &length);
00738        break;
00739     default:
00740        crv = CKR_OPERATION_NOT_INITIALIZED;
00741        break;
00742     }
00743 
00744     if (crv != CKR_OK) {
00745         PORT_SetError( PK11_MapError(crv) );
00746        *outlen = 0;
00747         rv = SECFailure;
00748     } else {
00749        *outlen = length;
00750     }
00751 
00752     if (context->fortezzaHack) {
00753        if (context->operation == CKA_ENCRYPT) {
00754            PORT_Assert(allocOut);
00755            PORT_Memcpy(saveOut, allocOut, length);
00756            PORT_Free(allocOut);
00757        }
00758        context->fortezzaHack = PR_FALSE;
00759     }
00760 
00761     /*
00762      * handle session starvation case.. use our last session to multiplex
00763      */
00764     if (!context->ownSession) {
00765        context->savedData = pk11_saveContext(context,context->savedData,
00766                             &context->savedLength);
00767        if (context->savedData == NULL) rv = SECFailure;
00768        
00769        /* clear out out session for others to use */
00770        pk11_Finalize(context);
00771     }
00772     PK11_ExitContextMonitor(context);
00773     return rv;
00774 }
00775 
00776 /*
00777  * execute a digest/signature operation
00778  */
00779 SECStatus
00780 PK11_DigestOp(PK11Context *context, const unsigned char * in, unsigned inLen) 
00781 {
00782     CK_RV crv = CKR_OK;
00783     SECStatus rv = SECSuccess;
00784 
00785     if (!in) {
00786         PORT_SetError(SEC_ERROR_INVALID_ARGS);
00787         return SECFailure;
00788     }
00789 
00790     /* if we ran out of session, we need to restore our previously stored
00791      * state.
00792      */
00793     context->init = PR_FALSE;
00794     PK11_EnterContextMonitor(context);
00795     if (!context->ownSession) {
00796         rv = pk11_restoreContext(context,context->savedData,
00797                                                  context->savedLength);
00798        if (rv != SECSuccess) {
00799            PK11_ExitContextMonitor(context);
00800            return rv;
00801        }
00802     }
00803 
00804     switch (context->operation) {
00805     /* also for MAC'ing */
00806     case CKA_SIGN:
00807        crv=PK11_GETTAB(context->slot)->C_SignUpdate(context->session,
00808                                                (unsigned char *)in, 
00809                                                inLen);
00810        break;
00811     case CKA_VERIFY:
00812        crv=PK11_GETTAB(context->slot)->C_VerifyUpdate(context->session,
00813                                                  (unsigned char *)in, 
00814                                                  inLen);
00815        break;
00816     case CKA_DIGEST:
00817        crv=PK11_GETTAB(context->slot)->C_DigestUpdate(context->session,
00818                                                  (unsigned char *)in, 
00819                                                  inLen);
00820        break;
00821     default:
00822        crv = CKR_OPERATION_NOT_INITIALIZED;
00823        break;
00824     }
00825 
00826     if (crv != CKR_OK) {
00827         PORT_SetError( PK11_MapError(crv) );
00828         rv = SECFailure;
00829     }
00830 
00831     /*
00832      * handle session starvation case.. use our last session to multiplex
00833      */
00834     if (!context->ownSession) {
00835        context->savedData = pk11_saveContext(context,context->savedData,
00836                             &context->savedLength);
00837        if (context->savedData == NULL) rv = SECFailure;
00838        
00839        /* clear out out session for others to use */
00840        pk11_Finalize(context);
00841     }
00842     PK11_ExitContextMonitor(context);
00843     return rv;
00844 }
00845 
00846 /*
00847  * Digest a key if possible./
00848  */
00849 SECStatus
00850 PK11_DigestKey(PK11Context *context, PK11SymKey *key)
00851 {
00852     CK_RV crv = CKR_OK;
00853     SECStatus rv = SECSuccess;
00854     PK11SymKey *newKey = NULL;
00855 
00856     if (!context || !key) {
00857         PORT_SetError(SEC_ERROR_INVALID_ARGS);
00858         return SECFailure;
00859     }
00860 
00861     /* if we ran out of session, we need to restore our previously stored
00862      * state.
00863      */
00864     if (context->slot != key->slot) {
00865        newKey = pk11_CopyToSlot(context->slot,CKM_SSL3_SHA1_MAC,CKA_SIGN,key);
00866     } else {
00867        newKey = PK11_ReferenceSymKey(key);
00868     }
00869 
00870     context->init = PR_FALSE;
00871     PK11_EnterContextMonitor(context);
00872     if (!context->ownSession) {
00873         rv = pk11_restoreContext(context,context->savedData,
00874                                                  context->savedLength);
00875        if (rv != SECSuccess) {
00876            PK11_ExitContextMonitor(context);
00877             PK11_FreeSymKey(newKey);
00878            return rv;
00879        }
00880     }
00881 
00882 
00883     if (newKey == NULL) {
00884        crv = CKR_KEY_TYPE_INCONSISTENT;
00885        if (key->data.data) {
00886            crv=PK11_GETTAB(context->slot)->C_DigestUpdate(context->session,
00887                                    key->data.data,key->data.len);
00888        }
00889     } else {
00890        crv=PK11_GETTAB(context->slot)->C_DigestKey(context->session,
00891                                                  newKey->objectID);
00892     }
00893 
00894     if (crv != CKR_OK) {
00895         PORT_SetError( PK11_MapError(crv) );
00896         rv = SECFailure;
00897     }
00898 
00899     /*
00900      * handle session starvation case.. use our last session to multiplex
00901      */
00902     if (!context->ownSession) {
00903        context->savedData = pk11_saveContext(context,context->savedData,
00904                             &context->savedLength);
00905        if (context->savedData == NULL) rv = SECFailure;
00906        
00907        /* clear out out session for others to use */
00908        pk11_Finalize(context);
00909     }
00910     PK11_ExitContextMonitor(context);
00911     if (newKey) PK11_FreeSymKey(newKey);
00912     return rv;
00913 }
00914 
00915 /*
00916  * externally callable version of the lowercase pk11_finalize().
00917  */
00918 SECStatus
00919 PK11_Finalize(PK11Context *context) {
00920     SECStatus rv;
00921 
00922     PK11_EnterContextMonitor(context);
00923     rv = pk11_Finalize(context);
00924     PK11_ExitContextMonitor(context);
00925     return rv;
00926 }
00927 
00928 /*
00929  * clean up a cipher operation, so the session can be used by
00930  * someone new.
00931  */
00932 SECStatus
00933 pk11_Finalize(PK11Context *context)
00934 {
00935     CK_ULONG count = 0;
00936     CK_RV crv;
00937     unsigned char stackBuf[256];
00938     unsigned char *buffer = NULL;
00939 
00940     if (!context->ownSession) {
00941        return SECSuccess;
00942     }
00943 
00944 finalize:
00945     switch (context->operation) {
00946     case CKA_ENCRYPT:
00947        crv=PK11_GETTAB(context->slot)->C_EncryptFinal(context->session,
00948                                                       buffer, &count);
00949        break;
00950     case CKA_DECRYPT:
00951        crv = PK11_GETTAB(context->slot)->C_DecryptFinal(context->session,
00952                                                         buffer, &count);
00953        break;
00954     case CKA_SIGN:
00955        crv=PK11_GETTAB(context->slot)->C_SignFinal(context->session,
00956                                                    buffer, &count);
00957        break;
00958     case CKA_VERIFY:
00959        crv=PK11_GETTAB(context->slot)->C_VerifyFinal(context->session,
00960                                                      buffer, count);
00961        break;
00962     case CKA_DIGEST:
00963        crv=PK11_GETTAB(context->slot)->C_DigestFinal(context->session,
00964                                                      buffer, &count);
00965        break;
00966     default:
00967        crv = CKR_OPERATION_NOT_INITIALIZED;
00968        break;
00969     }
00970 
00971     if (crv != CKR_OK) {
00972        if (buffer != stackBuf) {
00973            PORT_Free(buffer);
00974        }
00975        if (crv == CKR_OPERATION_NOT_INITIALIZED) {
00976            /* if there's no operation, it is finalized */
00977            return SECSuccess;
00978        }
00979         PORT_SetError( PK11_MapError(crv) );
00980         return SECFailure;
00981     }
00982 
00983     /* try to finalize the session with a buffer */
00984     if (buffer == NULL) { 
00985        if (count <= sizeof stackBuf) {
00986            buffer = stackBuf;
00987        } else {
00988            buffer = PORT_Alloc(count);
00989            if (buffer == NULL) {
00990               PORT_SetError(SEC_ERROR_NO_MEMORY);
00991               return SECFailure;
00992            }
00993        }
00994        goto finalize;
00995     }
00996     if (buffer != stackBuf) {
00997        PORT_Free(buffer);
00998     }
00999     return SECSuccess;
01000 }
01001 
01002 /*
01003  *  Return the final digested or signed data...
01004  *  this routine can either take pre initialized data, or allocate data
01005  *  either out of an arena or out of the standard heap.
01006  */
01007 SECStatus
01008 PK11_DigestFinal(PK11Context *context,unsigned char *data, 
01009                      unsigned int *outLen, unsigned int length)
01010 {
01011     CK_ULONG len;
01012     CK_RV crv;
01013     SECStatus rv;
01014 
01015 
01016     /* if we ran out of session, we need to restore our previously stored
01017      * state.
01018      */
01019     PK11_EnterContextMonitor(context);
01020     if (!context->ownSession) {
01021         rv = pk11_restoreContext(context,context->savedData,
01022                                                  context->savedLength);
01023        if (rv != SECSuccess) {
01024            PK11_ExitContextMonitor(context);
01025            return rv;
01026        }
01027     }
01028 
01029     len = length;
01030     switch (context->operation) {
01031     case CKA_SIGN:
01032        crv=PK11_GETTAB(context->slot)->C_SignFinal(context->session,
01033                             data,&len);
01034        break;
01035     case CKA_VERIFY:
01036        crv=PK11_GETTAB(context->slot)->C_VerifyFinal(context->session,
01037                             data,len);
01038        break;
01039     case CKA_DIGEST:
01040        crv=PK11_GETTAB(context->slot)->C_DigestFinal(context->session,
01041                             data,&len);
01042        break;
01043     case CKA_ENCRYPT:
01044        crv=PK11_GETTAB(context->slot)->C_EncryptFinal(context->session,
01045                             data, &len);
01046        break;
01047     case CKA_DECRYPT:
01048        crv = PK11_GETTAB(context->slot)->C_DecryptFinal(context->session,
01049                             data, &len);
01050        break;
01051     default:
01052        crv = CKR_OPERATION_NOT_INITIALIZED;
01053        break;
01054     }
01055     PK11_ExitContextMonitor(context);
01056 
01057     *outLen = (unsigned int) len;
01058     context->init = PR_FALSE; /* allow Begin to start up again */
01059 
01060 
01061     if (crv != CKR_OK) {
01062         PORT_SetError( PK11_MapError(crv) );
01063        return SECFailure;
01064     }
01065     return SECSuccess;
01066 }
01067