|
1 /* This Source Code Form is subject to the terms of the Mozilla Public |
|
2 * License, v. 2.0. If a copy of the MPL was not distributed with this |
|
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
4 /* |
|
5 * This file PK11Contexts which are used in multipart hashing, |
|
6 * encryption/decryption, and signing/verication operations. |
|
7 */ |
|
8 |
|
9 #include "seccomon.h" |
|
10 #include "secmod.h" |
|
11 #include "nssilock.h" |
|
12 #include "secmodi.h" |
|
13 #include "secmodti.h" |
|
14 #include "pkcs11.h" |
|
15 #include "pk11func.h" |
|
16 #include "secitem.h" |
|
17 #include "secoid.h" |
|
18 #include "sechash.h" |
|
19 #include "secerr.h" |
|
20 |
|
21 static const SECItem pk11_null_params = { 0 }; |
|
22 |
|
23 /********************************************************************** |
|
24 * |
|
25 * Now Deal with Crypto Contexts |
|
26 * |
|
27 **********************************************************************/ |
|
28 |
|
29 /* |
|
30 * the monitors... |
|
31 */ |
|
32 void |
|
33 PK11_EnterContextMonitor(PK11Context *cx) { |
|
34 /* if we own the session and our slot is ThreadSafe, only monitor |
|
35 * the Context */ |
|
36 if ((cx->ownSession) && (cx->slot->isThreadSafe)) { |
|
37 /* Should this use monitors instead? */ |
|
38 PZ_Lock(cx->sessionLock); |
|
39 } else { |
|
40 PK11_EnterSlotMonitor(cx->slot); |
|
41 } |
|
42 } |
|
43 |
|
44 void |
|
45 PK11_ExitContextMonitor(PK11Context *cx) { |
|
46 /* if we own the session and our slot is ThreadSafe, only monitor |
|
47 * the Context */ |
|
48 if ((cx->ownSession) && (cx->slot->isThreadSafe)) { |
|
49 /* Should this use monitors instead? */ |
|
50 PZ_Unlock(cx->sessionLock); |
|
51 } else { |
|
52 PK11_ExitSlotMonitor(cx->slot); |
|
53 } |
|
54 } |
|
55 |
|
56 /* |
|
57 * Free up a Cipher Context |
|
58 */ |
|
59 void |
|
60 PK11_DestroyContext(PK11Context *context, PRBool freeit) |
|
61 { |
|
62 pk11_CloseSession(context->slot,context->session,context->ownSession); |
|
63 /* initialize the critical fields of the context */ |
|
64 if (context->savedData != NULL ) PORT_Free(context->savedData); |
|
65 if (context->key) PK11_FreeSymKey(context->key); |
|
66 if (context->param && context->param != &pk11_null_params) |
|
67 SECITEM_FreeItem(context->param, PR_TRUE); |
|
68 if (context->sessionLock) PZ_DestroyLock(context->sessionLock); |
|
69 PK11_FreeSlot(context->slot); |
|
70 if (freeit) PORT_Free(context); |
|
71 } |
|
72 |
|
73 /* |
|
74 * save the current context. Allocate Space if necessary. |
|
75 */ |
|
76 static unsigned char * |
|
77 pk11_saveContextHelper(PK11Context *context, unsigned char *buffer, |
|
78 unsigned long *savedLength) |
|
79 { |
|
80 CK_RV crv; |
|
81 |
|
82 /* If buffer is NULL, this will get the length */ |
|
83 crv = PK11_GETTAB(context->slot)->C_GetOperationState(context->session, |
|
84 (CK_BYTE_PTR)buffer, |
|
85 savedLength); |
|
86 if (!buffer || (crv == CKR_BUFFER_TOO_SMALL)) { |
|
87 /* the given buffer wasn't big enough (or was NULL), but we |
|
88 * have the length, so try again with a new buffer and the |
|
89 * correct length |
|
90 */ |
|
91 unsigned long bufLen = *savedLength; |
|
92 buffer = PORT_Alloc(bufLen); |
|
93 if (buffer == NULL) { |
|
94 return (unsigned char *)NULL; |
|
95 } |
|
96 crv = PK11_GETTAB(context->slot)->C_GetOperationState( |
|
97 context->session, |
|
98 (CK_BYTE_PTR)buffer, |
|
99 savedLength); |
|
100 if (crv != CKR_OK) { |
|
101 PORT_ZFree(buffer, bufLen); |
|
102 } |
|
103 } |
|
104 if (crv != CKR_OK) { |
|
105 PORT_SetError( PK11_MapError(crv) ); |
|
106 return (unsigned char *)NULL; |
|
107 } |
|
108 return buffer; |
|
109 } |
|
110 |
|
111 void * |
|
112 pk11_saveContext(PK11Context *context, void *space, unsigned long *savedLength) |
|
113 { |
|
114 return pk11_saveContextHelper(context, |
|
115 (unsigned char *)space, savedLength); |
|
116 } |
|
117 |
|
118 /* |
|
119 * restore the current context |
|
120 */ |
|
121 SECStatus |
|
122 pk11_restoreContext(PK11Context *context,void *space, unsigned long savedLength) |
|
123 { |
|
124 CK_RV crv; |
|
125 CK_OBJECT_HANDLE objectID = (context->key) ? context->key->objectID: |
|
126 CK_INVALID_HANDLE; |
|
127 |
|
128 PORT_Assert(space != NULL); |
|
129 if (space == NULL) { |
|
130 PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); |
|
131 return SECFailure; |
|
132 } |
|
133 crv = PK11_GETTAB(context->slot)->C_SetOperationState(context->session, |
|
134 (CK_BYTE_PTR)space, savedLength, objectID, 0); |
|
135 if (crv != CKR_OK) { |
|
136 PORT_SetError( PK11_MapError(crv)); |
|
137 return SECFailure; |
|
138 } |
|
139 return SECSuccess; |
|
140 } |
|
141 |
|
142 SECStatus pk11_Finalize(PK11Context *context); |
|
143 |
|
144 /* |
|
145 * Context initialization. Used by all flavors of CreateContext |
|
146 */ |
|
147 static SECStatus |
|
148 pk11_context_init(PK11Context *context, CK_MECHANISM *mech_info) |
|
149 { |
|
150 CK_RV crv; |
|
151 PK11SymKey *symKey = context->key; |
|
152 SECStatus rv = SECSuccess; |
|
153 |
|
154 switch (context->operation) { |
|
155 case CKA_ENCRYPT: |
|
156 crv=PK11_GETTAB(context->slot)->C_EncryptInit(context->session, |
|
157 mech_info, symKey->objectID); |
|
158 break; |
|
159 case CKA_DECRYPT: |
|
160 if (context->fortezzaHack) { |
|
161 CK_ULONG count = 0;; |
|
162 /* generate the IV for fortezza */ |
|
163 crv=PK11_GETTAB(context->slot)->C_EncryptInit(context->session, |
|
164 mech_info, symKey->objectID); |
|
165 if (crv != CKR_OK) break; |
|
166 PK11_GETTAB(context->slot)->C_EncryptFinal(context->session, |
|
167 NULL, &count); |
|
168 } |
|
169 crv=PK11_GETTAB(context->slot)->C_DecryptInit(context->session, |
|
170 mech_info, symKey->objectID); |
|
171 break; |
|
172 case CKA_SIGN: |
|
173 crv=PK11_GETTAB(context->slot)->C_SignInit(context->session, |
|
174 mech_info, symKey->objectID); |
|
175 break; |
|
176 case CKA_VERIFY: |
|
177 crv=PK11_GETTAB(context->slot)->C_SignInit(context->session, |
|
178 mech_info, symKey->objectID); |
|
179 break; |
|
180 case CKA_DIGEST: |
|
181 crv=PK11_GETTAB(context->slot)->C_DigestInit(context->session, |
|
182 mech_info); |
|
183 break; |
|
184 default: |
|
185 crv = CKR_OPERATION_NOT_INITIALIZED; |
|
186 break; |
|
187 } |
|
188 |
|
189 if (crv != CKR_OK) { |
|
190 PORT_SetError( PK11_MapError(crv) ); |
|
191 return SECFailure; |
|
192 } |
|
193 |
|
194 /* |
|
195 * handle session starvation case.. use our last session to multiplex |
|
196 */ |
|
197 if (!context->ownSession) { |
|
198 context->savedData = pk11_saveContext(context,context->savedData, |
|
199 &context->savedLength); |
|
200 if (context->savedData == NULL) rv = SECFailure; |
|
201 /* clear out out session for others to use */ |
|
202 pk11_Finalize(context); |
|
203 } |
|
204 return rv; |
|
205 } |
|
206 |
|
207 |
|
208 /* |
|
209 * Common Helper Function do come up with a new context. |
|
210 */ |
|
211 static PK11Context *pk11_CreateNewContextInSlot(CK_MECHANISM_TYPE type, |
|
212 PK11SlotInfo *slot, CK_ATTRIBUTE_TYPE operation, PK11SymKey *symKey, |
|
213 SECItem *param) |
|
214 { |
|
215 CK_MECHANISM mech_info; |
|
216 PK11Context *context; |
|
217 SECStatus rv; |
|
218 |
|
219 PORT_Assert(slot != NULL); |
|
220 if (!slot || (!symKey && ((operation != CKA_DIGEST) || |
|
221 (type == CKM_SKIPJACK_CBC64)))) { |
|
222 PORT_SetError(SEC_ERROR_INVALID_ARGS); |
|
223 return NULL; |
|
224 } |
|
225 context = (PK11Context *) PORT_Alloc(sizeof(PK11Context)); |
|
226 if (context == NULL) { |
|
227 return NULL; |
|
228 } |
|
229 |
|
230 /* now deal with the fortezza hack... the fortezza hack is an attempt |
|
231 * to get around the issue of the card not allowing you to do a FORTEZZA |
|
232 * LoadIV/Encrypt, which was added because such a combination could be |
|
233 * use to circumvent the key escrow system. Unfortunately SSL needs to |
|
234 * do this kind of operation, so in SSL we do a loadIV (to verify it), |
|
235 * Then GenerateIV, and through away the first 8 bytes on either side |
|
236 * of the connection.*/ |
|
237 context->fortezzaHack = PR_FALSE; |
|
238 if (type == CKM_SKIPJACK_CBC64) { |
|
239 if (symKey->origin == PK11_OriginFortezzaHack) { |
|
240 context->fortezzaHack = PR_TRUE; |
|
241 } |
|
242 } |
|
243 |
|
244 /* initialize the critical fields of the context */ |
|
245 context->operation = operation; |
|
246 context->key = symKey ? PK11_ReferenceSymKey(symKey) : NULL; |
|
247 context->slot = PK11_ReferenceSlot(slot); |
|
248 context->session = pk11_GetNewSession(slot,&context->ownSession); |
|
249 context->cx = symKey ? symKey->cx : NULL; |
|
250 /* get our session */ |
|
251 context->savedData = NULL; |
|
252 |
|
253 /* save the parameters so that some digesting stuff can do multiple |
|
254 * begins on a single context */ |
|
255 context->type = type; |
|
256 if (param) { |
|
257 if (param->len > 0) { |
|
258 context->param = SECITEM_DupItem(param); |
|
259 } else { |
|
260 context->param = (SECItem *)&pk11_null_params; |
|
261 } |
|
262 } else { |
|
263 PORT_SetError(SEC_ERROR_INVALID_ARGS); |
|
264 context->param = NULL; |
|
265 } |
|
266 context->init = PR_FALSE; |
|
267 context->sessionLock = PZ_NewLock(nssILockPK11cxt); |
|
268 if ((context->param == NULL) || (context->sessionLock == NULL)) { |
|
269 PK11_DestroyContext(context,PR_TRUE); |
|
270 return NULL; |
|
271 } |
|
272 |
|
273 mech_info.mechanism = type; |
|
274 mech_info.pParameter = param->data; |
|
275 mech_info.ulParameterLen = param->len; |
|
276 PK11_EnterContextMonitor(context); |
|
277 rv = pk11_context_init(context,&mech_info); |
|
278 PK11_ExitContextMonitor(context); |
|
279 |
|
280 if (rv != SECSuccess) { |
|
281 PK11_DestroyContext(context,PR_TRUE); |
|
282 return NULL; |
|
283 } |
|
284 context->init = PR_TRUE; |
|
285 return context; |
|
286 } |
|
287 |
|
288 |
|
289 /* |
|
290 * put together the various PK11_Create_Context calls used by different |
|
291 * parts of libsec. |
|
292 */ |
|
293 PK11Context * |
|
294 __PK11_CreateContextByRawKey(PK11SlotInfo *slot, CK_MECHANISM_TYPE type, |
|
295 PK11Origin origin, CK_ATTRIBUTE_TYPE operation, SECItem *key, |
|
296 SECItem *param, void *wincx) |
|
297 { |
|
298 PK11SymKey *symKey = NULL; |
|
299 PK11Context *context = NULL; |
|
300 |
|
301 /* first get a slot */ |
|
302 if (slot == NULL) { |
|
303 slot = PK11_GetBestSlot(type,wincx); |
|
304 if (slot == NULL) { |
|
305 PORT_SetError( SEC_ERROR_NO_MODULE ); |
|
306 goto loser; |
|
307 } |
|
308 } else { |
|
309 PK11_ReferenceSlot(slot); |
|
310 } |
|
311 |
|
312 /* now import the key */ |
|
313 symKey = PK11_ImportSymKey(slot, type, origin, operation, key, wincx); |
|
314 if (symKey == NULL) goto loser; |
|
315 |
|
316 context = PK11_CreateContextBySymKey(type, operation, symKey, param); |
|
317 |
|
318 loser: |
|
319 if (symKey) { |
|
320 PK11_FreeSymKey(symKey); |
|
321 } |
|
322 if (slot) { |
|
323 PK11_FreeSlot(slot); |
|
324 } |
|
325 |
|
326 return context; |
|
327 } |
|
328 |
|
329 PK11Context * |
|
330 PK11_CreateContextByRawKey(PK11SlotInfo *slot, CK_MECHANISM_TYPE type, |
|
331 PK11Origin origin, CK_ATTRIBUTE_TYPE operation, SECItem *key, |
|
332 SECItem *param, void *wincx) |
|
333 { |
|
334 return __PK11_CreateContextByRawKey(slot, type, origin, operation, |
|
335 key, param, wincx); |
|
336 } |
|
337 |
|
338 |
|
339 /* |
|
340 * Create a context from a key. We really should make sure we aren't using |
|
341 * the same key in multiple session! |
|
342 */ |
|
343 PK11Context * |
|
344 PK11_CreateContextBySymKey(CK_MECHANISM_TYPE type,CK_ATTRIBUTE_TYPE operation, |
|
345 PK11SymKey *symKey, SECItem *param) |
|
346 { |
|
347 PK11SymKey *newKey; |
|
348 PK11Context *context; |
|
349 |
|
350 /* if this slot doesn't support the mechanism, go to a slot that does */ |
|
351 newKey = pk11_ForceSlot(symKey,type,operation); |
|
352 if (newKey == NULL) { |
|
353 PK11_ReferenceSymKey(symKey); |
|
354 } else { |
|
355 symKey = newKey; |
|
356 } |
|
357 |
|
358 |
|
359 /* Context Adopts the symKey.... */ |
|
360 context = pk11_CreateNewContextInSlot(type, symKey->slot, operation, symKey, |
|
361 param); |
|
362 PK11_FreeSymKey(symKey); |
|
363 return context; |
|
364 } |
|
365 |
|
366 /* |
|
367 * Digest contexts don't need keys, but the do need to find a slot. |
|
368 * Macing should use PK11_CreateContextBySymKey. |
|
369 */ |
|
370 PK11Context * |
|
371 PK11_CreateDigestContext(SECOidTag hashAlg) |
|
372 { |
|
373 /* digesting has to work without authentication to the slot */ |
|
374 CK_MECHANISM_TYPE type; |
|
375 PK11SlotInfo *slot; |
|
376 PK11Context *context; |
|
377 SECItem param; |
|
378 |
|
379 type = PK11_AlgtagToMechanism(hashAlg); |
|
380 slot = PK11_GetBestSlot(type, NULL); |
|
381 if (slot == NULL) { |
|
382 PORT_SetError( SEC_ERROR_NO_MODULE ); |
|
383 return NULL; |
|
384 } |
|
385 |
|
386 /* maybe should really be PK11_GenerateNewParam?? */ |
|
387 param.data = NULL; |
|
388 param.len = 0; |
|
389 param.type = 0; |
|
390 |
|
391 context = pk11_CreateNewContextInSlot(type, slot, CKA_DIGEST, NULL, ¶m); |
|
392 PK11_FreeSlot(slot); |
|
393 return context; |
|
394 } |
|
395 |
|
396 /* |
|
397 * create a new context which is the clone of the state of old context. |
|
398 */ |
|
399 PK11Context * PK11_CloneContext(PK11Context *old) |
|
400 { |
|
401 PK11Context *newcx; |
|
402 PRBool needFree = PR_FALSE; |
|
403 SECStatus rv = SECSuccess; |
|
404 void *data; |
|
405 unsigned long len; |
|
406 |
|
407 newcx = pk11_CreateNewContextInSlot(old->type, old->slot, old->operation, |
|
408 old->key, old->param); |
|
409 if (newcx == NULL) return NULL; |
|
410 |
|
411 /* now clone the save state. First we need to find the save state |
|
412 * of the old session. If the old context owns it's session, |
|
413 * the state needs to be saved, otherwise the state is in saveData. */ |
|
414 if (old->ownSession) { |
|
415 PK11_EnterContextMonitor(old); |
|
416 data=pk11_saveContext(old,NULL,&len); |
|
417 PK11_ExitContextMonitor(old); |
|
418 needFree = PR_TRUE; |
|
419 } else { |
|
420 data = old->savedData; |
|
421 len = old->savedLength; |
|
422 } |
|
423 |
|
424 if (data == NULL) { |
|
425 PK11_DestroyContext(newcx,PR_TRUE); |
|
426 return NULL; |
|
427 } |
|
428 |
|
429 /* now copy that state into our new context. Again we have different |
|
430 * work if the new context owns it's own session. If it does, we |
|
431 * restore the state gathered above. If it doesn't, we copy the |
|
432 * saveData pointer... */ |
|
433 if (newcx->ownSession) { |
|
434 PK11_EnterContextMonitor(newcx); |
|
435 rv = pk11_restoreContext(newcx,data,len); |
|
436 PK11_ExitContextMonitor(newcx); |
|
437 } else { |
|
438 PORT_Assert(newcx->savedData != NULL); |
|
439 if ((newcx->savedData == NULL) || (newcx->savedLength < len)) { |
|
440 PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); |
|
441 rv = SECFailure; |
|
442 } else { |
|
443 PORT_Memcpy(newcx->savedData,data,len); |
|
444 newcx->savedLength = len; |
|
445 } |
|
446 } |
|
447 |
|
448 if (needFree) PORT_Free(data); |
|
449 |
|
450 if (rv != SECSuccess) { |
|
451 PK11_DestroyContext(newcx,PR_TRUE); |
|
452 return NULL; |
|
453 } |
|
454 return newcx; |
|
455 } |
|
456 |
|
457 /* |
|
458 * save the current context state into a variable. Required to make FORTEZZA |
|
459 * work. |
|
460 */ |
|
461 SECStatus |
|
462 PK11_SaveContext(PK11Context *cx,unsigned char *save,int *len, int saveLength) |
|
463 { |
|
464 unsigned char * data = NULL; |
|
465 CK_ULONG length = saveLength; |
|
466 |
|
467 if (cx->ownSession) { |
|
468 PK11_EnterContextMonitor(cx); |
|
469 data = pk11_saveContextHelper(cx, save, &length); |
|
470 PK11_ExitContextMonitor(cx); |
|
471 if (data) *len = length; |
|
472 } else if ((unsigned) saveLength >= cx->savedLength) { |
|
473 data = (unsigned char*)cx->savedData; |
|
474 if (cx->savedData) { |
|
475 PORT_Memcpy(save,cx->savedData,cx->savedLength); |
|
476 } |
|
477 *len = cx->savedLength; |
|
478 } |
|
479 if (data != NULL) { |
|
480 if (cx->ownSession) { |
|
481 PORT_ZFree(data, length); |
|
482 } |
|
483 return SECSuccess; |
|
484 } else { |
|
485 return SECFailure; |
|
486 } |
|
487 } |
|
488 |
|
489 /* same as above, but may allocate the return buffer. */ |
|
490 unsigned char * |
|
491 PK11_SaveContextAlloc(PK11Context *cx, |
|
492 unsigned char *preAllocBuf, unsigned int pabLen, |
|
493 unsigned int *stateLen) |
|
494 { |
|
495 unsigned char *stateBuf = NULL; |
|
496 unsigned long length = (unsigned long)pabLen; |
|
497 |
|
498 if (cx->ownSession) { |
|
499 PK11_EnterContextMonitor(cx); |
|
500 stateBuf = pk11_saveContextHelper(cx, preAllocBuf, &length); |
|
501 PK11_ExitContextMonitor(cx); |
|
502 *stateLen = (stateBuf != NULL) ? length : 0; |
|
503 } else { |
|
504 if (pabLen < cx->savedLength) { |
|
505 stateBuf = (unsigned char *)PORT_Alloc(cx->savedLength); |
|
506 if (!stateBuf) { |
|
507 return (unsigned char *)NULL; |
|
508 } |
|
509 } else { |
|
510 stateBuf = preAllocBuf; |
|
511 } |
|
512 if (cx->savedData) { |
|
513 PORT_Memcpy(stateBuf, cx->savedData, cx->savedLength); |
|
514 } |
|
515 *stateLen = cx->savedLength; |
|
516 } |
|
517 return stateBuf; |
|
518 } |
|
519 |
|
520 /* |
|
521 * restore the context state into a new running context. Also required for |
|
522 * FORTEZZA . |
|
523 */ |
|
524 SECStatus |
|
525 PK11_RestoreContext(PK11Context *cx,unsigned char *save,int len) |
|
526 { |
|
527 SECStatus rv = SECSuccess; |
|
528 if (cx->ownSession) { |
|
529 PK11_EnterContextMonitor(cx); |
|
530 pk11_Finalize(cx); |
|
531 rv = pk11_restoreContext(cx,save,len); |
|
532 PK11_ExitContextMonitor(cx); |
|
533 } else { |
|
534 PORT_Assert(cx->savedData != NULL); |
|
535 if ((cx->savedData == NULL) || (cx->savedLength < (unsigned) len)) { |
|
536 PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); |
|
537 rv = SECFailure; |
|
538 } else { |
|
539 PORT_Memcpy(cx->savedData,save,len); |
|
540 cx->savedLength = len; |
|
541 } |
|
542 } |
|
543 return rv; |
|
544 } |
|
545 |
|
546 /* |
|
547 * This is to get FIPS compliance until we can convert |
|
548 * libjar to use PK11_ hashing functions. It returns PR_FALSE |
|
549 * if we can't get a PK11 Context. |
|
550 */ |
|
551 PRBool |
|
552 PK11_HashOK(SECOidTag algID) { |
|
553 PK11Context *cx; |
|
554 |
|
555 cx = PK11_CreateDigestContext(algID); |
|
556 if (cx == NULL) return PR_FALSE; |
|
557 PK11_DestroyContext(cx, PR_TRUE); |
|
558 return PR_TRUE; |
|
559 } |
|
560 |
|
561 |
|
562 |
|
563 /* |
|
564 * start a new digesting or Mac'ing operation on this context |
|
565 */ |
|
566 SECStatus PK11_DigestBegin(PK11Context *cx) |
|
567 { |
|
568 CK_MECHANISM mech_info; |
|
569 SECStatus rv; |
|
570 |
|
571 if (cx->init == PR_TRUE) { |
|
572 return SECSuccess; |
|
573 } |
|
574 |
|
575 /* |
|
576 * make sure the old context is clear first |
|
577 */ |
|
578 PK11_EnterContextMonitor(cx); |
|
579 pk11_Finalize(cx); |
|
580 |
|
581 mech_info.mechanism = cx->type; |
|
582 mech_info.pParameter = cx->param->data; |
|
583 mech_info.ulParameterLen = cx->param->len; |
|
584 rv = pk11_context_init(cx,&mech_info); |
|
585 PK11_ExitContextMonitor(cx); |
|
586 |
|
587 if (rv != SECSuccess) { |
|
588 return SECFailure; |
|
589 } |
|
590 cx->init = PR_TRUE; |
|
591 return SECSuccess; |
|
592 } |
|
593 |
|
594 SECStatus |
|
595 PK11_HashBuf(SECOidTag hashAlg, unsigned char *out, const unsigned char *in, |
|
596 PRInt32 len) { |
|
597 PK11Context *context; |
|
598 unsigned int max_length; |
|
599 unsigned int out_length; |
|
600 SECStatus rv; |
|
601 |
|
602 /* len will be passed to PK11_DigestOp as unsigned. */ |
|
603 if (len < 0) { |
|
604 PORT_SetError(SEC_ERROR_INVALID_ARGS); |
|
605 return SECFailure; |
|
606 } |
|
607 |
|
608 context = PK11_CreateDigestContext(hashAlg); |
|
609 if (context == NULL) return SECFailure; |
|
610 |
|
611 rv = PK11_DigestBegin(context); |
|
612 if (rv != SECSuccess) { |
|
613 PK11_DestroyContext(context, PR_TRUE); |
|
614 return rv; |
|
615 } |
|
616 |
|
617 rv = PK11_DigestOp(context, in, len); |
|
618 if (rv != SECSuccess) { |
|
619 PK11_DestroyContext(context, PR_TRUE); |
|
620 return rv; |
|
621 } |
|
622 |
|
623 /* XXX This really should have been an argument to this function! */ |
|
624 max_length = HASH_ResultLenByOidTag(hashAlg); |
|
625 PORT_Assert(max_length); |
|
626 if (!max_length) |
|
627 max_length = HASH_LENGTH_MAX; |
|
628 |
|
629 rv = PK11_DigestFinal(context,out,&out_length,max_length); |
|
630 PK11_DestroyContext(context, PR_TRUE); |
|
631 return rv; |
|
632 } |
|
633 |
|
634 |
|
635 /* |
|
636 * execute a bulk encryption operation |
|
637 */ |
|
638 SECStatus |
|
639 PK11_CipherOp(PK11Context *context, unsigned char * out, int *outlen, |
|
640 int maxout, const unsigned char *in, int inlen) |
|
641 { |
|
642 CK_RV crv = CKR_OK; |
|
643 CK_ULONG length = maxout; |
|
644 CK_ULONG offset =0; |
|
645 SECStatus rv = SECSuccess; |
|
646 unsigned char *saveOut = out; |
|
647 unsigned char *allocOut = NULL; |
|
648 |
|
649 /* if we ran out of session, we need to restore our previously stored |
|
650 * state. |
|
651 */ |
|
652 PK11_EnterContextMonitor(context); |
|
653 if (!context->ownSession) { |
|
654 rv = pk11_restoreContext(context,context->savedData, |
|
655 context->savedLength); |
|
656 if (rv != SECSuccess) { |
|
657 PK11_ExitContextMonitor(context); |
|
658 return rv; |
|
659 } |
|
660 } |
|
661 |
|
662 /* |
|
663 * The fortezza hack is to send 8 extra bytes on the first encrypted and |
|
664 * lose them on the first decrypt. |
|
665 */ |
|
666 if (context->fortezzaHack) { |
|
667 unsigned char random[8]; |
|
668 if (context->operation == CKA_ENCRYPT) { |
|
669 PK11_ExitContextMonitor(context); |
|
670 rv = PK11_GenerateRandom(random,sizeof(random)); |
|
671 PK11_EnterContextMonitor(context); |
|
672 |
|
673 /* since we are offseting the output, we can't encrypt back into |
|
674 * the same buffer... allocate a temporary buffer just for this |
|
675 * call. */ |
|
676 allocOut = out = (unsigned char*)PORT_Alloc(maxout); |
|
677 if (out == NULL) { |
|
678 PK11_ExitContextMonitor(context); |
|
679 return SECFailure; |
|
680 } |
|
681 crv = PK11_GETTAB(context->slot)->C_EncryptUpdate(context->session, |
|
682 random,sizeof(random),out,&length); |
|
683 |
|
684 out += length; |
|
685 maxout -= length; |
|
686 offset = length; |
|
687 } else if (context->operation == CKA_DECRYPT) { |
|
688 length = sizeof(random); |
|
689 crv = PK11_GETTAB(context->slot)->C_DecryptUpdate(context->session, |
|
690 (CK_BYTE_PTR)in,sizeof(random),random,&length); |
|
691 inlen -= length; |
|
692 in += length; |
|
693 context->fortezzaHack = PR_FALSE; |
|
694 } |
|
695 } |
|
696 |
|
697 switch (context->operation) { |
|
698 case CKA_ENCRYPT: |
|
699 length = maxout; |
|
700 crv=PK11_GETTAB(context->slot)->C_EncryptUpdate(context->session, |
|
701 (CK_BYTE_PTR)in, inlen, |
|
702 out, &length); |
|
703 length += offset; |
|
704 break; |
|
705 case CKA_DECRYPT: |
|
706 length = maxout; |
|
707 crv=PK11_GETTAB(context->slot)->C_DecryptUpdate(context->session, |
|
708 (CK_BYTE_PTR)in, inlen, |
|
709 out, &length); |
|
710 break; |
|
711 default: |
|
712 crv = CKR_OPERATION_NOT_INITIALIZED; |
|
713 break; |
|
714 } |
|
715 |
|
716 if (crv != CKR_OK) { |
|
717 PORT_SetError( PK11_MapError(crv) ); |
|
718 *outlen = 0; |
|
719 rv = SECFailure; |
|
720 } else { |
|
721 *outlen = length; |
|
722 } |
|
723 |
|
724 if (context->fortezzaHack) { |
|
725 if (context->operation == CKA_ENCRYPT) { |
|
726 PORT_Assert(allocOut); |
|
727 PORT_Memcpy(saveOut, allocOut, length); |
|
728 PORT_Free(allocOut); |
|
729 } |
|
730 context->fortezzaHack = PR_FALSE; |
|
731 } |
|
732 |
|
733 /* |
|
734 * handle session starvation case.. use our last session to multiplex |
|
735 */ |
|
736 if (!context->ownSession) { |
|
737 context->savedData = pk11_saveContext(context,context->savedData, |
|
738 &context->savedLength); |
|
739 if (context->savedData == NULL) rv = SECFailure; |
|
740 |
|
741 /* clear out out session for others to use */ |
|
742 pk11_Finalize(context); |
|
743 } |
|
744 PK11_ExitContextMonitor(context); |
|
745 return rv; |
|
746 } |
|
747 |
|
748 /* |
|
749 * execute a digest/signature operation |
|
750 */ |
|
751 SECStatus |
|
752 PK11_DigestOp(PK11Context *context, const unsigned char * in, unsigned inLen) |
|
753 { |
|
754 CK_RV crv = CKR_OK; |
|
755 SECStatus rv = SECSuccess; |
|
756 |
|
757 if (inLen == 0) { |
|
758 return SECSuccess; |
|
759 } |
|
760 if (!in) { |
|
761 PORT_SetError(SEC_ERROR_INVALID_ARGS); |
|
762 return SECFailure; |
|
763 } |
|
764 |
|
765 /* if we ran out of session, we need to restore our previously stored |
|
766 * state. |
|
767 */ |
|
768 context->init = PR_FALSE; |
|
769 PK11_EnterContextMonitor(context); |
|
770 if (!context->ownSession) { |
|
771 rv = pk11_restoreContext(context,context->savedData, |
|
772 context->savedLength); |
|
773 if (rv != SECSuccess) { |
|
774 PK11_ExitContextMonitor(context); |
|
775 return rv; |
|
776 } |
|
777 } |
|
778 |
|
779 switch (context->operation) { |
|
780 /* also for MAC'ing */ |
|
781 case CKA_SIGN: |
|
782 crv=PK11_GETTAB(context->slot)->C_SignUpdate(context->session, |
|
783 (unsigned char *)in, |
|
784 inLen); |
|
785 break; |
|
786 case CKA_VERIFY: |
|
787 crv=PK11_GETTAB(context->slot)->C_VerifyUpdate(context->session, |
|
788 (unsigned char *)in, |
|
789 inLen); |
|
790 break; |
|
791 case CKA_DIGEST: |
|
792 crv=PK11_GETTAB(context->slot)->C_DigestUpdate(context->session, |
|
793 (unsigned char *)in, |
|
794 inLen); |
|
795 break; |
|
796 default: |
|
797 crv = CKR_OPERATION_NOT_INITIALIZED; |
|
798 break; |
|
799 } |
|
800 |
|
801 if (crv != CKR_OK) { |
|
802 PORT_SetError( PK11_MapError(crv) ); |
|
803 rv = SECFailure; |
|
804 } |
|
805 |
|
806 /* |
|
807 * handle session starvation case.. use our last session to multiplex |
|
808 */ |
|
809 if (!context->ownSession) { |
|
810 context->savedData = pk11_saveContext(context,context->savedData, |
|
811 &context->savedLength); |
|
812 if (context->savedData == NULL) rv = SECFailure; |
|
813 |
|
814 /* clear out out session for others to use */ |
|
815 pk11_Finalize(context); |
|
816 } |
|
817 PK11_ExitContextMonitor(context); |
|
818 return rv; |
|
819 } |
|
820 |
|
821 /* |
|
822 * Digest a key if possible./ |
|
823 */ |
|
824 SECStatus |
|
825 PK11_DigestKey(PK11Context *context, PK11SymKey *key) |
|
826 { |
|
827 CK_RV crv = CKR_OK; |
|
828 SECStatus rv = SECSuccess; |
|
829 PK11SymKey *newKey = NULL; |
|
830 |
|
831 if (!context || !key) { |
|
832 PORT_SetError(SEC_ERROR_INVALID_ARGS); |
|
833 return SECFailure; |
|
834 } |
|
835 |
|
836 /* if we ran out of session, we need to restore our previously stored |
|
837 * state. |
|
838 */ |
|
839 if (context->slot != key->slot) { |
|
840 newKey = pk11_CopyToSlot(context->slot,CKM_SSL3_SHA1_MAC,CKA_SIGN,key); |
|
841 } else { |
|
842 newKey = PK11_ReferenceSymKey(key); |
|
843 } |
|
844 |
|
845 context->init = PR_FALSE; |
|
846 PK11_EnterContextMonitor(context); |
|
847 if (!context->ownSession) { |
|
848 rv = pk11_restoreContext(context,context->savedData, |
|
849 context->savedLength); |
|
850 if (rv != SECSuccess) { |
|
851 PK11_ExitContextMonitor(context); |
|
852 PK11_FreeSymKey(newKey); |
|
853 return rv; |
|
854 } |
|
855 } |
|
856 |
|
857 |
|
858 if (newKey == NULL) { |
|
859 crv = CKR_KEY_TYPE_INCONSISTENT; |
|
860 if (key->data.data) { |
|
861 crv=PK11_GETTAB(context->slot)->C_DigestUpdate(context->session, |
|
862 key->data.data,key->data.len); |
|
863 } |
|
864 } else { |
|
865 crv=PK11_GETTAB(context->slot)->C_DigestKey(context->session, |
|
866 newKey->objectID); |
|
867 } |
|
868 |
|
869 if (crv != CKR_OK) { |
|
870 PORT_SetError( PK11_MapError(crv) ); |
|
871 rv = SECFailure; |
|
872 } |
|
873 |
|
874 /* |
|
875 * handle session starvation case.. use our last session to multiplex |
|
876 */ |
|
877 if (!context->ownSession) { |
|
878 context->savedData = pk11_saveContext(context,context->savedData, |
|
879 &context->savedLength); |
|
880 if (context->savedData == NULL) rv = SECFailure; |
|
881 |
|
882 /* clear out out session for others to use */ |
|
883 pk11_Finalize(context); |
|
884 } |
|
885 PK11_ExitContextMonitor(context); |
|
886 if (newKey) PK11_FreeSymKey(newKey); |
|
887 return rv; |
|
888 } |
|
889 |
|
890 /* |
|
891 * externally callable version of the lowercase pk11_finalize(). |
|
892 */ |
|
893 SECStatus |
|
894 PK11_Finalize(PK11Context *context) { |
|
895 SECStatus rv; |
|
896 |
|
897 PK11_EnterContextMonitor(context); |
|
898 rv = pk11_Finalize(context); |
|
899 PK11_ExitContextMonitor(context); |
|
900 return rv; |
|
901 } |
|
902 |
|
903 /* |
|
904 * clean up a cipher operation, so the session can be used by |
|
905 * someone new. |
|
906 */ |
|
907 SECStatus |
|
908 pk11_Finalize(PK11Context *context) |
|
909 { |
|
910 CK_ULONG count = 0; |
|
911 CK_RV crv; |
|
912 unsigned char stackBuf[256]; |
|
913 unsigned char *buffer = NULL; |
|
914 |
|
915 if (!context->ownSession) { |
|
916 return SECSuccess; |
|
917 } |
|
918 |
|
919 finalize: |
|
920 switch (context->operation) { |
|
921 case CKA_ENCRYPT: |
|
922 crv=PK11_GETTAB(context->slot)->C_EncryptFinal(context->session, |
|
923 buffer, &count); |
|
924 break; |
|
925 case CKA_DECRYPT: |
|
926 crv = PK11_GETTAB(context->slot)->C_DecryptFinal(context->session, |
|
927 buffer, &count); |
|
928 break; |
|
929 case CKA_SIGN: |
|
930 crv=PK11_GETTAB(context->slot)->C_SignFinal(context->session, |
|
931 buffer, &count); |
|
932 break; |
|
933 case CKA_VERIFY: |
|
934 crv=PK11_GETTAB(context->slot)->C_VerifyFinal(context->session, |
|
935 buffer, count); |
|
936 break; |
|
937 case CKA_DIGEST: |
|
938 crv=PK11_GETTAB(context->slot)->C_DigestFinal(context->session, |
|
939 buffer, &count); |
|
940 break; |
|
941 default: |
|
942 crv = CKR_OPERATION_NOT_INITIALIZED; |
|
943 break; |
|
944 } |
|
945 |
|
946 if (crv != CKR_OK) { |
|
947 if (buffer != stackBuf) { |
|
948 PORT_Free(buffer); |
|
949 } |
|
950 if (crv == CKR_OPERATION_NOT_INITIALIZED) { |
|
951 /* if there's no operation, it is finalized */ |
|
952 return SECSuccess; |
|
953 } |
|
954 PORT_SetError( PK11_MapError(crv) ); |
|
955 return SECFailure; |
|
956 } |
|
957 |
|
958 /* try to finalize the session with a buffer */ |
|
959 if (buffer == NULL) { |
|
960 if (count <= sizeof stackBuf) { |
|
961 buffer = stackBuf; |
|
962 } else { |
|
963 buffer = PORT_Alloc(count); |
|
964 if (buffer == NULL) { |
|
965 PORT_SetError(SEC_ERROR_NO_MEMORY); |
|
966 return SECFailure; |
|
967 } |
|
968 } |
|
969 goto finalize; |
|
970 } |
|
971 if (buffer != stackBuf) { |
|
972 PORT_Free(buffer); |
|
973 } |
|
974 return SECSuccess; |
|
975 } |
|
976 |
|
977 /* |
|
978 * Return the final digested or signed data... |
|
979 * this routine can either take pre initialized data, or allocate data |
|
980 * either out of an arena or out of the standard heap. |
|
981 */ |
|
982 SECStatus |
|
983 PK11_DigestFinal(PK11Context *context,unsigned char *data, |
|
984 unsigned int *outLen, unsigned int length) |
|
985 { |
|
986 CK_ULONG len; |
|
987 CK_RV crv; |
|
988 SECStatus rv; |
|
989 |
|
990 |
|
991 /* if we ran out of session, we need to restore our previously stored |
|
992 * state. |
|
993 */ |
|
994 PK11_EnterContextMonitor(context); |
|
995 if (!context->ownSession) { |
|
996 rv = pk11_restoreContext(context,context->savedData, |
|
997 context->savedLength); |
|
998 if (rv != SECSuccess) { |
|
999 PK11_ExitContextMonitor(context); |
|
1000 return rv; |
|
1001 } |
|
1002 } |
|
1003 |
|
1004 len = length; |
|
1005 switch (context->operation) { |
|
1006 case CKA_SIGN: |
|
1007 crv=PK11_GETTAB(context->slot)->C_SignFinal(context->session, |
|
1008 data,&len); |
|
1009 break; |
|
1010 case CKA_VERIFY: |
|
1011 crv=PK11_GETTAB(context->slot)->C_VerifyFinal(context->session, |
|
1012 data,len); |
|
1013 break; |
|
1014 case CKA_DIGEST: |
|
1015 crv=PK11_GETTAB(context->slot)->C_DigestFinal(context->session, |
|
1016 data,&len); |
|
1017 break; |
|
1018 case CKA_ENCRYPT: |
|
1019 crv=PK11_GETTAB(context->slot)->C_EncryptFinal(context->session, |
|
1020 data, &len); |
|
1021 break; |
|
1022 case CKA_DECRYPT: |
|
1023 crv = PK11_GETTAB(context->slot)->C_DecryptFinal(context->session, |
|
1024 data, &len); |
|
1025 break; |
|
1026 default: |
|
1027 crv = CKR_OPERATION_NOT_INITIALIZED; |
|
1028 break; |
|
1029 } |
|
1030 PK11_ExitContextMonitor(context); |
|
1031 |
|
1032 *outLen = (unsigned int) len; |
|
1033 context->init = PR_FALSE; /* allow Begin to start up again */ |
|
1034 |
|
1035 |
|
1036 if (crv != CKR_OK) { |
|
1037 PORT_SetError( PK11_MapError(crv) ); |
|
1038 return SECFailure; |
|
1039 } |
|
1040 return SECSuccess; |
|
1041 } |
|
1042 |