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 #ifndef _KERNEL
  27 #include <strings.h>
  28 #include <limits.h>
  29 #include <assert.h>
  30 #include <security/cryptoki.h>
  31 #endif
  32 
  33 #include <sys/types.h>
  34 #include <modes/modes.h>
  35 #include <sys/crypto/common.h>
  36 #include <sys/crypto/impl.h>
  37 
  38 #ifdef _LITTLE_ENDIAN
  39 #include <sys/byteorder.h>
  40 #endif
  41 
  42 /*
  43  * Encrypt and decrypt multiple blocks of data in counter mode.
  44  */
  45 int
  46 ctr_mode_contiguous_blocks(ctr_ctx_t *ctx, char *data, size_t length,
  47     crypto_data_t *out, size_t block_size,
  48     int (*cipher)(const void *ks, const uint8_t *pt, uint8_t *ct),
  49     void (*xor_block)(uint8_t *, uint8_t *))
  50 {
  51         size_t remainder = length;
  52         size_t need;
  53         uint8_t *datap = (uint8_t *)data;
  54         uint8_t *blockp;
  55         uint8_t *lastp;
  56         void *iov_or_mp;
  57         offset_t offset;
  58         uint8_t *out_data_1;
  59         uint8_t *out_data_2;
  60         size_t out_data_1_len;
  61         uint64_t counter;
  62 #ifdef _LITTLE_ENDIAN
  63         uint8_t *p;
  64 #endif
  65 
  66         if (length + ctx->ctr_remainder_len < block_size) {
  67                 /* accumulate bytes here and return */
  68                 bcopy(datap,
  69                     (uint8_t *)ctx->ctr_remainder + ctx->ctr_remainder_len,
  70                     length);
  71                 ctx->ctr_remainder_len += length;
  72                 ctx->ctr_copy_to = datap;
  73                 return (CRYPTO_SUCCESS);
  74         }
  75 
  76         lastp = (uint8_t *)ctx->ctr_cb;
  77         if (out != NULL)
  78                 crypto_init_ptrs(out, &iov_or_mp, &offset);
  79 
  80         do {
  81                 /* Unprocessed data from last call. */
  82                 if (ctx->ctr_remainder_len > 0) {
  83                         need = block_size - ctx->ctr_remainder_len;
  84 
  85                         if (need > remainder)
  86                                 return (CRYPTO_DATA_LEN_RANGE);
  87 
  88                         bcopy(datap, &((uint8_t *)ctx->ctr_remainder)
  89                             [ctx->ctr_remainder_len], need);
  90 
  91                         blockp = (uint8_t *)ctx->ctr_remainder;
  92                 } else {
  93                         blockp = datap;
  94                 }
  95 
  96                 /* ctr_cb is the counter block */
  97                 cipher(ctx->ctr_keysched, (uint8_t *)ctx->ctr_cb,
  98                     (uint8_t *)ctx->ctr_tmp);
  99 
 100                 lastp = (uint8_t *)ctx->ctr_tmp;
 101 
 102                 /*
 103                  * Increment counter. Counter bits are confined
 104                  * to the bottom 64 bits of the counter block.
 105                  */
 106 #ifdef _LITTLE_ENDIAN
 107                 counter = ntohll(ctx->ctr_cb[1] & ctx->ctr_counter_mask);
 108                 counter = htonll(counter + 1);
 109 #else
 110                 counter = ctx->ctr_cb[1] & ctx->ctr_counter_mask;
 111                 counter++;
 112 #endif  /* _LITTLE_ENDIAN */
 113                 counter &= ctx->ctr_counter_mask;
 114                 ctx->ctr_cb[1] =
 115                     (ctx->ctr_cb[1] & ~(ctx->ctr_counter_mask)) | counter;
 116 
 117                 /*
 118                  * XOR the previous cipher block or IV with the
 119                  * current clear block.
 120                  */
 121                 xor_block(blockp, lastp);
 122 
 123                 if (out == NULL) {
 124                         if (ctx->ctr_remainder_len > 0) {
 125                                 bcopy(lastp, ctx->ctr_copy_to,
 126                                     ctx->ctr_remainder_len);
 127                                 bcopy(lastp + ctx->ctr_remainder_len, datap,
 128                                     need);
 129                         }
 130                 } else {
 131                         crypto_get_ptrs(out, &iov_or_mp, &offset, &out_data_1,
 132                             &out_data_1_len, &out_data_2, block_size);
 133 
 134                         /* copy block to where it belongs */
 135                         bcopy(lastp, out_data_1, out_data_1_len);
 136                         if (out_data_2 != NULL) {
 137                                 bcopy(lastp + out_data_1_len, out_data_2,
 138                                     block_size - out_data_1_len);
 139                         }
 140                         /* update offset */
 141                         out->cd_offset += block_size;
 142                 }
 143 
 144                 /* Update pointer to next block of data to be processed. */
 145                 if (ctx->ctr_remainder_len != 0) {
 146                         datap += need;
 147                         ctx->ctr_remainder_len = 0;
 148                 } else {
 149                         datap += block_size;
 150                 }
 151 
 152                 remainder = (size_t)&data[length] - (size_t)datap;
 153 
 154                 /* Incomplete last block. */
 155                 if (remainder > 0 && remainder < block_size) {
 156                         bcopy(datap, ctx->ctr_remainder, remainder);
 157                         ctx->ctr_remainder_len = remainder;
 158                         ctx->ctr_copy_to = datap;
 159                         goto out;
 160                 }
 161                 ctx->ctr_copy_to = NULL;
 162 
 163         } while (remainder > 0);
 164 
 165 out:
 166         return (CRYPTO_SUCCESS);
 167 }
 168 
 169 int
 170 ctr_mode_final(ctr_ctx_t *ctx, crypto_data_t *out,
 171     int (*encrypt_block)(const void *, const uint8_t *, uint8_t *))
 172 {
 173         uint8_t *lastp;
 174         void *iov_or_mp;
 175         offset_t offset;
 176         uint8_t *out_data_1;
 177         uint8_t *out_data_2;
 178         size_t out_data_1_len;
 179         uint8_t *p;
 180         int i;
 181 
 182         if (out->cd_length < ctx->ctr_remainder_len)
 183                 return (CRYPTO_DATA_LEN_RANGE);
 184 
 185         encrypt_block(ctx->ctr_keysched, (uint8_t *)ctx->ctr_cb,
 186             (uint8_t *)ctx->ctr_tmp);
 187 
 188         lastp = (uint8_t *)ctx->ctr_tmp;
 189         p = (uint8_t *)ctx->ctr_remainder;
 190         for (i = 0; i < ctx->ctr_remainder_len; i++) {
 191                 p[i] ^= lastp[i];
 192         }
 193 
 194         crypto_init_ptrs(out, &iov_or_mp, &offset);
 195         crypto_get_ptrs(out, &iov_or_mp, &offset, &out_data_1,
 196             &out_data_1_len, &out_data_2, ctx->ctr_remainder_len);
 197 
 198         bcopy(p, out_data_1, out_data_1_len);
 199         if (out_data_2 != NULL) {
 200                 bcopy((uint8_t *)p + out_data_1_len,
 201                     out_data_2, ctx->ctr_remainder_len - out_data_1_len);
 202         }
 203         out->cd_offset += ctx->ctr_remainder_len;
 204         ctx->ctr_remainder_len = 0;
 205         return (CRYPTO_SUCCESS);
 206 }
 207 
 208 int
 209 ctr_init_ctx(ctr_ctx_t *ctr_ctx, ulong_t count, uint8_t *cb,
 210 void (*copy_block)(uint8_t *, uint8_t *))
 211 {
 212         uint64_t mask = 0;
 213 
 214         if (count == 0 || count > 64) {
 215                 return (CRYPTO_MECHANISM_PARAM_INVALID);
 216         }
 217         while (count-- > 0)
 218                 mask |= (1ULL << count);
 219 
 220 #ifdef _LITTLE_ENDIAN
 221         mask = htonll(mask);
 222 #endif
 223         ctr_ctx->ctr_counter_mask = mask;
 224         copy_block(cb, (uchar_t *)ctr_ctx->ctr_cb);
 225         ctr_ctx->ctr_lastp = (uint8_t *)&ctr_ctx->ctr_cb[0];
 226         ctr_ctx->ctr_flags |= CTR_MODE;
 227         return (CRYPTO_SUCCESS);
 228 }
 229 
 230 /* ARGSUSED */
 231 void *
 232 ctr_alloc_ctx(int kmflag)
 233 {
 234         ctr_ctx_t *ctr_ctx;
 235 
 236 #ifdef _KERNEL
 237         if ((ctr_ctx = kmem_zalloc(sizeof (ctr_ctx_t), kmflag)) == NULL)
 238 #else
 239         if ((ctr_ctx = calloc(1, sizeof (ctr_ctx_t))) == NULL)
 240 #endif
 241                 return (NULL);
 242 
 243         ctr_ctx->ctr_flags = CTR_MODE;
 244         return (ctr_ctx);
 245 }