1 /*
2  * Copyright 2005 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 #include <k5-int.h>
9 #include <enc_provider.h>
10 
11 #define BLOCK_SIZE 16
12 
13 #define XOR_BLOCK(src, dst) \
14 	(dst)[0] ^= (src)[0]; \
15 	(dst)[1] ^= (src)[1]; \
16 	(dst)[2] ^= (src)[2]; \
17 	(dst)[3] ^= (src)[3]; \
18 	(dst)[4] ^= (src)[4]; \
19 	(dst)[5] ^= (src)[5]; \
20 	(dst)[6] ^= (src)[6]; \
21 	(dst)[7] ^= (src)[7]; \
22 	(dst)[8] ^= (src)[8]; \
23 	(dst)[9] ^= (src)[9]; \
24 	(dst)[10] ^= (src)[10]; \
25 	(dst)[11] ^= (src)[11]; \
26 	(dst)[12] ^= (src)[12]; \
27 	(dst)[13] ^= (src)[13]; \
28 	(dst)[14] ^= (src)[14]; \
29 	(dst)[15] ^= (src)[15]
30 
31 #define xorblock(x,y) XOR_BLOCK(y, x)
32 
33 /*ARGSUSED*/
34 krb5_error_code
35 krb5int_aes_encrypt(krb5_context context,
36 	const krb5_keyblock *key, const krb5_data *ivec,
37 	const krb5_data *input, krb5_data *output)
38 {
39     krb5_error_code ret = 0;
40     unsigned char tmp[BLOCK_SIZE], tmp2[BLOCK_SIZE], tmp3[BLOCK_SIZE];
41     int nblocks = 0, blockno;
42 #ifdef _KERNEL
43     crypto_context_t kef_ctx;
44     crypto_mechanism_t mech;
45     int result = 0;
46 #else
47     CK_RV rv;
48     KRB5_MECH_TO_PKCS algos;
49     CK_MECHANISM mechanism;
50     CK_ULONG outlen;
51 #endif /* _KERNEL */
52 
53     if (ivec && ivec->data != NULL && ivec->length >= BLOCK_SIZE)
54 	(void) memcpy(tmp, ivec->data, BLOCK_SIZE);
55     else
56 	(void) memset(tmp, 0, BLOCK_SIZE);
57 
58     nblocks = (input->length + BLOCK_SIZE - 1) / BLOCK_SIZE;
59 
60 #ifndef _KERNEL
61     rv = get_algo(key->enctype, &algos);
62     if (rv != CKR_OK)
63 	goto cleanup;
64 
65     rv = init_key_uef(krb_ctx_hSession(context), (krb5_keyblock *)key);
66     if (rv != CKR_OK)
67 	goto cleanup;
68 
69     mechanism.mechanism = algos.enc_algo;
70     mechanism.pParameter = (ivec != NULL ? ivec->data : NULL);
71     mechanism.ulParameterLen = (ivec != NULL ? ivec->length : 0);
72 
73     rv = C_EncryptInit(krb_ctx_hSession(context), &mechanism, key->hKey);
74 
75     if (rv != CKR_OK) {
76 	KRB5_LOG(KRB5_ERR, "C_EncryptInit failed in "
77 		"krb5int_aes_encrypt: rv = 0x%x", rv);
78 	goto cleanup;
79     }
80 #endif /* !_KERNEL */
81 
82     if (nblocks == 1) {
83 #ifndef _KERNEL
84 	/* XXX Used for DK function.  */
85 	outlen = (CK_ULONG)output->length;
86 	rv = C_Encrypt(krb_ctx_hSession(context),
87 		(CK_BYTE_PTR)input->data,
88 		(CK_ULONG)input->length,
89 		(CK_BYTE_PTR)output->data,
90 		(CK_ULONG_PTR)&outlen);
91 	output->length = (unsigned int)outlen;
92 #else
93 	ret = k5_ef_crypto((const char *)input->data,
94 		(char *)output->data,
95 		input->length, (krb5_keyblock *)key,
96 		(krb5_data *)ivec, FALSE);
97 #endif /* _KERNEL */
98     } else {
99 	int nleft;
100 #ifdef _KERNEL
101 	crypto_data_t ct, pt;
102 
103 	mech.cm_type = key->kef_mt;
104 	mech.cm_param = (ivec != NULL ? ivec->data : NULL);
105 	mech.cm_param_len = (ivec != NULL ? ivec->length : 0);
106 
107 	result = crypto_encrypt_init(&mech,
108 			(crypto_key_t *)&key->kef_key,
109 			key->key_tmpl, &kef_ctx, NULL);
110 	if (result != CRYPTO_SUCCESS)
111 		goto cleanup;
112 	ct.cd_format = CRYPTO_DATA_RAW;
113 	ct.cd_offset = 0;
114 	ct.cd_length = BLOCK_SIZE;
115 	ct.cd_miscdata = NULL;
116 
117 	pt.cd_format = CRYPTO_DATA_RAW;
118 	pt.cd_offset = 0;
119 	pt.cd_length = BLOCK_SIZE;
120 	pt.cd_miscdata = NULL;
121 #else
122 	CK_ULONG outlen = BLOCK_SIZE;
123 #endif /* _KERNEL */
124 
125 	for (blockno = 0; blockno < nblocks - 2; blockno++) {
126 	    xorblock(tmp, (uchar_t *)input->data + blockno * BLOCK_SIZE);
127 #ifdef _KERNEL
128 	    pt.cd_raw.iov_base = (char *)tmp;
129 	    pt.cd_raw.iov_len = BLOCK_SIZE;
130 	    ct.cd_raw.iov_base = (char *)output->data + blockno * BLOCK_SIZE;
131 	    ct.cd_raw.iov_len = BLOCK_SIZE;
132 
133 	    result = crypto_encrypt_update(kef_ctx, &pt, &ct, NULL);
134 	    if (result != CRYPTO_SUCCESS) {
135 		KRB5_LOG(KRB5_ERR,
136 			"crypto_encrypt_update: error: rv = 0x%08x",
137 			result);
138 		goto cleanup;
139 	    }
140 	    bcopy(output->data + blockno * BLOCK_SIZE, tmp, BLOCK_SIZE);
141 #else
142 	    rv = C_EncryptUpdate(krb_ctx_hSession(context),
143 		(CK_BYTE_PTR)tmp,
144 		(CK_ULONG)BLOCK_SIZE,
145 		(CK_BYTE_PTR)output->data + blockno * BLOCK_SIZE,
146 		(CK_ULONG_PTR)&outlen);
147 
148 	    if (rv != CKR_OK)
149 		goto cleanup;
150 	    (void) memcpy(tmp, output->data + blockno * BLOCK_SIZE,
151 		outlen);
152 #endif
153 	}
154 	/* Do final CTS step for last two blocks (the second of which
155 	   may or may not be incomplete).  */
156 
157 	xorblock(tmp, (uchar_t *)input->data + (nblocks - 2) * BLOCK_SIZE);
158 
159 #ifdef _KERNEL
160 	pt.cd_raw.iov_base = (char *)tmp;
161 	pt.cd_raw.iov_len = BLOCK_SIZE;
162 
163 	ct.cd_raw.iov_base = (char *)tmp2;
164 	ct.cd_raw.iov_len = BLOCK_SIZE;
165 
166 	result = crypto_encrypt_update(kef_ctx, &pt, &ct, NULL);
167 	if (result != CRYPTO_SUCCESS) {
168 	    KRB5_LOG(KRB5_ERR,
169 		"crypto_encrypt_update: error: rv = 0x%08x", result);
170 	    goto cleanup;
171 	}
172 #else
173 	rv = C_EncryptUpdate(krb_ctx_hSession(context),
174 		(CK_BYTE_PTR)tmp,
175 		(CK_ULONG)BLOCK_SIZE,
176 		(CK_BYTE_PTR)tmp2,
177 		(CK_ULONG_PTR)&outlen);
178 
179 	if (rv != CKR_OK)
180 	    goto cleanup;
181 #endif /* _KERNEL */
182 
183 	nleft = input->length - (nblocks - 1) * BLOCK_SIZE;
184 	(void) memcpy(output->data + (nblocks - 1) * BLOCK_SIZE,
185 		tmp2, nleft);
186 	(void) memcpy(tmp, tmp2, BLOCK_SIZE);
187 
188 	/*
189 	 * This effectively adds 0's as pad bytes if the last
190 	 * block is not exactly BLOCK_SIZE.
191 	 */
192 	(void) memset(tmp3, 0, sizeof(tmp3));
193 
194 	(void) memcpy(tmp3, input->data + (nblocks - 1) * BLOCK_SIZE, nleft);
195 	xorblock(tmp, tmp3);
196 #ifdef _KERNEL
197 	pt.cd_raw.iov_base = (char *)tmp;
198 	pt.cd_raw.iov_len = BLOCK_SIZE;
199 
200 	ct.cd_raw.iov_base = (char *)tmp2;
201 	ct.cd_raw.iov_len = BLOCK_SIZE;
202 
203 	result = crypto_encrypt_update(kef_ctx, &pt, &ct, NULL);
204 	if (result != CRYPTO_SUCCESS) {
205 	    KRB5_LOG(KRB5_ERR,
206 		"crypto_encrypt_update: error: rv = 0x%08x", result);
207 	    goto cleanup;
208 	}
209 #else
210 	rv = C_EncryptUpdate(krb_ctx_hSession(context),
211 		(CK_BYTE_PTR)tmp,
212 		(CK_ULONG)BLOCK_SIZE,
213 		(CK_BYTE_PTR)tmp2,
214 		(CK_ULONG_PTR)&outlen);
215 
216 	if (rv != CKR_OK)
217 	    goto cleanup;
218 #endif /* _KERNEL */
219 
220 	(void) memcpy(output->data + (nblocks - 2) * BLOCK_SIZE,
221 		tmp2, BLOCK_SIZE);
222 
223 	if (ivec && ivec->data != NULL && ivec->length >= BLOCK_SIZE) {
224 		(void) memcpy(ivec->data, tmp2, BLOCK_SIZE);
225 	}
226 
227 #ifdef _KERNEL
228 	ct.cd_raw.iov_base = (char *)tmp2;
229 	ct.cd_raw.iov_len = BLOCK_SIZE;
230 
231 	result = crypto_encrypt_final(kef_ctx, &ct, NULL);
232 #else
233 	/* Close the crypto session, ignore the output */
234 	rv = C_EncryptFinal(krb_ctx_hSession(context),
235 		(CK_BYTE_PTR)tmp2, (CK_ULONG_PTR)&outlen);
236 
237 	if (rv != CKR_OK)
238 	    goto cleanup;
239 #endif /* _KERNEL */
240     }
241 
242 cleanup:
243 
244 #ifdef _KERNEL
245     ret = result;
246 #else
247     if (rv != CKR_OK)
248 	ret = PKCS_ERR;
249 #endif /* _KERNEL */
250 
251     if (ret)
252 	bzero(output->data, input->length);
253 
254     return (ret);
255 }
256 
257 /*ARGSUSED*/
258 krb5_error_code
259 krb5int_aes_decrypt(krb5_context context,
260 	const krb5_keyblock *key, const krb5_data *ivec,
261 	const krb5_data *input, krb5_data *output)
262 {
263     krb5_error_code ret = 0;
264     unsigned char tmp[BLOCK_SIZE], tmp2[BLOCK_SIZE], tmp3[BLOCK_SIZE];
265     int nblocks = 0, blockno;
266 #ifdef _KERNEL
267     crypto_context_t kef_ctx;
268     crypto_mechanism_t mech;
269     int result = 0;
270 #else
271     CK_RV rv;
272     KRB5_MECH_TO_PKCS algos;
273     CK_MECHANISM mechanism;
274     CK_ULONG outlen;
275 #endif /* _KERNEL */
276 
277     if (ivec && ivec->data != NULL && ivec->length >= BLOCK_SIZE)
278 	(void) memcpy(tmp, ivec->data, BLOCK_SIZE);
279     else
280 	(void) memset(tmp, 0, BLOCK_SIZE);
281 
282     nblocks = (input->length + BLOCK_SIZE - 1) / BLOCK_SIZE;
283 
284 #ifndef _KERNEL
285     rv = get_algo(key->enctype, &algos);
286     if (rv != CKR_OK)
287 	goto cleanup;
288 
289     rv = init_key_uef(krb_ctx_hSession(context), (krb5_keyblock *)key);
290     if (rv != CKR_OK) {
291 	goto cleanup;
292     }
293 
294     mechanism.mechanism = algos.enc_algo;
295     mechanism.pParameter = (ivec != NULL ? ivec->data : NULL);
296     mechanism.ulParameterLen = (ivec != NULL ? ivec->length : 0);
297 
298     rv = C_DecryptInit(krb_ctx_hSession(context), &mechanism, key->hKey);
299 
300     if (rv != CKR_OK) {
301 	KRB5_LOG(KRB5_ERR, "C_DecryptInit failed in "
302 		"krb5int_aes_decrypt: rv = 0x%x", rv);
303 	goto cleanup;
304     }
305 #endif
306 
307     if (nblocks == 1) {
308 	if (input->length < BLOCK_SIZE)
309 	    return (KRB5_CRYPTO_INTERNAL);
310 #ifndef _KERNEL
311 	rv = C_Decrypt(krb_ctx_hSession(context),
312 		(CK_BYTE_PTR)input->data,
313 		(CK_ULONG)input->length,
314 		(CK_BYTE_PTR)output->data,
315 		(CK_ULONG_PTR)&output->length);
316 #else
317 	ret = k5_ef_crypto((const char *)input->data, (char *)output->data,
318 		input->length, (krb5_keyblock *)key,
319 		(krb5_data *)ivec, FALSE);
320 #endif /* _KERNEL */
321 
322     } else {
323 #ifdef _KERNEL
324 	crypto_data_t ct, pt;
325 
326 	(void) memset(&ct, 0, sizeof(ct));
327 	(void) memset(&pt, 0, sizeof(pt));
328 
329 	mech.cm_type = key->kef_mt;
330 	mech.cm_param = (ivec != NULL ? ivec->data : NULL);
331 	mech.cm_param_len = (ivec != NULL ? ivec->length : 0);
332 
333 	result = crypto_decrypt_init(&mech,
334 			(crypto_key_t *)&key->kef_key,
335 			key->key_tmpl, &kef_ctx, NULL);
336 	if (result != CRYPTO_SUCCESS)
337 		goto cleanup;
338 	ct.cd_format = CRYPTO_DATA_RAW;
339 	ct.cd_offset = 0;
340 	ct.cd_length = BLOCK_SIZE;
341 	ct.cd_raw.iov_len = BLOCK_SIZE;
342 
343 	pt.cd_format = CRYPTO_DATA_RAW;
344 	pt.cd_offset = 0;
345 	pt.cd_length = BLOCK_SIZE;
346 	pt.cd_raw.iov_len = BLOCK_SIZE;
347 #endif /* _KERNEL */
348 
349 	for (blockno = 0; blockno < nblocks - 2; blockno++) {
350 #ifdef _KERNEL
351 	    KRB5_LOG(KRB5_INFO, "krb5int_aes_decrypt: blockno = %d",
352 		blockno);
353 	    ct.cd_raw.iov_base = (char *)input->data + blockno * BLOCK_SIZE;
354 	    pt.cd_raw.iov_base = (char *)tmp2;
355 
356 	    result = crypto_decrypt_update(kef_ctx, &ct, &pt, NULL);
357 	    if (result != CRYPTO_SUCCESS) {
358 		KRB5_LOG(KRB5_ERR,
359 			"crypto_decrypt_update: error: rv = 0x%08x",
360 			result);
361 		goto cleanup;
362 	    }
363 #else
364 	    outlen = sizeof(tmp2);
365 	    rv = C_DecryptUpdate(krb_ctx_hSession(context),
366 		(CK_BYTE_PTR)input->data + blockno * BLOCK_SIZE,
367 		(CK_ULONG)BLOCK_SIZE,
368 		(CK_BYTE_PTR)tmp2,
369 		(CK_ULONG_PTR)&outlen);
370 
371 	    if (rv != CKR_OK)
372 		goto cleanup;
373 
374 #endif /* _KERNEL */
375 	    xorblock(tmp2, tmp);
376 	    (void) memcpy(output->data + blockno * BLOCK_SIZE,
377 			tmp2, BLOCK_SIZE);
378 	    (void) memcpy(tmp, input->data + blockno * BLOCK_SIZE,
379 			BLOCK_SIZE);
380 	}
381 	/* Do last two blocks, the second of which (next-to-last block
382 	   of plaintext) may be incomplete.  */
383 #ifdef _KERNEL
384 	ct.cd_raw.iov_base = (char *)input->data + (nblocks - 2) * BLOCK_SIZE;
385 	ct.cd_raw.iov_len = BLOCK_SIZE;
386 	pt.cd_raw.iov_base = (char *)tmp2;
387 	pt.cd_raw.iov_len = BLOCK_SIZE;
388 
389 	result = crypto_decrypt_update(kef_ctx, &ct, &pt, NULL);
390 	if (result != CRYPTO_SUCCESS) {
391 	    KRB5_LOG(KRB5_ERR,
392 		"crypto_decrypt_update: error: rv = 0x%08x",
393 		result);
394 		goto cleanup;
395 	}
396 #else
397 	outlen = sizeof(tmp2);
398 	rv = C_DecryptUpdate(krb_ctx_hSession(context),
399 		(CK_BYTE_PTR)input->data + (nblocks - 2) * BLOCK_SIZE,
400 		(CK_ULONG)BLOCK_SIZE,
401 		(CK_BYTE_PTR)tmp2,
402 		(CK_ULONG_PTR)&outlen);
403 
404 	if (rv != CKR_OK)
405 	    goto cleanup;
406 #endif /* _KERNEL */
407 
408 	/* Set tmp3 to last ciphertext block, padded.  */
409 	(void) memset(tmp3, 0, sizeof(tmp3));
410 	(void) memcpy(tmp3, input->data + (nblocks - 1) * BLOCK_SIZE,
411 	       input->length - (nblocks - 1) * BLOCK_SIZE);
412 
413 	/* Set tmp2 to last (possibly partial) plaintext block, and save it.  */
414 	xorblock(tmp2, tmp3);
415 	(void) memcpy(output->data + (nblocks - 1) * BLOCK_SIZE, tmp2,
416 	       input->length - (nblocks - 1) * BLOCK_SIZE);
417 
418 	/*
419 	 * Maybe keep the trailing part, and copy in the
420 	 * last ciphertext block.
421 	 */
422 	(void) memcpy(tmp2, tmp3, input->length - (nblocks - 1) * BLOCK_SIZE);
423 
424 	/*
425 	 * Decrypt, to get next to last plaintext block xor
426 	 * previous ciphertext.
427 	 */
428 #ifdef _KERNEL
429 	ct.cd_raw.iov_base = (char *)tmp2;
430 	ct.cd_raw.iov_len = BLOCK_SIZE;
431 	pt.cd_raw.iov_base = (char *)tmp3;
432 	pt.cd_raw.iov_len = BLOCK_SIZE;
433 
434 	result = crypto_decrypt_update(kef_ctx, &ct, &pt, NULL);
435 	if (result != CRYPTO_SUCCESS) {
436 	    KRB5_LOG(KRB5_ERR,
437 		"crypto_decrypt_update: error: rv = 0x%08x",
438 		result);
439 		goto cleanup;
440 	}
441 #else
442 	outlen = sizeof(tmp3);
443 	rv = C_DecryptUpdate(krb_ctx_hSession(context),
444 	    (CK_BYTE_PTR)tmp2,
445 	    (CK_ULONG)BLOCK_SIZE,
446 	    (CK_BYTE_PTR)tmp3,
447 	    (CK_ULONG_PTR)&outlen);
448 
449 	if (rv != CKR_OK)
450 	    goto cleanup;
451 #endif /* _KERNEL */
452 
453 	xorblock(tmp3, tmp);
454 	(void) memcpy(output->data + (nblocks - 2) * BLOCK_SIZE,
455 		tmp3, BLOCK_SIZE);
456 
457 	if (ivec)
458 		(void) memcpy(ivec->data,
459 			input->data + (nblocks - 2) * BLOCK_SIZE, BLOCK_SIZE);
460 
461 #ifdef _KERNEL
462 	pt.cd_raw.iov_base = (char *)tmp2;
463 	pt.cd_raw.iov_len = BLOCK_SIZE;
464 	result = crypto_decrypt_final(kef_ctx, &pt, NULL);
465 #else
466 	outlen = sizeof(tmp2);
467 	rv = C_DecryptFinal(krb_ctx_hSession(context),
468 		(CK_BYTE_PTR)tmp2, (CK_ULONG_PTR)&outlen);
469 
470 	if (rv != CKR_OK)
471 	    goto cleanup;
472 #endif /* _KERNEL */
473     }
474 cleanup:
475 #ifdef _KERNEL
476     ret = result;
477 #else
478     if (rv != CKR_OK)
479 	ret = PKCS_ERR;
480 #endif /* _KERNEL */
481 
482     if (ret)
483 	bzero(output->data, input->length);
484 
485     return (ret);
486 }
487 
488 static krb5_error_code
489 k5_aes_make_key(krb5_context context,
490 	const krb5_data *randombits, krb5_keyblock *key)
491 {
492     krb5_error_code ret = 0;
493     if (key->length != 16 && key->length != 32)
494 	return(KRB5_BAD_KEYSIZE);
495     if (randombits->length != key->length)
496 	return(KRB5_CRYPTO_INTERNAL);
497 
498     key->magic = KV5M_KEYBLOCK;
499     key->dk_list = NULL;
500 
501 #ifdef _KERNEL
502     key->kef_key.ck_data = NULL;
503     key->key_tmpl = NULL;
504     (void) memcpy(key->contents, randombits->data, randombits->length);
505     ret = init_key_kef(context->kef_cipher_mt, key);
506 #else
507     key->hKey = CK_INVALID_HANDLE;
508     (void) memcpy(key->contents, randombits->data, randombits->length);
509     ret = init_key_uef(krb_ctx_hSession(context), key);
510 #endif /* _KERNEL */
511 
512     KRB5_LOG0(KRB5_INFO, "k5_aes_make_key() end\n");
513     return (ret);
514 }
515 
516 /*ARGSUSED*/
517 static krb5_error_code
518 krb5int_aes_init_state (krb5_context context, const krb5_keyblock *key,
519 	krb5_keyusage usage, krb5_data *state)
520 {
521     if (!state)
522 	return (0);
523 
524     if (state && state->data)
525 	FREE(state->data, state->length);
526 
527     state->length = BLOCK_SIZE;
528     state->data = (void *) MALLOC(BLOCK_SIZE);
529 
530     if (state->data == NULL)
531 	return ENOMEM;
532 
533     (void) memset(state->data, 0, state->length);
534     return (0);
535 }
536 
537 const struct krb5_enc_provider krb5int_enc_aes128 = {
538     BLOCK_SIZE,
539     16, 16,
540     krb5int_aes_encrypt,
541     krb5int_aes_decrypt,
542     k5_aes_make_key,
543     krb5int_aes_init_state,
544     krb5int_default_free_state
545 };
546 
547 const struct krb5_enc_provider krb5int_enc_aes256 = {
548     BLOCK_SIZE,
549     32, 32,
550     krb5int_aes_encrypt,
551     krb5int_aes_decrypt,
552     k5_aes_make_key,
553     krb5int_aes_init_state,
554     krb5int_default_free_state
555 };
556