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 } 321