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 2010 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 #include <sys/strsun.h>
27 #include <sys/systm.h>
28 #include <sys/sysmacros.h>
29 #include <sys/kmem.h>
30 #include <sys/md5.h>
31 #include <sys/sha1.h>
32 #include <sys/sha2.h>
33 #include <modes/modes.h>
34 #include <sys/crypto/common.h>
35 #include <sys/crypto/impl.h>
36 
37 /*
38  * Utility routine to apply the command, 'cmd', to the
39  * data in the uio structure.
40  */
41 int
42 crypto_uio_data(crypto_data_t *data, uchar_t *buf, int len, cmd_type_t cmd,
43     void *digest_ctx, void (*update)())
44 {
45 	uio_t *uiop = data->cd_uio;
46 	off_t offset = data->cd_offset;
47 	size_t length = len;
48 	uint_t vec_idx;
49 	size_t cur_len;
50 	uchar_t *datap;
51 
52 	ASSERT(data->cd_format == CRYPTO_DATA_UIO);
53 	if (uiop->uio_segflg != UIO_SYSSPACE) {
54 		return (CRYPTO_ARGUMENTS_BAD);
55 	}
56 
57 	/*
58 	 * Jump to the first iovec containing data to be
59 	 * processed.
60 	 */
61 	for (vec_idx = 0; vec_idx < uiop->uio_iovcnt &&
62 	    offset >= uiop->uio_iov[vec_idx].iov_len;
63 	    offset -= uiop->uio_iov[vec_idx++].iov_len)
64 		;
65 
66 	if (vec_idx == uiop->uio_iovcnt) {
67 		/*
68 		 * The caller specified an offset that is larger than
69 		 * the total size of the buffers it provided.
70 		 */
71 		return (CRYPTO_DATA_LEN_RANGE);
72 	}
73 
74 	while (vec_idx < uiop->uio_iovcnt && length > 0) {
75 		cur_len = MIN(uiop->uio_iov[vec_idx].iov_len -
76 		    offset, length);
77 
78 		datap = (uchar_t *)(uiop->uio_iov[vec_idx].iov_base +
79 		    offset);
80 		switch (cmd) {
81 		case COPY_FROM_DATA:
82 			bcopy(datap, buf, cur_len);
83 			buf += cur_len;
84 			break;
85 		case COPY_TO_DATA:
86 			bcopy(buf, datap, cur_len);
87 			buf += cur_len;
88 			break;
89 		case COMPARE_TO_DATA:
90 			if (bcmp(datap, buf, cur_len))
91 				return (CRYPTO_SIGNATURE_INVALID);
92 			buf += cur_len;
93 			break;
94 		case MD5_DIGEST_DATA:
95 		case SHA1_DIGEST_DATA:
96 		case SHA2_DIGEST_DATA:
97 		case GHASH_DATA:
98 			update(digest_ctx, datap, cur_len);
99 			break;
100 		}
101 
102 		length -= cur_len;
103 		vec_idx++;
104 		offset = 0;
105 	}
106 
107 	if (vec_idx == uiop->uio_iovcnt && length > 0) {
108 		/*
109 		 * The end of the specified iovec's was reached but
110 		 * the length requested could not be processed.
111 		 */
112 		switch (cmd) {
113 		case COPY_TO_DATA:
114 			data->cd_length = len;
115 			return (CRYPTO_BUFFER_TOO_SMALL);
116 		default:
117 			return (CRYPTO_DATA_LEN_RANGE);
118 		}
119 	}
120 
121 	return (CRYPTO_SUCCESS);
122 }
123 
124 /*
125  * Utility routine to apply the command, 'cmd', to the
126  * data in the mblk structure.
127  */
128 int
129 crypto_mblk_data(crypto_data_t *data, uchar_t *buf, int len, cmd_type_t cmd,
130     void *digest_ctx, void (*update)())
131 {
132 	off_t offset = data->cd_offset;
133 	size_t length = len;
134 	mblk_t *mp;
135 	size_t cur_len;
136 	uchar_t *datap;
137 
138 	ASSERT(data->cd_format == CRYPTO_DATA_MBLK);
139 	/*
140 	 * Jump to the first mblk_t containing data to be processed.
141 	 */
142 	for (mp = data->cd_mp; mp != NULL && offset >= MBLKL(mp);
143 	    offset -= MBLKL(mp), mp = mp->b_cont)
144 		;
145 	if (mp == NULL) {
146 		/*
147 		 * The caller specified an offset that is larger
148 		 * than the total size of the buffers it provided.
149 		 */
150 		return (CRYPTO_DATA_LEN_RANGE);
151 	}
152 
153 	/*
154 	 * Now do the processing on the mblk chain.
155 	 */
156 	while (mp != NULL && length > 0) {
157 		cur_len = MIN(MBLKL(mp) - offset, length);
158 
159 		datap = (uchar_t *)(mp->b_rptr + offset);
160 		switch (cmd) {
161 		case COPY_FROM_DATA:
162 			bcopy(datap, buf, cur_len);
163 			buf += cur_len;
164 			break;
165 		case COPY_TO_DATA:
166 			bcopy(buf, datap, cur_len);
167 			buf += cur_len;
168 			break;
169 		case COMPARE_TO_DATA:
170 			if (bcmp(datap, buf, cur_len))
171 				return (CRYPTO_SIGNATURE_INVALID);
172 			buf += cur_len;
173 			break;
174 		case MD5_DIGEST_DATA:
175 		case SHA1_DIGEST_DATA:
176 		case SHA2_DIGEST_DATA:
177 		case GHASH_DATA:
178 			update(digest_ctx, datap, cur_len);
179 			break;
180 		}
181 
182 		length -= cur_len;
183 		offset = 0;
184 		mp = mp->b_cont;
185 	}
186 
187 	if (mp == NULL && length > 0) {
188 		/*
189 		 * The end of the mblk was reached but the length
190 		 * requested could not be processed.
191 		 */
192 		switch (cmd) {
193 		case COPY_TO_DATA:
194 			data->cd_length = len;
195 			return (CRYPTO_BUFFER_TOO_SMALL);
196 		default:
197 			return (CRYPTO_DATA_LEN_RANGE);
198 		}
199 	}
200 
201 	return (CRYPTO_SUCCESS);
202 }
203 
204 /*
205  * Utility routine to copy a buffer to a crypto_data structure.
206  */
207 int
208 crypto_put_output_data(uchar_t *buf, crypto_data_t *output, int len)
209 {
210 	switch (output->cd_format) {
211 	case CRYPTO_DATA_RAW:
212 		if (output->cd_raw.iov_len < len) {
213 			output->cd_length = len;
214 			return (CRYPTO_BUFFER_TOO_SMALL);
215 		}
216 		bcopy(buf, (uchar_t *)(output->cd_raw.iov_base +
217 		    output->cd_offset), len);
218 		break;
219 
220 	case CRYPTO_DATA_UIO:
221 		return (crypto_uio_data(output, buf, len,
222 		    COPY_TO_DATA, NULL, NULL));
223 
224 	case CRYPTO_DATA_MBLK:
225 		return (crypto_mblk_data(output, buf, len,
226 		    COPY_TO_DATA, NULL, NULL));
227 
228 	default:
229 		return (CRYPTO_ARGUMENTS_BAD);
230 	}
231 
232 	return (CRYPTO_SUCCESS);
233 }
234 
235 /*
236  * Utility routine to get data from a crypto_data structure.
237  *
238  * '*dptr' contains a pointer to a buffer on return. 'buf'
239  * is allocated by the caller and is ignored for CRYPTO_DATA_RAW case.
240  */
241 int
242 crypto_get_input_data(crypto_data_t *input, uchar_t **dptr, uchar_t *buf)
243 {
244 	int rv;
245 
246 	switch (input->cd_format) {
247 	case CRYPTO_DATA_RAW:
248 		if (input->cd_raw.iov_len < input->cd_length)
249 			return (CRYPTO_ARGUMENTS_BAD);
250 		*dptr = (uchar_t *)(input->cd_raw.iov_base +
251 		    input->cd_offset);
252 		break;
253 
254 	case CRYPTO_DATA_UIO:
255 		if ((rv = crypto_uio_data(input, buf, input->cd_length,
256 		    COPY_FROM_DATA, NULL, NULL)) != CRYPTO_SUCCESS)
257 			return (rv);
258 		*dptr = buf;
259 		break;
260 
261 	case CRYPTO_DATA_MBLK:
262 		if ((rv = crypto_mblk_data(input, buf, input->cd_length,
263 		    COPY_FROM_DATA, NULL, NULL)) != CRYPTO_SUCCESS)
264 			return (rv);
265 		*dptr = buf;
266 		break;
267 
268 	default:
269 		return (CRYPTO_ARGUMENTS_BAD);
270 	}
271 
272 	return (CRYPTO_SUCCESS);
273 }
274 
275 int
276 crypto_copy_key_to_ctx(crypto_key_t *in_key, crypto_key_t **out_key,
277     size_t *out_size, int kmflag)
278 {
279 	int i, count;
280 	size_t len;
281 	caddr_t attr_val;
282 	crypto_object_attribute_t *k_attrs = NULL;
283 	crypto_key_t *key;
284 
285 	ASSERT(in_key->ck_format == CRYPTO_KEY_ATTR_LIST);
286 
287 	count = in_key->ck_count;
288 	/* figure out how much memory to allocate for everything */
289 	len = sizeof (crypto_key_t) +
290 	    count * sizeof (crypto_object_attribute_t);
291 	for (i = 0; i < count; i++) {
292 		len += roundup(in_key->ck_attrs[i].oa_value_len,
293 		    sizeof (caddr_t));
294 	}
295 
296 	/* one big allocation for everything */
297 	key = kmem_alloc(len, kmflag);
298 	if (key == NULL)
299 		return (CRYPTO_HOST_MEMORY);
300 	k_attrs = (crypto_object_attribute_t *)(void *)((caddr_t)key +
301 	    sizeof (crypto_key_t));
302 
303 	attr_val = (caddr_t)k_attrs +
304 	    count * sizeof (crypto_object_attribute_t);
305 	for (i = 0; i < count; i++) {
306 		k_attrs[i].oa_type = in_key->ck_attrs[i].oa_type;
307 		bcopy(in_key->ck_attrs[i].oa_value, attr_val,
308 		    in_key->ck_attrs[i].oa_value_len);
309 		k_attrs[i].oa_value = attr_val;
310 		k_attrs[i].oa_value_len = in_key->ck_attrs[i].oa_value_len;
311 		attr_val += roundup(k_attrs[i].oa_value_len, sizeof (caddr_t));
312 	}
313 
314 	key->ck_format = CRYPTO_KEY_ATTR_LIST;
315 	key->ck_count = count;
316 	key->ck_attrs = k_attrs;
317 	*out_key = key;
318 	*out_size = len;		/* save the size to be freed */
319 
320 	return (CRYPTO_SUCCESS);
321 }
322 
323 int
324 crypto_digest_data(crypto_data_t *data, void *dctx, uchar_t *digest,
325     void (*update)(), void (*final)(), uchar_t flag)
326 {
327 	int rv, dlen;
328 	uchar_t *dptr;
329 
330 	ASSERT(flag & CRYPTO_DO_MD5 || flag & CRYPTO_DO_SHA1 ||
331 	    flag & CRYPTO_DO_SHA2);
332 	if (data == NULL) {
333 		ASSERT((flag & CRYPTO_DO_UPDATE) == 0);
334 		goto dofinal;
335 	}
336 
337 	dlen = data->cd_length;
338 
339 	if (flag & CRYPTO_DO_UPDATE) {
340 
341 		switch (data->cd_format) {
342 		case CRYPTO_DATA_RAW:
343 			dptr = (uchar_t *)(data->cd_raw.iov_base +
344 			    data->cd_offset);
345 
346 			update(dctx, dptr, dlen);
347 
348 		break;
349 
350 		case CRYPTO_DATA_UIO:
351 			if (flag & CRYPTO_DO_MD5)
352 				rv = crypto_uio_data(data, NULL, dlen,
353 				    MD5_DIGEST_DATA, dctx, update);
354 
355 			else if (flag & CRYPTO_DO_SHA1)
356 				rv = crypto_uio_data(data, NULL, dlen,
357 				    SHA1_DIGEST_DATA, dctx, update);
358 
359 			else
360 				rv = crypto_uio_data(data, NULL, dlen,
361 				    SHA2_DIGEST_DATA, dctx, update);
362 
363 			if (rv != CRYPTO_SUCCESS)
364 				return (rv);
365 
366 			break;
367 
368 		case CRYPTO_DATA_MBLK:
369 			if (flag & CRYPTO_DO_MD5)
370 				rv = crypto_mblk_data(data, NULL, dlen,
371 				    MD5_DIGEST_DATA, dctx, update);
372 
373 			else if (flag & CRYPTO_DO_SHA1)
374 				rv = crypto_mblk_data(data, NULL, dlen,
375 				    SHA1_DIGEST_DATA, dctx, update);
376 
377 			else
378 				rv = crypto_mblk_data(data, NULL, dlen,
379 				    SHA2_DIGEST_DATA, dctx, update);
380 
381 			if (rv != CRYPTO_SUCCESS)
382 				return (rv);
383 
384 			break;
385 		}
386 	}
387 
388 dofinal:
389 	if (flag & CRYPTO_DO_FINAL) {
390 		final(digest, dctx);
391 	}
392 
393 	return (CRYPTO_SUCCESS);
394 }
395 
396 int
397 crypto_update_iov(void *ctx, crypto_data_t *input, crypto_data_t *output,
398     int (*cipher)(void *, caddr_t, size_t, crypto_data_t *),
399     void (*copy_block)(uint8_t *, uint64_t *))
400 {
401 	common_ctx_t *common_ctx = ctx;
402 	int rv;
403 
404 	if (input->cd_miscdata != NULL) {
405 		copy_block((uint8_t *)input->cd_miscdata,
406 		    &common_ctx->cc_iv[0]);
407 	}
408 
409 	if (input->cd_raw.iov_len < input->cd_length)
410 		return (CRYPTO_ARGUMENTS_BAD);
411 
412 	rv = (cipher)(ctx, input->cd_raw.iov_base + input->cd_offset,
413 	    input->cd_length, (input == output) ? NULL : output);
414 
415 	return (rv);
416 }
417 
418 int
419 crypto_update_uio(void *ctx, crypto_data_t *input, crypto_data_t *output,
420     int (*cipher)(void *, caddr_t, size_t, crypto_data_t *),
421     void (*copy_block)(uint8_t *, uint64_t *))
422 {
423 	common_ctx_t *common_ctx = ctx;
424 	uio_t *uiop = input->cd_uio;
425 	off_t offset = input->cd_offset;
426 	size_t length = input->cd_length;
427 	uint_t vec_idx;
428 	size_t cur_len;
429 
430 	if (input->cd_miscdata != NULL) {
431 		copy_block((uint8_t *)input->cd_miscdata,
432 		    &common_ctx->cc_iv[0]);
433 	}
434 
435 	if (input->cd_uio->uio_segflg != UIO_SYSSPACE) {
436 		return (CRYPTO_ARGUMENTS_BAD);
437 	}
438 
439 	/*
440 	 * Jump to the first iovec containing data to be
441 	 * processed.
442 	 */
443 	for (vec_idx = 0; vec_idx < uiop->uio_iovcnt &&
444 	    offset >= uiop->uio_iov[vec_idx].iov_len;
445 	    offset -= uiop->uio_iov[vec_idx++].iov_len)
446 		;
447 	if (vec_idx == uiop->uio_iovcnt) {
448 		/*
449 		 * The caller specified an offset that is larger than the
450 		 * total size of the buffers it provided.
451 		 */
452 		return (CRYPTO_DATA_LEN_RANGE);
453 	}
454 
455 	/*
456 	 * Now process the iovecs.
457 	 */
458 	while (vec_idx < uiop->uio_iovcnt && length > 0) {
459 		cur_len = MIN(uiop->uio_iov[vec_idx].iov_len -
460 		    offset, length);
461 
462 		(cipher)(ctx, uiop->uio_iov[vec_idx].iov_base + offset,
463 		    cur_len, (input == output) ? NULL : output);
464 
465 		length -= cur_len;
466 		vec_idx++;
467 		offset = 0;
468 	}
469 
470 	if (vec_idx == uiop->uio_iovcnt && length > 0) {
471 		/*
472 		 * The end of the specified iovec's was reached but
473 		 * the length requested could not be processed, i.e.
474 		 * The caller requested to digest more data than it provided.
475 		 */
476 
477 		return (CRYPTO_DATA_LEN_RANGE);
478 	}
479 
480 	return (CRYPTO_SUCCESS);
481 }
482 
483 int
484 crypto_update_mp(void *ctx, crypto_data_t *input, crypto_data_t *output,
485     int (*cipher)(void *, caddr_t, size_t, crypto_data_t *),
486     void (*copy_block)(uint8_t *, uint64_t *))
487 {
488 	common_ctx_t *common_ctx = ctx;
489 	off_t offset = input->cd_offset;
490 	size_t length = input->cd_length;
491 	mblk_t *mp;
492 	size_t cur_len;
493 
494 	if (input->cd_miscdata != NULL) {
495 		copy_block((uint8_t *)input->cd_miscdata,
496 		    &common_ctx->cc_iv[0]);
497 	}
498 
499 	/*
500 	 * Jump to the first mblk_t containing data to be processed.
501 	 */
502 	for (mp = input->cd_mp; mp != NULL && offset >= MBLKL(mp);
503 	    offset -= MBLKL(mp), mp = mp->b_cont)
504 		;
505 	if (mp == NULL) {
506 		/*
507 		 * The caller specified an offset that is larger than the
508 		 * total size of the buffers it provided.
509 		 */
510 		return (CRYPTO_DATA_LEN_RANGE);
511 	}
512 
513 	/*
514 	 * Now do the processing on the mblk chain.
515 	 */
516 	while (mp != NULL && length > 0) {
517 		cur_len = MIN(MBLKL(mp) - offset, length);
518 		(cipher)(ctx, (char *)(mp->b_rptr + offset), cur_len,
519 		    (input == output) ? NULL : output);
520 
521 		length -= cur_len;
522 		offset = 0;
523 		mp = mp->b_cont;
524 	}
525 
526 	if (mp == NULL && length > 0) {
527 		/*
528 		 * The end of the mblk was reached but the length requested
529 		 * could not be processed, i.e. The caller requested
530 		 * to digest more data than it provided.
531 		 */
532 		return (CRYPTO_DATA_LEN_RANGE);
533 	}
534 
535 	return (CRYPTO_SUCCESS);
536 }
537 
538 /*
539  * Utility routine to look up a attribute of type, 'type',
540  * in the key.
541  */
542 int
543 crypto_get_key_attr(crypto_key_t *key, crypto_attr_type_t type,
544     uchar_t **value, ssize_t *value_len)
545 {
546 	int i;
547 
548 	ASSERT(key->ck_format == CRYPTO_KEY_ATTR_LIST);
549 	for (i = 0; i < key->ck_count; i++) {
550 		if (key->ck_attrs[i].oa_type == type) {
551 			*value = (uchar_t *)key->ck_attrs[i].oa_value;
552 			*value_len = key->ck_attrs[i].oa_value_len;
553 			return (CRYPTO_SUCCESS);
554 		}
555 	}
556 
557 	return (CRYPTO_FAILED);
558 }
559