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