xref: /illumos-gate/usr/src/common/crypto/modes/ctr.c (revision 23c57df7)
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  * Encrypt and decrypt multiple blocks of data in counter mode.
42  */
43 int
44 ctr_mode_contiguous_blocks(ctr_ctx_t *ctx, char *data, size_t length,
45     crypto_data_t *out, size_t block_size,
46     int (*cipher)(const void *ks, const uint8_t *pt, uint8_t *ct),
47     void (*xor_block)(uint8_t *, uint8_t *))
48 {
49 	size_t remainder = length;
50 	size_t need;
51 	uint8_t *datap = (uint8_t *)data;
52 	uint8_t *blockp;
53 	uint8_t *lastp;
54 	void *iov_or_mp;
55 	offset_t offset;
56 	uint8_t *out_data_1;
57 	uint8_t *out_data_2;
58 	size_t out_data_1_len;
59 	uint64_t counter;
60 #ifdef _LITTLE_ENDIAN
61 	uint8_t *p;
62 #endif
63 
64 	if (length + ctx->ctr_remainder_len < block_size) {
65 		/* accumulate bytes here and return */
66 		bcopy(datap,
67 		    (uint8_t *)ctx->ctr_remainder + ctx->ctr_remainder_len,
68 		    length);
69 		ctx->ctr_remainder_len += length;
70 		ctx->ctr_copy_to = datap;
71 		return (CRYPTO_SUCCESS);
72 	}
73 
74 	lastp = (uint8_t *)ctx->ctr_cb;
75 	if (out != NULL)
76 		crypto_init_ptrs(out, &iov_or_mp, &offset);
77 
78 	do {
79 		/* Unprocessed data from last call. */
80 		if (ctx->ctr_remainder_len > 0) {
81 			need = block_size - ctx->ctr_remainder_len;
82 
83 			if (need > remainder)
84 				return (CRYPTO_DATA_LEN_RANGE);
85 
86 			bcopy(datap, &((uint8_t *)ctx->ctr_remainder)
87 			    [ctx->ctr_remainder_len], need);
88 
89 			blockp = (uint8_t *)ctx->ctr_remainder;
90 		} else {
91 			blockp = datap;
92 		}
93 
94 		/* ctr_cb is the counter block */
95 		cipher(ctx->ctr_keysched, (uint8_t *)ctx->ctr_cb,
96 		    (uint8_t *)ctx->ctr_tmp);
97 
98 		lastp = (uint8_t *)ctx->ctr_tmp;
99 
100 		/*
101 		 * Increment counter. Counter bits are confined
102 		 * to the bottom 64 bits of the counter block.
103 		 */
104 		counter = ctx->ctr_cb[1] & ctx->ctr_counter_mask;
105 #ifdef _LITTLE_ENDIAN
106 		p = (uint8_t *)&counter;
107 		counter = (((uint64_t)p[0] << 56) |
108 		    ((uint64_t)p[1] << 48) |
109 		    ((uint64_t)p[2] << 40) |
110 		    ((uint64_t)p[3] << 32) |
111 		    ((uint64_t)p[4] << 24) |
112 		    ((uint64_t)p[5] << 16) |
113 		    ((uint64_t)p[6] << 8) |
114 		    (uint64_t)p[7]);
115 #endif
116 		counter++;
117 #ifdef _LITTLE_ENDIAN
118 		counter = (((uint64_t)p[0] << 56) |
119 		    ((uint64_t)p[1] << 48) |
120 		    ((uint64_t)p[2] << 40) |
121 		    ((uint64_t)p[3] << 32) |
122 		    ((uint64_t)p[4] << 24) |
123 		    ((uint64_t)p[5] << 16) |
124 		    ((uint64_t)p[6] << 8) |
125 		    (uint64_t)p[7]);
126 #endif
127 		counter &= ctx->ctr_counter_mask;
128 		ctx->ctr_cb[1] =
129 		    (ctx->ctr_cb[1] & ~(ctx->ctr_counter_mask)) | counter;
130 
131 		/*
132 		 * XOR the previous cipher block or IV with the
133 		 * current clear block.
134 		 */
135 		xor_block(blockp, lastp);
136 
137 		if (out == NULL) {
138 			if (ctx->ctr_remainder_len > 0) {
139 				bcopy(lastp, ctx->ctr_copy_to,
140 				    ctx->ctr_remainder_len);
141 				bcopy(lastp + ctx->ctr_remainder_len, datap,
142 				    need);
143 			}
144 		} else {
145 			crypto_get_ptrs(out, &iov_or_mp, &offset, &out_data_1,
146 			    &out_data_1_len, &out_data_2, block_size);
147 
148 			/* copy block to where it belongs */
149 			bcopy(lastp, out_data_1, out_data_1_len);
150 			if (out_data_2 != NULL) {
151 				bcopy(lastp + out_data_1_len, out_data_2,
152 				    block_size - out_data_1_len);
153 			}
154 			/* update offset */
155 			out->cd_offset += block_size;
156 		}
157 
158 		/* Update pointer to next block of data to be processed. */
159 		if (ctx->ctr_remainder_len != 0) {
160 			datap += need;
161 			ctx->ctr_remainder_len = 0;
162 		} else {
163 			datap += block_size;
164 		}
165 
166 		remainder = (size_t)&data[length] - (size_t)datap;
167 
168 		/* Incomplete last block. */
169 		if (remainder > 0 && remainder < block_size) {
170 			bcopy(datap, ctx->ctr_remainder, remainder);
171 			ctx->ctr_remainder_len = remainder;
172 			ctx->ctr_copy_to = datap;
173 			goto out;
174 		}
175 		ctx->ctr_copy_to = NULL;
176 
177 	} while (remainder > 0);
178 
179 out:
180 	return (CRYPTO_SUCCESS);
181 }
182 
183 int
184 ctr_mode_final(ctr_ctx_t *ctx, crypto_data_t *out,
185     int (*encrypt_block)(const void *, const uint8_t *, uint8_t *))
186 {
187 	uint8_t *lastp;
188 	void *iov_or_mp;
189 	offset_t offset;
190 	uint8_t *out_data_1;
191 	uint8_t *out_data_2;
192 	size_t out_data_1_len;
193 	uint8_t *p;
194 	int i;
195 
196 	if (out->cd_length < ctx->ctr_remainder_len)
197 		return (CRYPTO_DATA_LEN_RANGE);
198 
199 	encrypt_block(ctx->ctr_keysched, (uint8_t *)ctx->ctr_cb,
200 	    (uint8_t *)ctx->ctr_tmp);
201 
202 	lastp = (uint8_t *)ctx->ctr_tmp;
203 	p = (uint8_t *)ctx->ctr_remainder;
204 	for (i = 0; i < ctx->ctr_remainder_len; i++) {
205 		p[i] ^= lastp[i];
206 	}
207 
208 	crypto_init_ptrs(out, &iov_or_mp, &offset);
209 	crypto_get_ptrs(out, &iov_or_mp, &offset, &out_data_1,
210 	    &out_data_1_len, &out_data_2, ctx->ctr_remainder_len);
211 
212 	bcopy(p, out_data_1, out_data_1_len);
213 	if (out_data_2 != NULL) {
214 		bcopy((uint8_t *)p + out_data_1_len,
215 		    out_data_2, ctx->ctr_remainder_len - out_data_1_len);
216 	}
217 	out->cd_offset += ctx->ctr_remainder_len;
218 	ctx->ctr_remainder_len = 0;
219 	return (CRYPTO_SUCCESS);
220 }
221 
222 int
223 ctr_init_ctx(ctr_ctx_t *ctr_ctx, ulong_t count, uint8_t *cb,
224 void (*copy_block)(uint8_t *, uint8_t *))
225 {
226 	uint64_t mask = 0;
227 #ifdef _LITTLE_ENDIAN
228 	uint8_t *p8;
229 #endif
230 
231 	if (count == 0 || count > 64) {
232 		return (CRYPTO_MECHANISM_PARAM_INVALID);
233 	}
234 	while (count-- > 0)
235 		mask |= (1ULL << count);
236 #ifdef _LITTLE_ENDIAN
237 	p8 = (uint8_t *)&mask;
238 	mask = (((uint64_t)p8[0] << 56) |
239 	    ((uint64_t)p8[1] << 48) |
240 	    ((uint64_t)p8[2] << 40) |
241 	    ((uint64_t)p8[3] << 32) |
242 	    ((uint64_t)p8[4] << 24) |
243 	    ((uint64_t)p8[5] << 16) |
244 	    ((uint64_t)p8[6] << 8) |
245 	    (uint64_t)p8[7]);
246 #endif
247 	ctr_ctx->ctr_counter_mask = mask;
248 	copy_block(cb, (uchar_t *)ctr_ctx->ctr_cb);
249 	ctr_ctx->ctr_lastp = (uint8_t *)&ctr_ctx->ctr_cb[0];
250 	ctr_ctx->ctr_flags |= CTR_MODE;
251 	return (CRYPTO_SUCCESS);
252 }
253 
254 /* ARGSUSED */
255 void *
256 ctr_alloc_ctx(int kmflag)
257 {
258 	ctr_ctx_t *ctr_ctx;
259 
260 #ifdef _KERNEL
261 	if ((ctr_ctx = kmem_zalloc(sizeof (ctr_ctx_t), kmflag)) == NULL)
262 #else
263 	if ((ctr_ctx = calloc(1, sizeof (ctr_ctx_t))) == NULL)
264 #endif
265 		return (NULL);
266 
267 	ctr_ctx->ctr_flags = CTR_MODE;
268 	return (ctr_ctx);
269 }
270