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 2009 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  * Utility routine to copy a buffer to a crypto_data structure.
33  */
34 
35 /*
36  * Utility routine to apply the command, 'cmd', to the
37  * data in the uio structure.
38  */
39 int
40 crypto_uio_data(crypto_data_t *data, uchar_t *buf, int len, cmd_type_t cmd,
41     void *digest_ctx, void (*update)(void))
42 {
43 	zfs_uio_t *uiop = data->cd_uio;
44 	off_t offset = data->cd_offset;
45 	size_t length = len;
46 	uint_t vec_idx;
47 	size_t cur_len;
48 	uchar_t *datap;
49 
50 	ASSERT(data->cd_format == CRYPTO_DATA_UIO);
51 	if (zfs_uio_segflg(uiop) != UIO_SYSSPACE) {
52 		return (CRYPTO_ARGUMENTS_BAD);
53 	}
54 
55 	/*
56 	 * Jump to the first iovec containing data to be
57 	 * processed.
58 	 */
59 	offset = zfs_uio_index_at_offset(uiop, offset, &vec_idx);
60 
61 	if (vec_idx == zfs_uio_iovcnt(uiop) && length > 0) {
62 		/*
63 		 * The caller specified an offset that is larger than
64 		 * the total size of the buffers it provided.
65 		 */
66 		return (CRYPTO_DATA_LEN_RANGE);
67 	}
68 
69 	while (vec_idx < zfs_uio_iovcnt(uiop) && length > 0) {
70 		cur_len = MIN(zfs_uio_iovlen(uiop, vec_idx) -
71 		    offset, length);
72 
73 		datap = (uchar_t *)(zfs_uio_iovbase(uiop, vec_idx) + offset);
74 		switch (cmd) {
75 		case COPY_FROM_DATA:
76 			bcopy(datap, buf, cur_len);
77 			buf += cur_len;
78 			break;
79 		case COPY_TO_DATA:
80 			bcopy(buf, datap, cur_len);
81 			buf += cur_len;
82 			break;
83 		case COMPARE_TO_DATA:
84 			if (bcmp(datap, buf, cur_len))
85 				return (CRYPTO_SIGNATURE_INVALID);
86 			buf += cur_len;
87 			break;
88 		case MD5_DIGEST_DATA:
89 		case SHA1_DIGEST_DATA:
90 		case SHA2_DIGEST_DATA:
91 		case GHASH_DATA:
92 			return (CRYPTO_ARGUMENTS_BAD);
93 		}
94 
95 		length -= cur_len;
96 		vec_idx++;
97 		offset = 0;
98 	}
99 
100 	if (vec_idx == zfs_uio_iovcnt(uiop) && length > 0) {
101 		/*
102 		 * The end of the specified iovec's was reached but
103 		 * the length requested could not be processed.
104 		 */
105 		switch (cmd) {
106 		case COPY_TO_DATA:
107 			data->cd_length = len;
108 			return (CRYPTO_BUFFER_TOO_SMALL);
109 		default:
110 			return (CRYPTO_DATA_LEN_RANGE);
111 		}
112 	}
113 
114 	return (CRYPTO_SUCCESS);
115 }
116 
117 int
118 crypto_put_output_data(uchar_t *buf, crypto_data_t *output, int len)
119 {
120 	switch (output->cd_format) {
121 	case CRYPTO_DATA_RAW:
122 		if (output->cd_raw.iov_len < len) {
123 			output->cd_length = len;
124 			return (CRYPTO_BUFFER_TOO_SMALL);
125 		}
126 		bcopy(buf, (uchar_t *)(output->cd_raw.iov_base +
127 		    output->cd_offset), len);
128 		break;
129 
130 	case CRYPTO_DATA_UIO:
131 		return (crypto_uio_data(output, buf, len,
132 		    COPY_TO_DATA, NULL, NULL));
133 	default:
134 		return (CRYPTO_ARGUMENTS_BAD);
135 	}
136 
137 	return (CRYPTO_SUCCESS);
138 }
139 
140 int
141 crypto_update_iov(void *ctx, crypto_data_t *input, crypto_data_t *output,
142     int (*cipher)(void *, caddr_t, size_t, crypto_data_t *),
143     void (*copy_block)(uint8_t *, uint64_t *))
144 {
145 	common_ctx_t *common_ctx = ctx;
146 	int rv;
147 
148 	ASSERT(input != output);
149 	if (input->cd_miscdata != NULL) {
150 		copy_block((uint8_t *)input->cd_miscdata,
151 		    &common_ctx->cc_iv[0]);
152 	}
153 
154 	if (input->cd_raw.iov_len < input->cd_length)
155 		return (CRYPTO_ARGUMENTS_BAD);
156 
157 	rv = (cipher)(ctx, input->cd_raw.iov_base + input->cd_offset,
158 	    input->cd_length, output);
159 
160 	return (rv);
161 }
162 
163 int
164 crypto_update_uio(void *ctx, crypto_data_t *input, crypto_data_t *output,
165     int (*cipher)(void *, caddr_t, size_t, crypto_data_t *),
166     void (*copy_block)(uint8_t *, uint64_t *))
167 {
168 	common_ctx_t *common_ctx = ctx;
169 	zfs_uio_t *uiop = input->cd_uio;
170 	off_t offset = input->cd_offset;
171 	size_t length = input->cd_length;
172 	uint_t vec_idx;
173 	size_t cur_len;
174 
175 	ASSERT(input != output);
176 	if (input->cd_miscdata != NULL) {
177 		copy_block((uint8_t *)input->cd_miscdata,
178 		    &common_ctx->cc_iv[0]);
179 	}
180 
181 	if (zfs_uio_segflg(input->cd_uio) != UIO_SYSSPACE) {
182 		return (CRYPTO_ARGUMENTS_BAD);
183 	}
184 
185 	/*
186 	 * Jump to the first iovec containing data to be
187 	 * processed.
188 	 */
189 	offset = zfs_uio_index_at_offset(uiop, offset, &vec_idx);
190 	if (vec_idx == zfs_uio_iovcnt(uiop) && length > 0) {
191 		/*
192 		 * The caller specified an offset that is larger than the
193 		 * total size of the buffers it provided.
194 		 */
195 		return (CRYPTO_DATA_LEN_RANGE);
196 	}
197 
198 	/*
199 	 * Now process the iovecs.
200 	 */
201 	while (vec_idx < zfs_uio_iovcnt(uiop) && length > 0) {
202 		cur_len = MIN(zfs_uio_iovlen(uiop, vec_idx) -
203 		    offset, length);
204 
205 		int rv = (cipher)(ctx, zfs_uio_iovbase(uiop, vec_idx) + offset,
206 		    cur_len, output);
207 
208 		if (rv != CRYPTO_SUCCESS) {
209 			return (rv);
210 		}
211 		length -= cur_len;
212 		vec_idx++;
213 		offset = 0;
214 	}
215 
216 	if (vec_idx == zfs_uio_iovcnt(uiop) && length > 0) {
217 		/*
218 		 * The end of the specified iovec's was reached but
219 		 * the length requested could not be processed, i.e.
220 		 * The caller requested to digest more data than it provided.
221 		 */
222 
223 		return (CRYPTO_DATA_LEN_RANGE);
224 	}
225 
226 	return (CRYPTO_SUCCESS);
227 }
228