Imported Upstream version 1.10.2
[platform/upstream/krb5.git] / src / lib / crypto / nss / enc_provider / enc_gen.c
1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /* lib/crypto/nss/enc_provider/enc_gen.c */
3 /*
4  * Copyright (c) 2010 Red Hat, Inc.
5  * All Rights Reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  *
11  *  * Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  *
14  *  * Redistributions in binary form must reproduce the above
15  *    copyright notice, this list of conditions and the following
16  *    disclaimer in the documentation and/or other materials provided
17  *    with the distribution.
18  *
19  *  * Neither the name of Red Hat, Inc., nor the names of its
20  *    contributors may be used to endorse or promote products derived
21  *    from this software without specific prior written permission.
22  *
23  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
24  * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
25  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
26  * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
27  * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
28  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
29  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
30  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
31  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
32  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
33  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34  */
35
36 #include "crypto_int.h"
37 #include "nss_gen.h"
38 #include "seccomon.h"
39 #include "secmod.h"
40 #include "pk11pub.h"
41 #include "nss.h"
42
43 /* 512 bits is bigger than anything defined to date */
44 #define MAX_KEY_LENGTH 64
45 #define MAX_BLOCK_SIZE 64
46
47 static NSSInitContext *k5_nss_ctx = NULL;
48 static pid_t k5_nss_pid = 0;
49 static k5_mutex_t k5_nss_lock = K5_MUTEX_PARTIAL_INITIALIZER;
50
51 struct stream_state {
52     struct stream_state *loopback;  /* To detect copying */
53     pid_t pid;                      /* To detect use across fork */
54     PK11Context *ctx;
55 };
56
57 struct cached_key {
58     pid_t pid;                  /* To detect use across fork */
59     PK11SymKey *symkey;
60 };
61
62 krb5_error_code
63 k5_nss_map_error(int nss_error)
64 {
65     /* Currently KRB5 does not define a full set of CRYPTO failures.
66      * for now just use KRB5_CRYPTO_INTERNAL.  We really should return
67      * errors for Not logged in, and maybe a few others. */
68     return KRB5_CRYPTO_INTERNAL;
69 }
70
71 krb5_error_code
72 k5_nss_map_last_error(void)
73 {
74     return k5_nss_map_error(PORT_GetError());
75 }
76
77 int
78 krb5int_crypto_impl_init(void)
79 {
80     return k5_mutex_finish_init(&k5_nss_lock);
81 }
82
83 void
84 krb5int_crypto_impl_cleanup(void)
85 {
86     k5_mutex_destroy(&k5_nss_lock);
87 }
88
89 /*
90  * krb5 doesn't have a call into the crypto engine to initialize it, so we do
91  * it here.  This code will try to piggyback on any application initialization
92  * done to NSS.  Otherwise get our one library init context.
93  */
94 #define NSS_KRB5_CONFIGDIR "sql:/etc/pki/nssdb"
95 krb5_error_code
96 k5_nss_init(void)
97 {
98     PRUint32 flags = NSS_INIT_READONLY | NSS_INIT_NOROOTINIT;
99     krb5_error_code ret;
100     SECStatus rv;
101     pid_t pid;
102
103     ret = k5_mutex_lock(&k5_nss_lock);
104     if (ret)
105         return ret;
106
107     pid = getpid();
108     if (k5_nss_ctx != NULL) {
109         /* Do nothing if the existing context is still good. */
110         if (k5_nss_pid == pid)
111             goto cleanup;
112         /* The caller has forked.  Restart the NSS modules.  This will
113          * invalidate all of our PKCS11 handles, which we're prepared for. */
114         rv = SECMOD_RestartModules(TRUE);
115         if (rv != SECSuccess) {
116             ret = k5_nss_map_last_error();
117             goto cleanup;
118         }
119         k5_nss_pid = pid;
120         goto cleanup;
121     }
122     k5_nss_ctx = NSS_InitContext(NSS_KRB5_CONFIGDIR, "", "", "", NULL, flags);
123     if (k5_nss_ctx == NULL) {
124         /* There may be no system database; try again without it. */
125         flags |= NSS_INIT_NOMODDB | NSS_INIT_NOCERTDB;
126         k5_nss_ctx = NSS_InitContext(NULL, "", "", "", NULL, flags);
127         if (k5_nss_ctx == NULL) {
128             ret = k5_nss_map_last_error();
129             goto cleanup;
130         }
131     }
132     k5_nss_pid = pid;
133
134 cleanup:
135     k5_mutex_unlock(&k5_nss_lock);
136     return ret;
137 }
138
139 PK11Context *
140 k5_nss_create_context(krb5_key krb_key, CK_MECHANISM_TYPE mechanism,
141                       CK_ATTRIBUTE_TYPE operation, SECItem *param)
142 {
143     struct cached_key *ckey = krb_key->cache;
144
145     return PK11_CreateContextBySymKey(mechanism, operation, ckey->symkey,
146                                       param);
147 }
148
149 static void inline
150 xor(unsigned char *x, unsigned char *y, int size)
151 {
152     int i;
153
154 #define ALIGNED(x,type) (!(((size_t)(x))&(sizeof(type)-1)))
155     if (ALIGNED(x,unsigned long) && ALIGNED(y, unsigned long)
156         && ALIGNED(size, unsigned long)) {
157         unsigned long *ux = (unsigned long *)x;
158         unsigned long *uy = (unsigned long *)y;
159         for (i=0; i < (int)(size/sizeof(unsigned long)); i++) {
160             *ux++ ^= *uy++;
161         }
162         return;
163     }
164     for (i=0; i < size; i++) {
165         *x++ ^= *y++;
166     }
167 }
168
169 krb5_error_code
170 k5_nss_gen_block_iov(krb5_key krb_key, CK_MECHANISM_TYPE mech,
171                      CK_ATTRIBUTE_TYPE operation, const krb5_data *ivec,
172                      krb5_crypto_iov *data, size_t num_data)
173 {
174     krb5_error_code ret = 0;
175     PK11Context *ctx = NULL;
176     SECStatus rv;
177     SECItem *param = NULL;
178     struct iov_block_state input_pos, output_pos;
179     unsigned char storage[MAX_BLOCK_SIZE];
180     unsigned char iv0[MAX_BLOCK_SIZE];
181     unsigned char *ptr = NULL,*lastptr = NULL;
182     SECItem iv;
183     size_t blocksize;
184     int length = 0;
185     int lastblock = -1;
186     int currentblock;
187
188     IOV_BLOCK_STATE_INIT(&input_pos);
189     IOV_BLOCK_STATE_INIT(&output_pos);
190
191     blocksize = PK11_GetBlockSize(mech, NULL);
192     assert(blocksize <= sizeof(storage));
193
194     if (ivec && ivec->data) {
195         iv.data = (unsigned char *)ivec->data;
196         iv.len = ivec->length;
197         if (operation == CKA_DECRYPT) {
198             int i, inputlength;
199
200             /* Count the blocks so we know which block is last. */
201             for (i = 0, inputlength = 0; i < (int)num_data; i++) {
202                 krb5_crypto_iov *iov = &data[i];
203
204                 if (ENCRYPT_IOV(iov))
205                     inputlength += iov->data.length;
206             }
207             lastblock = (inputlength/blocksize) -1;
208         }
209     } else {
210         memset(iv0, 0, sizeof(iv0));
211         iv.data = iv0;
212         iv.len = blocksize;
213     }
214     param = PK11_ParamFromIV(mech, &iv);
215
216     ctx = k5_nss_create_context(krb_key, mech, operation, param);
217     if (ctx == NULL) {
218         ret = k5_nss_map_last_error();
219         goto done;
220     }
221
222     for (currentblock = 0;;currentblock++) {
223         if (!krb5int_c_iov_get_block_nocopy(storage, blocksize, data, num_data,
224                                             &input_pos, &ptr))
225             break;
226
227         lastptr = NULL;
228
229         /* only set if we are decrypting */
230         if (lastblock == currentblock)
231             memcpy(ivec->data, ptr, blocksize);
232
233         rv = PK11_CipherOp(ctx, ptr, &length, blocksize, ptr, blocksize);
234         if (rv != SECSuccess) {
235             ret = k5_nss_map_last_error();
236             break;
237         }
238
239         lastptr = ptr;
240         krb5int_c_iov_put_block_nocopy(data, num_data, storage, blocksize,
241                                        &output_pos, ptr);
242     }
243
244     if (lastptr && ivec && ivec->data && operation == CKA_ENCRYPT) {
245         memcpy(ivec->data, lastptr, blocksize);
246     }
247 done:
248     if (ctx) {
249         PK11_Finalize(ctx);
250         PK11_DestroyContext(ctx, PR_TRUE);
251     }
252     if (param)
253         SECITEM_FreeItem(param, PR_TRUE);
254     return ret;
255 }
256
257 krb5_error_code
258 k5_nss_stream_init_state(krb5_data *new_state)
259 {
260     struct stream_state *sstate;
261
262     /* Create a state structure with an uninitialized context. */
263     sstate = calloc(1, sizeof(*sstate));
264     if (sstate == NULL)
265         return ENOMEM;
266     sstate->loopback = NULL;
267     new_state->data = (char *) sstate;
268     new_state->length = sizeof(*sstate);
269     return 0;
270 }
271
272 void
273 k5_nss_stream_free_state(krb5_data *state)
274 {
275     struct stream_state *sstate = (struct stream_state *) state->data;
276
277     /* Clean up the OpenSSL context if it was initialized. */
278     if (sstate && sstate->loopback == sstate) {
279         PK11_Finalize(sstate->ctx);
280         PK11_DestroyContext(sstate->ctx, PR_TRUE);
281     }
282     free(sstate);
283 }
284
285 krb5_error_code
286 k5_nss_gen_stream_iov(krb5_key krb_key, krb5_data *state,
287                       CK_MECHANISM_TYPE mech, CK_ATTRIBUTE_TYPE operation,
288                       krb5_crypto_iov *data, size_t num_data)
289 {
290     int ret = 0;
291     PK11Context *ctx = NULL;
292     SECStatus rv;
293     SECItem  param;
294     krb5_crypto_iov *iov;
295     struct stream_state *sstate = NULL;
296     int i;
297
298     param.data = NULL;
299     param.len = 0;
300
301     sstate = (state == NULL) ? NULL : (struct stream_state *) state->data;
302     if (sstate == NULL || sstate->loopback == NULL) {
303         ctx = k5_nss_create_context(krb_key, mech, operation, &param);
304         if (ctx == NULL) {
305             ret = k5_nss_map_last_error();
306             goto done;
307         }
308         if (sstate) {
309             sstate->loopback = sstate;
310             sstate->pid = getpid();
311             sstate->ctx = ctx;
312         }
313     } else {
314         /* Cipher state can't be copied or used across a fork. */
315         if (sstate->loopback != sstate || sstate->pid != getpid())
316             return EINVAL;
317         ctx = sstate->ctx;
318     }
319
320     for (i=0; i < (int)num_data; i++) {
321         int return_length;
322         iov = &data[i];
323         if (iov->data.length <= 0)
324             break;
325
326         if (ENCRYPT_IOV(iov)) {
327             rv = PK11_CipherOp(ctx, (unsigned char *)iov->data.data,
328                                &return_length, iov->data.length,
329                                (unsigned char *)iov->data.data,
330                                iov->data.length);
331             if (rv != SECSuccess) {
332                 ret = k5_nss_map_last_error();
333                 goto done;
334             }
335             iov->data.length = return_length;
336         }
337     }
338 done:
339     if (!state && ctx) {
340         PK11_Finalize(ctx);
341         PK11_DestroyContext(ctx, PR_TRUE);
342     }
343     return ret;
344 }
345
346 krb5_error_code
347 k5_nss_gen_cts_iov(krb5_key krb_key, CK_MECHANISM_TYPE mech,
348                    CK_ATTRIBUTE_TYPE operation, const krb5_data *ivec,
349                    krb5_crypto_iov *data, size_t num_data)
350 {
351     krb5_error_code ret = 0;
352     PK11Context *ctx = NULL;
353     SECStatus rv;
354     SECItem *param = NULL;
355     struct iov_block_state input_pos, output_pos;
356     unsigned char storage[MAX_BLOCK_SIZE];
357     unsigned char recover1[MAX_BLOCK_SIZE];
358     unsigned char recover2[MAX_BLOCK_SIZE];
359     unsigned char block1[MAX_BLOCK_SIZE];
360     unsigned char block2[MAX_BLOCK_SIZE];
361     unsigned char iv0[MAX_BLOCK_SIZE];
362     unsigned char *ptr = NULL;
363     SECItem iv;
364     size_t blocksize;
365     size_t bulk_length, remainder;
366     size_t input_length, lastblock;
367     size_t length;
368     int i, len;
369
370     IOV_BLOCK_STATE_INIT(&input_pos);
371     IOV_BLOCK_STATE_INIT(&output_pos);
372
373     blocksize = PK11_GetBlockSize(mech, NULL);
374     assert(blocksize <= sizeof(storage));
375
376     if (ivec) {
377         iv.data = (unsigned char *)ivec->data;
378         iv.len = ivec->length;
379     } else {
380         memset(iv0, 0, sizeof(iv0));
381         iv.data = iv0;
382         iv.len = blocksize;
383     }
384     param = PK11_ParamFromIV(mech, &iv);
385
386     for (i = 0, input_length = 0; i < (int)num_data; i++) {
387         krb5_crypto_iov *iov = &data[i];
388
389         if (ENCRYPT_IOV(iov))
390             input_length += iov->data.length;
391     }
392     /* Must be at least a block or we fail. */
393     if (input_length < blocksize) {
394         ret = EINVAL;
395         goto done;
396     }
397
398     bulk_length = (input_length / blocksize)*blocksize;
399     remainder = input_length - bulk_length;
400     /* Do the block swap even if the input data is aligned, only
401      * drop it if we are encrypting exactly one block. */
402     if (remainder == 0 && bulk_length != blocksize) {
403         remainder = blocksize;
404         bulk_length -= blocksize;
405     }
406
407     ctx = k5_nss_create_context(krb_key, mech, operation, param);
408     if (ctx == NULL) {
409         ret = k5_nss_map_last_error();
410         goto done;
411     }
412
413     /* Now we bulk encrypt each block in the loop.  We need to know where to
414      * stop to do special processing.  For single block operations we stop at
415      * the end.  For all others we stop and the last second to last block
416      * (counting partial blocks).  For decrypt operations we need to save cn-2
417      * so we stop at the third to last block if it exists, otherwise cn-2 = the
418      * iv. */
419     lastblock = bulk_length;
420     if (remainder) {
421         /* We need to process the last full block and last partitial block
422          * differently. */
423         lastblock = bulk_length - blocksize;
424         if (operation == CKA_DECRYPT) {
425             if (bulk_length > blocksize) {
426                 /* Stop at cn-2 so we can save it before going on. */
427                 lastblock = bulk_length - 2*blocksize;
428             } else {
429                 /* iv is cn-2, save it now, cn - 2. */
430                 memcpy(recover1, iv.data, blocksize);
431                 memcpy(recover2, iv.data, blocksize);
432             }
433         }
434     }
435     for (length = 0; length < lastblock; length += blocksize) {
436         if (!krb5int_c_iov_get_block_nocopy(storage, blocksize, data, num_data,
437                                             &input_pos, &ptr))
438             break;
439
440         rv = PK11_CipherOp(ctx, ptr, &len, blocksize, ptr, blocksize);
441         if (rv != SECSuccess) {
442             ret = k5_nss_map_last_error();
443             break;
444         }
445
446         krb5int_c_iov_put_block_nocopy(data, num_data, storage, blocksize,
447                                        &output_pos, ptr);
448     }
449     if (remainder) {
450         if (operation == CKA_DECRYPT) {
451             if (bulk_length > blocksize) {
452                 /* we need to save cn-2 */
453                 if (!krb5int_c_iov_get_block_nocopy(storage, blocksize, data,
454                                                     num_data, &input_pos,
455                                                     &ptr))
456                     goto done; /* shouldn't happen */
457
458                 /* save cn-2 */
459                 memcpy(recover1, ptr, blocksize);
460                 memcpy(recover2, ptr, blocksize);
461
462                 /* now process it as normal */
463                 rv = PK11_CipherOp(ctx, ptr, &len, blocksize, ptr, blocksize);
464                 if (rv != SECSuccess) {
465                     ret = k5_nss_map_last_error();
466                     goto done;
467                 }
468
469                 krb5int_c_iov_put_block_nocopy(data, num_data, storage,
470                                                blocksize, &output_pos, ptr);
471             }
472         }
473         /* fetch the last 2 blocks */
474         memset(block1, 0, blocksize); /* last block, could be partial */
475         krb5int_c_iov_get_block(block2, blocksize, data, num_data, &input_pos);
476         krb5int_c_iov_get_block(block1, remainder, data, num_data, &input_pos);
477         if (operation == CKA_DECRYPT) {
478             /* recover1 and recover2 are xor values to recover the true
479              * underlying data of the last 2 decrypts. This keeps us from
480              * having to try to reset our IV to do the final decryption. */
481             /* Currently: block1 is cn || 0, block2 is cn-1.
482              * recover1 & recover2 is set to cn-2. */
483             /* recover2 recovers pn || c' from p'n-1. The raw decrypted block
484              * will be p'n-1 xor with cn-2 while pn || c' = p'n-1 xor cn || 0.
485              * recover2 is cn-2 xor cn || 0, so we can simple xor recover1
486              * with the raw decrypted block. */
487             /* recover1 recovers pn-1 from the raw decryption of cn || c'.
488              * the raw decrypt of cn || c' = p'n xor cn-1 while
489              * pn-1 = p'n xor cn-2
490              * recover1 is cn-2 xor cn-1, so we can simple xor recover 2 with
491              * the raw decrypt of cn||c' to get pn-1. */
492             xor(recover1, block2, blocksize);
493             xor(recover2, block1, blocksize);
494             if (ivec && ivec->data)
495                 memcpy(ivec->data, block2, blocksize);
496         }
497         rv = PK11_CipherOp(ctx, block2, &len, blocksize, block2, blocksize);
498         if (rv != SECSuccess) {
499             ret = k5_nss_map_last_error();
500             goto done;
501         }
502         if (operation == CKA_DECRYPT) {
503             /* block2 now has p'n-1 xor cn-2. */
504             xor(block2, recover2, blocksize);
505             /* block 2 now has pn || c'. */
506             /* copy c' into cn || c'. */
507             memcpy(block1 + remainder, block2 + remainder,
508                    blocksize - remainder);
509         }
510         rv = PK11_CipherOp(ctx, block1, &len, blocksize, block1, blocksize);
511         if (rv != SECSuccess) {
512             ret = k5_nss_map_last_error();
513             goto done;
514         }
515         if (operation == CKA_DECRYPT) {
516             /* block1 now has p'n xor cn-1 */
517             xor(block1, recover1, blocksize);
518             /* block 1 now has pn-1 */
519         } else {
520             if (ivec && ivec->data) {
521                 memcpy(ivec->data, block1, blocksize);
522             }
523         }
524         krb5int_c_iov_put_block(data,num_data, block1, blocksize, &output_pos);
525         krb5int_c_iov_put_block(data,num_data, block2, remainder, &output_pos);
526     }
527
528 done:
529     if (ctx) {
530         PK11_Finalize(ctx);
531         PK11_DestroyContext(ctx, PR_TRUE);
532     }
533     if (param)
534         SECITEM_FreeItem(param, PR_TRUE);
535     return ret;
536 }
537
538 krb5_error_code
539 k5_nss_gen_cbcmac_iov(krb5_key krb_key, CK_MECHANISM_TYPE mech,
540                       const krb5_data *ivec, const krb5_crypto_iov *data,
541                       size_t num_data, krb5_data *output)
542 {
543     krb5_error_code ret = 0;
544     PK11Context *ctx = NULL;
545     SECStatus rv;
546     SECItem *param = NULL;
547     struct iov_block_state input_pos, output_pos;
548     unsigned char block[MAX_BLOCK_SIZE], *lastblock;
549     unsigned char iv0[MAX_BLOCK_SIZE];
550     SECItem iv;
551     size_t blocksize;
552     int length = 0;
553     int currentblock;
554
555     IOV_BLOCK_STATE_INIT(&input_pos);
556     IOV_BLOCK_STATE_INIT(&output_pos);
557
558     blocksize = PK11_GetBlockSize(mech, NULL);
559     assert(blocksize <= sizeof(block));
560     if (output->length < blocksize)
561         return KRB5_BAD_MSIZE;
562
563     if (ivec && ivec->data) {
564         iv.data = (unsigned char *)ivec->data;
565         iv.len = ivec->length;
566     } else {
567         memset(iv0, 0, sizeof(iv0));
568         iv.data = iv0;
569         iv.len = blocksize;
570     }
571     param = PK11_ParamFromIV(mech, &iv);
572
573     ctx = k5_nss_create_context(krb_key, mech, CKA_ENCRYPT, param);
574     if (ctx == NULL) {
575         ret = k5_nss_map_last_error();
576         goto done;
577     }
578
579     lastblock = iv.data;
580     for (currentblock = 0;;currentblock++) {
581         if (!krb5int_c_iov_get_block(block, blocksize, data, num_data,
582                                      &input_pos))
583             break;
584         rv = PK11_CipherOp(ctx, block, &length, blocksize, block, blocksize);
585         if (rv != SECSuccess) {
586             ret = k5_nss_map_last_error();
587             goto done;
588         }
589         lastblock = block;
590     }
591     memcpy(output->data, lastblock, blocksize);
592
593 done:
594     if (ctx) {
595         PK11_Finalize(ctx);
596         PK11_DestroyContext(ctx, PR_TRUE);
597     }
598     if (param)
599         SECITEM_FreeItem(param, PR_TRUE);
600     return ret;
601 }
602
603 void
604 k5_nss_gen_cleanup(krb5_key krb_key)
605 {
606     struct cached_key *ckey = krb_key->cache;
607
608     if (ckey) {
609         PK11_FreeSymKey(ckey->symkey);
610         free(ckey);
611         krb_key->cache = NULL;
612     }
613 }
614
615 krb5_error_code
616 k5_nss_gen_import(krb5_key krb_key, CK_MECHANISM_TYPE mech,
617                   CK_ATTRIBUTE_TYPE operation)
618 {
619     krb5_error_code ret = 0;
620     pid_t pid = getpid();
621     struct cached_key *ckey = krb_key->cache;
622     PK11SymKey *symkey;
623     PK11SlotInfo *slot = NULL;
624     SECItem raw_key;
625 #ifdef FAKE_FIPS
626     PK11SymKey *wrapping_key = NULL;
627     PK11Context *ctx = NULL;
628     SECItem wrapped_key;
629     SECItem params;
630     unsigned char wrapped_key_data[MAX_KEY_LENGTH];
631     unsigned char padded_key_data[MAX_KEY_LENGTH];
632     int wrapping_index, series, blocksize;
633     int keyLength;
634     CK_MECHANISM_TYPE mechanism;
635     SECStatus rv;
636 #endif
637
638     if (ckey && ckey->pid == pid)
639         return 0;
640
641     ret = k5_nss_init();
642     if (ret)
643         return ret;
644
645     if (ckey) {
646         /* Discard the no-longer-valid symkey and steal its container. */
647         PK11_FreeSymKey(ckey->symkey);
648         ckey->symkey = NULL;
649         krb_key->cache = NULL;
650     } else {
651         /* Allocate a new container. */
652         ckey = k5alloc(sizeof(*ckey), &ret);
653         if (ckey == NULL)
654             return ret;
655     }
656
657     slot = PK11_GetBestSlot(mech, NULL);
658     if (slot == NULL) {
659         ret = k5_nss_map_last_error();
660         goto done;
661     }
662     raw_key.data = krb_key->keyblock.contents;
663     raw_key.len = krb_key->keyblock.length;
664
665 #ifdef FAKE_FIPS
666     /* First, fetch a wrapping key. */
667     wrapping_index = PK11_GetCurrentWrapIndex(slot);
668     series = PK11_GetSlotSeries(slot);
669     wrapping_key = PK11_GetWrapKey(slot, wrapping_index,
670                                    CKM_INVALID_MECHANISM, series, NULL);
671     if (wrapping_key == NULL) {
672         /* One doesn't exist, create one. */
673         mechanism = PK11_GetBestWrapMechanism(slot);
674         keyLength = PK11_GetBestKeyLength(slot, mechanism);
675         wrapping_key = PK11_TokenKeyGenWithFlags(slot, mechanism, NULL,
676                                                  keyLength, NULL,
677                                                  CKF_UNWRAP | CKF_ENCRYPT, 0,
678                                                  NULL);
679         if (!wrapping_key) {
680             ret = k5_nss_map_last_error();
681             goto done;
682         }
683         PK11_SetWrapKey(slot, wrapping_index, wrapping_key);
684     }
685
686     /* Now encrypt the data with the wrapping key. */
687     mechanism = PK11_GetMechanism(wrapping_key);
688     params.data = NULL;
689     params.len = 0;
690     ctx = PK11_CreateContextBySymKey(mechanism, CKA_ENCRYPT,
691                                      wrapping_key, &params);
692     if (ctx == NULL) {
693         ret = k5_nss_map_last_error();
694         goto done;
695     }
696
697     wrapped_key.data = wrapped_key_data;
698     wrapped_key.len = sizeof(wrapped_key_data);
699     blocksize = PK11_GetBlockSize(mechanism, NULL);
700     keyLength = raw_key.len;
701
702     /*
703      * ECB modes need keys in integral multiples of the block size.
704      * if the key isn't and integral multiple, pad it with zero. Unwrap
705      * will use the length parameter to appropriately set the key.
706      */
707     if ((raw_key.len % blocksize) != 0) {
708         int keyblocks = (raw_key.len + (blocksize - 1)) / blocksize;
709         keyLength = keyblocks * blocksize;
710         assert(keyLength <= sizeof(padded_key_data));
711         memset(padded_key_data, 0, keyLength);
712         memcpy(padded_key_data, raw_key.data, raw_key.len);
713         raw_key.data = padded_key_data;
714     }
715     rv = PK11_CipherOp(ctx, wrapped_key.data, (int *)&wrapped_key.len,
716                        sizeof(wrapped_key_data), raw_key.data, keyLength);
717     if (keyLength != raw_key.len) {
718         /* Clear our copy of the key bits. */
719         memset(padded_key_data, 0, keyLength);
720     }
721     if (rv != SECSuccess) {
722         ret = k5_nss_map_last_error();
723         goto done;
724     }
725     PK11_Finalize(ctx);
726     PK11_DestroyContext(ctx, PR_TRUE);
727     ctx = NULL;
728
729     /* Now now we have a 'wrapped' version of the, we can import it into
730      * the token without running afoul with FIPS. */
731     symkey = PK11_UnwrapSymKey(wrapping_key, mechanism, &params, &wrapped_key,
732                                mech, operation, raw_key.len);
733 #else
734     symkey = PK11_ImportSymKey(slot, mech, PK11_OriginGenerated, operation,
735                                &raw_key, NULL);
736 #endif
737     if (symkey == NULL) {
738         ret = k5_nss_map_last_error();
739         goto done;
740     }
741     ckey->pid = pid;
742     ckey->symkey = symkey;
743     krb_key->cache = ckey;
744     ckey = NULL;
745
746 done:
747     free(ckey);
748     if (slot)
749         PK11_FreeSlot(slot);
750 #ifdef FAKE_FIPS
751     if (ctx) {
752         PK11_Finalize(ctx);
753         PK11_DestroyContext(ctx, PR_TRUE);
754     }
755     if (wrapping_key)
756         PK11_FreeSymKey(wrapping_key);
757 #endif
758
759     return ret;
760 }