xref: /illumos-gate/usr/src/uts/common/gssapi/mechs/krb5/crypto/enc_provider/aes_provider.c (revision c54c769d4c1cde75dd28975fb0090a8f944651a6)
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 			bcopy(nlibp, orig_input, BLOCK_SIZE + partialamount);
564 
565 			/* swap */
566 			bcopy(nlibp, tmp, BLOCK_SIZE);
567 			bcopy(libp, nlibp, BLOCK_SIZE);
568 			bcopy(tmp, libp, BLOCK_SIZE);
569 		}
570 
571 		ret = k5_ef_crypto((const char *)input->data,
572 			(char *)output->data,
573 			input->length, (krb5_keyblock *)key,
574 			&local_iv, FALSE);
575 
576 		if (nblocks > 1) {
577 			/* restore orig input data */
578 			bcopy(orig_input, nlibp, BLOCK_SIZE + partialamount);
579 		}
580 
581 		if (ret != 0) {
582 		    KRB5_LOG(KRB5_ERR,
583 			    "k5_ef_crypto returned error: ret = 0x%08x",
584 			    ret);
585 		    goto cleanup;
586 		}
587 
588 	} else {
589 		krb5_data tmp_ivec;
590 		char tmp_ivec_data[BLOCK_SIZE], tmp_input_data[BLOCK_SIZE],
591 			tmp_output_data[BLOCK_SIZE];
592 		/* pointers to Cn, Cn-1, Cn-2 CipherText */
593 		char *Cn, *Cn_1, *Cn_2;
594 		long length;
595 
596 		/*
597 		 * Complex case:
598 		 *
599 		 * Decrypting in CTS where there is a partial block of
600 		 * ciphertext.
601 		 */
602 
603 		/* setting pointers to CipherText for later use */
604 		Cn = input->data + (input->length - partialamount);
605 		/* Cn - 1 */
606 		Cn_1 = Cn - BLOCK_SIZE;
607 		/* Cn - 2 */
608 		Cn_2 = Cn_1 - BLOCK_SIZE;
609 
610 		if (nblocks > 2) {
611 			/* set length to include blocks C0 thru Cn-2 */
612 			length = input->length - (BLOCK_SIZE + partialamount);
613 
614 			/*
615 			 * First decrypt C0 thru Cn-2 using CBC with the input
616 			 * ivec.
617 			 */
618 			ret = k5_ef_crypto((const char *)input->data,
619 				output->data, length, (krb5_keyblock *)key,
620 				&local_iv, FALSE);
621 
622 			if (ret != 0) {
623 			    KRB5_LOG(KRB5_ERR,
624 				    "k5_ef_crypto: error: ret = 0x%08x",
625 				    ret);
626 			    goto cleanup;
627 			}
628 		}
629 		/*
630 		 * Prepare to decrypt Cn-1 using a ivec of Cn with 0 padding.
631 		 */
632 		bzero(tmp_ivec_data, sizeof (tmp_ivec_data));
633 		/* the tmp ivec data holds Cn with 0 padding */
634 		bcopy(Cn, tmp_ivec_data, partialamount);
635 		tmp_ivec.data = tmp_ivec_data;
636 		tmp_ivec.length = sizeof (tmp_ivec_data);
637 
638 		/* decrypt 1 block */
639 		length = BLOCK_SIZE;
640 
641 		/*
642 		 * Now decrypt using Cn-1 input, Cn + 0 padding for ivec, Pn &
643 		 * C' output
644 		 */
645 		ret = k5_ef_crypto((const char *)Cn_1,
646 		    tmp_output_data, length,
647 		    (krb5_keyblock *)key, &tmp_ivec, FALSE);
648 
649 		if (ret != 0) {
650 		    KRB5_LOG(KRB5_ERR,
651 			    "k5_ef_crypto: error: ret = 0x%08x",
652 			    ret);
653 		    goto cleanup;
654 		}
655 		/*
656 		 * tmp input data should hold Cn with C'
657 		 * Note, tmp_output_data contains Pn + C',
658 		 */
659 		/* copy Cn */
660 		bcopy(Cn, tmp_input_data, partialamount);
661 		/* copy C' */
662 		bcopy(tmp_output_data + partialamount,
663 		    tmp_input_data + partialamount,
664 		    (BLOCK_SIZE - partialamount));
665 
666 		/* copy Pn in tmp output to output->data */
667 		bcopy(tmp_output_data,
668 		    output->data + (input->length - partialamount),
669 		    partialamount);
670 
671 		if (nblocks > 2) {
672 			/* use Cn-2 as ivec */
673 			tmp_ivec.data = Cn_2;
674 		} else {
675 			/* use 0 as ivec because Cn-2 does not exist */
676 			bzero(tmp_ivec_data, sizeof (tmp_ivec_data));
677 		}
678 
679 		/*
680 		 * Now decrypt Cn + C' input, using either Cn-2 or 0 for ivec
681 		 * (set above), Pn-1 output.
682 		 */
683 		ret = k5_ef_crypto((const char *)tmp_input_data,
684 			(char *)output->data +
685 				(input->length - (BLOCK_SIZE + partialamount)),
686 			length, (krb5_keyblock *)key,
687 			&tmp_ivec, FALSE);
688 
689 		if (ret != 0) {
690 		    KRB5_LOG(KRB5_ERR,
691 			    "k5_ef_crypto: error: ret = 0x%08x",
692 			    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
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 	assert(output != NULL);
735 	assert(input->length == output->length);
736 	assert(key != NULL);
737 
738 	if (ivec != NULL) {
739 		/*
740 		 * This function updates ivec->data if the ivec is passed in so
741 		 * it better have a data pointer and a proper length.
742 		 */
743 		if (ivec->data == NULL || ivec->length != BLOCK_SIZE) {
744 			assert(ivec->data != NULL);
745 			assert(ivec->length == BLOCK_SIZE);
746 			KRB5_LOG1(KRB5_ERR, "In krb5int_aes_decrypt: error "
747 			    "ivec->data = %p ivec->length = %d", ivec->data,
748 			    ivec->length);
749 			ret = KRB5_CRYPTO_INTERNAL;
750 			goto cleanup;
751 		}
752 	}
753 
754 	/* number of input blocks including partial block */
755 	nblocks = (input->length + BLOCK_SIZE - 1) / BLOCK_SIZE;
756 	KRB5_LOG(KRB5_INFO, "nblocks = %d", nblocks);
757 	/* get # of bytes in partially filled block */
758 	partialamount = input->length % BLOCK_SIZE;
759 	KRB5_LOG(KRB5_INFO, "partialamount = %d", partialamount);
760 
761 	rv = get_algo(key->enctype, &algos);
762 	if (rv != CKR_OK)
763 		goto cleanup;
764 	assert(algos.enc_algo == CKM_AES_CBC);
765 
766 	rv = init_key_uef(krb_ctx_hSession(context), (krb5_keyblock *)key);
767 	if (rv != CKR_OK) {
768 		goto cleanup;
769 	}
770 
771 	mechanism.mechanism = algos.enc_algo;
772 	if (ivec == NULL) {
773 		bzero(tmp_ivec, sizeof (tmp_ivec));
774 		mechanism.pParameter = tmp_ivec;
775 		mechanism.ulParameterLen = sizeof (tmp_ivec);
776 	} else {
777 		mechanism.pParameter = ivec->data;
778 		mechanism.ulParameterLen = ivec->length;
779 	}
780 
781 	if (nblocks == 1 || (partialamount == 0)) {
782 		char orig_input[BLOCK_SIZE * 2];
783 		/*
784 		 * nlibp = next to last input block pointer
785 		 * libp = last input block pointer
786 		 */
787 		char *nlibp, *libp;
788 
789 		/*
790 		 * Simple case:
791 		 *
792 		 * Swap last 2 ciphertext blocks (all must be full), then use
793 		 * CBC to implement CTS.
794 		 */
795 		if (nblocks > 1) {
796 			/*
797 			 * swap last 2 ciphertext blocks to implement CTS
798 			 */
799 			char tmp[BLOCK_SIZE];
800 
801 			/*
802 			 * Note, the side effect with this is that we are
803 			 * modifying the input->data!
804 			 */
805 			nlibp = input->data + ((nblocks - 2) * BLOCK_SIZE);
806 			libp = input->data + ((nblocks - 1) * BLOCK_SIZE);
807 
808 			/* first save orig input data for later restore */
809 			bcopy(nlibp, orig_input, BLOCK_SIZE + partialamount);
810 
811 			bcopy(nlibp, tmp, BLOCK_SIZE);
812 			bcopy(libp, nlibp, BLOCK_SIZE);
813 			bcopy(tmp, libp, BLOCK_SIZE);
814 		} else {
815 			if (input->length < BLOCK_SIZE)
816 				return (KRB5_CRYPTO_INTERNAL);
817 		}
818 
819 		/*
820 		 * Note, since CBC is assumed to be the underlying mode, this
821 		 * call to C_DecryptInit is setting the IV.  The IV in use here
822 		 * is either the ivec passed in or a block of 0's.  All calls to
823 		 * C_DecryptInit set the IV in this function.
824 		 */
825 		rv = C_DecryptInit(krb_ctx_hSession(context), &mechanism,
826 				key->hKey);
827 		if (rv != CKR_OK) {
828 			KRB5_LOG(KRB5_ERR, "C_DecryptInit failed in "
829 			    "krb5int_aes_decrypt: rv = 0x%x", rv);
830 			goto cleanup;
831 		}
832 
833 		/*
834 		 * C_Encrypt/Decrypt requires a pointer to long, not a pointer
835 		 * to int cast to pointer to long!!!
836 		 */
837 		outlen = output->length;
838 
839 		rv = C_Decrypt(krb_ctx_hSession(context),
840 			    (CK_BYTE_PTR)input->data,
841 			    input->length,
842 			    (CK_BYTE_PTR)output->data,
843 			    &outlen);
844 
845 		if (nblocks > 1) {
846 			/* restore orig input data */
847 			bcopy(orig_input, nlibp, BLOCK_SIZE + partialamount);
848 		}
849 	} else {
850 		char tmp_ivec_data[BLOCK_SIZE], tmp_input_data[BLOCK_SIZE],
851 			tmp_output_data[BLOCK_SIZE];
852 		/* pointers to Cn, Cn-1, Cn-2 CipherText */
853 		char *Cn, *Cn_1, *Cn_2;
854 		CK_ULONG length;
855 
856 		/*
857 		 * Complex case:
858 		 *
859 		 * Decrypting in CTS where there is a partial block of
860 		 * ciphertext.
861 		 */
862 
863 		/* setting pointers to CipherText for later use */
864 		Cn = input->data + (input->length - partialamount);
865 		/* Cn - 1 */
866 		Cn_1 = Cn - BLOCK_SIZE;
867 		/* Cn - 2 */
868 		Cn_2 = Cn_1 - BLOCK_SIZE;
869 
870 		if (nblocks > 2) {
871 			rv = C_DecryptInit(krb_ctx_hSession(context),
872 				&mechanism, key->hKey);
873 			if (rv != CKR_OK) {
874 				KRB5_LOG(KRB5_ERR, "C_DecryptInit failed in "
875 				    "krb5int_aes_decrypt: rv = 0x%x", rv);
876 				goto cleanup;
877 			}
878 			/* set length to include blocks C0 thru Cn-2 */
879 			length = input->length - (BLOCK_SIZE + partialamount);
880 			outlen = length;
881 			/*
882 			 * First decrypt C0 thru Cn-2 using CBC with the input
883 			 * ivec.
884 			 */
885 			rv = C_Decrypt(krb_ctx_hSession(context),
886 				    (CK_BYTE_PTR)input->data,
887 				    length,
888 				    (CK_BYTE_PTR)output->data,
889 				    &outlen);
890 			if (rv != CKR_OK)
891 				goto cleanup;
892 		}
893 
894 		/*
895 		 * Prepare to decrypt Cn-1 using a ivec of Cn with 0 padding.
896 		 */
897 		bzero(tmp_ivec_data, sizeof (tmp_ivec_data));
898 		/* the tmp ivec data holds Cn with 0 padding */
899 		bcopy(Cn, tmp_ivec_data, partialamount);
900 
901 		/* decrypt 1 block */
902 		length = BLOCK_SIZE;
903 		outlen = length;
904 
905 		/* set ivec to Cn with 0 padding */
906 		mechanism.pParameter = tmp_ivec_data;
907 		mechanism.ulParameterLen = sizeof (tmp_ivec_data);
908 
909 		rv = C_DecryptInit(krb_ctx_hSession(context), &mechanism,
910 			    key->hKey);
911 		if (rv != CKR_OK) {
912 			KRB5_LOG(KRB5_ERR, "C_DecryptInit failed in "
913 			    "krb5int_aes_decrypt: rv = 0x%x", rv);
914 			goto cleanup;
915 		}
916 
917 		/*
918 		 * Now decrypt using Cn-1 input, Cn + 0 padding for ivec, Pn &
919 		 * C' output
920 		 */
921 		rv = C_Decrypt(krb_ctx_hSession(context),
922 			    (CK_BYTE_PTR)Cn_1,
923 			    length,
924 			    (CK_BYTE_PTR)tmp_output_data,
925 			    &outlen);
926 
927 		if (rv != CKR_OK)
928 			goto cleanup;
929 
930 		/*
931 		 * tmp input data should hold Cn with C'
932 		 * Note, tmp_output_data contains Pn + C',
933 		 */
934 		/* copy Cn */
935 		bcopy(Cn, tmp_input_data, partialamount);
936 		/* copy C' */
937 		bcopy(tmp_output_data + partialamount,
938 		    tmp_input_data + partialamount,
939 		    (BLOCK_SIZE - partialamount));
940 
941 		/* copy Pn in tmp output to output->data last block */
942 		bcopy(tmp_output_data,
943 		    output->data + (input->length - partialamount),
944 		    partialamount);
945 
946 		if (nblocks > 2) {
947 			/* use Cn-2 as ivec */
948 			mechanism.pParameter = Cn_2;
949 		} else {
950 			/*
951 			 * nblocks == 2
952 			 *
953 			 * Cn-2 does not exist so either use 0 if input ivec
954 			 * does not exist or use the input ivec.
955 			 */
956 			if (ivec == NULL) {
957 				bzero(tmp_ivec_data, sizeof (tmp_ivec_data));
958 			} else {
959 				/* use original input ivec */
960 				mechanism.pParameter = ivec->data;
961 				mechanism.ulParameterLen = ivec->length;
962 			}
963 		}
964 
965 		rv = C_DecryptInit(krb_ctx_hSession(context), &mechanism,
966 			    key->hKey);
967 		if (rv != CKR_OK) {
968 			KRB5_LOG(KRB5_ERR, "C_DecryptInit failed in "
969 			    "krb5int_aes_decrypt: rv = 0x%x", rv);
970 			goto cleanup;
971 		}
972 
973 		/*
974 		 * Now decrypt Cn + C' input, using either Cn-2, original input
975 		 * ivec or 0 for ivec (set above), Pn-1 output.
976 		 */
977 		rv = C_Decrypt(krb_ctx_hSession(context),
978 			    (CK_BYTE_PTR)tmp_input_data,
979 			    length,
980 			    (CK_BYTE_PTR)output->data + (input->length -
981 				(BLOCK_SIZE + partialamount)),
982 			    &outlen);
983 		if (rv != CKR_OK)
984 			goto cleanup;
985 	} /* end partial block processing */
986 
987 	/*
988 	 * The ivec is updated to allow the caller to chain ivecs, done for the
989 	 * kcmd (rsh/rcp/etc...).  Note this is not done for 1 block although I
990 	 * am not sure why but I'm continuing the tradition from the MIT code.
991 	 */
992 	if (nblocks > 1 && ivec) {
993 		(void) memcpy(ivec->data,
994 			input->data + ((nblocks - 2) * BLOCK_SIZE),
995 			BLOCK_SIZE);
996 	}
997 
998 cleanup:
999 	if (rv != CKR_OK)
1000 		ret = PKCS_ERR;
1001 
1002 	if (ret)
1003 		bzero(output->data, input->length);
1004 
1005 	return (ret);
1006 }
1007 
1008 #endif /* _KERNEL */
1009 
1010 static krb5_error_code
1011 k5_aes_make_key(krb5_context context,
1012 	const krb5_data *randombits, krb5_keyblock *key)
1013 {
1014 	krb5_error_code ret = 0;
1015 	if (key->length != 16 && key->length != 32)
1016 		return (KRB5_BAD_KEYSIZE);
1017 	if (randombits->length != key->length)
1018 		return (KRB5_CRYPTO_INTERNAL);
1019 
1020 	key->magic = KV5M_KEYBLOCK;
1021 	key->dk_list = NULL;
1022 
1023 #ifdef _KERNEL
1024 	key->kef_key.ck_data = NULL;
1025 	key->key_tmpl = NULL;
1026 	(void) memcpy(key->contents, randombits->data, randombits->length);
1027 	ret = init_key_kef(context->kef_cipher_mt, key);
1028 #else
1029 	key->hKey = CK_INVALID_HANDLE;
1030 	(void) memcpy(key->contents, randombits->data, randombits->length);
1031 	ret = init_key_uef(krb_ctx_hSession(context), key);
1032 #endif /* _KERNEL */
1033 
1034 	KRB5_LOG0(KRB5_INFO, "k5_aes_make_key() end\n");
1035 	return (ret);
1036 }
1037 
1038 /*ARGSUSED*/
1039 static krb5_error_code
1040 krb5int_aes_init_state(krb5_context context, const krb5_keyblock *key,
1041 	krb5_keyusage usage, krb5_data *state)
1042 {
1043 	if (!state)
1044 		return (0);
1045 
1046 	if (state && state->data)
1047 		FREE(state->data, state->length);
1048 
1049 	state->length = BLOCK_SIZE;
1050 	state->data = (void *) MALLOC(BLOCK_SIZE);
1051 
1052 	if (state->data == NULL)
1053 		return (ENOMEM);
1054 
1055 	(void) memset(state->data, 0, state->length);
1056 	return (0);
1057 }
1058 
1059 const struct krb5_enc_provider krb5int_enc_aes128 = {
1060     BLOCK_SIZE,
1061     16, 16,
1062     krb5int_aes_encrypt,
1063     krb5int_aes_decrypt,
1064     k5_aes_make_key,
1065     krb5int_aes_init_state,
1066     krb5int_default_free_state
1067 };
1068 
1069 const struct krb5_enc_provider krb5int_enc_aes256 = {
1070     BLOCK_SIZE,
1071     32, 32,
1072     krb5int_aes_encrypt,
1073     krb5int_aes_decrypt,
1074     k5_aes_make_key,
1075     krb5int_aes_init_state,
1076     krb5int_default_free_state
1077 };
1078