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