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