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 /* we know that partial amount is 0, because */ 564 /* nblocks is > 1, so we copy the last two blocks */ 565 bcopy(nlibp, orig_input, sizeof (orig_input)); 566 567 /* swap */ 568 bcopy(nlibp, tmp, BLOCK_SIZE); 569 bcopy(libp, nlibp, BLOCK_SIZE); 570 bcopy(tmp, libp, BLOCK_SIZE); 571 } 572 573 ret = k5_ef_crypto((const char *)input->data, 574 (char *)output->data, 575 input->length, (krb5_keyblock *)key, 576 &local_iv, FALSE); 577 578 if (nblocks > 1) { 579 /* restore orig input data */ 580 bcopy(orig_input, nlibp, sizeof (orig_input)); 581 } 582 583 if (ret != 0) { 584 KRB5_LOG(KRB5_ERR, 585 "k5_ef_crypto returned error: ret = 0x%08x", 586 ret); 587 goto cleanup; 588 } 589 590 } else { 591 krb5_data tmp_ivec; 592 char tmp_ivec_data[BLOCK_SIZE], tmp_input_data[BLOCK_SIZE], 593 tmp_output_data[BLOCK_SIZE]; 594 /* pointers to Cn, Cn-1, Cn-2 CipherText */ 595 char *Cn, *Cn_1, *Cn_2; 596 long length; 597 598 /* 599 * Complex case: 600 * 601 * Decrypting in CTS where there is a partial block of 602 * ciphertext. 603 */ 604 605 /* setting pointers to CipherText for later use */ 606 Cn = input->data + (input->length - partialamount); 607 /* Cn - 1 */ 608 Cn_1 = Cn - BLOCK_SIZE; 609 /* Cn - 2 */ 610 Cn_2 = Cn_1 - BLOCK_SIZE; 611 612 if (nblocks > 2) { 613 /* set length to include blocks C0 thru Cn-2 */ 614 length = input->length - (BLOCK_SIZE + partialamount); 615 616 /* 617 * First decrypt C0 thru Cn-2 using CBC with the input 618 * ivec. 619 */ 620 ret = k5_ef_crypto((const char *)input->data, 621 output->data, length, (krb5_keyblock *)key, 622 &local_iv, FALSE); 623 624 if (ret != 0) { 625 KRB5_LOG(KRB5_ERR, 626 "k5_ef_crypto: error: ret = 0x%08x", 627 ret); 628 goto cleanup; 629 } 630 } 631 /* 632 * Prepare to decrypt Cn-1 using a ivec of Cn with 0 padding. 633 */ 634 bzero(tmp_ivec_data, sizeof (tmp_ivec_data)); 635 /* the tmp ivec data holds Cn with 0 padding */ 636 bcopy(Cn, tmp_ivec_data, partialamount); 637 tmp_ivec.data = tmp_ivec_data; 638 tmp_ivec.length = sizeof (tmp_ivec_data); 639 640 /* decrypt 1 block */ 641 length = BLOCK_SIZE; 642 643 /* 644 * Now decrypt using Cn-1 input, Cn + 0 padding for ivec, Pn & 645 * C' output 646 */ 647 ret = k5_ef_crypto((const char *)Cn_1, 648 tmp_output_data, length, 649 (krb5_keyblock *)key, &tmp_ivec, FALSE); 650 651 if (ret != 0) { 652 KRB5_LOG(KRB5_ERR, 653 "k5_ef_crypto: error: ret = 0x%08x", 654 ret); 655 goto cleanup; 656 } 657 /* 658 * tmp input data should hold Cn with C' 659 * Note, tmp_output_data contains Pn + C', 660 */ 661 /* copy Cn */ 662 bcopy(Cn, tmp_input_data, partialamount); 663 /* copy C' */ 664 bcopy(tmp_output_data + partialamount, 665 tmp_input_data + partialamount, 666 (BLOCK_SIZE - partialamount)); 667 668 /* copy Pn in tmp output to output->data */ 669 bcopy(tmp_output_data, 670 output->data + (input->length - partialamount), 671 partialamount); 672 673 if (nblocks > 2) { 674 /* use Cn-2 as ivec */ 675 tmp_ivec.data = Cn_2; 676 } else { 677 /* use 0 as ivec because Cn-2 does not exist */ 678 bzero(tmp_ivec_data, sizeof (tmp_ivec_data)); 679 } 680 681 /* 682 * Now decrypt Cn + C' input, using either Cn-2 or 0 for ivec 683 * (set above), Pn-1 output. 684 */ 685 ret = k5_ef_crypto((const char *)tmp_input_data, 686 (char *)output->data + 687 (input->length - (BLOCK_SIZE + partialamount)), 688 length, (krb5_keyblock *)key, 689 &tmp_ivec, FALSE); 690 691 if (ret != 0) { 692 KRB5_LOG(KRB5_ERR, 693 "k5_ef_crypto: error: ret = 0x%08x", 694 ret); 695 goto cleanup; 696 } 697 698 } /* end partial block processing */ 699 /* 700 * The ivec is updated to allow the caller to chain ivecs. At this 701 * point I don't think any kernel callers are using this however the 702 * userland version of this function does it so this should be done in 703 * kernel for consistency's sake. This is not done for 1 block, got 704 * this from MIT. 705 */ 706 if (nblocks > 1 && ivec) { 707 (void) memcpy(ivec->data, 708 input->data + ((nblocks - 2) * BLOCK_SIZE), 709 BLOCK_SIZE); 710 } 711 712 cleanup: 713 if (ret) 714 bzero(output->data, output->length); 715 716 return (ret); 717 } 718 719 #else /* User Space */ 720 721 /*ARGSUSED*/ 722 krb5_error_code 723 krb5int_aes_decrypt(krb5_context context, 724 const krb5_keyblock *key, const krb5_data *ivec, 725 const krb5_data *input, krb5_data *output) 726 { 727 krb5_error_code ret = 0; 728 int nblocks, partialamount; 729 CK_RV rv; 730 KRB5_MECH_TO_PKCS algos; 731 CK_MECHANISM mechanism; 732 CK_ULONG outlen; 733 char tmp_ivec[BLOCK_SIZE]; 734 735 assert(input != NULL); 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 } else { 819 if (input->length < BLOCK_SIZE) 820 return (KRB5_CRYPTO_INTERNAL); 821 } 822 823 /* 824 * Note, since CBC is assumed to be the underlying mode, this 825 * call to C_DecryptInit is setting the IV. The IV in use here 826 * is either the ivec passed in or a block of 0's. All calls to 827 * C_DecryptInit set the IV in this function. 828 */ 829 rv = C_DecryptInit(krb_ctx_hSession(context), &mechanism, 830 key->hKey); 831 if (rv != CKR_OK) { 832 KRB5_LOG(KRB5_ERR, "C_DecryptInit failed in " 833 "krb5int_aes_decrypt: rv = 0x%x", rv); 834 goto cleanup; 835 } 836 837 /* 838 * C_Encrypt/Decrypt requires a pointer to long, not a pointer 839 * to int cast to pointer to long!!! 840 */ 841 outlen = output->length; 842 843 rv = C_Decrypt(krb_ctx_hSession(context), 844 (CK_BYTE_PTR)input->data, 845 input->length, 846 (CK_BYTE_PTR)output->data, 847 &outlen); 848 849 if (nblocks > 1) { 850 /* restore orig input data */ 851 bcopy(orig_input, nlibp, sizeof (orig_input)); 852 } 853 } else { 854 char tmp_ivec_data[BLOCK_SIZE], tmp_input_data[BLOCK_SIZE], 855 tmp_output_data[BLOCK_SIZE]; 856 /* pointers to Cn, Cn-1, Cn-2 CipherText */ 857 char *Cn, *Cn_1, *Cn_2; 858 CK_ULONG length; 859 860 /* 861 * Complex case: 862 * 863 * Decrypting in CTS where there is a partial block of 864 * ciphertext. 865 */ 866 867 /* setting pointers to CipherText for later use */ 868 Cn = input->data + (input->length - partialamount); 869 /* Cn - 1 */ 870 Cn_1 = Cn - BLOCK_SIZE; 871 /* Cn - 2 */ 872 Cn_2 = Cn_1 - BLOCK_SIZE; 873 874 if (nblocks > 2) { 875 rv = C_DecryptInit(krb_ctx_hSession(context), 876 &mechanism, key->hKey); 877 if (rv != CKR_OK) { 878 KRB5_LOG(KRB5_ERR, "C_DecryptInit failed in " 879 "krb5int_aes_decrypt: rv = 0x%x", rv); 880 goto cleanup; 881 } 882 /* set length to include blocks C0 thru Cn-2 */ 883 length = input->length - (BLOCK_SIZE + partialamount); 884 outlen = length; 885 /* 886 * First decrypt C0 thru Cn-2 using CBC with the input 887 * ivec. 888 */ 889 rv = C_Decrypt(krb_ctx_hSession(context), 890 (CK_BYTE_PTR)input->data, 891 length, 892 (CK_BYTE_PTR)output->data, 893 &outlen); 894 if (rv != CKR_OK) 895 goto cleanup; 896 } 897 898 /* 899 * Prepare to decrypt Cn-1 using a ivec of Cn with 0 padding. 900 */ 901 bzero(tmp_ivec_data, sizeof (tmp_ivec_data)); 902 /* the tmp ivec data holds Cn with 0 padding */ 903 bcopy(Cn, tmp_ivec_data, partialamount); 904 905 /* decrypt 1 block */ 906 length = BLOCK_SIZE; 907 outlen = length; 908 909 /* set ivec to Cn with 0 padding */ 910 mechanism.pParameter = tmp_ivec_data; 911 mechanism.ulParameterLen = sizeof (tmp_ivec_data); 912 913 rv = C_DecryptInit(krb_ctx_hSession(context), &mechanism, 914 key->hKey); 915 if (rv != CKR_OK) { 916 KRB5_LOG(KRB5_ERR, "C_DecryptInit failed in " 917 "krb5int_aes_decrypt: rv = 0x%x", rv); 918 goto cleanup; 919 } 920 921 /* 922 * Now decrypt using Cn-1 input, Cn + 0 padding for ivec, Pn & 923 * C' output 924 */ 925 rv = C_Decrypt(krb_ctx_hSession(context), 926 (CK_BYTE_PTR)Cn_1, 927 length, 928 (CK_BYTE_PTR)tmp_output_data, 929 &outlen); 930 931 if (rv != CKR_OK) 932 goto cleanup; 933 934 /* 935 * tmp input data should hold Cn with C' 936 * Note, tmp_output_data contains Pn + C', 937 */ 938 /* copy Cn */ 939 bcopy(Cn, tmp_input_data, partialamount); 940 /* copy C' */ 941 bcopy(tmp_output_data + partialamount, 942 tmp_input_data + partialamount, 943 (BLOCK_SIZE - partialamount)); 944 945 /* copy Pn in tmp output to output->data last block */ 946 bcopy(tmp_output_data, 947 output->data + (input->length - partialamount), 948 partialamount); 949 950 if (nblocks > 2) { 951 /* use Cn-2 as ivec */ 952 mechanism.pParameter = Cn_2; 953 } else { 954 /* 955 * nblocks == 2 956 * 957 * Cn-2 does not exist so either use 0 if input ivec 958 * does not exist or use the input ivec. 959 */ 960 if (ivec == NULL) { 961 bzero(tmp_ivec_data, sizeof (tmp_ivec_data)); 962 } else { 963 /* use original input ivec */ 964 mechanism.pParameter = ivec->data; 965 mechanism.ulParameterLen = ivec->length; 966 } 967 } 968 969 rv = C_DecryptInit(krb_ctx_hSession(context), &mechanism, 970 key->hKey); 971 if (rv != CKR_OK) { 972 KRB5_LOG(KRB5_ERR, "C_DecryptInit failed in " 973 "krb5int_aes_decrypt: rv = 0x%x", rv); 974 goto cleanup; 975 } 976 977 /* 978 * Now decrypt Cn + C' input, using either Cn-2, original input 979 * ivec or 0 for ivec (set above), Pn-1 output. 980 */ 981 rv = C_Decrypt(krb_ctx_hSession(context), 982 (CK_BYTE_PTR)tmp_input_data, 983 length, 984 (CK_BYTE_PTR)output->data + (input->length - 985 (BLOCK_SIZE + partialamount)), 986 &outlen); 987 if (rv != CKR_OK) 988 goto cleanup; 989 } /* end partial block processing */ 990 991 /* 992 * The ivec is updated to allow the caller to chain ivecs, done for the 993 * kcmd (rsh/rcp/etc...). Note this is not done for 1 block although I 994 * am not sure why but I'm continuing the tradition from the MIT code. 995 */ 996 if (nblocks > 1 && ivec) { 997 (void) memcpy(ivec->data, 998 input->data + ((nblocks - 2) * BLOCK_SIZE), 999 BLOCK_SIZE); 1000 } 1001 1002 cleanup: 1003 if (rv != CKR_OK) 1004 ret = PKCS_ERR; 1005 1006 if (ret) 1007 bzero(output->data, input->length); 1008 1009 return (ret); 1010 } 1011 1012 #endif /* _KERNEL */ 1013 1014 static krb5_error_code 1015 k5_aes_make_key(krb5_context context, 1016 const krb5_data *randombits, krb5_keyblock *key) 1017 { 1018 krb5_error_code ret = 0; 1019 if (key->length != 16 && key->length != 32) 1020 return (KRB5_BAD_KEYSIZE); 1021 if (randombits->length != key->length) 1022 return (KRB5_CRYPTO_INTERNAL); 1023 1024 key->magic = KV5M_KEYBLOCK; 1025 key->dk_list = NULL; 1026 1027 #ifdef _KERNEL 1028 key->kef_key.ck_data = NULL; 1029 key->key_tmpl = NULL; 1030 (void) memcpy(key->contents, randombits->data, randombits->length); 1031 ret = init_key_kef(context->kef_cipher_mt, key); 1032 #else 1033 key->hKey = CK_INVALID_HANDLE; 1034 (void) memcpy(key->contents, randombits->data, randombits->length); 1035 ret = init_key_uef(krb_ctx_hSession(context), key); 1036 #endif /* _KERNEL */ 1037 1038 KRB5_LOG0(KRB5_INFO, "k5_aes_make_key() end\n"); 1039 return (ret); 1040 } 1041 1042 /*ARGSUSED*/ 1043 static krb5_error_code 1044 krb5int_aes_init_state(krb5_context context, const krb5_keyblock *key, 1045 krb5_keyusage usage, krb5_data *state) 1046 { 1047 if (!state) 1048 return (0); 1049 1050 if (state && state->data) 1051 FREE(state->data, state->length); 1052 1053 state->length = BLOCK_SIZE; 1054 state->data = (void *) MALLOC(BLOCK_SIZE); 1055 1056 if (state->data == NULL) 1057 return (ENOMEM); 1058 1059 (void) memset(state->data, 0, state->length); 1060 return (0); 1061 } 1062 1063 const struct krb5_enc_provider krb5int_enc_aes128 = { 1064 BLOCK_SIZE, 1065 16, 16, 1066 krb5int_aes_encrypt, 1067 krb5int_aes_decrypt, 1068 k5_aes_make_key, 1069 krb5int_aes_init_state, 1070 krb5int_default_free_state 1071 }; 1072 1073 const struct krb5_enc_provider krb5int_enc_aes256 = { 1074 BLOCK_SIZE, 1075 32, 32, 1076 krb5int_aes_encrypt, 1077 krb5int_aes_decrypt, 1078 k5_aes_make_key, 1079 krb5int_aes_init_state, 1080 krb5int_default_free_state 1081 }; 1082