1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 /* 22 * Copyright 2010 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 /* 27 * RC4 provider for the Kernel Cryptographic Framework (KCF) 28 */ 29 30 #include <sys/types.h> 31 #include <sys/systm.h> 32 #include <sys/modctl.h> 33 #include <sys/cmn_err.h> 34 #include <sys/ddi.h> 35 #include <sys/crypto/common.h> 36 #include <sys/crypto/spi.h> 37 #include <sys/sysmacros.h> 38 #include <sys/strsun.h> 39 #include <arcfour.h> 40 41 extern struct mod_ops mod_cryptoops; 42 43 /* 44 * Module linkage information for the kernel. 45 */ 46 static struct modlcrypto modlcrypto = { 47 &mod_cryptoops, 48 "RC4 Kernel SW Provider" 49 }; 50 51 static struct modlinkage modlinkage = { 52 MODREV_1, 53 (void *)&modlcrypto, 54 NULL 55 }; 56 57 /* 58 * CSPI information (entry points, provider info, etc.) 59 */ 60 61 #define RC4_MECH_INFO_TYPE 0 62 /* 63 * Mechanism info structure passed to KCF during registration. 64 */ 65 static crypto_mech_info_t rc4_mech_info_tab[] = { 66 {SUN_CKM_RC4, RC4_MECH_INFO_TYPE, 67 CRYPTO_FG_ENCRYPT | CRYPTO_FG_ENCRYPT_ATOMIC | 68 CRYPTO_FG_DECRYPT | CRYPTO_FG_DECRYPT_ATOMIC, 69 ARCFOUR_MIN_KEY_BITS, ARCFOUR_MAX_KEY_BITS, 70 CRYPTO_KEYSIZE_UNIT_IN_BITS | CRYPTO_CAN_SHARE_OPSTATE} 71 }; 72 73 static void rc4_provider_status(crypto_provider_handle_t, uint_t *); 74 75 static crypto_control_ops_t rc4_control_ops = { 76 rc4_provider_status 77 }; 78 79 static int rc4_common_init(crypto_ctx_t *, crypto_mechanism_t *, 80 crypto_key_t *, crypto_spi_ctx_template_t, crypto_req_handle_t); 81 82 static int rc4_crypt_update(crypto_ctx_t *, crypto_data_t *, crypto_data_t *, 83 crypto_req_handle_t); 84 85 static int rc4_crypt_final(crypto_ctx_t *, crypto_data_t *, 86 crypto_req_handle_t); 87 88 static int rc4_crypt(crypto_ctx_t *, crypto_data_t *, crypto_data_t *, 89 crypto_req_handle_t); 90 91 static int rc4_crypt_atomic(crypto_provider_handle_t, crypto_session_id_t, 92 crypto_mechanism_t *, crypto_key_t *, crypto_data_t *, 93 crypto_data_t *, crypto_spi_ctx_template_t, crypto_req_handle_t); 94 95 96 static crypto_cipher_ops_t rc4_cipher_ops = { 97 rc4_common_init, 98 rc4_crypt, 99 rc4_crypt_update, 100 rc4_crypt_final, 101 rc4_crypt_atomic, 102 rc4_common_init, 103 rc4_crypt, 104 rc4_crypt_update, 105 rc4_crypt_final, 106 rc4_crypt_atomic 107 }; 108 109 static int rc4_free_context(crypto_ctx_t *); 110 111 static crypto_ctx_ops_t rc4_ctx_ops = { 112 NULL, 113 rc4_free_context 114 }; 115 116 static crypto_ops_t rc4_crypto_ops = { 117 &rc4_control_ops, 118 NULL, 119 &rc4_cipher_ops, 120 NULL, 121 NULL, 122 NULL, 123 NULL, 124 NULL, 125 NULL, 126 NULL, 127 NULL, 128 NULL, 129 NULL, 130 &rc4_ctx_ops 131 }; 132 133 static crypto_provider_info_t rc4_prov_info = { 134 CRYPTO_SPI_VERSION_1, 135 "RC4 Software Provider", 136 CRYPTO_SW_PROVIDER, 137 {&modlinkage}, 138 NULL, 139 &rc4_crypto_ops, 140 sizeof (rc4_mech_info_tab)/sizeof (crypto_mech_info_t), 141 rc4_mech_info_tab 142 }; 143 144 static crypto_kcf_provider_handle_t rc4_prov_handle = NULL; 145 146 static mblk_t *advance_position(mblk_t *, off_t, uchar_t **); 147 static int crypto_arcfour_crypt(ARCFour_key *, uchar_t *, crypto_data_t *, 148 int); 149 150 int 151 _init(void) 152 { 153 int ret; 154 155 if ((ret = mod_install(&modlinkage)) != 0) 156 return (ret); 157 158 /* Register with KCF. If the registration fails, remove the module. */ 159 if (crypto_register_provider(&rc4_prov_info, &rc4_prov_handle)) { 160 (void) mod_remove(&modlinkage); 161 return (EACCES); 162 } 163 164 return (0); 165 } 166 167 int 168 _fini(void) 169 { 170 /* Unregister from KCF if module is registered */ 171 if (rc4_prov_handle != NULL) { 172 if (crypto_unregister_provider(rc4_prov_handle)) 173 return (EBUSY); 174 175 rc4_prov_handle = NULL; 176 } 177 178 return (mod_remove(&modlinkage)); 179 } 180 181 int 182 _info(struct modinfo *modinfop) 183 { 184 return (mod_info(&modlinkage, modinfop)); 185 } 186 187 188 /* 189 * KCF software provider control entry points. 190 */ 191 /* ARGSUSED */ 192 static void 193 rc4_provider_status(crypto_provider_handle_t provider, uint_t *status) 194 { 195 *status = CRYPTO_PROVIDER_READY; 196 } 197 198 /* ARGSUSED */ 199 static int 200 rc4_common_init(crypto_ctx_t *ctx, crypto_mechanism_t *mechanism, 201 crypto_key_t *key, crypto_spi_ctx_template_t template, 202 crypto_req_handle_t req) 203 { 204 ARCFour_key *keystream; 205 206 if ((mechanism)->cm_type != RC4_MECH_INFO_TYPE) 207 return (CRYPTO_MECHANISM_INVALID); 208 209 if (key->ck_format != CRYPTO_KEY_RAW) 210 return (CRYPTO_KEY_TYPE_INCONSISTENT); 211 212 if (key->ck_length < ARCFOUR_MIN_KEY_BITS || 213 key->ck_length > ARCFOUR_MAX_KEY_BITS) { 214 return (CRYPTO_KEY_SIZE_RANGE); 215 } 216 217 /* 218 * Allocate an RC4 key stream. 219 */ 220 if ((keystream = kmem_alloc(sizeof (ARCFour_key), 221 crypto_kmflag(req))) == NULL) 222 return (CRYPTO_HOST_MEMORY); 223 224 arcfour_key_init(keystream, key->ck_data, 225 CRYPTO_BITS2BYTES(key->ck_length)); 226 227 ctx->cc_provider_private = keystream; 228 229 return (CRYPTO_SUCCESS); 230 } 231 232 static int 233 rc4_crypt(crypto_ctx_t *ctx, crypto_data_t *input, crypto_data_t *output, 234 crypto_req_handle_t req) 235 { 236 int ret; 237 238 ret = rc4_crypt_update(ctx, input, output, req); 239 240 if (ret != CRYPTO_BUFFER_TOO_SMALL) 241 (void) rc4_free_context(ctx); 242 243 return (ret); 244 } 245 246 /* ARGSUSED */ 247 static int 248 rc4_crypt_update(crypto_ctx_t *ctx, crypto_data_t *input, crypto_data_t *output, 249 crypto_req_handle_t req) 250 { 251 int ret = CRYPTO_SUCCESS; 252 253 ARCFour_key *key; 254 off_t saveoffset; 255 256 ASSERT(ctx->cc_provider_private != NULL); 257 258 if ((ctx->cc_flags & CRYPTO_USE_OPSTATE) && ctx->cc_opstate != NULL) 259 key = ctx->cc_opstate; 260 else 261 key = ctx->cc_provider_private; 262 263 /* Simple case: in-line encipherment */ 264 265 if (output == NULL) { 266 switch (input->cd_format) { 267 case CRYPTO_DATA_RAW: { 268 char *start, *end; 269 start = input->cd_raw.iov_base + input->cd_offset; 270 271 end = input->cd_raw.iov_base + input->cd_raw.iov_len; 272 273 if (start + input->cd_length > end) 274 return (CRYPTO_DATA_INVALID); 275 276 arcfour_crypt(key, (uchar_t *)start, (uchar_t *)start, 277 input->cd_length); 278 break; 279 } 280 case CRYPTO_DATA_MBLK: { 281 uchar_t *start, *end; 282 size_t len, left; 283 mblk_t *mp = input->cd_mp, *mp1, *mp2; 284 285 ASSERT(mp != NULL); 286 287 mp1 = advance_position(mp, input->cd_offset, &start); 288 289 if (mp1 == NULL) 290 return (CRYPTO_DATA_LEN_RANGE); 291 292 mp2 = advance_position(mp, input->cd_offset + 293 input->cd_length, &end); 294 295 if (mp2 == NULL) 296 return (CRYPTO_DATA_LEN_RANGE); 297 298 left = input->cd_length; 299 while (mp1 != NULL) { 300 if (_PTRDIFF(mp1->b_wptr, start) > left) { 301 len = left; 302 arcfour_crypt(key, start, start, len); 303 mp1 = NULL; 304 } else { 305 len = _PTRDIFF(mp1->b_wptr, start); 306 arcfour_crypt(key, start, start, len); 307 mp1 = mp1->b_cont; 308 start = mp1->b_rptr; 309 left -= len; 310 } 311 } 312 break; 313 } 314 case CRYPTO_DATA_UIO: { 315 uio_t *uiop = input->cd_uio; 316 off_t offset = input->cd_offset; 317 size_t length = input->cd_length; 318 uint_t vec_idx; 319 size_t cur_len; 320 321 /* 322 * Jump to the first iovec containing data to be 323 * processed. 324 */ 325 for (vec_idx = 0; vec_idx < uiop->uio_iovcnt && 326 offset >= uiop->uio_iov[vec_idx].iov_len; 327 offset -= uiop->uio_iov[vec_idx++].iov_len) 328 ; 329 if (vec_idx == uiop->uio_iovcnt) { 330 return (CRYPTO_DATA_LEN_RANGE); 331 } 332 333 /* 334 * Now process the iovecs. 335 */ 336 while (vec_idx < uiop->uio_iovcnt && length > 0) { 337 uchar_t *start; 338 iovec_t *iovp = &(uiop->uio_iov[vec_idx]); 339 340 cur_len = MIN(iovp->iov_len - offset, length); 341 342 start = (uchar_t *)(iovp->iov_base + offset); 343 arcfour_crypt(key, start + offset, 344 start + offset, cur_len); 345 346 length -= cur_len; 347 vec_idx++; 348 offset = 0; 349 } 350 351 if (vec_idx == uiop->uio_iovcnt && length > 0) { 352 353 return (CRYPTO_DATA_LEN_RANGE); 354 } 355 break; 356 } 357 } 358 return (CRYPTO_SUCCESS); 359 } 360 361 /* 362 * We need to just return the length needed to store the output. 363 * We should not destroy the context for the following case. 364 */ 365 366 if (input->cd_length > output->cd_length) { 367 output->cd_length = input->cd_length; 368 return (CRYPTO_BUFFER_TOO_SMALL); 369 } 370 371 saveoffset = output->cd_offset; 372 373 switch (input->cd_format) { 374 case CRYPTO_DATA_RAW: { 375 char *start, *end; 376 start = input->cd_raw.iov_base + input->cd_offset; 377 378 end = input->cd_raw.iov_base + input->cd_raw.iov_len; 379 380 if (start + input->cd_length > end) 381 return (CRYPTO_DATA_LEN_RANGE); 382 383 ret = crypto_arcfour_crypt(key, (uchar_t *)start, output, 384 input->cd_length); 385 386 if (ret != CRYPTO_SUCCESS) 387 return (ret); 388 break; 389 } 390 case CRYPTO_DATA_MBLK: { 391 uchar_t *start, *end; 392 size_t len, left; 393 mblk_t *mp = input->cd_mp, *mp1, *mp2; 394 395 ASSERT(mp != NULL); 396 397 mp1 = advance_position(mp, input->cd_offset, &start); 398 399 if (mp1 == NULL) 400 return (CRYPTO_DATA_LEN_RANGE); 401 402 mp2 = advance_position(mp, input->cd_offset + input->cd_length, 403 &end); 404 405 if (mp2 == NULL) 406 return (CRYPTO_DATA_LEN_RANGE); 407 408 left = input->cd_length; 409 while (mp1 != NULL) { 410 if (_PTRDIFF(mp1->b_wptr, start) > left) { 411 len = left; 412 ret = crypto_arcfour_crypt(key, start, output, 413 len); 414 if (ret != CRYPTO_SUCCESS) 415 return (ret); 416 mp1 = NULL; 417 } else { 418 len = _PTRDIFF(mp1->b_wptr, start); 419 ret = crypto_arcfour_crypt(key, start, output, 420 len); 421 if (ret != CRYPTO_SUCCESS) 422 return (ret); 423 mp1 = mp1->b_cont; 424 start = mp1->b_rptr; 425 left -= len; 426 output->cd_offset += len; 427 } 428 } 429 break; 430 } 431 case CRYPTO_DATA_UIO: { 432 uio_t *uiop = input->cd_uio; 433 off_t offset = input->cd_offset; 434 size_t length = input->cd_length; 435 uint_t vec_idx; 436 size_t cur_len; 437 438 /* 439 * Jump to the first iovec containing data to be 440 * processed. 441 */ 442 for (vec_idx = 0; vec_idx < uiop->uio_iovcnt && 443 offset >= uiop->uio_iov[vec_idx].iov_len; 444 offset -= uiop->uio_iov[vec_idx++].iov_len) 445 ; 446 if (vec_idx == uiop->uio_iovcnt) { 447 return (CRYPTO_DATA_LEN_RANGE); 448 } 449 450 /* 451 * Now process the iovecs. 452 */ 453 while (vec_idx < uiop->uio_iovcnt && length > 0) { 454 uchar_t *start; 455 iovec_t *iovp = &(uiop->uio_iov[vec_idx]); 456 cur_len = MIN(iovp->iov_len - offset, length); 457 458 start = (uchar_t *)(iovp->iov_base + offset); 459 ret = crypto_arcfour_crypt(key, start + offset, 460 output, cur_len); 461 if (ret != CRYPTO_SUCCESS) 462 return (ret); 463 464 length -= cur_len; 465 vec_idx++; 466 offset = 0; 467 output->cd_offset += cur_len; 468 } 469 470 if (vec_idx == uiop->uio_iovcnt && length > 0) { 471 472 return (CRYPTO_DATA_LEN_RANGE); 473 } 474 } 475 } 476 477 output->cd_offset = saveoffset; 478 output->cd_length = input->cd_length; 479 480 return (ret); 481 } 482 483 /* ARGSUSED */ 484 static int rc4_crypt_final(crypto_ctx_t *ctx, crypto_data_t *data, 485 crypto_req_handle_t req) 486 { 487 /* No final part for streams ciphers. Just free the context */ 488 if (data != NULL) 489 data->cd_length = 0; 490 491 return (rc4_free_context(ctx)); 492 } 493 494 /* ARGSUSED */ 495 static int 496 rc4_crypt_atomic(crypto_provider_handle_t handle, crypto_session_id_t session, 497 crypto_mechanism_t *mechanism, crypto_key_t *key, crypto_data_t *input, 498 crypto_data_t *output, crypto_spi_ctx_template_t template, 499 crypto_req_handle_t req) 500 { 501 crypto_ctx_t ctx; 502 int ret; 503 504 bzero(&ctx, sizeof (crypto_ctx_t)); 505 ret = rc4_common_init(&ctx, mechanism, key, template, req); 506 507 if (ret != CRYPTO_SUCCESS) 508 return (ret); 509 510 ret = rc4_crypt_update(&ctx, input, output, req); 511 512 (void) rc4_free_context(&ctx); 513 514 return (ret); 515 } 516 517 /* ARGSUSED */ 518 static int 519 rc4_free_context(crypto_ctx_t *ctx) 520 { 521 ARCFour_key *keystream = ctx->cc_provider_private; 522 523 if (keystream != NULL) { 524 bzero(keystream, sizeof (ARCFour_key)); 525 kmem_free(keystream, sizeof (ARCFour_key)); 526 ctx->cc_provider_private = NULL; 527 } 528 529 return (CRYPTO_SUCCESS); 530 } 531 532 /* Encrypts a contiguous input 'in' into the 'out' crypto_data_t */ 533 534 static int 535 crypto_arcfour_crypt(ARCFour_key *key, uchar_t *in, crypto_data_t *out, 536 int length) 537 { 538 switch (out->cd_format) { 539 case CRYPTO_DATA_RAW: { 540 uchar_t *start, *end; 541 start = (uchar_t *)(out->cd_raw.iov_base + 542 out->cd_offset); 543 544 end = (uchar_t *)(out->cd_raw.iov_base + 545 out->cd_raw.iov_len); 546 547 if (start + out->cd_length > end) 548 return (CRYPTO_DATA_LEN_RANGE); 549 550 arcfour_crypt(key, in, start, length); 551 552 return (CRYPTO_SUCCESS); 553 } 554 case CRYPTO_DATA_MBLK: { 555 uchar_t *start, *end; 556 size_t len, left; 557 mblk_t *mp = out->cd_mp, *mp1, *mp2; 558 559 ASSERT(mp != NULL); 560 561 mp1 = advance_position(mp, out->cd_offset, &start); 562 563 if (mp1 == NULL) 564 return (CRYPTO_DATA_LEN_RANGE); 565 566 mp2 = advance_position(mp, out->cd_offset + 567 out->cd_length, &end); 568 569 if (mp2 == NULL) 570 return (CRYPTO_DATA_LEN_RANGE); 571 572 left = length; 573 while (mp1 != NULL) { 574 if (_PTRDIFF(mp1->b_wptr, start) > left) { 575 len = left; 576 arcfour_crypt(key, in, start, len); 577 mp1 = NULL; 578 } else { 579 len = _PTRDIFF(mp1->b_wptr, start); 580 arcfour_crypt(key, in, start, len); 581 mp1 = mp1->b_cont; 582 start = mp1->b_rptr; 583 left -= len; 584 } 585 } 586 break; 587 } 588 case CRYPTO_DATA_UIO: { 589 uio_t *uiop = out->cd_uio; 590 off_t offset = out->cd_offset; 591 size_t len = length; 592 uint_t vec_idx; 593 size_t cur_len; 594 595 /* 596 * Jump to the first iovec containing data to be 597 * processed. 598 */ 599 for (vec_idx = 0; vec_idx < uiop->uio_iovcnt && 600 offset >= uiop->uio_iov[vec_idx].iov_len; 601 offset -= uiop->uio_iov[vec_idx++].iov_len) 602 ; 603 if (vec_idx == uiop->uio_iovcnt) { 604 return (CRYPTO_DATA_LEN_RANGE); 605 } 606 607 /* 608 * Now process the iovecs. 609 */ 610 while (vec_idx < uiop->uio_iovcnt && len > 0) { 611 uchar_t *start; 612 iovec_t *iovp = &(uiop->uio_iov[vec_idx]); 613 cur_len = MIN(iovp->iov_len - offset, len); 614 615 start = (uchar_t *)(iovp->iov_base + offset); 616 arcfour_crypt(key, start + offset, 617 start + offset, cur_len); 618 619 len -= cur_len; 620 vec_idx++; 621 offset = 0; 622 } 623 624 if (vec_idx == uiop->uio_iovcnt && len > 0) { 625 return (CRYPTO_DATA_LEN_RANGE); 626 } 627 break; 628 } 629 default: 630 return (CRYPTO_DATA_INVALID); 631 } 632 return (CRYPTO_SUCCESS); 633 } 634 635 /* 636 * Advances 'offset' bytes from the beginning of the first block in 'mp', 637 * possibly jumping across b_cont boundary 638 * '*cpp' is set to the position of the byte we want, and the block where 639 * 'cpp' is returned. 640 */ 641 static mblk_t * 642 advance_position(mblk_t *mp, off_t offset, uchar_t **cpp) 643 { 644 mblk_t *mp1 = mp; 645 size_t l; 646 off_t o = offset; 647 648 while (mp1 != NULL) { 649 l = MBLKL(mp1); 650 651 if (l <= o) { 652 o -= l; 653 mp1 = mp1->b_cont; 654 } else { 655 *cpp = (uchar_t *)(mp1->b_rptr + o); 656 break; 657 } 658 } 659 return (mp1); 660 } 661