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