1 /*
2 * Copyright 2010 Sun Microsystems, Inc. All rights reserved.
3 * Use is subject to license terms.
4 */
5
6 /*
7 * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
8 * Note, this file is cstyle and lint clean and should stay that way.
9 * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
10 */
11
12 #include <k5-int.h>
13 #include <enc_provider.h>
14
15 #define BLOCK_SIZE 16
16
17 /*
18 * AES encrypt using CipherText Stealing mode built on top of CBC mode. CBC is
19 * being used because the Solaris Cryptographic Framework/PKCS11 does not
20 * currently support CTS while CBC is supported. CBC as compared to ECB that
21 * was previously used allows crypto providers to do the crypto more
22 * efficiently. In addition there is a crypto card (SCA6000) that did not
23 * provide ECB mode so krb was unable to take advantage. If CTS mode is ever
24 * supported by the Solaris Cryptographic Framework then this code should be
25 * changed to use that.
26 *
27 * CTS is based on what is described in Schneier's Applied Cryptography and RFC
28 * 3962.
29 */
30
31 #ifdef _KERNEL
32 /*ARGSUSED*/
33 krb5_error_code
krb5int_aes_encrypt(krb5_context context,const krb5_keyblock * key,const krb5_data * ivec,const krb5_data * input,krb5_data * output)34 krb5int_aes_encrypt(krb5_context context,
35 const krb5_keyblock *key, const krb5_data *ivec,
36 const krb5_data *input, krb5_data *output)
37 {
38 int ret = 0;
39 int nblocks, partialamount;
40 crypto_mechanism_t mech;
41 /*
42 * nlobp = next to last output block pointer, lobp = last output block
43 * pointer
44 */
45 char *nlobp, *lobp;
46 char local_iv_data[BLOCK_SIZE];
47 krb5_data local_iv;
48
49 KRB5_LOG0(KRB5_INFO, "In krb5int_aes_encrypt(kernel): start");
50
51 ASSERT(input != NULL);
52 if (input->length < BLOCK_SIZE)
53 return (KRB5_BAD_MSIZE);
54 ASSERT(output != NULL);
55 ASSERT(input->length == output->length);
56 ASSERT(key != NULL);
57 ASSERT(key->key_tmpl != NULL);
58 ASSERT(key->kef_mt == crypto_mech2id(SUN_CKM_AES_CBC));
59
60 if (ivec != NULL) {
61 /*
62 * This function updates ivec->data if the ivec is passed in so
63 * it better have a data pointer and a proper length.
64 */
65 if (ivec->data == NULL || ivec->length != BLOCK_SIZE) {
66 ASSERT(ivec->data != NULL);
67 ASSERT(ivec->length == BLOCK_SIZE);
68 KRB5_LOG1(KRB5_ERR, "In krb5int_aes_encrypt: error "
69 "ivec->data = %p ivec->length = %d",
70 (void *)ivec->data, ivec->length);
71 ret = KRB5_CRYPTO_INTERNAL;
72 goto cleanup;
73 }
74 }
75
76 /* number of input blocks including partial block */
77 nblocks = (input->length + BLOCK_SIZE - 1) / BLOCK_SIZE;
78 KRB5_LOG(KRB5_INFO, "nblocks = %d", nblocks);
79 /* get # of bytes in partially filled block */
80 partialamount = input->length % BLOCK_SIZE;
81 KRB5_LOG(KRB5_INFO, "partialamount = %d", partialamount);
82
83 if (nblocks == 1 || (partialamount == 0)) {
84 /*
85 * Simple case:
86 *
87 * Use CBC for all plaintext blocks, all must be full, then swap
88 * last 2 ciphertext blocks to implement CTS. Note, CBC needs a
89 * non-NULL IV.
90 */
91 if (ivec != NULL) {
92 local_iv.data = ivec->data;
93 local_iv.length = ivec->length;
94 } else {
95 bzero(local_iv_data, sizeof (local_iv_data));
96 local_iv.data = local_iv_data;
97 local_iv.length = sizeof (local_iv_data);
98 }
99
100 /* Note using TRUE here because encryption is desired */
101 ret = k5_ef_crypto((const char *)input->data,
102 (char *)output->data,
103 input->length, (krb5_keyblock *)key,
104 &local_iv, TRUE);
105
106 if (ret != 0) {
107 KRB5_LOG(KRB5_ERR,
108 "k5_ef_crypto: error: ret = 0x%08x", ret);
109 goto cleanup;
110 }
111
112 if (nblocks > 1) {
113 /*
114 * swap last 2 ciphertext blocks to implement CTS
115 */
116 char tmp[BLOCK_SIZE];
117
118 nlobp = (char *)(output->data +
119 ((nblocks - 2) * BLOCK_SIZE));
120 lobp = (char *)(output->data +
121 ((nblocks - 1) * BLOCK_SIZE));
122
123 bcopy(nlobp, tmp, BLOCK_SIZE);
124 bcopy(lobp, nlobp, BLOCK_SIZE);
125 bcopy(tmp, lobp, BLOCK_SIZE);
126 }
127 } else {
128 /*
129 * Complex case:
130 *
131 * This implements CTS mode where there is > 1 block and the
132 * last block is partially filled Uses CBC mode in the kCF, then
133 * does some swapping.
134 *
135 * pt = plain text, ct = cipher text
136 */
137 char tmp_pt[BLOCK_SIZE], tmp_ct[BLOCK_SIZE];
138 /* Note the iovec below is NOT the ivec in the crypto sense */
139 struct iovec iovarray_pt[2], iovarray_ct[2];
140 struct uio uio_pt, uio_ct;
141 /* ct = ciphertext, pt = plaintext */
142 crypto_data_t ct, pt;
143
144 /* tmp_pt will provide 0 padding for last parital pt block */
145 bzero(tmp_pt, sizeof (tmp_pt));
146
147 /*
148 * Setup the uio/iovecs so only one call to crypto_encrypt() is
149 * made. Plaintext first.
150 */
151 pt.cd_format = CRYPTO_DATA_UIO;
152 pt.cd_offset = 0;
153 pt.cd_length = nblocks * BLOCK_SIZE;
154 pt.cd_miscdata = NULL;
155 bzero(&uio_pt, sizeof (uio_pt));
156 pt.cd_uio = &uio_pt;
157 pt.cd_uio->uio_iov = iovarray_pt;
158 pt.cd_uio->uio_iovcnt = 2;
159 pt.cd_uio->uio_segflg = UIO_SYSSPACE;
160
161 /*
162 * first iovec has all full blocks of pt.
163 */
164 pt.cd_uio->uio_iov[0].iov_base = (char *)input->data;
165 /* use full block input */
166 pt.cd_uio->uio_iov[0].iov_len = input->length - partialamount;
167
168 KRB5_LOG(KRB5_INFO, "pt0 iov_len = %d",
169 (int)pt.cd_uio->uio_iov[0].iov_len);
170
171 /*
172 * second iovec has the parital pt and 0 padding
173 */
174 pt.cd_uio->uio_iov[1].iov_base = tmp_pt;
175 /*
176 * since the first iovec includes the last partial pt,
177 * set length to enough bytes to pad out to a full block
178 */
179 bcopy(input->data + (input->length - partialamount), tmp_pt,
180 partialamount);
181 pt.cd_uio->uio_iov[1].iov_len = BLOCK_SIZE;
182
183 /* setup ciphertext iovecs */
184 ct.cd_format = CRYPTO_DATA_UIO;
185 ct.cd_offset = 0;
186 ct.cd_length = nblocks * BLOCK_SIZE;
187 ct.cd_miscdata = NULL;
188 bzero(&uio_ct, sizeof (uio_ct));
189 ct.cd_uio = &uio_ct;
190 ct.cd_uio->uio_iov = iovarray_ct;
191 ct.cd_uio->uio_iovcnt = 2;
192 ct.cd_uio->uio_segflg = UIO_SYSSPACE;
193
194 /*
195 * First iovec has almost all the ct but not the ct for the last
196 * partial pt with the padding. That will be stored in the
197 * secont ct iovec.
198 */
199 ct.cd_uio->uio_iov[0].iov_base = (char *)output->data;
200 ct.cd_uio->uio_iov[0].iov_len = output->length - partialamount;
201 KRB5_LOG(KRB5_INFO, "ct0 iov_len = %d",
202 (int)ct.cd_uio->uio_iov[0].iov_len);
203 /*
204 * Second iovec has the last ciphertext block
205 */
206 ct.cd_uio->uio_iov[1].iov_base = tmp_ct;
207 ct.cd_uio->uio_iov[1].iov_len = BLOCK_SIZE;
208
209 /* This had better be AES CBC mode! */
210 mech.cm_type = key->kef_mt;
211
212 if (ivec == NULL) {
213 bzero(local_iv_data, sizeof (local_iv_data));
214 mech.cm_param = local_iv_data;
215 mech.cm_param_len = sizeof (local_iv_data);
216 } else {
217 mech.cm_param = ivec->data;
218 mech.cm_param_len = ivec->length;
219 }
220
221 /* encrypt using AES CBC */
222 ret = crypto_encrypt(&mech, &pt, (crypto_key_t *)&key->kef_key,
223 key->key_tmpl, &ct, NULL);
224
225 if (ret != CRYPTO_SUCCESS) {
226 KRB5_LOG(KRB5_ERR,
227 "crypto_encrypt: error: ret = 0x%08x",
228 ret);
229 goto cleanup;
230 }
231
232 /*
233 * Swap:
234 * copy the next to last ct to last partial output block (only
235 * the partial amount is copied).
236 */
237 nlobp = (char *)(output->data + ((nblocks - 2) * BLOCK_SIZE));
238 lobp = (char *)(output->data + ((nblocks - 1) * BLOCK_SIZE));
239
240 bcopy(nlobp, lobp, partialamount);
241 /*
242 * copy the last ct output block to next to last output block
243 */
244 bcopy(tmp_ct, nlobp, BLOCK_SIZE);
245
246 } /* end partial block processing */
247
248 /*
249 * The ivec is updated to allow the caller to chain ivecs. At this
250 * point I don't think any kernel callers are using this however the
251 * userland version of this function does it so this should be done in
252 * kernel for consistency's sake. This is not done for 1 block, got
253 * this from MIT. Note, the next to last output block is copied because
254 * it contains the last full block of cipher text.
255 */
256 if (nblocks > 1 && ivec)
257 (void) memcpy(ivec->data, nlobp, BLOCK_SIZE);
258
259 cleanup:
260 if (ret)
261 bzero(output->data, output->length);
262 return (ret);
263 }
264
265 #else /* User Space */
266
267 /*ARGSUSED*/
268 krb5_error_code
krb5int_aes_encrypt(krb5_context context,const krb5_keyblock * key,const krb5_data * ivec,const krb5_data * input,krb5_data * output)269 krb5int_aes_encrypt(krb5_context context,
270 const krb5_keyblock *key, const krb5_data *ivec,
271 const krb5_data *input, krb5_data *output)
272 {
273 krb5_error_code ret = 0;
274 int nblocks, partialamount;
275 CK_RV rv;
276 KRB5_MECH_TO_PKCS algos;
277 CK_MECHANISM mechanism;
278 CK_ULONG outlen;
279 /*
280 * nlobp = next to last output block pointer, lobp = last output block
281 * pointer
282 */
283 char *nlobp, *lobp;
284 char tmp_ivec[BLOCK_SIZE];
285
286 assert(input != NULL);
287 if (input->length < BLOCK_SIZE)
288 return (KRB5_BAD_MSIZE);
289 assert(output != NULL);
290 assert(input->length == output->length);
291 assert(key != NULL);
292
293 if (ivec != NULL) {
294 /*
295 * This function updates ivec->data if the ivec is passed in so
296 * it better have a data pointer and a proper length.
297 */
298 if (ivec->data == NULL || ivec->length != BLOCK_SIZE) {
299 assert(ivec->data != NULL);
300 assert(ivec->length == BLOCK_SIZE);
301 KRB5_LOG1(KRB5_ERR, "In krb5int_aes_encrypt: error "
302 "ivec->data = %p ivec->length = %d", ivec->data,
303 ivec->length);
304 ret = KRB5_CRYPTO_INTERNAL;
305 goto cleanup;
306 }
307 }
308
309 /* number of input blocks including partial block */
310 nblocks = (input->length + BLOCK_SIZE - 1) / BLOCK_SIZE;
311 KRB5_LOG(KRB5_INFO, "nblocks = %d", nblocks);
312 /* get # of bytes in partially filled block */
313 partialamount = input->length % BLOCK_SIZE;
314 KRB5_LOG(KRB5_INFO, "partialamount = %d", partialamount);
315
316 rv = get_algo(key->enctype, &algos);
317 if (rv != CKR_OK)
318 goto cleanup;
319 assert(algos.enc_algo == CKM_AES_CBC);
320
321 rv = init_key_uef(krb_ctx_hSession(context), (krb5_keyblock *)key);
322 if (rv != CKR_OK)
323 goto cleanup;
324
325 mechanism.mechanism = algos.enc_algo;
326
327 if (ivec == NULL) {
328 bzero(tmp_ivec, sizeof (tmp_ivec));
329 mechanism.pParameter = tmp_ivec;
330 mechanism.ulParameterLen = sizeof (tmp_ivec);
331 } else {
332 mechanism.pParameter = ivec->data;
333 mechanism.ulParameterLen = ivec->length;
334 }
335 /*
336 * Note, since CBC is assumed to be the underlying mode, this
337 * call to C_EncryptInit is setting the IV. The IV in use here
338 * is either the ivec passed in or a block of 0's.
339 */
340 rv = C_EncryptInit(krb_ctx_hSession(context), &mechanism, key->hKey);
341
342 if (rv != CKR_OK) {
343 KRB5_LOG(KRB5_ERR, "C_EncryptInit failed in "
344 "krb5int_aes_encrypt: rv = 0x%x", rv);
345 goto cleanup;
346 }
347
348 if (nblocks == 1 || (partialamount == 0)) {
349 /*
350 * Simple case:
351 *
352 * Use CBC for all plaintext blocks, all must be full, then swap
353 * last 2 ciphertext blocks to implement CTS.
354 */
355
356 /*
357 * C_Encrypt/Decrypt requires a pointer to long, not a pointer
358 * to int cast to pointer to long!!!
359 */
360 outlen = output->length;
361
362 rv = C_Encrypt(krb_ctx_hSession(context),
363 (CK_BYTE_PTR)input->data,
364 input->length,
365 (CK_BYTE_PTR)output->data,
366 &outlen);
367
368 if (rv != CKR_OK) {
369 KRB5_LOG(KRB5_ERR, "C_Encrypt failed in "
370 "krb5int_aes_encrypt: rv = 0x%x", rv);
371 goto cleanup;
372 }
373
374 assert(output->length == (unsigned int)outlen);
375
376 if (nblocks > 1) {
377 /*
378 * swap last 2 ciphertext blocks to implement CTS
379 */
380 char tmp[BLOCK_SIZE];
381
382 nlobp = (char *)(output->data +
383 ((nblocks - 2) * BLOCK_SIZE));
384 lobp = (char *)(output->data +
385 ((nblocks - 1) * BLOCK_SIZE));
386
387 bcopy(nlobp, tmp, BLOCK_SIZE);
388 bcopy(lobp, nlobp, BLOCK_SIZE);
389 bcopy(tmp, lobp, BLOCK_SIZE);
390 }
391 } else {
392 /*
393 * Complex case:
394 *
395 * This implements CTS mode where there is > 1 block and the
396 * last block is partially filled. Uses CBC mode in uCF/PKCS11,
397 * then does some swapping.
398 *
399 * pt = plain text, ct = cipher text
400 */
401 char tmp_pt[BLOCK_SIZE], tmp_ct[BLOCK_SIZE];
402
403 /*
404 * encrypt from P0...Pn-1 using CBC, last block of output is Cn
405 * & C'
406 */
407 outlen = input->length - partialamount;
408
409 rv = C_EncryptUpdate(krb_ctx_hSession(context),
410 (CK_BYTE_PTR)input->data,
411 input->length - partialamount,
412 (CK_BYTE_PTR)output->data,
413 &outlen);
414
415 if (rv != CKR_OK) {
416 KRB5_LOG(KRB5_ERR, "C_EncryptUpdate failed in "
417 "krb5int_aes_encrypt: rv = 0x%x", rv);
418 goto cleanup;
419 }
420
421 /* tmp_pt will provide 0 padding for last parital pt block */
422 bzero(tmp_pt, sizeof (tmp_pt));
423 /* copy Pn to tmp_pt which has 0 padding */
424 bcopy(input->data + (input->length - partialamount), tmp_pt,
425 partialamount);
426
427 /* encrypt Pn with 0 padding, Cn & C' ivec, output is Cn-1 */
428 outlen = sizeof (tmp_ct);
429
430 rv = C_EncryptUpdate(krb_ctx_hSession(context),
431 (CK_BYTE_PTR)tmp_pt,
432 BLOCK_SIZE,
433 (CK_BYTE_PTR)tmp_ct,
434 &outlen);
435
436 if (rv != CKR_OK) {
437 KRB5_LOG(KRB5_ERR, "C_Encrypt failed in "
438 "krb5int_aes_encrypt: rv = 0x%x", rv);
439 goto cleanup;
440 }
441
442 nlobp = (char *)(output->data + ((nblocks - 2) * BLOCK_SIZE));
443 lobp = (char *)(output->data + ((nblocks - 1) * BLOCK_SIZE));
444
445 /* copy Cn from next to last output block to last block */
446 bcopy(nlobp, lobp, partialamount);
447 /* copy Cn-1 from tmp_ct to next to last output block */
448 bcopy(tmp_ct, nlobp, BLOCK_SIZE);
449
450 /* Close the crypto session, ignore the output */
451 rv = C_EncryptFinal(krb_ctx_hSession(context),
452 (CK_BYTE_PTR)tmp_ct, &outlen);
453
454 if (rv != CKR_OK)
455 goto cleanup;
456 }
457 /*
458 * The ivec is updated to allow the caller to chain ivecs, done for the
459 * kcmd (rsh/rcp/etc...). Note this is not done for 1 block although I
460 * am not sure why but I'm continuing the tradition from the MIT code.
461 * Note, the next to last output block is copied because it contains the
462 * last full block of cipher text.
463 */
464 if (nblocks > 1 && ivec)
465 (void) memcpy(ivec->data, nlobp, BLOCK_SIZE);
466
467 cleanup:
468 if (rv != CKR_OK)
469 ret = PKCS_ERR;
470
471 if (ret)
472 bzero(output->data, input->length);
473
474 return (ret);
475 }
476 #endif /* _KERNEL */
477
478 /*
479 * AES Decrypt using CipherText Stealing mode built on top of CBC mode. See the
480 * krb5int_aes_encrypt() comments for the reason CBC is being used.
481 */
482
483 #ifdef _KERNEL
484 /*ARGSUSED*/
485 krb5_error_code
krb5int_aes_decrypt(krb5_context context,const krb5_keyblock * key,const krb5_data * ivec,const krb5_data * input,krb5_data * output)486 krb5int_aes_decrypt(krb5_context context,
487 const krb5_keyblock *key, const krb5_data *ivec,
488 const krb5_data *input, krb5_data *output)
489 {
490 krb5_error_code ret = 0;
491 int nblocks, partialamount;
492 char local_iv_data[BLOCK_SIZE];
493 krb5_data local_iv;
494
495 KRB5_LOG0(KRB5_INFO, "In krb5int_aes_decrypt: start");
496
497 ASSERT(input != NULL);
498 if (input->length < BLOCK_SIZE)
499 return (KRB5_BAD_MSIZE);
500 ASSERT(output != NULL);
501 ASSERT(input->length == output->length);
502 ASSERT(key != NULL);
503 ASSERT(key->kef_mt == crypto_mech2id(SUN_CKM_AES_CBC));
504
505 if (ivec != NULL) {
506 /*
507 * This function updates ivec->data if the ivec is passed in so
508 * it better have a data pointer and a proper length.
509 */
510 if (ivec->data == NULL || ivec->length != BLOCK_SIZE) {
511 ASSERT(ivec->data != NULL);
512 ASSERT(ivec->length == BLOCK_SIZE);
513 KRB5_LOG1(KRB5_ERR, "In krb5int_aes_decrypt: error "
514 "ivec->data = %p ivec->length = %d",
515 (void *)ivec->data, ivec->length);
516 ret = KRB5_CRYPTO_INTERNAL;
517 goto cleanup;
518 }
519 }
520
521 /* number of input blocks including partial block */
522 nblocks = (input->length + BLOCK_SIZE - 1) / BLOCK_SIZE;
523 KRB5_LOG(KRB5_INFO, "nblocks = %d", nblocks);
524 /* get # of bytes in partially filled block */
525 partialamount = input->length % BLOCK_SIZE;
526 KRB5_LOG(KRB5_INFO, "partialamount = %d", partialamount);
527
528 if (ivec != NULL) {
529 local_iv.data = ivec->data;
530 local_iv.length = ivec->length;
531 } else {
532 bzero(local_iv_data, sizeof (local_iv_data));
533 local_iv.data = local_iv_data;
534 local_iv.length = sizeof (local_iv_data);
535 }
536
537 if (nblocks == 1 || (partialamount == 0)) {
538 char orig_input[BLOCK_SIZE * 2];
539 /*
540 * nlibp = next to last input block pointer
541 * libp = last input block pointer
542 */
543 char *nlibp, *libp;
544
545 /*
546 * Simple case:
547 *
548 * Swap last 2 ciphertext blocks (all must be full), then use
549 * CBC to implement CTS.
550 */
551
552 if (nblocks > 1) {
553 /*
554 * swap last 2 ciphertext blocks to implement CTS
555 */
556 char tmp[BLOCK_SIZE];
557
558 nlibp = input->data + ((nblocks - 2) * BLOCK_SIZE);
559 libp = input->data + ((nblocks - 1) * BLOCK_SIZE);
560
561 /* first save orig input data for later restore */
562 /* we know that partial amount is 0, because */
563 /* nblocks is > 1, so we copy the last two blocks */
564 bcopy(nlibp, orig_input, sizeof (orig_input));
565
566 /* swap */
567 bcopy(nlibp, tmp, BLOCK_SIZE);
568 bcopy(libp, nlibp, BLOCK_SIZE);
569 bcopy(tmp, libp, BLOCK_SIZE);
570 }
571
572 ret = k5_ef_crypto((const char *)input->data,
573 (char *)output->data,
574 input->length, (krb5_keyblock *)key,
575 &local_iv, FALSE);
576
577 if (nblocks > 1) {
578 /* restore orig input data */
579 bcopy(orig_input, nlibp, sizeof (orig_input));
580 }
581
582 if (ret != 0) {
583 KRB5_LOG(KRB5_ERR,
584 "k5_ef_crypto returned error: ret = 0x%08x",
585 ret);
586 goto cleanup;
587 }
588
589 } else {
590 krb5_data tmp_ivec;
591 char tmp_ivec_data[BLOCK_SIZE], tmp_input_data[BLOCK_SIZE],
592 tmp_output_data[BLOCK_SIZE];
593 /* pointers to Cn, Cn-1, Cn-2 CipherText */
594 char *Cn, *Cn_1, *Cn_2;
595 long length;
596
597 /*
598 * Complex case:
599 *
600 * Decrypting in CTS where there is a partial block of
601 * ciphertext.
602 */
603
604 /* setting pointers to CipherText for later use */
605 Cn = input->data + (input->length - partialamount);
606 /* Cn - 1 */
607 Cn_1 = Cn - BLOCK_SIZE;
608 /* Cn - 2 */
609 Cn_2 = Cn_1 - BLOCK_SIZE;
610
611 if (nblocks > 2) {
612 /* set length to include blocks C0 thru Cn-2 */
613 length = input->length - (BLOCK_SIZE + partialamount);
614
615 /*
616 * First decrypt C0 thru Cn-2 using CBC with the input
617 * ivec.
618 */
619 ret = k5_ef_crypto((const char *)input->data,
620 output->data, length, (krb5_keyblock *)key,
621 &local_iv, FALSE);
622
623 if (ret != 0) {
624 KRB5_LOG(KRB5_ERR,
625 "k5_ef_crypto: error: ret = 0x%08x",
626 ret);
627 goto cleanup;
628 }
629 }
630 /*
631 * Prepare to decrypt Cn-1 using a ivec of Cn with 0 padding.
632 */
633 bzero(tmp_ivec_data, sizeof (tmp_ivec_data));
634 /* the tmp ivec data holds Cn with 0 padding */
635 bcopy(Cn, tmp_ivec_data, partialamount);
636 tmp_ivec.data = tmp_ivec_data;
637 tmp_ivec.length = sizeof (tmp_ivec_data);
638
639 /* decrypt 1 block */
640 length = BLOCK_SIZE;
641
642 /*
643 * Now decrypt using Cn-1 input, Cn + 0 padding for ivec, Pn &
644 * C' output
645 */
646 ret = k5_ef_crypto((const char *)Cn_1,
647 tmp_output_data, length,
648 (krb5_keyblock *)key, &tmp_ivec, FALSE);
649
650 if (ret != 0) {
651 KRB5_LOG(KRB5_ERR,
652 "k5_ef_crypto: error: ret = 0x%08x",
653 ret);
654 goto cleanup;
655 }
656 /*
657 * tmp input data should hold Cn with C'
658 * Note, tmp_output_data contains Pn + C',
659 */
660 /* copy Cn */
661 bcopy(Cn, tmp_input_data, partialamount);
662 /* copy C' */
663 bcopy(tmp_output_data + partialamount,
664 tmp_input_data + partialamount,
665 (BLOCK_SIZE - partialamount));
666
667 /* copy Pn in tmp output to output->data */
668 bcopy(tmp_output_data,
669 output->data + (input->length - partialamount),
670 partialamount);
671
672 if (nblocks > 2) {
673 /* use Cn-2 as ivec */
674 tmp_ivec.data = Cn_2;
675 } else {
676 /* use 0 as ivec because Cn-2 does not exist */
677 bzero(tmp_ivec_data, sizeof (tmp_ivec_data));
678 }
679
680 /*
681 * Now decrypt Cn + C' input, using either Cn-2 or 0 for ivec
682 * (set above), Pn-1 output.
683 */
684 ret = k5_ef_crypto((const char *)tmp_input_data,
685 (char *)output->data +
686 (input->length - (BLOCK_SIZE + partialamount)),
687 length, (krb5_keyblock *)key,
688 &tmp_ivec, FALSE);
689
690 if (ret != 0) {
691 KRB5_LOG(KRB5_ERR,
692 "k5_ef_crypto: error: ret = 0x%08x", ret);
693 goto cleanup;
694 }
695
696 } /* end partial block processing */
697 /*
698 * The ivec is updated to allow the caller to chain ivecs. At this
699 * point I don't think any kernel callers are using this however the
700 * userland version of this function does it so this should be done in
701 * kernel for consistency's sake. This is not done for 1 block, got
702 * this from MIT.
703 */
704 if (nblocks > 1 && ivec) {
705 (void) memcpy(ivec->data,
706 input->data + ((nblocks - 2) * BLOCK_SIZE),
707 BLOCK_SIZE);
708 }
709
710 cleanup:
711 if (ret)
712 bzero(output->data, output->length);
713
714 return (ret);
715 }
716
717 #else /* User Space */
718
719 /*ARGSUSED*/
720 krb5_error_code
krb5int_aes_decrypt(krb5_context context,const krb5_keyblock * key,const krb5_data * ivec,const krb5_data * input,krb5_data * output)721 krb5int_aes_decrypt(krb5_context context,
722 const krb5_keyblock *key, const krb5_data *ivec,
723 const krb5_data *input, krb5_data *output)
724 {
725 krb5_error_code ret = 0;
726 int nblocks, partialamount;
727 CK_RV rv;
728 KRB5_MECH_TO_PKCS algos;
729 CK_MECHANISM mechanism;
730 CK_ULONG outlen;
731 char tmp_ivec[BLOCK_SIZE];
732
733 assert(input != NULL);
734 if (input->length < BLOCK_SIZE)
735 return (KRB5_BAD_MSIZE);
736 assert(output != NULL);
737 assert(input->length == output->length);
738 assert(key != NULL);
739
740 if (ivec != NULL) {
741 /*
742 * This function updates ivec->data if the ivec is passed in so
743 * it better have a data pointer and a proper length.
744 */
745 if (ivec->data == NULL || ivec->length != BLOCK_SIZE) {
746 assert(ivec->data != NULL);
747 assert(ivec->length == BLOCK_SIZE);
748 KRB5_LOG1(KRB5_ERR, "In krb5int_aes_decrypt: error "
749 "ivec->data = %p ivec->length = %d", ivec->data,
750 ivec->length);
751 ret = KRB5_CRYPTO_INTERNAL;
752 goto cleanup;
753 }
754 }
755
756 /* number of input blocks including partial block */
757 nblocks = (input->length + BLOCK_SIZE - 1) / BLOCK_SIZE;
758 KRB5_LOG(KRB5_INFO, "nblocks = %d", nblocks);
759 /* get # of bytes in partially filled block */
760 partialamount = input->length % BLOCK_SIZE;
761 KRB5_LOG(KRB5_INFO, "partialamount = %d", partialamount);
762
763 rv = get_algo(key->enctype, &algos);
764 if (rv != CKR_OK)
765 goto cleanup;
766 assert(algos.enc_algo == CKM_AES_CBC);
767
768 rv = init_key_uef(krb_ctx_hSession(context), (krb5_keyblock *)key);
769 if (rv != CKR_OK) {
770 goto cleanup;
771 }
772
773 mechanism.mechanism = algos.enc_algo;
774 if (ivec == NULL) {
775 bzero(tmp_ivec, sizeof (tmp_ivec));
776 mechanism.pParameter = tmp_ivec;
777 mechanism.ulParameterLen = sizeof (tmp_ivec);
778 } else {
779 mechanism.pParameter = ivec->data;
780 mechanism.ulParameterLen = ivec->length;
781 }
782
783 if (nblocks == 1 || (partialamount == 0)) {
784 char orig_input[BLOCK_SIZE * 2];
785 /*
786 * nlibp = next to last input block pointer
787 * libp = last input block pointer
788 */
789 char *nlibp, *libp;
790
791 /*
792 * Simple case:
793 *
794 * Swap last 2 ciphertext blocks (all must be full), then use
795 * CBC to implement CTS.
796 */
797 if (nblocks > 1) {
798 /*
799 * swap last 2 ciphertext blocks to implement CTS
800 */
801 char tmp[BLOCK_SIZE];
802
803 /*
804 * Note, the side effect with this is that we are
805 * modifying the input->data!
806 */
807 nlibp = input->data + ((nblocks - 2) * BLOCK_SIZE);
808 libp = input->data + ((nblocks - 1) * BLOCK_SIZE);
809
810 /* first save orig input data for later restore */
811 /* we know that partial amount is 0, because */
812 /* nblocks is > 1, so we copy the last two blocks */
813 bcopy(nlibp, orig_input, sizeof (orig_input));
814
815 bcopy(nlibp, tmp, BLOCK_SIZE);
816 bcopy(libp, nlibp, BLOCK_SIZE);
817 bcopy(tmp, libp, BLOCK_SIZE);
818 }
819
820 /*
821 * Note, since CBC is assumed to be the underlying mode, this
822 * call to C_DecryptInit is setting the IV. The IV in use here
823 * is either the ivec passed in or a block of 0's. All calls to
824 * C_DecryptInit set the IV in this function.
825 */
826 rv = C_DecryptInit(krb_ctx_hSession(context), &mechanism,
827 key->hKey);
828 if (rv != CKR_OK) {
829 KRB5_LOG(KRB5_ERR, "C_DecryptInit failed in "
830 "krb5int_aes_decrypt: rv = 0x%x", rv);
831 goto cleanup;
832 }
833
834 /*
835 * C_Encrypt/Decrypt requires a pointer to long, not a pointer
836 * to int cast to pointer to long!!!
837 */
838 outlen = output->length;
839
840 rv = C_Decrypt(krb_ctx_hSession(context),
841 (CK_BYTE_PTR)input->data,
842 input->length,
843 (CK_BYTE_PTR)output->data,
844 &outlen);
845
846 if (nblocks > 1) {
847 /* restore orig input data */
848 bcopy(orig_input, nlibp, sizeof (orig_input));
849 }
850 } else {
851 char tmp_ivec_data[BLOCK_SIZE], tmp_input_data[BLOCK_SIZE],
852 tmp_output_data[BLOCK_SIZE];
853 /* pointers to Cn, Cn-1, Cn-2 CipherText */
854 char *Cn, *Cn_1, *Cn_2;
855 CK_ULONG length;
856
857 /*
858 * Complex case:
859 *
860 * Decrypting in CTS where there is a partial block of
861 * ciphertext.
862 */
863
864 /* setting pointers to CipherText for later use */
865 Cn = input->data + (input->length - partialamount);
866 /* Cn - 1 */
867 Cn_1 = Cn - BLOCK_SIZE;
868 /* Cn - 2 */
869 Cn_2 = Cn_1 - BLOCK_SIZE;
870
871 if (nblocks > 2) {
872 rv = C_DecryptInit(krb_ctx_hSession(context),
873 &mechanism, key->hKey);
874 if (rv != CKR_OK) {
875 KRB5_LOG(KRB5_ERR, "C_DecryptInit failed in "
876 "krb5int_aes_decrypt: rv = 0x%x", rv);
877 goto cleanup;
878 }
879 /* set length to include blocks C0 thru Cn-2 */
880 length = input->length - (BLOCK_SIZE + partialamount);
881 outlen = length;
882 /*
883 * First decrypt C0 thru Cn-2 using CBC with the input
884 * ivec.
885 */
886 rv = C_Decrypt(krb_ctx_hSession(context),
887 (CK_BYTE_PTR)input->data,
888 length,
889 (CK_BYTE_PTR)output->data,
890 &outlen);
891 if (rv != CKR_OK)
892 goto cleanup;
893 }
894
895 /*
896 * Prepare to decrypt Cn-1 using a ivec of Cn with 0 padding.
897 */
898 bzero(tmp_ivec_data, sizeof (tmp_ivec_data));
899 /* the tmp ivec data holds Cn with 0 padding */
900 bcopy(Cn, tmp_ivec_data, partialamount);
901
902 /* decrypt 1 block */
903 length = BLOCK_SIZE;
904 outlen = length;
905
906 /* set ivec to Cn with 0 padding */
907 mechanism.pParameter = tmp_ivec_data;
908 mechanism.ulParameterLen = sizeof (tmp_ivec_data);
909
910 rv = C_DecryptInit(krb_ctx_hSession(context), &mechanism,
911 key->hKey);
912 if (rv != CKR_OK) {
913 KRB5_LOG(KRB5_ERR, "C_DecryptInit failed in "
914 "krb5int_aes_decrypt: rv = 0x%x", rv);
915 goto cleanup;
916 }
917
918 /*
919 * Now decrypt using Cn-1 input, Cn + 0 padding for ivec, Pn &
920 * C' output
921 */
922 rv = C_Decrypt(krb_ctx_hSession(context),
923 (CK_BYTE_PTR)Cn_1,
924 length,
925 (CK_BYTE_PTR)tmp_output_data,
926 &outlen);
927
928 if (rv != CKR_OK)
929 goto cleanup;
930
931 /*
932 * tmp input data should hold Cn with C'
933 * Note, tmp_output_data contains Pn + C',
934 */
935 /* copy Cn */
936 bcopy(Cn, tmp_input_data, partialamount);
937 /* copy C' */
938 bcopy(tmp_output_data + partialamount,
939 tmp_input_data + partialamount,
940 (BLOCK_SIZE - partialamount));
941
942 /* copy Pn in tmp output to output->data last block */
943 bcopy(tmp_output_data,
944 output->data + (input->length - partialamount),
945 partialamount);
946
947 if (nblocks > 2) {
948 /* use Cn-2 as ivec */
949 mechanism.pParameter = Cn_2;
950 } else {
951 /*
952 * nblocks == 2
953 *
954 * Cn-2 does not exist so either use 0 if input ivec
955 * does not exist or use the input ivec.
956 */
957 if (ivec == NULL) {
958 bzero(tmp_ivec_data, sizeof (tmp_ivec_data));
959 } else {
960 /* use original input ivec */
961 mechanism.pParameter = ivec->data;
962 mechanism.ulParameterLen = ivec->length;
963 }
964 }
965
966 rv = C_DecryptInit(krb_ctx_hSession(context), &mechanism,
967 key->hKey);
968 if (rv != CKR_OK) {
969 KRB5_LOG(KRB5_ERR, "C_DecryptInit failed in "
970 "krb5int_aes_decrypt: rv = 0x%x", rv);
971 goto cleanup;
972 }
973
974 /*
975 * Now decrypt Cn + C' input, using either Cn-2, original input
976 * ivec or 0 for ivec (set above), Pn-1 output.
977 */
978 rv = C_Decrypt(krb_ctx_hSession(context),
979 (CK_BYTE_PTR)tmp_input_data,
980 length,
981 (CK_BYTE_PTR)output->data + (input->length -
982 (BLOCK_SIZE + partialamount)),
983 &outlen);
984 if (rv != CKR_OK)
985 goto cleanup;
986 } /* end partial block processing */
987
988 /*
989 * The ivec is updated to allow the caller to chain ivecs, done for the
990 * kcmd (rsh/rcp/etc...). Note this is not done for 1 block although I
991 * am not sure why but I'm continuing the tradition from the MIT code.
992 */
993 if (nblocks > 1 && ivec) {
994 (void) memcpy(ivec->data,
995 input->data + ((nblocks - 2) * BLOCK_SIZE),
996 BLOCK_SIZE);
997 }
998
999 cleanup:
1000 if (rv != CKR_OK)
1001 ret = PKCS_ERR;
1002
1003 if (ret)
1004 bzero(output->data, input->length);
1005
1006 return (ret);
1007 }
1008
1009 #endif /* _KERNEL */
1010
1011 static krb5_error_code
k5_aes_make_key(krb5_context context,const krb5_data * randombits,krb5_keyblock * key)1012 k5_aes_make_key(krb5_context context,
1013 const krb5_data *randombits, krb5_keyblock *key)
1014 {
1015 krb5_error_code ret = 0;
1016 if (key->length != 16 && key->length != 32)
1017 return (KRB5_BAD_KEYSIZE);
1018 if (randombits->length != key->length)
1019 return (KRB5_CRYPTO_INTERNAL);
1020
1021 key->magic = KV5M_KEYBLOCK;
1022 key->dk_list = NULL;
1023
1024 #ifdef _KERNEL
1025 key->kef_key.ck_data = NULL;
1026 key->key_tmpl = NULL;
1027 (void) memcpy(key->contents, randombits->data, randombits->length);
1028 ret = init_key_kef(context->kef_cipher_mt, key);
1029 #else
1030 key->hKey = CK_INVALID_HANDLE;
1031 (void) memcpy(key->contents, randombits->data, randombits->length);
1032 ret = init_key_uef(krb_ctx_hSession(context), key);
1033 #endif /* _KERNEL */
1034
1035 KRB5_LOG0(KRB5_INFO, "k5_aes_make_key() end\n");
1036 return (ret);
1037 }
1038
1039 /*ARGSUSED*/
1040 static krb5_error_code
krb5int_aes_init_state(krb5_context context,const krb5_keyblock * key,krb5_keyusage usage,krb5_data * state)1041 krb5int_aes_init_state(krb5_context context, const krb5_keyblock *key,
1042 krb5_keyusage usage, krb5_data *state)
1043 {
1044 if (!state)
1045 return (0);
1046
1047 if (state && state->data)
1048 FREE(state->data, state->length);
1049
1050 state->length = BLOCK_SIZE;
1051 state->data = (void *) MALLOC(BLOCK_SIZE);
1052
1053 if (state->data == NULL)
1054 return (ENOMEM);
1055
1056 (void) memset(state->data, 0, state->length);
1057 return (0);
1058 }
1059
1060 const struct krb5_enc_provider krb5int_enc_aes128 = {
1061 BLOCK_SIZE,
1062 16, 16,
1063 krb5int_aes_encrypt,
1064 krb5int_aes_decrypt,
1065 k5_aes_make_key,
1066 krb5int_aes_init_state,
1067 krb5int_default_free_state
1068 };
1069
1070 const struct krb5_enc_provider krb5int_enc_aes256 = {
1071 BLOCK_SIZE,
1072 32, 32,
1073 krb5int_aes_encrypt,
1074 krb5int_aes_decrypt,
1075 k5_aes_make_key,
1076 krb5int_aes_init_state,
1077 krb5int_default_free_state
1078 };
1079