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