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