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 2008 Sun Microsystems, Inc.  All rights reserved.
  23  * Use is subject to license terms.
  24  */
  25 /*
  26  * Copyright 2015 by Saso Kiselkov. All rights reserved.
  27  */
  28 
  29 #ifndef _KERNEL
  30 #include <strings.h>
  31 #include <limits.h>
  32 #include <assert.h>
  33 #include <security/cryptoki.h>
  34 #endif
  35 
  36 #include <sys/types.h>
  37 #define INLINE_CRYPTO_GET_PTRS
  38 #include <modes/modes.h>
  39 #include <sys/crypto/common.h>
  40 #include <sys/crypto/impl.h>
  41 
  42 boolean_t ecb_fastpath_enabled = B_TRUE;
  43 
  44 /*
  45  * Algorithm independent ECB functions.
  46  * `cipher' is a single-block version of the cipher function to be performed
  47  * on each input block. `cipher_ecb' is an optional parameter, which if
  48  * passed and the input/output conditions allow it, will be invoked for the
  49  * entire input buffer once to accelerate the operation.
  50  */
  51 int
  52 ecb_cipher_contiguous_blocks(ecb_ctx_t *ctx, char *data, size_t length,
  53     crypto_data_t *out, size_t block_size,
  54     int (*cipher)(const void *ks, const uint8_t *pt, uint8_t *ct),
  55     int (*cipher_ecb)(const void *ks, const uint8_t *pt, uint8_t *ct,
  56     uint64_t len))
  57 {
  58         size_t remainder = length;
  59         size_t need;
  60         uint8_t *datap = (uint8_t *)data;
  61         uint8_t *blockp;
  62         uint8_t *lastp;
  63         void *iov_or_mp;
  64         offset_t offset;
  65         uint8_t *out_data_1;
  66         uint8_t *out_data_2;
  67         size_t out_data_1_len;
  68 
  69         /*
  70          * ECB encryption/decryption fastpath requirements:
  71          * - fastpath is enabled
  72          * - caller passed an accelerated ECB version of the cipher function
  73          * - input is block-aligned
  74          * - output is a single contiguous region or the user requested that
  75          *   we overwrite their input buffer (input/output aliasing allowed)
  76          */
  77         if (ecb_fastpath_enabled && cipher_ecb != NULL &&
  78             ctx->ecb_remainder_len == 0 && length % block_size == 0 &&
  79             (out == NULL || CRYPTO_DATA_IS_SINGLE_BLOCK(out))) {
  80                 if (out == NULL) {
  81                         cipher_ecb(ctx->ecb_keysched, (uint8_t *)data,
  82                             (uint8_t *)data, length);
  83                 } else {
  84                         cipher_ecb(ctx->ecb_keysched, (uint8_t *)data,
  85                             CRYPTO_DATA_FIRST_BLOCK(out), length);
  86                         out->cd_offset += length;
  87                 }
  88                 return (CRYPTO_SUCCESS);
  89         }
  90 
  91         if (length + ctx->ecb_remainder_len < block_size) {
  92                 /* accumulate bytes here and return */
  93                 bcopy(datap,
  94                     (uint8_t *)ctx->ecb_remainder + ctx->ecb_remainder_len,
  95                     length);
  96                 ctx->ecb_remainder_len += length;
  97                 ctx->ecb_copy_to = datap;
  98                 return (CRYPTO_SUCCESS);
  99         }
 100 
 101         lastp = (uint8_t *)ctx->ecb_iv;
 102         if (out != NULL)
 103                 crypto_init_ptrs(out, &iov_or_mp, &offset);
 104 
 105         do {
 106                 /* Unprocessed data from last call. */
 107                 if (ctx->ecb_remainder_len > 0) {
 108                         need = block_size - ctx->ecb_remainder_len;
 109 
 110                         if (need > remainder)
 111                                 return (CRYPTO_DATA_LEN_RANGE);
 112 
 113                         bcopy(datap, &((uint8_t *)ctx->ecb_remainder)
 114                             [ctx->ecb_remainder_len], need);
 115 
 116                         blockp = (uint8_t *)ctx->ecb_remainder;
 117                 } else {
 118                         blockp = datap;
 119                 }
 120 
 121                 if (out == NULL) {
 122                         cipher(ctx->ecb_keysched, blockp, blockp);
 123 
 124                         ctx->ecb_lastp = blockp;
 125                         lastp = blockp;
 126 
 127                         if (ctx->ecb_remainder_len > 0) {
 128                                 bcopy(blockp, ctx->ecb_copy_to,
 129                                     ctx->ecb_remainder_len);
 130                                 bcopy(blockp + ctx->ecb_remainder_len, datap,
 131                                     need);
 132                         }
 133                 } else {
 134                         cipher(ctx->ecb_keysched, blockp, lastp);
 135                         crypto_get_ptrs(out, &iov_or_mp, &offset, &out_data_1,
 136                             &out_data_1_len, &out_data_2, block_size);
 137 
 138                         /* copy block to where it belongs */
 139                         bcopy(lastp, out_data_1, out_data_1_len);
 140                         if (out_data_2 != NULL) {
 141                                 bcopy(lastp + out_data_1_len, out_data_2,
 142                                     block_size - out_data_1_len);
 143                         }
 144                         /* update offset */
 145                         out->cd_offset += block_size;
 146                 }
 147 
 148                 /* Update pointer to next block of data to be processed. */
 149                 if (ctx->ecb_remainder_len != 0) {
 150                         datap += need;
 151                         ctx->ecb_remainder_len = 0;
 152                 } else {
 153                         datap += block_size;
 154                 }
 155 
 156                 remainder = (size_t)&data[length] - (size_t)datap;
 157 
 158                 /* Incomplete last block. */
 159                 if (remainder > 0 && remainder < block_size) {
 160                         bcopy(datap, ctx->ecb_remainder, remainder);
 161                         ctx->ecb_remainder_len = remainder;
 162                         ctx->ecb_copy_to = datap;
 163                         goto out;
 164                 }
 165                 ctx->ecb_copy_to = NULL;
 166 
 167         } while (remainder > 0);
 168 
 169 out:
 170         return (CRYPTO_SUCCESS);
 171 }
 172 
 173 /* ARGSUSED */
 174 void *
 175 ecb_alloc_ctx(int kmflag)
 176 {
 177         ecb_ctx_t *ecb_ctx;
 178 
 179 #ifdef _KERNEL
 180         if ((ecb_ctx = kmem_zalloc(sizeof (ecb_ctx_t), kmflag)) == NULL)
 181 #else
 182         if ((ecb_ctx = calloc(1, sizeof (ecb_ctx_t))) == NULL)
 183 #endif
 184                 return (NULL);
 185 
 186         ecb_ctx->ecb_flags = ECB_MODE;
 187         return (ecb_ctx);
 188 }