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