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