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