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 https://opensource.org/licenses/CDDL-1.0. 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#include <sys/zfs_context.h> 27#include <modes/modes.h> 28#include <sys/crypto/common.h> 29#include <sys/crypto/impl.h> 30 31/* 32 * Algorithm independent CBC functions. 33 */ 34int 35cbc_encrypt_contiguous_blocks(cbc_ctx_t *ctx, char *data, size_t length, 36 crypto_data_t *out, size_t block_size, 37 int (*encrypt)(const void *, const uint8_t *, uint8_t *), 38 void (*copy_block)(uint8_t *, uint8_t *), 39 void (*xor_block)(uint8_t *, uint8_t *)) 40{ 41 size_t remainder = length; 42 size_t need = 0; 43 uint8_t *datap = (uint8_t *)data; 44 uint8_t *blockp; 45 uint8_t *lastp; 46 void *iov_or_mp; 47 offset_t offset; 48 uint8_t *out_data_1; 49 uint8_t *out_data_2; 50 size_t out_data_1_len; 51 52 if (length + ctx->cbc_remainder_len < block_size) { 53 /* accumulate bytes here and return */ 54 memcpy((uint8_t *)ctx->cbc_remainder + ctx->cbc_remainder_len, 55 datap, 56 length); 57 ctx->cbc_remainder_len += length; 58 ctx->cbc_copy_to = datap; 59 return (CRYPTO_SUCCESS); 60 } 61 62 lastp = (uint8_t *)ctx->cbc_iv; 63 crypto_init_ptrs(out, &iov_or_mp, &offset); 64 65 do { 66 /* Unprocessed data from last call. */ 67 if (ctx->cbc_remainder_len > 0) { 68 need = block_size - ctx->cbc_remainder_len; 69 70 if (need > remainder) 71 return (CRYPTO_DATA_LEN_RANGE); 72 73 memcpy(&((uint8_t *)ctx->cbc_remainder) 74 [ctx->cbc_remainder_len], datap, need); 75 76 blockp = (uint8_t *)ctx->cbc_remainder; 77 } else { 78 blockp = datap; 79 } 80 81 /* 82 * XOR the previous cipher block or IV with the 83 * current clear block. 84 */ 85 xor_block(blockp, lastp); 86 encrypt(ctx->cbc_keysched, lastp, lastp); 87 crypto_get_ptrs(out, &iov_or_mp, &offset, &out_data_1, 88 &out_data_1_len, &out_data_2, block_size); 89 90 /* copy block to where it belongs */ 91 if (out_data_1_len == block_size) { 92 copy_block(lastp, out_data_1); 93 } else { 94 memcpy(out_data_1, lastp, out_data_1_len); 95 if (out_data_2 != NULL) { 96 memcpy(out_data_2, 97 lastp + out_data_1_len, 98 block_size - out_data_1_len); 99 } 100 } 101 /* update offset */ 102 out->cd_offset += block_size; 103 104 /* Update pointer to next block of data to be processed. */ 105 if (ctx->cbc_remainder_len != 0) { 106 datap += need; 107 ctx->cbc_remainder_len = 0; 108 } else { 109 datap += block_size; 110 } 111 112 remainder = (size_t)&data[length] - (size_t)datap; 113 114 /* Incomplete last block. */ 115 if (remainder > 0 && remainder < block_size) { 116 memcpy(ctx->cbc_remainder, datap, remainder); 117 ctx->cbc_remainder_len = remainder; 118 ctx->cbc_copy_to = datap; 119 goto out; 120 } 121 ctx->cbc_copy_to = NULL; 122 123 } while (remainder > 0); 124 125out: 126 /* 127 * Save the last encrypted block in the context. 128 */ 129 if (ctx->cbc_lastp != NULL) { 130 copy_block((uint8_t *)ctx->cbc_lastp, (uint8_t *)ctx->cbc_iv); 131 ctx->cbc_lastp = (uint8_t *)ctx->cbc_iv; 132 } 133 134 return (CRYPTO_SUCCESS); 135} 136 137#define OTHER(a, ctx) \ 138 (((a) == (ctx)->cbc_lastblock) ? (ctx)->cbc_iv : (ctx)->cbc_lastblock) 139 140int 141cbc_decrypt_contiguous_blocks(cbc_ctx_t *ctx, char *data, size_t length, 142 crypto_data_t *out, size_t block_size, 143 int (*decrypt)(const void *, const uint8_t *, uint8_t *), 144 void (*copy_block)(uint8_t *, uint8_t *), 145 void (*xor_block)(uint8_t *, uint8_t *)) 146{ 147 size_t remainder = length; 148 size_t need = 0; 149 uint8_t *datap = (uint8_t *)data; 150 uint8_t *blockp; 151 uint8_t *lastp; 152 void *iov_or_mp; 153 offset_t offset; 154 uint8_t *out_data_1; 155 uint8_t *out_data_2; 156 size_t out_data_1_len; 157 158 if (length + ctx->cbc_remainder_len < block_size) { 159 /* accumulate bytes here and return */ 160 memcpy((uint8_t *)ctx->cbc_remainder + ctx->cbc_remainder_len, 161 datap, 162 length); 163 ctx->cbc_remainder_len += length; 164 ctx->cbc_copy_to = datap; 165 return (CRYPTO_SUCCESS); 166 } 167 168 lastp = ctx->cbc_lastp; 169 crypto_init_ptrs(out, &iov_or_mp, &offset); 170 171 do { 172 /* Unprocessed data from last call. */ 173 if (ctx->cbc_remainder_len > 0) { 174 need = block_size - ctx->cbc_remainder_len; 175 176 if (need > remainder) 177 return (CRYPTO_ENCRYPTED_DATA_LEN_RANGE); 178 179 memcpy(&((uint8_t *)ctx->cbc_remainder) 180 [ctx->cbc_remainder_len], datap, need); 181 182 blockp = (uint8_t *)ctx->cbc_remainder; 183 } else { 184 blockp = datap; 185 } 186 187 /* LINTED: pointer alignment */ 188 copy_block(blockp, (uint8_t *)OTHER((uint64_t *)lastp, ctx)); 189 190 decrypt(ctx->cbc_keysched, blockp, 191 (uint8_t *)ctx->cbc_remainder); 192 blockp = (uint8_t *)ctx->cbc_remainder; 193 194 /* 195 * XOR the previous cipher block or IV with the 196 * currently decrypted block. 197 */ 198 xor_block(lastp, blockp); 199 200 /* LINTED: pointer alignment */ 201 lastp = (uint8_t *)OTHER((uint64_t *)lastp, ctx); 202 203 crypto_get_ptrs(out, &iov_or_mp, &offset, &out_data_1, 204 &out_data_1_len, &out_data_2, block_size); 205 206 memcpy(out_data_1, blockp, out_data_1_len); 207 if (out_data_2 != NULL) { 208 memcpy(out_data_2, blockp + out_data_1_len, 209 block_size - out_data_1_len); 210 } 211 212 /* update offset */ 213 out->cd_offset += block_size; 214 215 /* Update pointer to next block of data to be processed. */ 216 if (ctx->cbc_remainder_len != 0) { 217 datap += need; 218 ctx->cbc_remainder_len = 0; 219 } else { 220 datap += block_size; 221 } 222 223 remainder = (size_t)&data[length] - (size_t)datap; 224 225 /* Incomplete last block. */ 226 if (remainder > 0 && remainder < block_size) { 227 memcpy(ctx->cbc_remainder, datap, remainder); 228 ctx->cbc_remainder_len = remainder; 229 ctx->cbc_lastp = lastp; 230 ctx->cbc_copy_to = datap; 231 return (CRYPTO_SUCCESS); 232 } 233 ctx->cbc_copy_to = NULL; 234 235 } while (remainder > 0); 236 237 ctx->cbc_lastp = lastp; 238 return (CRYPTO_SUCCESS); 239} 240 241int 242cbc_init_ctx(cbc_ctx_t *cbc_ctx, char *param, size_t param_len, 243 size_t block_size, void (*copy_block)(uint8_t *, uint64_t *)) 244{ 245 /* Copy IV into context. */ 246 ASSERT3P(param, !=, NULL); 247 ASSERT3U(param_len, ==, block_size); 248 249 copy_block((uchar_t *)param, cbc_ctx->cbc_iv); 250 251 return (CRYPTO_SUCCESS); 252} 253 254void * 255cbc_alloc_ctx(int kmflag) 256{ 257 cbc_ctx_t *cbc_ctx; 258 259 if ((cbc_ctx = kmem_zalloc(sizeof (cbc_ctx_t), kmflag)) == NULL) 260 return (NULL); 261 262 cbc_ctx->cbc_flags = CBC_MODE; 263 return (cbc_ctx); 264} 265