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