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 #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  */
34 int
cbc_encrypt_contiguous_blocks(cbc_ctx_t * ctx,char * data,size_t length,crypto_data_t * out,size_t block_size,int (* encrypt)(const void *,const uint8_t *,uint8_t *),void (* copy_block)(uint8_t *,uint8_t *),void (* xor_block)(uint8_t *,uint8_t *))35 cbc_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 		bcopy(datap,
55 		    (uint8_t *)ctx->cbc_remainder + ctx->cbc_remainder_len,
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 			bcopy(datap, &((uint8_t *)ctx->cbc_remainder)
74 			    [ctx->cbc_remainder_len], 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 			bcopy(lastp, out_data_1, out_data_1_len);
95 			if (out_data_2 != NULL) {
96 				bcopy(lastp + out_data_1_len,
97 				    out_data_2,
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 			bcopy(datap, ctx->cbc_remainder, 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 
125 out:
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 
140 /* ARGSUSED */
141 int
cbc_decrypt_contiguous_blocks(cbc_ctx_t * ctx,char * data,size_t length,crypto_data_t * out,size_t block_size,int (* decrypt)(const void *,const uint8_t *,uint8_t *),void (* copy_block)(uint8_t *,uint8_t *),void (* xor_block)(uint8_t *,uint8_t *))142 cbc_decrypt_contiguous_blocks(cbc_ctx_t *ctx, char *data, size_t length,
143     crypto_data_t *out, size_t block_size,
144     int (*decrypt)(const void *, const uint8_t *, uint8_t *),
145     void (*copy_block)(uint8_t *, uint8_t *),
146     void (*xor_block)(uint8_t *, uint8_t *))
147 {
148 	size_t remainder = length;
149 	size_t need = 0;
150 	uint8_t *datap = (uint8_t *)data;
151 	uint8_t *blockp;
152 	uint8_t *lastp;
153 	void *iov_or_mp;
154 	offset_t offset;
155 	uint8_t *out_data_1;
156 	uint8_t *out_data_2;
157 	size_t out_data_1_len;
158 
159 	if (length + ctx->cbc_remainder_len < block_size) {
160 		/* accumulate bytes here and return */
161 		bcopy(datap,
162 		    (uint8_t *)ctx->cbc_remainder + ctx->cbc_remainder_len,
163 		    length);
164 		ctx->cbc_remainder_len += length;
165 		ctx->cbc_copy_to = datap;
166 		return (CRYPTO_SUCCESS);
167 	}
168 
169 	lastp = ctx->cbc_lastp;
170 	crypto_init_ptrs(out, &iov_or_mp, &offset);
171 
172 	do {
173 		/* Unprocessed data from last call. */
174 		if (ctx->cbc_remainder_len > 0) {
175 			need = block_size - ctx->cbc_remainder_len;
176 
177 			if (need > remainder)
178 				return (CRYPTO_ENCRYPTED_DATA_LEN_RANGE);
179 
180 			bcopy(datap, &((uint8_t *)ctx->cbc_remainder)
181 			    [ctx->cbc_remainder_len], need);
182 
183 			blockp = (uint8_t *)ctx->cbc_remainder;
184 		} else {
185 			blockp = datap;
186 		}
187 
188 		/* LINTED: pointer alignment */
189 		copy_block(blockp, (uint8_t *)OTHER((uint64_t *)lastp, ctx));
190 
191 		decrypt(ctx->cbc_keysched, blockp,
192 		    (uint8_t *)ctx->cbc_remainder);
193 		blockp = (uint8_t *)ctx->cbc_remainder;
194 
195 		/*
196 		 * XOR the previous cipher block or IV with the
197 		 * currently decrypted block.
198 		 */
199 		xor_block(lastp, blockp);
200 
201 		/* LINTED: pointer alignment */
202 		lastp = (uint8_t *)OTHER((uint64_t *)lastp, ctx);
203 
204 		crypto_get_ptrs(out, &iov_or_mp, &offset, &out_data_1,
205 		    &out_data_1_len, &out_data_2, block_size);
206 
207 		bcopy(blockp, out_data_1, out_data_1_len);
208 		if (out_data_2 != NULL) {
209 			bcopy(blockp + out_data_1_len, out_data_2,
210 			    block_size - out_data_1_len);
211 		}
212 
213 		/* update offset */
214 		out->cd_offset += block_size;
215 
216 		/* Update pointer to next block of data to be processed. */
217 		if (ctx->cbc_remainder_len != 0) {
218 			datap += need;
219 			ctx->cbc_remainder_len = 0;
220 		} else {
221 			datap += block_size;
222 		}
223 
224 		remainder = (size_t)&data[length] - (size_t)datap;
225 
226 		/* Incomplete last block. */
227 		if (remainder > 0 && remainder < block_size) {
228 			bcopy(datap, ctx->cbc_remainder, remainder);
229 			ctx->cbc_remainder_len = remainder;
230 			ctx->cbc_lastp = lastp;
231 			ctx->cbc_copy_to = datap;
232 			return (CRYPTO_SUCCESS);
233 		}
234 		ctx->cbc_copy_to = NULL;
235 
236 	} while (remainder > 0);
237 
238 	ctx->cbc_lastp = lastp;
239 	return (CRYPTO_SUCCESS);
240 }
241 
242 int
cbc_init_ctx(cbc_ctx_t * cbc_ctx,char * param,size_t param_len,size_t block_size,void (* copy_block)(uint8_t *,uint64_t *))243 cbc_init_ctx(cbc_ctx_t *cbc_ctx, char *param, size_t param_len,
244     size_t block_size, void (*copy_block)(uint8_t *, uint64_t *))
245 {
246 	/*
247 	 * Copy IV into context.
248 	 *
249 	 * If cm_param == NULL then the IV comes from the
250 	 * cd_miscdata field in the crypto_data structure.
251 	 */
252 	if (param != NULL) {
253 		ASSERT(param_len == block_size);
254 		copy_block((uchar_t *)param, cbc_ctx->cbc_iv);
255 	}
256 
257 	cbc_ctx->cbc_lastp = (uint8_t *)&cbc_ctx->cbc_iv[0];
258 	cbc_ctx->cbc_flags |= CBC_MODE;
259 	return (CRYPTO_SUCCESS);
260 }
261 
262 /* ARGSUSED */
263 void *
cbc_alloc_ctx(int kmflag)264 cbc_alloc_ctx(int kmflag)
265 {
266 	cbc_ctx_t *cbc_ctx;
267 
268 	if ((cbc_ctx = kmem_zalloc(sizeof (cbc_ctx_t), kmflag)) == NULL)
269 		return (NULL);
270 
271 	cbc_ctx->cbc_flags = CBC_MODE;
272 	return (cbc_ctx);
273 }
274