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