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 /*
  39  * Algorithm independent CBC functions.
  40  */
  41 int
  42 cbc_encrypt_contiguous_blocks(cbc_ctx_t *ctx, char *data, size_t length,
  43     crypto_data_t *out, size_t block_size,
  44     int (*encrypt)(const void *, const uint8_t *, uint8_t *),
  45     void (*copy_block)(uint8_t *, uint8_t *),
  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 
  59         if (length + ctx->cbc_remainder_len < block_size) {
  60                 /* accumulate bytes here and return */
  61                 bcopy(datap,
  62                     (uint8_t *)ctx->cbc_remainder + ctx->cbc_remainder_len,
  63                     length);
  64                 ctx->cbc_remainder_len += length;
  65                 ctx->cbc_copy_to = datap;
  66                 return (CRYPTO_SUCCESS);
  67         }
  68 
  69         lastp = (uint8_t *)ctx->cbc_iv;
  70         if (out != NULL)
  71                 crypto_init_ptrs(out, &iov_or_mp, &offset);
  72 
  73         do {
  74                 /* Unprocessed data from last call. */
  75                 if (ctx->cbc_remainder_len > 0) {
  76                         need = block_size - ctx->cbc_remainder_len;
  77 
  78                         if (need > remainder)
  79                                 return (CRYPTO_DATA_LEN_RANGE);
  80 
  81                         bcopy(datap, &((uint8_t *)ctx->cbc_remainder)
  82                             [ctx->cbc_remainder_len], need);
  83 
  84                         blockp = (uint8_t *)ctx->cbc_remainder;
  85                 } else {
  86                         blockp = datap;
  87                 }
  88 
  89                 if (out == NULL) {
  90                         /*
  91                          * XOR the previous cipher block or IV with the
  92                          * current clear block.
  93                          */
  94                         xor_block(lastp, blockp);
  95                         encrypt(ctx->cbc_keysched, blockp, blockp);
  96 
  97                         ctx->cbc_lastp = blockp;
  98                         lastp = blockp;
  99 
 100                         if (ctx->cbc_remainder_len > 0) {
 101                                 bcopy(blockp, ctx->cbc_copy_to,
 102                                     ctx->cbc_remainder_len);
 103                                 bcopy(blockp + ctx->cbc_remainder_len, datap,
 104                                     need);
 105                         }
 106                 } else {
 107                         /*
 108                          * XOR the previous cipher block or IV with the
 109                          * current clear block.
 110                          */
 111                         xor_block(blockp, lastp);
 112                         encrypt(ctx->cbc_keysched, lastp, lastp);
 113                         crypto_get_ptrs(out, &iov_or_mp, &offset, &out_data_1,
 114                             &out_data_1_len, &out_data_2, block_size);
 115 
 116                         /* copy block to where it belongs */
 117                         if (out_data_1_len == block_size) {
 118                                 copy_block(lastp, out_data_1);
 119                         } else {
 120                                 bcopy(lastp, out_data_1, out_data_1_len);
 121                                 if (out_data_2 != NULL) {
 122                                         bcopy(lastp + out_data_1_len,
 123                                             out_data_2,
 124                                             block_size - out_data_1_len);
 125                                 }
 126                         }
 127                         /* update offset */
 128                         out->cd_offset += block_size;
 129                 }
 130 
 131                 /* Update pointer to next block of data to be processed. */
 132                 if (ctx->cbc_remainder_len != 0) {
 133                         datap += need;
 134                         ctx->cbc_remainder_len = 0;
 135                 } else {
 136                         datap += block_size;
 137                 }
 138 
 139                 remainder = (size_t)&data[length] - (size_t)datap;
 140 
 141                 /* Incomplete last block. */
 142                 if (remainder > 0 && remainder < block_size) {
 143                         bcopy(datap, ctx->cbc_remainder, remainder);
 144                         ctx->cbc_remainder_len = remainder;
 145                         ctx->cbc_copy_to = datap;
 146                         goto out;
 147                 }
 148                 ctx->cbc_copy_to = NULL;
 149 
 150         } while (remainder > 0);
 151 
 152 out:
 153         /*
 154          * Save the last encrypted block in the context.
 155          */
 156         if (ctx->cbc_lastp != NULL) {
 157                 copy_block((uint8_t *)ctx->cbc_lastp, (uint8_t *)ctx->cbc_iv);
 158                 ctx->cbc_lastp = (uint8_t *)ctx->cbc_iv;
 159         }
 160 
 161         return (CRYPTO_SUCCESS);
 162 }
 163 
 164 #define OTHER(a, ctx) \
 165         (((a) == (ctx)->cbc_lastblock) ? (ctx)->cbc_iv : (ctx)->cbc_lastblock)
 166 
 167 /* ARGSUSED */
 168 int
 169 cbc_decrypt_contiguous_blocks(cbc_ctx_t *ctx, char *data, size_t length,
 170     crypto_data_t *out, size_t block_size,
 171     int (*decrypt)(const void *, const uint8_t *, uint8_t *),
 172     void (*copy_block)(uint8_t *, uint8_t *),
 173     void (*xor_block)(uint8_t *, uint8_t *))
 174 {
 175         size_t remainder = length;
 176         size_t need;
 177         uint8_t *datap = (uint8_t *)data;
 178         uint8_t *blockp;
 179         uint8_t *lastp;
 180         void *iov_or_mp;
 181         offset_t offset;
 182         uint8_t *out_data_1;
 183         uint8_t *out_data_2;
 184         size_t out_data_1_len;
 185 
 186         if (length + ctx->cbc_remainder_len < block_size) {
 187                 /* accumulate bytes here and return */
 188                 bcopy(datap,
 189                     (uint8_t *)ctx->cbc_remainder + ctx->cbc_remainder_len,
 190                     length);
 191                 ctx->cbc_remainder_len += length;
 192                 ctx->cbc_copy_to = datap;
 193                 return (CRYPTO_SUCCESS);
 194         }
 195 
 196         lastp = ctx->cbc_lastp;
 197         if (out != NULL)
 198                 crypto_init_ptrs(out, &iov_or_mp, &offset);
 199 
 200         do {
 201                 /* Unprocessed data from last call. */
 202                 if (ctx->cbc_remainder_len > 0) {
 203                         need = block_size - ctx->cbc_remainder_len;
 204 
 205                         if (need > remainder)
 206                                 return (CRYPTO_ENCRYPTED_DATA_LEN_RANGE);
 207 
 208                         bcopy(datap, &((uint8_t *)ctx->cbc_remainder)
 209                             [ctx->cbc_remainder_len], need);
 210 
 211                         blockp = (uint8_t *)ctx->cbc_remainder;
 212                 } else {
 213                         blockp = datap;
 214                 }
 215 
 216                 /* LINTED: pointer alignment */
 217                 copy_block(blockp, (uint8_t *)OTHER((uint64_t *)lastp, ctx));
 218 
 219                 if (out != NULL) {
 220                         decrypt(ctx->cbc_keysched, blockp,
 221                             (uint8_t *)ctx->cbc_remainder);
 222                         blockp = (uint8_t *)ctx->cbc_remainder;
 223                 } else {
 224                         decrypt(ctx->cbc_keysched, blockp, blockp);
 225                 }
 226 
 227                 /*
 228                  * XOR the previous cipher block or IV with the
 229                  * currently decrypted block.
 230                  */
 231                 xor_block(lastp, blockp);
 232 
 233                 /* LINTED: pointer alignment */
 234                 lastp = (uint8_t *)OTHER((uint64_t *)lastp, ctx);
 235 
 236                 if (out != NULL) {
 237                         crypto_get_ptrs(out, &iov_or_mp, &offset, &out_data_1,
 238                             &out_data_1_len, &out_data_2, block_size);
 239 
 240                         bcopy(blockp, out_data_1, out_data_1_len);
 241                         if (out_data_2 != NULL) {
 242                                 bcopy(blockp + out_data_1_len, out_data_2,
 243                                     block_size - out_data_1_len);
 244                         }
 245 
 246                         /* update offset */
 247                         out->cd_offset += block_size;
 248 
 249                 } else if (ctx->cbc_remainder_len > 0) {
 250                         /* copy temporary block to where it belongs */
 251                         bcopy(blockp, ctx->cbc_copy_to, ctx->cbc_remainder_len);
 252                         bcopy(blockp + ctx->cbc_remainder_len, datap, need);
 253                 }
 254 
 255                 /* Update pointer to next block of data to be processed. */
 256                 if (ctx->cbc_remainder_len != 0) {
 257                         datap += need;
 258                         ctx->cbc_remainder_len = 0;
 259                 } else {
 260                         datap += block_size;
 261                 }
 262 
 263                 remainder = (size_t)&data[length] - (size_t)datap;
 264 
 265                 /* Incomplete last block. */
 266                 if (remainder > 0 && remainder < block_size) {
 267                         bcopy(datap, ctx->cbc_remainder, remainder);
 268                         ctx->cbc_remainder_len = remainder;
 269                         ctx->cbc_lastp = lastp;
 270                         ctx->cbc_copy_to = datap;
 271                         return (CRYPTO_SUCCESS);
 272                 }
 273                 ctx->cbc_copy_to = NULL;
 274 
 275         } while (remainder > 0);
 276 
 277         ctx->cbc_lastp = lastp;
 278         return (CRYPTO_SUCCESS);
 279 }
 280 
 281 int
 282 cbc_init_ctx(cbc_ctx_t *cbc_ctx, char *param, size_t param_len,
 283     size_t block_size, void (*copy_block)(uint8_t *, uint64_t *))
 284 {
 285         /*
 286          * Copy IV into context.
 287          *
 288          * If cm_param == NULL then the IV comes from the
 289          * cd_miscdata field in the crypto_data structure.
 290          */
 291         if (param != NULL) {
 292 #ifdef _KERNEL
 293                 ASSERT(param_len == block_size);
 294 #else
 295                 assert(param_len == block_size);
 296 #endif
 297                 copy_block((uchar_t *)param, cbc_ctx->cbc_iv);
 298         }
 299 
 300         cbc_ctx->cbc_lastp = (uint8_t *)&cbc_ctx->cbc_iv[0];
 301         cbc_ctx->cbc_flags |= CBC_MODE;
 302         return (CRYPTO_SUCCESS);
 303 }
 304 
 305 /* ARGSUSED */
 306 void *
 307 cbc_alloc_ctx(int kmflag)
 308 {
 309         cbc_ctx_t *cbc_ctx;
 310 
 311 #ifdef _KERNEL
 312         if ((cbc_ctx = kmem_zalloc(sizeof (cbc_ctx_t), kmflag)) == NULL)
 313 #else
 314         if ((cbc_ctx = calloc(1, sizeof (cbc_ctx_t))) == NULL)
 315 #endif
 316                 return (NULL);
 317 
 318         cbc_ctx->cbc_flags = CBC_MODE;
 319         return (cbc_ctx);
 320 }