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