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