xref: /illumos-gate/usr/src/uts/common/gssapi/mechs/krb5/crypto/enc_provider/aes_provider.c (revision 1be2e5dfebda7cac010af97aae7a3a1b45649aed)
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 		/* 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
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 	assert(output != NULL);
288 	assert(input->length == output->length);
289 	assert(key != NULL);
290 
291 	if (ivec != NULL) {
292 		/*
293 		 * This function updates ivec->data if the ivec is passed in so
294 		 * it better have a data pointer and a proper length.
295 		 */
296 		if (ivec->data == NULL || ivec->length != BLOCK_SIZE) {
297 			assert(ivec->data != NULL);
298 			assert(ivec->length == BLOCK_SIZE);
299 			KRB5_LOG1(KRB5_ERR, "In krb5int_aes_encrypt: error "
300 			    "ivec->data = %p ivec->length = %d", ivec->data,
301 			    ivec->length);
302 			ret = KRB5_CRYPTO_INTERNAL;
303 			goto cleanup;
304 		}
305 	}
306 
307 	/* number of input blocks including partial block */
308 	nblocks = (input->length + BLOCK_SIZE - 1) / BLOCK_SIZE;
309 	KRB5_LOG(KRB5_INFO, "nblocks = %d", nblocks);
310 	/* get # of bytes in partially filled block */
311 	partialamount = input->length % BLOCK_SIZE;
312 	KRB5_LOG(KRB5_INFO, "partialamount = %d", partialamount);
313 
314 	rv = get_algo(key->enctype, &algos);
315 	if (rv != CKR_OK)
316 		goto cleanup;
317 	assert(algos.enc_algo == CKM_AES_CBC);
318 
319 	rv = init_key_uef(krb_ctx_hSession(context), (krb5_keyblock *)key);
320 	if (rv != CKR_OK)
321 		goto cleanup;
322 
323 	mechanism.mechanism = algos.enc_algo;
324 
325 	if (ivec == NULL) {
326 		bzero(tmp_ivec, sizeof (tmp_ivec));
327 		mechanism.pParameter = tmp_ivec;
328 		mechanism.ulParameterLen = sizeof (tmp_ivec);
329 	} else {
330 		mechanism.pParameter = ivec->data;
331 		mechanism.ulParameterLen = ivec->length;
332 	}
333 	/*
334 	 * Note, since CBC is assumed to be the underlying mode, this
335 	 * call to C_EncryptInit is setting the IV.  The IV in use here
336 	 * is either the ivec passed in or a block of 0's.
337 	 */
338 	rv = C_EncryptInit(krb_ctx_hSession(context), &mechanism, key->hKey);
339 
340 	if (rv != CKR_OK) {
341 		KRB5_LOG(KRB5_ERR, "C_EncryptInit failed in "
342 		    "krb5int_aes_encrypt: rv = 0x%x", rv);
343 		goto cleanup;
344 	}
345 
346 	if (nblocks == 1 || (partialamount == 0)) {
347 		/*
348 		 * Simple case:
349 		 *
350 		 * Use CBC for all plaintext blocks, all must be full, then swap
351 		 * last 2 ciphertext blocks to implement CTS.
352 		 */
353 
354 		/*
355 		 * C_Encrypt/Decrypt requires a pointer to long, not a pointer
356 		 * to int cast to pointer to long!!!
357 		 */
358 		outlen = output->length;
359 
360 		rv = C_Encrypt(krb_ctx_hSession(context),
361 		    (CK_BYTE_PTR)input->data,
362 		    input->length,
363 		    (CK_BYTE_PTR)output->data,
364 		    &outlen);
365 
366 		if (rv != CKR_OK) {
367 			KRB5_LOG(KRB5_ERR, "C_Encrypt failed in "
368 			    "krb5int_aes_encrypt: rv = 0x%x", rv);
369 			goto cleanup;
370 		}
371 
372 		assert(output->length == (unsigned int)outlen);
373 
374 		if (nblocks > 1) {
375 			/*
376 			 * swap last 2 ciphertext blocks to implement CTS
377 			 */
378 			char tmp[BLOCK_SIZE];
379 
380 			nlobp = (char *)(output->data +
381 			    ((nblocks - 2) * BLOCK_SIZE));
382 			lobp = (char *)(output->data +
383 			    ((nblocks - 1) * BLOCK_SIZE));
384 
385 			bcopy(nlobp, tmp, BLOCK_SIZE);
386 			bcopy(lobp, nlobp, BLOCK_SIZE);
387 			bcopy(tmp, lobp, BLOCK_SIZE);
388 		}
389 	} else {
390 		/*
391 		 * Complex case:
392 		 *
393 		 * This implements CTS mode where there is > 1 block and the
394 		 * last block is partially filled. Uses CBC mode in uCF/PKCS11,
395 		 * then does some swapping.
396 		 *
397 		 * pt = plain text, ct = cipher text
398 		 */
399 		char tmp_pt[BLOCK_SIZE], tmp_ct[BLOCK_SIZE];
400 
401 		/*
402 		 * encrypt from P0...Pn-1 using CBC, last block of output is Cn
403 		 * & C'
404 		 */
405 		outlen = input->length - partialamount;
406 
407 		rv = C_EncryptUpdate(krb_ctx_hSession(context),
408 		    (CK_BYTE_PTR)input->data,
409 		    input->length - partialamount,
410 		    (CK_BYTE_PTR)output->data,
411 		    &outlen);
412 
413 		if (rv != CKR_OK) {
414 			KRB5_LOG(KRB5_ERR, "C_EncryptUpdate failed in "
415 			    "krb5int_aes_encrypt: rv = 0x%x", rv);
416 			goto cleanup;
417 		}
418 
419 		/* tmp_pt will provide 0 padding for last parital pt block */
420 		bzero(tmp_pt, sizeof (tmp_pt));
421 		/* copy Pn to tmp_pt which has 0 padding */
422 		bcopy(input->data + (input->length - partialamount), tmp_pt,
423 		    partialamount);
424 
425 		/* encrypt Pn with 0 padding, Cn & C' ivec, output is Cn-1 */
426 		outlen = sizeof (tmp_ct);
427 
428 		rv = C_EncryptUpdate(krb_ctx_hSession(context),
429 		    (CK_BYTE_PTR)tmp_pt,
430 		    BLOCK_SIZE,
431 		    (CK_BYTE_PTR)tmp_ct,
432 		    &outlen);
433 
434 		if (rv != CKR_OK) {
435 			KRB5_LOG(KRB5_ERR, "C_Encrypt failed in "
436 			    "krb5int_aes_encrypt: rv = 0x%x", rv);
437 			goto cleanup;
438 		}
439 
440 		nlobp = (char *)(output->data + ((nblocks - 2) * BLOCK_SIZE));
441 		lobp = (char *)(output->data + ((nblocks - 1) * BLOCK_SIZE));
442 
443 		/* copy Cn from next to last output block to last block */
444 		bcopy(nlobp, lobp, partialamount);
445 		/* copy Cn-1 from tmp_ct to next to last output block */
446 		bcopy(tmp_ct, nlobp, BLOCK_SIZE);
447 
448 		/* Close the crypto session, ignore the output */
449 		rv = C_EncryptFinal(krb_ctx_hSession(context),
450 		    (CK_BYTE_PTR)tmp_ct, &outlen);
451 
452 		if (rv != CKR_OK)
453 			goto cleanup;
454 	}
455 	/*
456 	 * The ivec is updated to allow the caller to chain ivecs, done for the
457 	 * kcmd (rsh/rcp/etc...).  Note this is not done for 1 block although I
458 	 * am not sure why but I'm continuing the tradition from the MIT code.
459 	 * Note, the next to last output block is copied because it contains the
460 	 * last full block of cipher text.
461 	 */
462 	if (nblocks > 1 && ivec)
463 		(void) memcpy(ivec->data, nlobp, BLOCK_SIZE);
464 
465 cleanup:
466 	if (rv != CKR_OK)
467 		ret = PKCS_ERR;
468 
469 	if (ret)
470 		bzero(output->data, input->length);
471 
472 	return (ret);
473 }
474 #endif /* _KERNEL */
475 
476 /*
477  * AES Decrypt using CipherText Stealing mode built on top of CBC mode.  See the
478  * krb5int_aes_encrypt() comments for the reason CBC is being used.
479  */
480 
481 #ifdef _KERNEL
482 /*ARGSUSED*/
483 krb5_error_code
484 krb5int_aes_decrypt(krb5_context context,
485 	const krb5_keyblock *key, const krb5_data *ivec,
486 	const krb5_data *input, krb5_data *output)
487 {
488 	krb5_error_code ret = 0;
489 	int nblocks, partialamount;
490 	char local_iv_data[BLOCK_SIZE];
491 	krb5_data local_iv;
492 
493 	KRB5_LOG0(KRB5_INFO, "In krb5int_aes_decrypt: start");
494 
495 	ASSERT(input != NULL);
496 	ASSERT(output != NULL);
497 	ASSERT(input->length == output->length);
498 	ASSERT(key != NULL);
499 	ASSERT(key->kef_mt == crypto_mech2id(SUN_CKM_AES_CBC));
500 
501 	if (ivec != NULL) {
502 		/*
503 		 * This function updates ivec->data if the ivec is passed in so
504 		 * it better have a data pointer and a proper length.
505 		 */
506 		if (ivec->data == NULL || ivec->length != BLOCK_SIZE) {
507 			ASSERT(ivec->data != NULL);
508 			ASSERT(ivec->length == BLOCK_SIZE);
509 			KRB5_LOG1(KRB5_ERR, "In krb5int_aes_decrypt: error "
510 			    "ivec->data = %p ivec->length = %d",
511 			    (void *)ivec->data, ivec->length);
512 			ret = KRB5_CRYPTO_INTERNAL;
513 			goto cleanup;
514 		}
515 	}
516 
517 	/* number of input blocks including partial block */
518 	nblocks = (input->length + BLOCK_SIZE - 1) / BLOCK_SIZE;
519 	KRB5_LOG(KRB5_INFO, "nblocks = %d", nblocks);
520 	/* get # of bytes in partially filled block */
521 	partialamount = input->length % BLOCK_SIZE;
522 	KRB5_LOG(KRB5_INFO, "partialamount = %d", partialamount);
523 
524 	if (ivec != NULL) {
525 		local_iv.data = ivec->data;
526 		local_iv.length = ivec->length;
527 	} else {
528 		bzero(local_iv_data, sizeof (local_iv_data));
529 		local_iv.data = local_iv_data;
530 		local_iv.length = sizeof (local_iv_data);
531 	}
532 
533 	if (nblocks == 1 || (partialamount == 0)) {
534 		char orig_input[BLOCK_SIZE * 2];
535 		/*
536 		 * nlibp = next to last input block pointer
537 		 * libp = last input block pointer
538 		 */
539 		char *nlibp, *libp;
540 
541 		/*
542 		 * Simple case:
543 		 *
544 		 * Swap last 2 ciphertext blocks (all must be full), then use
545 		 * CBC to implement CTS.
546 		 */
547 
548 		if (nblocks > 1) {
549 			/*
550 			 * swap last 2 ciphertext blocks to implement CTS
551 			 */
552 			char tmp[BLOCK_SIZE];
553 
554 			nlibp = input->data + ((nblocks - 2) * BLOCK_SIZE);
555 			libp = input->data + ((nblocks - 1) * BLOCK_SIZE);
556 
557 			/* first save orig input data for later restore */
558 			/* we know that partial amount is 0, because */
559 			/* nblocks is > 1, so we copy the last two blocks */
560 			bcopy(nlibp, orig_input, sizeof (orig_input));
561 
562 			/* swap */
563 			bcopy(nlibp, tmp, BLOCK_SIZE);
564 			bcopy(libp, nlibp, BLOCK_SIZE);
565 			bcopy(tmp, libp, BLOCK_SIZE);
566 		}
567 
568 		ret = k5_ef_crypto((const char *)input->data,
569 		    (char *)output->data,
570 		    input->length, (krb5_keyblock *)key,
571 		    &local_iv, FALSE);
572 
573 		if (nblocks > 1) {
574 			/* restore orig input data */
575 			bcopy(orig_input, nlibp, sizeof (orig_input));
576 		}
577 
578 		if (ret != 0) {
579 			KRB5_LOG(KRB5_ERR,
580 			    "k5_ef_crypto returned error: ret = 0x%08x",
581 			    ret);
582 			goto cleanup;
583 		}
584 
585 	} else {
586 		krb5_data tmp_ivec;
587 		char tmp_ivec_data[BLOCK_SIZE], tmp_input_data[BLOCK_SIZE],
588 		    tmp_output_data[BLOCK_SIZE];
589 		/* pointers to Cn, Cn-1, Cn-2 CipherText */
590 		char *Cn, *Cn_1, *Cn_2;
591 		long length;
592 
593 		/*
594 		 * Complex case:
595 		 *
596 		 * Decrypting in CTS where there is a partial block of
597 		 * ciphertext.
598 		 */
599 
600 		/* setting pointers to CipherText for later use */
601 		Cn = input->data + (input->length - partialamount);
602 		/* Cn - 1 */
603 		Cn_1 = Cn - BLOCK_SIZE;
604 		/* Cn - 2 */
605 		Cn_2 = Cn_1 - BLOCK_SIZE;
606 
607 		if (nblocks > 2) {
608 			/* set length to include blocks C0 thru Cn-2 */
609 			length = input->length - (BLOCK_SIZE + partialamount);
610 
611 			/*
612 			 * First decrypt C0 thru Cn-2 using CBC with the input
613 			 * ivec.
614 			 */
615 			ret = k5_ef_crypto((const char *)input->data,
616 			    output->data, length, (krb5_keyblock *)key,
617 			    &local_iv, FALSE);
618 
619 			if (ret != 0) {
620 				KRB5_LOG(KRB5_ERR,
621 				    "k5_ef_crypto: error: ret = 0x%08x",
622 				    ret);
623 				goto cleanup;
624 			}
625 		}
626 		/*
627 		 * Prepare to decrypt Cn-1 using a ivec of Cn with 0 padding.
628 		 */
629 		bzero(tmp_ivec_data, sizeof (tmp_ivec_data));
630 		/* the tmp ivec data holds Cn with 0 padding */
631 		bcopy(Cn, tmp_ivec_data, partialamount);
632 		tmp_ivec.data = tmp_ivec_data;
633 		tmp_ivec.length = sizeof (tmp_ivec_data);
634 
635 		/* decrypt 1 block */
636 		length = BLOCK_SIZE;
637 
638 		/*
639 		 * Now decrypt using Cn-1 input, Cn + 0 padding for ivec, Pn &
640 		 * C' output
641 		 */
642 		ret = k5_ef_crypto((const char *)Cn_1,
643 		    tmp_output_data, length,
644 		    (krb5_keyblock *)key, &tmp_ivec, FALSE);
645 
646 		if (ret != 0) {
647 			KRB5_LOG(KRB5_ERR,
648 			    "k5_ef_crypto: error: ret = 0x%08x",
649 			    ret);
650 			goto cleanup;
651 		}
652 		/*
653 		 * tmp input data should hold Cn with C'
654 		 * Note, tmp_output_data contains Pn + C',
655 		 */
656 		/* copy Cn */
657 		bcopy(Cn, tmp_input_data, partialamount);
658 		/* copy C' */
659 		bcopy(tmp_output_data + partialamount,
660 		    tmp_input_data + partialamount,
661 		    (BLOCK_SIZE - partialamount));
662 
663 		/* copy Pn in tmp output to output->data */
664 		bcopy(tmp_output_data,
665 		    output->data + (input->length - partialamount),
666 		    partialamount);
667 
668 		if (nblocks > 2) {
669 			/* use Cn-2 as ivec */
670 			tmp_ivec.data = Cn_2;
671 		} else {
672 			/* use 0 as ivec because Cn-2 does not exist */
673 			bzero(tmp_ivec_data, sizeof (tmp_ivec_data));
674 		}
675 
676 		/*
677 		 * Now decrypt Cn + C' input, using either Cn-2 or 0 for ivec
678 		 * (set above), Pn-1 output.
679 		 */
680 		ret = k5_ef_crypto((const char *)tmp_input_data,
681 		    (char *)output->data +
682 		    (input->length - (BLOCK_SIZE + partialamount)),
683 		    length, (krb5_keyblock *)key,
684 		    &tmp_ivec, FALSE);
685 
686 		if (ret != 0) {
687 			KRB5_LOG(KRB5_ERR,
688 			    "k5_ef_crypto: error: ret = 0x%08x", ret);
689 			goto cleanup;
690 		}
691 
692 	} /* end partial block processing */
693 	/*
694 	 * The ivec is updated to allow the caller to chain ivecs.  At this
695 	 * point I don't think any kernel callers are using this however the
696 	 * userland version of this function does it so this should be done in
697 	 * kernel for consistency's sake.  This is not done for 1 block, got
698 	 * this from MIT.
699 	 */
700 	if (nblocks > 1 && ivec) {
701 		(void) memcpy(ivec->data,
702 		    input->data + ((nblocks - 2) * BLOCK_SIZE),
703 		    BLOCK_SIZE);
704 	}
705 
706 cleanup:
707 	if (ret)
708 		bzero(output->data, output->length);
709 
710 	return (ret);
711 }
712 
713 #else /* User Space */
714 
715 /*ARGSUSED*/
716 krb5_error_code
717 krb5int_aes_decrypt(krb5_context context,
718 	const krb5_keyblock *key, const krb5_data *ivec,
719 	const krb5_data *input, krb5_data *output)
720 {
721 	krb5_error_code ret = 0;
722 	int nblocks, partialamount;
723 	CK_RV rv;
724 	KRB5_MECH_TO_PKCS algos;
725 	CK_MECHANISM mechanism;
726 	CK_ULONG outlen;
727 	char tmp_ivec[BLOCK_SIZE];
728 
729 	assert(input != NULL);
730 	assert(output != NULL);
731 	assert(input->length == output->length);
732 	assert(key != NULL);
733 
734 	if (ivec != NULL) {
735 		/*
736 		 * This function updates ivec->data if the ivec is passed in so
737 		 * it better have a data pointer and a proper length.
738 		 */
739 		if (ivec->data == NULL || ivec->length != BLOCK_SIZE) {
740 			assert(ivec->data != NULL);
741 			assert(ivec->length == BLOCK_SIZE);
742 			KRB5_LOG1(KRB5_ERR, "In krb5int_aes_decrypt: error "
743 			    "ivec->data = %p ivec->length = %d", ivec->data,
744 			    ivec->length);
745 			ret = KRB5_CRYPTO_INTERNAL;
746 			goto cleanup;
747 		}
748 	}
749 
750 	/* number of input blocks including partial block */
751 	nblocks = (input->length + BLOCK_SIZE - 1) / BLOCK_SIZE;
752 	KRB5_LOG(KRB5_INFO, "nblocks = %d", nblocks);
753 	/* get # of bytes in partially filled block */
754 	partialamount = input->length % BLOCK_SIZE;
755 	KRB5_LOG(KRB5_INFO, "partialamount = %d", partialamount);
756 
757 	rv = get_algo(key->enctype, &algos);
758 	if (rv != CKR_OK)
759 		goto cleanup;
760 	assert(algos.enc_algo == CKM_AES_CBC);
761 
762 	rv = init_key_uef(krb_ctx_hSession(context), (krb5_keyblock *)key);
763 	if (rv != CKR_OK) {
764 		goto cleanup;
765 	}
766 
767 	mechanism.mechanism = algos.enc_algo;
768 	if (ivec == NULL) {
769 		bzero(tmp_ivec, sizeof (tmp_ivec));
770 		mechanism.pParameter = tmp_ivec;
771 		mechanism.ulParameterLen = sizeof (tmp_ivec);
772 	} else {
773 		mechanism.pParameter = ivec->data;
774 		mechanism.ulParameterLen = ivec->length;
775 	}
776 
777 	if (nblocks == 1 || (partialamount == 0)) {
778 		char orig_input[BLOCK_SIZE * 2];
779 		/*
780 		 * nlibp = next to last input block pointer
781 		 * libp = last input block pointer
782 		 */
783 		char *nlibp, *libp;
784 
785 		/*
786 		 * Simple case:
787 		 *
788 		 * Swap last 2 ciphertext blocks (all must be full), then use
789 		 * CBC to implement CTS.
790 		 */
791 		if (nblocks > 1) {
792 			/*
793 			 * swap last 2 ciphertext blocks to implement CTS
794 			 */
795 			char tmp[BLOCK_SIZE];
796 
797 			/*
798 			 * Note, the side effect with this is that we are
799 			 * modifying the input->data!
800 			 */
801 			nlibp = input->data + ((nblocks - 2) * BLOCK_SIZE);
802 			libp = input->data + ((nblocks - 1) * BLOCK_SIZE);
803 
804 			/* first save orig input data for later restore */
805 			/* we know that partial amount is 0, because */
806 			/* nblocks is > 1, so we copy the last two blocks */
807 			bcopy(nlibp, orig_input, sizeof (orig_input));
808 
809 			bcopy(nlibp, tmp, BLOCK_SIZE);
810 			bcopy(libp, nlibp, BLOCK_SIZE);
811 			bcopy(tmp, libp, BLOCK_SIZE);
812 		} else {
813 			if (input->length < BLOCK_SIZE)
814 				return (KRB5_CRYPTO_INTERNAL);
815 		}
816 
817 		/*
818 		 * Note, since CBC is assumed to be the underlying mode, this
819 		 * call to C_DecryptInit is setting the IV.  The IV in use here
820 		 * is either the ivec passed in or a block of 0's.  All calls to
821 		 * C_DecryptInit set the IV in this function.
822 		 */
823 		rv = C_DecryptInit(krb_ctx_hSession(context), &mechanism,
824 		    key->hKey);
825 		if (rv != CKR_OK) {
826 			KRB5_LOG(KRB5_ERR, "C_DecryptInit failed in "
827 			    "krb5int_aes_decrypt: rv = 0x%x", rv);
828 			goto cleanup;
829 		}
830 
831 		/*
832 		 * C_Encrypt/Decrypt requires a pointer to long, not a pointer
833 		 * to int cast to pointer to long!!!
834 		 */
835 		outlen = output->length;
836 
837 		rv = C_Decrypt(krb_ctx_hSession(context),
838 		    (CK_BYTE_PTR)input->data,
839 		    input->length,
840 		    (CK_BYTE_PTR)output->data,
841 		    &outlen);
842 
843 		if (nblocks > 1) {
844 			/* restore orig input data */
845 			bcopy(orig_input, nlibp, sizeof (orig_input));
846 		}
847 	} else {
848 		char tmp_ivec_data[BLOCK_SIZE], tmp_input_data[BLOCK_SIZE],
849 		    tmp_output_data[BLOCK_SIZE];
850 		/* pointers to Cn, Cn-1, Cn-2 CipherText */
851 		char *Cn, *Cn_1, *Cn_2;
852 		CK_ULONG length;
853 
854 		/*
855 		 * Complex case:
856 		 *
857 		 * Decrypting in CTS where there is a partial block of
858 		 * ciphertext.
859 		 */
860 
861 		/* setting pointers to CipherText for later use */
862 		Cn = input->data + (input->length - partialamount);
863 		/* Cn - 1 */
864 		Cn_1 = Cn - BLOCK_SIZE;
865 		/* Cn - 2 */
866 		Cn_2 = Cn_1 - BLOCK_SIZE;
867 
868 		if (nblocks > 2) {
869 			rv = C_DecryptInit(krb_ctx_hSession(context),
870 			    &mechanism, key->hKey);
871 			if (rv != CKR_OK) {
872 				KRB5_LOG(KRB5_ERR, "C_DecryptInit failed in "
873 				    "krb5int_aes_decrypt: rv = 0x%x", rv);
874 				goto cleanup;
875 			}
876 			/* set length to include blocks C0 thru Cn-2 */
877 			length = input->length - (BLOCK_SIZE + partialamount);
878 			outlen = length;
879 			/*
880 			 * First decrypt C0 thru Cn-2 using CBC with the input
881 			 * ivec.
882 			 */
883 			rv = C_Decrypt(krb_ctx_hSession(context),
884 			    (CK_BYTE_PTR)input->data,
885 			    length,
886 			    (CK_BYTE_PTR)output->data,
887 			    &outlen);
888 			if (rv != CKR_OK)
889 				goto cleanup;
890 		}
891 
892 		/*
893 		 * Prepare to decrypt Cn-1 using a ivec of Cn with 0 padding.
894 		 */
895 		bzero(tmp_ivec_data, sizeof (tmp_ivec_data));
896 		/* the tmp ivec data holds Cn with 0 padding */
897 		bcopy(Cn, tmp_ivec_data, partialamount);
898 
899 		/* decrypt 1 block */
900 		length = BLOCK_SIZE;
901 		outlen = length;
902 
903 		/* set ivec to Cn with 0 padding */
904 		mechanism.pParameter = tmp_ivec_data;
905 		mechanism.ulParameterLen = sizeof (tmp_ivec_data);
906 
907 		rv = C_DecryptInit(krb_ctx_hSession(context), &mechanism,
908 		    key->hKey);
909 		if (rv != CKR_OK) {
910 			KRB5_LOG(KRB5_ERR, "C_DecryptInit failed in "
911 			    "krb5int_aes_decrypt: rv = 0x%x", rv);
912 			goto cleanup;
913 		}
914 
915 		/*
916 		 * Now decrypt using Cn-1 input, Cn + 0 padding for ivec, Pn &
917 		 * C' output
918 		 */
919 		rv = C_Decrypt(krb_ctx_hSession(context),
920 		    (CK_BYTE_PTR)Cn_1,
921 		    length,
922 		    (CK_BYTE_PTR)tmp_output_data,
923 		    &outlen);
924 
925 		if (rv != CKR_OK)
926 			goto cleanup;
927 
928 		/*
929 		 * tmp input data should hold Cn with C'
930 		 * Note, tmp_output_data contains Pn + C',
931 		 */
932 		/* copy Cn */
933 		bcopy(Cn, tmp_input_data, partialamount);
934 		/* copy C' */
935 		bcopy(tmp_output_data + partialamount,
936 		    tmp_input_data + partialamount,
937 		    (BLOCK_SIZE - partialamount));
938 
939 		/* copy Pn in tmp output to output->data last block */
940 		bcopy(tmp_output_data,
941 		    output->data + (input->length - partialamount),
942 		    partialamount);
943 
944 		if (nblocks > 2) {
945 			/* use Cn-2 as ivec */
946 			mechanism.pParameter = Cn_2;
947 		} else {
948 			/*
949 			 * nblocks == 2
950 			 *
951 			 * Cn-2 does not exist so either use 0 if input ivec
952 			 * does not exist or use the input ivec.
953 			 */
954 			if (ivec == NULL) {
955 				bzero(tmp_ivec_data, sizeof (tmp_ivec_data));
956 			} else {
957 				/* use original input ivec */
958 				mechanism.pParameter = ivec->data;
959 				mechanism.ulParameterLen = ivec->length;
960 			}
961 		}
962 
963 		rv = C_DecryptInit(krb_ctx_hSession(context), &mechanism,
964 		    key->hKey);
965 		if (rv != CKR_OK) {
966 			KRB5_LOG(KRB5_ERR, "C_DecryptInit failed in "
967 			    "krb5int_aes_decrypt: rv = 0x%x", rv);
968 			goto cleanup;
969 		}
970 
971 		/*
972 		 * Now decrypt Cn + C' input, using either Cn-2, original input
973 		 * ivec or 0 for ivec (set above), Pn-1 output.
974 		 */
975 		rv = C_Decrypt(krb_ctx_hSession(context),
976 		    (CK_BYTE_PTR)tmp_input_data,
977 		    length,
978 		    (CK_BYTE_PTR)output->data + (input->length -
979 		    (BLOCK_SIZE + partialamount)),
980 		    &outlen);
981 		if (rv != CKR_OK)
982 			goto cleanup;
983 	} /* end partial block processing */
984 
985 	/*
986 	 * The ivec is updated to allow the caller to chain ivecs, done for the
987 	 * kcmd (rsh/rcp/etc...).  Note this is not done for 1 block although I
988 	 * am not sure why but I'm continuing the tradition from the MIT code.
989 	 */
990 	if (nblocks > 1 && ivec) {
991 		(void) memcpy(ivec->data,
992 		    input->data + ((nblocks - 2) * BLOCK_SIZE),
993 		    BLOCK_SIZE);
994 	}
995 
996 cleanup:
997 	if (rv != CKR_OK)
998 		ret = PKCS_ERR;
999 
1000 	if (ret)
1001 		bzero(output->data, input->length);
1002 
1003 	return (ret);
1004 }
1005 
1006 #endif /* _KERNEL */
1007 
1008 static krb5_error_code
1009 k5_aes_make_key(krb5_context context,
1010 	const krb5_data *randombits, krb5_keyblock *key)
1011 {
1012 	krb5_error_code ret = 0;
1013 	if (key->length != 16 && key->length != 32)
1014 		return (KRB5_BAD_KEYSIZE);
1015 	if (randombits->length != key->length)
1016 		return (KRB5_CRYPTO_INTERNAL);
1017 
1018 	key->magic = KV5M_KEYBLOCK;
1019 	key->dk_list = NULL;
1020 
1021 #ifdef _KERNEL
1022 	key->kef_key.ck_data = NULL;
1023 	key->key_tmpl = NULL;
1024 	(void) memcpy(key->contents, randombits->data, randombits->length);
1025 	ret = init_key_kef(context->kef_cipher_mt, key);
1026 #else
1027 	key->hKey = CK_INVALID_HANDLE;
1028 	(void) memcpy(key->contents, randombits->data, randombits->length);
1029 	ret = init_key_uef(krb_ctx_hSession(context), key);
1030 #endif /* _KERNEL */
1031 
1032 	KRB5_LOG0(KRB5_INFO, "k5_aes_make_key() end\n");
1033 	return (ret);
1034 }
1035 
1036 /*ARGSUSED*/
1037 static krb5_error_code
1038 krb5int_aes_init_state(krb5_context context, const krb5_keyblock *key,
1039 	krb5_keyusage usage, krb5_data *state)
1040 {
1041 	if (!state)
1042 		return (0);
1043 
1044 	if (state && state->data)
1045 		FREE(state->data, state->length);
1046 
1047 	state->length = BLOCK_SIZE;
1048 	state->data = (void *) MALLOC(BLOCK_SIZE);
1049 
1050 	if (state->data == NULL)
1051 		return (ENOMEM);
1052 
1053 	(void) memset(state->data, 0, state->length);
1054 	return (0);
1055 }
1056 
1057 const struct krb5_enc_provider krb5int_enc_aes128 = {
1058     BLOCK_SIZE,
1059     16, 16,
1060     krb5int_aes_encrypt,
1061     krb5int_aes_decrypt,
1062     k5_aes_make_key,
1063     krb5int_aes_init_state,
1064     krb5int_default_free_state
1065 };
1066 
1067 const struct krb5_enc_provider krb5int_enc_aes256 = {
1068     BLOCK_SIZE,
1069     32, 32,
1070     krb5int_aes_encrypt,
1071     krb5int_aes_decrypt,
1072     k5_aes_make_key,
1073     krb5int_aes_init_state,
1074     krb5int_default_free_state
1075 };
1076