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