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         .co_control_ops = &rc4_control_ops,
 118         .co_digest_ops = NULL,
 119         .co_cipher_ops = &rc4_cipher_ops,
 120         .co_mac_ops = NULL,
 121         .co_sign_ops = NULL,
 122         .co_verify_ops = NULL,
 123         .co_dual_ops = NULL,
 124         .co_dual_cipher_mac_ops = NULL,
 125         .co_random_ops = NULL,
 126         .co_session_ops = NULL,
 127         .co_object_ops = NULL,
 128         .co_key_ops = NULL,
 129         .co_provider_ops = NULL,
 130         .co_ctx_ops = &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 }