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 #include <sys/strsun.h>
  27 #include <sys/systm.h>
  28 #include <sys/sysmacros.h>
  29 #include <sys/kmem.h>
  30 #include <sys/md5.h>
  31 #include <sys/sha1.h>
  32 #include <sys/sha2.h>
  33 #include <modes/modes.h>
  34 #include <sys/crypto/common.h>
  35 #include <sys/crypto/impl.h>
  36 
  37 /*
  38  * Utility routine to apply the command, 'cmd', to the
  39  * data in the uio structure.
  40  */
  41 int
  42 crypto_uio_data(crypto_data_t *data, uchar_t *buf, int len, cmd_type_t cmd,
  43     void *digest_ctx, void (*update)())
  44 {
  45         uio_t *uiop = data->cd_uio;
  46         off_t offset = data->cd_offset;
  47         size_t length = len;
  48         uint_t vec_idx;
  49         size_t cur_len;
  50         uchar_t *datap;
  51 
  52         ASSERT(data->cd_format == CRYPTO_DATA_UIO);
  53         if (uiop->uio_segflg != UIO_SYSSPACE) {
  54                 return (CRYPTO_ARGUMENTS_BAD);
  55         }
  56 
  57         /*
  58          * Jump to the first iovec containing data to be
  59          * processed.
  60          */
  61         for (vec_idx = 0; vec_idx < uiop->uio_iovcnt &&
  62             offset >= uiop->uio_iov[vec_idx].iov_len;
  63             offset -= uiop->uio_iov[vec_idx++].iov_len)
  64                 ;
  65 
  66         if (vec_idx == uiop->uio_iovcnt) {
  67                 /*
  68                  * The caller specified an offset that is larger than
  69                  * the total size of the buffers it provided.
  70                  */
  71                 return (CRYPTO_DATA_LEN_RANGE);
  72         }
  73 
  74         while (vec_idx < uiop->uio_iovcnt && length > 0) {
  75                 cur_len = MIN(uiop->uio_iov[vec_idx].iov_len -
  76                     offset, length);
  77 
  78                 datap = (uchar_t *)(uiop->uio_iov[vec_idx].iov_base +
  79                     offset);
  80                 switch (cmd) {
  81                 case COPY_FROM_DATA:
  82                         bcopy(datap, buf, cur_len);
  83                         buf += cur_len;
  84                         break;
  85                 case COPY_TO_DATA:
  86                         bcopy(buf, datap, cur_len);
  87                         buf += cur_len;
  88                         break;
  89                 case COMPARE_TO_DATA:
  90                         if (bcmp(datap, buf, cur_len))
  91                                 return (CRYPTO_SIGNATURE_INVALID);
  92                         buf += cur_len;
  93                         break;
  94                 case MD5_DIGEST_DATA:
  95                 case SHA1_DIGEST_DATA:
  96                 case SHA2_DIGEST_DATA:
  97                 case GHASH_DATA:
  98                         update(digest_ctx, datap, cur_len);
  99                         break;
 100                 }
 101 
 102                 length -= cur_len;
 103                 vec_idx++;
 104                 offset = 0;
 105         }
 106 
 107         if (vec_idx == uiop->uio_iovcnt && length > 0) {
 108                 /*
 109                  * The end of the specified iovec's was reached but
 110                  * the length requested could not be processed.
 111                  */
 112                 switch (cmd) {
 113                 case COPY_TO_DATA:
 114                         data->cd_length = len;
 115                         return (CRYPTO_BUFFER_TOO_SMALL);
 116                 default:
 117                         return (CRYPTO_DATA_LEN_RANGE);
 118                 }
 119         }
 120 
 121         return (CRYPTO_SUCCESS);
 122 }
 123 
 124 /*
 125  * Utility routine to apply the command, 'cmd', to the
 126  * data in the mblk structure.
 127  */
 128 int
 129 crypto_mblk_data(crypto_data_t *data, uchar_t *buf, int len, cmd_type_t cmd,
 130     void *digest_ctx, void (*update)())
 131 {
 132         off_t offset = data->cd_offset;
 133         size_t length = len;
 134         mblk_t *mp;
 135         size_t cur_len;
 136         uchar_t *datap;
 137 
 138         ASSERT(data->cd_format == CRYPTO_DATA_MBLK);
 139         /*
 140          * Jump to the first mblk_t containing data to be processed.
 141          */
 142         for (mp = data->cd_mp; mp != NULL && offset >= MBLKL(mp);
 143             offset -= MBLKL(mp), mp = mp->b_cont)
 144                 ;
 145         if (mp == NULL) {
 146                 /*
 147                  * The caller specified an offset that is larger
 148                  * than the total size of the buffers it provided.
 149                  */
 150                 return (CRYPTO_DATA_LEN_RANGE);
 151         }
 152 
 153         /*
 154          * Now do the processing on the mblk chain.
 155          */
 156         while (mp != NULL && length > 0) {
 157                 cur_len = MIN(MBLKL(mp) - offset, length);
 158 
 159                 datap = (uchar_t *)(mp->b_rptr + offset);
 160                 switch (cmd) {
 161                 case COPY_FROM_DATA:
 162                         bcopy(datap, buf, cur_len);
 163                         buf += cur_len;
 164                         break;
 165                 case COPY_TO_DATA:
 166                         bcopy(buf, datap, cur_len);
 167                         buf += cur_len;
 168                         break;
 169                 case COMPARE_TO_DATA:
 170                         if (bcmp(datap, buf, cur_len))
 171                                 return (CRYPTO_SIGNATURE_INVALID);
 172                         buf += cur_len;
 173                         break;
 174                 case MD5_DIGEST_DATA:
 175                 case SHA1_DIGEST_DATA:
 176                 case SHA2_DIGEST_DATA:
 177                 case GHASH_DATA:
 178                         update(digest_ctx, datap, cur_len);
 179                         break;
 180                 }
 181 
 182                 length -= cur_len;
 183                 offset = 0;
 184                 mp = mp->b_cont;
 185         }
 186 
 187         if (mp == NULL && length > 0) {
 188                 /*
 189                  * The end of the mblk was reached but the length
 190                  * requested could not be processed.
 191                  */
 192                 switch (cmd) {
 193                 case COPY_TO_DATA:
 194                         data->cd_length = len;
 195                         return (CRYPTO_BUFFER_TOO_SMALL);
 196                 default:
 197                         return (CRYPTO_DATA_LEN_RANGE);
 198                 }
 199         }
 200 
 201         return (CRYPTO_SUCCESS);
 202 }
 203 
 204 /*
 205  * Utility routine to copy a buffer to a crypto_data structure.
 206  */
 207 int
 208 crypto_put_output_data(uchar_t *buf, crypto_data_t *output, int len)
 209 {
 210         switch (output->cd_format) {
 211         case CRYPTO_DATA_RAW:
 212                 if (output->cd_raw.iov_len < len) {
 213                         output->cd_length = len;
 214                         return (CRYPTO_BUFFER_TOO_SMALL);
 215                 }
 216                 bcopy(buf, (uchar_t *)(output->cd_raw.iov_base +
 217                     output->cd_offset), len);
 218                 break;
 219 
 220         case CRYPTO_DATA_UIO:
 221                 return (crypto_uio_data(output, buf, len,
 222                     COPY_TO_DATA, NULL, NULL));
 223 
 224         case CRYPTO_DATA_MBLK:
 225                 return (crypto_mblk_data(output, buf, len,
 226                     COPY_TO_DATA, NULL, NULL));
 227 
 228         default:
 229                 return (CRYPTO_ARGUMENTS_BAD);
 230         }
 231 
 232         return (CRYPTO_SUCCESS);
 233 }
 234 
 235 /*
 236  * Utility routine to get data from a crypto_data structure.
 237  *
 238  * '*dptr' contains a pointer to a buffer on return. 'buf'
 239  * is allocated by the caller and is ignored for CRYPTO_DATA_RAW case.
 240  */
 241 int
 242 crypto_get_input_data(crypto_data_t *input, uchar_t **dptr, uchar_t *buf)
 243 {
 244         int rv;
 245 
 246         switch (input->cd_format) {
 247         case CRYPTO_DATA_RAW:
 248                 if (input->cd_raw.iov_len < input->cd_length)
 249                         return (CRYPTO_ARGUMENTS_BAD);
 250                 *dptr = (uchar_t *)(input->cd_raw.iov_base +
 251                     input->cd_offset);
 252                 break;
 253 
 254         case CRYPTO_DATA_UIO:
 255                 if ((rv = crypto_uio_data(input, buf, input->cd_length,
 256                     COPY_FROM_DATA, NULL, NULL)) != CRYPTO_SUCCESS)
 257                         return (rv);
 258                 *dptr = buf;
 259                 break;
 260 
 261         case CRYPTO_DATA_MBLK:
 262                 if ((rv = crypto_mblk_data(input, buf, input->cd_length,
 263                     COPY_FROM_DATA, NULL, NULL)) != CRYPTO_SUCCESS)
 264                         return (rv);
 265                 *dptr = buf;
 266                 break;
 267 
 268         default:
 269                 return (CRYPTO_ARGUMENTS_BAD);
 270         }
 271 
 272         return (CRYPTO_SUCCESS);
 273 }
 274 
 275 int
 276 crypto_copy_key_to_ctx(crypto_key_t *in_key, crypto_key_t **out_key,
 277     size_t *out_size, int kmflag)
 278 {
 279         int i, count;
 280         size_t len;
 281         caddr_t attr_val;
 282         crypto_object_attribute_t *k_attrs = NULL;
 283         crypto_key_t *key;
 284 
 285         ASSERT(in_key->ck_format == CRYPTO_KEY_ATTR_LIST);
 286 
 287         count = in_key->ck_count;
 288         /* figure out how much memory to allocate for everything */
 289         len = sizeof (crypto_key_t) +
 290             count * sizeof (crypto_object_attribute_t);
 291         for (i = 0; i < count; i++) {
 292                 len += roundup(in_key->ck_attrs[i].oa_value_len,
 293                     sizeof (caddr_t));
 294         }
 295 
 296         /* one big allocation for everything */
 297         key = kmem_alloc(len, kmflag);
 298         if (key == NULL)
 299                 return (CRYPTO_HOST_MEMORY);
 300         k_attrs = (crypto_object_attribute_t *)(void *)((caddr_t)key +
 301             sizeof (crypto_key_t));
 302 
 303         attr_val = (caddr_t)k_attrs +
 304             count * sizeof (crypto_object_attribute_t);
 305         for (i = 0; i < count; i++) {
 306                 k_attrs[i].oa_type = in_key->ck_attrs[i].oa_type;
 307                 bcopy(in_key->ck_attrs[i].oa_value, attr_val,
 308                     in_key->ck_attrs[i].oa_value_len);
 309                 k_attrs[i].oa_value = attr_val;
 310                 k_attrs[i].oa_value_len = in_key->ck_attrs[i].oa_value_len;
 311                 attr_val += roundup(k_attrs[i].oa_value_len, sizeof (caddr_t));
 312         }
 313 
 314         key->ck_format = CRYPTO_KEY_ATTR_LIST;
 315         key->ck_count = count;
 316         key->ck_attrs = k_attrs;
 317         *out_key = key;
 318         *out_size = len;                /* save the size to be freed */
 319 
 320         return (CRYPTO_SUCCESS);
 321 }
 322 
 323 int
 324 crypto_digest_data(crypto_data_t *data, void *dctx, uchar_t *digest,
 325     void (*update)(), void (*final)(), uchar_t flag)
 326 {
 327         int rv, dlen;
 328         uchar_t *dptr;
 329 
 330         ASSERT(flag & CRYPTO_DO_MD5 || flag & CRYPTO_DO_SHA1 ||
 331             flag & CRYPTO_DO_SHA2);
 332         if (data == NULL) {
 333                 ASSERT((flag & CRYPTO_DO_UPDATE) == 0);
 334                 goto dofinal;
 335         }
 336 
 337         dlen = data->cd_length;
 338 
 339         if (flag & CRYPTO_DO_UPDATE) {
 340 
 341                 switch (data->cd_format) {
 342                 case CRYPTO_DATA_RAW:
 343                         dptr = (uchar_t *)(data->cd_raw.iov_base +
 344                             data->cd_offset);
 345 
 346                         update(dctx, dptr, dlen);
 347 
 348                 break;
 349 
 350                 case CRYPTO_DATA_UIO:
 351                         if (flag & CRYPTO_DO_MD5)
 352                                 rv = crypto_uio_data(data, NULL, dlen,
 353                                     MD5_DIGEST_DATA, dctx, update);
 354 
 355                         else if (flag & CRYPTO_DO_SHA1)
 356                                 rv = crypto_uio_data(data, NULL, dlen,
 357                                     SHA1_DIGEST_DATA, dctx, update);
 358 
 359                         else
 360                                 rv = crypto_uio_data(data, NULL, dlen,
 361                                     SHA2_DIGEST_DATA, dctx, update);
 362 
 363                         if (rv != CRYPTO_SUCCESS)
 364                                 return (rv);
 365 
 366                         break;
 367 
 368                 case CRYPTO_DATA_MBLK:
 369                         if (flag & CRYPTO_DO_MD5)
 370                                 rv = crypto_mblk_data(data, NULL, dlen,
 371                                     MD5_DIGEST_DATA, dctx, update);
 372 
 373                         else if (flag & CRYPTO_DO_SHA1)
 374                                 rv = crypto_mblk_data(data, NULL, dlen,
 375                                     SHA1_DIGEST_DATA, dctx, update);
 376 
 377                         else
 378                                 rv = crypto_mblk_data(data, NULL, dlen,
 379                                     SHA2_DIGEST_DATA, dctx, update);
 380 
 381                         if (rv != CRYPTO_SUCCESS)
 382                                 return (rv);
 383 
 384                         break;
 385                 }
 386         }
 387 
 388 dofinal:
 389         if (flag & CRYPTO_DO_FINAL) {
 390                 final(digest, dctx);
 391         }
 392 
 393         return (CRYPTO_SUCCESS);
 394 }
 395 
 396 int
 397 crypto_update_iov(void *ctx, crypto_data_t *input, crypto_data_t *output,
 398     int (*cipher)(void *, caddr_t, size_t, crypto_data_t *),
 399     void (*copy_block)(uint8_t *, uint64_t *))
 400 {
 401         common_ctx_t *common_ctx = ctx;
 402         int rv;
 403 
 404         if (input->cd_miscdata != NULL) {
 405                 copy_block((uint8_t *)input->cd_miscdata,
 406                     &common_ctx->cc_iv[0]);
 407         }
 408 
 409         if (input->cd_raw.iov_len < input->cd_length)
 410                 return (CRYPTO_ARGUMENTS_BAD);
 411 
 412         rv = (cipher)(ctx, input->cd_raw.iov_base + input->cd_offset,
 413             input->cd_length, (input == output) ? NULL : output);
 414 
 415         return (rv);
 416 }
 417 
 418 int
 419 crypto_update_uio(void *ctx, crypto_data_t *input, crypto_data_t *output,
 420     int (*cipher)(void *, caddr_t, size_t, crypto_data_t *),
 421     void (*copy_block)(uint8_t *, uint64_t *))
 422 {
 423         common_ctx_t *common_ctx = ctx;
 424         uio_t *uiop = input->cd_uio;
 425         off_t offset = input->cd_offset;
 426         size_t length = input->cd_length;
 427         uint_t vec_idx;
 428         size_t cur_len;
 429 
 430         if (input->cd_miscdata != NULL) {
 431                 copy_block((uint8_t *)input->cd_miscdata,
 432                     &common_ctx->cc_iv[0]);
 433         }
 434 
 435         if (input->cd_uio->uio_segflg != UIO_SYSSPACE) {
 436                 return (CRYPTO_ARGUMENTS_BAD);
 437         }
 438 
 439         /*
 440          * Jump to the first iovec containing data to be
 441          * processed.
 442          */
 443         for (vec_idx = 0; vec_idx < uiop->uio_iovcnt &&
 444             offset >= uiop->uio_iov[vec_idx].iov_len;
 445             offset -= uiop->uio_iov[vec_idx++].iov_len)
 446                 ;
 447         if (vec_idx == uiop->uio_iovcnt) {
 448                 /*
 449                  * The caller specified an offset that is larger than the
 450                  * total size of the buffers it provided.
 451                  */
 452                 return (CRYPTO_DATA_LEN_RANGE);
 453         }
 454 
 455         /*
 456          * Now process the iovecs.
 457          */
 458         while (vec_idx < uiop->uio_iovcnt && length > 0) {
 459                 cur_len = MIN(uiop->uio_iov[vec_idx].iov_len -
 460                     offset, length);
 461 
 462                 (cipher)(ctx, uiop->uio_iov[vec_idx].iov_base + offset,
 463                     cur_len, (input == output) ? NULL : output);
 464 
 465                 length -= cur_len;
 466                 vec_idx++;
 467                 offset = 0;
 468         }
 469 
 470         if (vec_idx == uiop->uio_iovcnt && length > 0) {
 471                 /*
 472                  * The end of the specified iovec's was reached but
 473                  * the length requested could not be processed, i.e.
 474                  * The caller requested to digest more data than it provided.
 475                  */
 476 
 477                 return (CRYPTO_DATA_LEN_RANGE);
 478         }
 479 
 480         return (CRYPTO_SUCCESS);
 481 }
 482 
 483 int
 484 crypto_update_mp(void *ctx, crypto_data_t *input, crypto_data_t *output,
 485     int (*cipher)(void *, caddr_t, size_t, crypto_data_t *),
 486     void (*copy_block)(uint8_t *, uint64_t *))
 487 {
 488         common_ctx_t *common_ctx = ctx;
 489         off_t offset = input->cd_offset;
 490         size_t length = input->cd_length;
 491         mblk_t *mp;
 492         size_t cur_len;
 493 
 494         if (input->cd_miscdata != NULL) {
 495                 copy_block((uint8_t *)input->cd_miscdata,
 496                     &common_ctx->cc_iv[0]);
 497         }
 498 
 499         /*
 500          * Jump to the first mblk_t containing data to be processed.
 501          */
 502         for (mp = input->cd_mp; mp != NULL && offset >= MBLKL(mp);
 503             offset -= MBLKL(mp), mp = mp->b_cont)
 504                 ;
 505         if (mp == NULL) {
 506                 /*
 507                  * The caller specified an offset that is larger than the
 508                  * total size of the buffers it provided.
 509                  */
 510                 return (CRYPTO_DATA_LEN_RANGE);
 511         }
 512 
 513         /*
 514          * Now do the processing on the mblk chain.
 515          */
 516         while (mp != NULL && length > 0) {
 517                 cur_len = MIN(MBLKL(mp) - offset, length);
 518                 (cipher)(ctx, (char *)(mp->b_rptr + offset), cur_len,
 519                     (input == output) ? NULL : output);
 520 
 521                 length -= cur_len;
 522                 offset = 0;
 523                 mp = mp->b_cont;
 524         }
 525 
 526         if (mp == NULL && length > 0) {
 527                 /*
 528                  * The end of the mblk was reached but the length requested
 529                  * could not be processed, i.e. The caller requested
 530                  * to digest more data than it provided.
 531                  */
 532                 return (CRYPTO_DATA_LEN_RANGE);
 533         }
 534 
 535         return (CRYPTO_SUCCESS);
 536 }
 537 
 538 /*
 539  * Utility routine to look up a attribute of type, 'type',
 540  * in the key.
 541  */
 542 int
 543 crypto_get_key_attr(crypto_key_t *key, crypto_attr_type_t type,
 544     uchar_t **value, ssize_t *value_len)
 545 {
 546         int i;
 547 
 548         ASSERT(key->ck_format == CRYPTO_KEY_ATTR_LIST);
 549         for (i = 0; i < key->ck_count; i++) {
 550                 if (key->ck_attrs[i].oa_type == type) {
 551                         *value = (uchar_t *)key->ck_attrs[i].oa_value;
 552                         *value_len = key->ck_attrs[i].oa_value_len;
 553                         return (CRYPTO_SUCCESS);
 554                 }
 555         }
 556 
 557         return (CRYPTO_FAILED);
 558 }