xref: /illumos-gate/usr/src/common/crypto/modes/cbc.c (revision bb0ade0978a02d3fe0b0165cd4725fdcb593fbfb)
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 #pragma ident	"%Z%%M%	%I%	%E% SMI"
27 
28 #ifndef _KERNEL
29 #include <strings.h>
30 #include <limits.h>
31 #include <assert.h>
32 #include <security/cryptoki.h>
33 #endif
34 
35 #include <sys/types.h>
36 #include <modes/modes.h>
37 #include <sys/crypto/common.h>
38 #include <sys/crypto/impl.h>
39 
40 /*
41  * Algorithm independent CBC functions.
42  */
43 int
44 cbc_encrypt_contiguous_blocks(cbc_ctx_t *ctx, char *data, size_t length,
45     crypto_data_t *out, size_t block_size,
46     int (*encrypt)(const void *, const uint8_t *, uint8_t *),
47     void (*copy_block)(uint8_t *, uint8_t *),
48     void (*xor_block)(uint8_t *, uint8_t *))
49 {
50 	size_t remainder = length;
51 	size_t need;
52 	uint8_t *datap = (uint8_t *)data;
53 	uint8_t *blockp;
54 	uint8_t *lastp;
55 	void *iov_or_mp;
56 	offset_t offset;
57 	uint8_t *out_data_1;
58 	uint8_t *out_data_2;
59 	size_t out_data_1_len;
60 
61 	if (length + ctx->cc_remainder_len < block_size) {
62 		/* accumulate bytes here and return */
63 		bcopy(datap,
64 		    (uint8_t *)ctx->cc_remainder + ctx->cc_remainder_len,
65 		    length);
66 		ctx->cc_remainder_len += length;
67 		ctx->cc_copy_to = datap;
68 		return (CRYPTO_SUCCESS);
69 	}
70 
71 	lastp = (uint8_t *)ctx->cc_iv;
72 	if (out != NULL)
73 		crypto_init_ptrs(out, &iov_or_mp, &offset);
74 
75 	do {
76 		/* Unprocessed data from last call. */
77 		if (ctx->cc_remainder_len > 0) {
78 			need = block_size - ctx->cc_remainder_len;
79 
80 			if (need > remainder)
81 				return (CRYPTO_DATA_LEN_RANGE);
82 
83 			bcopy(datap, &((uint8_t *)ctx->cc_remainder)
84 			    [ctx->cc_remainder_len], need);
85 
86 			blockp = (uint8_t *)ctx->cc_remainder;
87 		} else {
88 			blockp = datap;
89 		}
90 
91 		if (out == NULL) {
92 			/*
93 			 * XOR the previous cipher block or IV with the
94 			 * current clear block.
95 			 */
96 			xor_block(lastp, blockp);
97 			encrypt(ctx->cc_keysched, blockp, blockp);
98 
99 			ctx->cc_lastp = blockp;
100 			lastp = blockp;
101 
102 			if (ctx->cc_remainder_len > 0) {
103 				bcopy(blockp, ctx->cc_copy_to,
104 				    ctx->cc_remainder_len);
105 				bcopy(blockp + ctx->cc_remainder_len, datap,
106 				    need);
107 			}
108 		} else {
109 			/*
110 			 * XOR the previous cipher block or IV with the
111 			 * current clear block.
112 			 */
113 			xor_block(blockp, lastp);
114 			encrypt(ctx->cc_keysched, lastp, lastp);
115 			crypto_get_ptrs(out, &iov_or_mp, &offset, &out_data_1,
116 			    &out_data_1_len, &out_data_2, block_size);
117 
118 			/* copy block to where it belongs */
119 			if (out_data_1_len == block_size) {
120 				copy_block(lastp, out_data_1);
121 			} else {
122 				bcopy(lastp, out_data_1, out_data_1_len);
123 				if (out_data_2 != NULL) {
124 					bcopy(lastp + out_data_1_len,
125 					    out_data_2,
126 					    block_size - out_data_1_len);
127 				}
128 			}
129 			/* update offset */
130 			out->cd_offset += block_size;
131 		}
132 
133 		/* Update pointer to next block of data to be processed. */
134 		if (ctx->cc_remainder_len != 0) {
135 			datap += need;
136 			ctx->cc_remainder_len = 0;
137 		} else {
138 			datap += block_size;
139 		}
140 
141 		remainder = (size_t)&data[length] - (size_t)datap;
142 
143 		/* Incomplete last block. */
144 		if (remainder > 0 && remainder < block_size) {
145 			bcopy(datap, ctx->cc_remainder, remainder);
146 			ctx->cc_remainder_len = remainder;
147 			ctx->cc_copy_to = datap;
148 			goto out;
149 		}
150 		ctx->cc_copy_to = NULL;
151 
152 	} while (remainder > 0);
153 
154 out:
155 	/*
156 	 * Save the last encrypted block in the context.
157 	 */
158 	if (ctx->cc_lastp != NULL) {
159 		copy_block((uint8_t *)ctx->cc_lastp, (uint8_t *)ctx->cc_iv);
160 		ctx->cc_lastp = (uint8_t *)ctx->cc_iv;
161 	}
162 
163 	return (CRYPTO_SUCCESS);
164 }
165 
166 #define	OTHER(a, ctx) \
167 	(((a) == (ctx)->cc_lastblock) ? (ctx)->cc_iv : (ctx)->cc_lastblock)
168 
169 /* ARGSUSED */
170 int
171 cbc_decrypt_contiguous_blocks(cbc_ctx_t *ctx, char *data, size_t length,
172     crypto_data_t *out, size_t block_size,
173     int (*decrypt)(const void *, const uint8_t *, uint8_t *),
174     void (*copy_block)(uint8_t *, uint8_t *),
175     void (*xor_block)(uint8_t *, uint8_t *))
176 {
177 	size_t remainder = length;
178 	size_t need;
179 	uint8_t *datap = (uint8_t *)data;
180 	uint8_t *blockp;
181 	uint8_t *lastp;
182 	void *iov_or_mp;
183 	offset_t offset;
184 	uint8_t *out_data_1;
185 	uint8_t *out_data_2;
186 	size_t out_data_1_len;
187 
188 	if (length + ctx->cc_remainder_len < block_size) {
189 		/* accumulate bytes here and return */
190 		bcopy(datap,
191 		    (uint8_t *)ctx->cc_remainder + ctx->cc_remainder_len,
192 		    length);
193 		ctx->cc_remainder_len += length;
194 		ctx->cc_copy_to = datap;
195 		return (CRYPTO_SUCCESS);
196 	}
197 
198 	lastp = ctx->cc_lastp;
199 	if (out != NULL)
200 		crypto_init_ptrs(out, &iov_or_mp, &offset);
201 
202 	do {
203 		/* Unprocessed data from last call. */
204 		if (ctx->cc_remainder_len > 0) {
205 			need = block_size - ctx->cc_remainder_len;
206 
207 			if (need > remainder)
208 				return (CRYPTO_ENCRYPTED_DATA_LEN_RANGE);
209 
210 			bcopy(datap, &((uint8_t *)ctx->cc_remainder)
211 			    [ctx->cc_remainder_len], need);
212 
213 			blockp = (uint8_t *)ctx->cc_remainder;
214 		} else {
215 			blockp = datap;
216 		}
217 
218 		/* LINTED: pointer alignment */
219 		copy_block(blockp, (uint8_t *)OTHER((uint64_t *)lastp, ctx));
220 
221 		if (out != NULL) {
222 			decrypt(ctx->cc_keysched, blockp,
223 			    (uint8_t *)ctx->cc_remainder);
224 			blockp = (uint8_t *)ctx->cc_remainder;
225 		} else {
226 			decrypt(ctx->cc_keysched, blockp, blockp);
227 		}
228 
229 		/*
230 		 * XOR the previous cipher block or IV with the
231 		 * currently decrypted block.
232 		 */
233 		xor_block(lastp, blockp);
234 
235 		/* LINTED: pointer alignment */
236 		lastp = (uint8_t *)OTHER((uint64_t *)lastp, ctx);
237 
238 		if (out != NULL) {
239 			crypto_get_ptrs(out, &iov_or_mp, &offset, &out_data_1,
240 			    &out_data_1_len, &out_data_2, block_size);
241 
242 			bcopy(blockp, out_data_1, out_data_1_len);
243 			if (out_data_2 != NULL) {
244 				bcopy(blockp + out_data_1_len, out_data_2,
245 				    block_size - out_data_1_len);
246 			}
247 
248 			/* update offset */
249 			out->cd_offset += block_size;
250 
251 		} else if (ctx->cc_remainder_len > 0) {
252 			/* copy temporary block to where it belongs */
253 			bcopy(blockp, ctx->cc_copy_to, ctx->cc_remainder_len);
254 			bcopy(blockp + ctx->cc_remainder_len, datap, need);
255 		}
256 
257 		/* Update pointer to next block of data to be processed. */
258 		if (ctx->cc_remainder_len != 0) {
259 			datap += need;
260 			ctx->cc_remainder_len = 0;
261 		} else {
262 			datap += block_size;
263 		}
264 
265 		remainder = (size_t)&data[length] - (size_t)datap;
266 
267 		/* Incomplete last block. */
268 		if (remainder > 0 && remainder < block_size) {
269 			bcopy(datap, ctx->cc_remainder, remainder);
270 			ctx->cc_remainder_len = remainder;
271 			ctx->cc_lastp = lastp;
272 			ctx->cc_copy_to = datap;
273 			return (CRYPTO_SUCCESS);
274 		}
275 		ctx->cc_copy_to = NULL;
276 
277 	} while (remainder > 0);
278 
279 	ctx->cc_lastp = lastp;
280 	return (CRYPTO_SUCCESS);
281 }
282 
283 int
284 cbc_init_ctx(cbc_ctx_t *cbc_ctx, char *param, size_t param_len,
285     size_t block_size, void (*copy_block)(uint8_t *, uint64_t *))
286 {
287 	/*
288 	 * Copy IV into context.
289 	 *
290 	 * If cm_param == NULL then the IV comes from the
291 	 * cd_miscdata field in the crypto_data structure.
292 	 */
293 	if (param != NULL) {
294 #ifdef _KERNEL
295 		ASSERT(param_len == block_size);
296 #else
297 		assert(param_len == block_size);
298 #endif
299 		copy_block((uchar_t *)param, cbc_ctx->cc_iv);
300 	}
301 
302 	cbc_ctx->cc_lastp = (uint8_t *)&cbc_ctx->cc_iv[0];
303 	cbc_ctx->cc_flags |= CBC_MODE;
304 	return (CRYPTO_SUCCESS);
305 }
306 
307 /* ARGSUSED */
308 void *
309 cbc_alloc_ctx(int kmflag)
310 {
311 	cbc_ctx_t *cbc_ctx;
312 
313 #ifdef _KERNEL
314 	if ((cbc_ctx = kmem_zalloc(sizeof (cbc_ctx_t), kmflag)) == NULL)
315 #else
316 	if ((cbc_ctx = calloc(1, sizeof (cbc_ctx_t))) == NULL)
317 #endif
318 		return (NULL);
319 
320 	cbc_ctx->cc_flags = CBC_MODE;
321 	return (cbc_ctx);
322 }
323