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  */
34 int
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 		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 
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 int
141 cbc_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 
241 int
242 cbc_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 
254 void *
255 cbc_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