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 /*
27 * RC4 provider for the Kernel Cryptographic Framework (KCF)
28 */
29
30 #include <sys/types.h>
31 #include <sys/systm.h>
32 #include <sys/modctl.h>
33 #include <sys/cmn_err.h>
34 #include <sys/ddi.h>
35 #include <sys/crypto/common.h>
36 #include <sys/crypto/spi.h>
37 #include <sys/sysmacros.h>
38 #include <sys/strsun.h>
39 #include <arcfour.h>
40
41 extern struct mod_ops mod_cryptoops;
42
43 /*
44 * Module linkage information for the kernel.
45 */
46 static struct modlcrypto modlcrypto = {
47 &mod_cryptoops,
48 "RC4 Kernel SW Provider"
49 };
50
51 static struct modlinkage modlinkage = {
52 MODREV_1,
53 (void *)&modlcrypto,
54 NULL
55 };
56
57 /*
58 * CSPI information (entry points, provider info, etc.)
59 */
60
61 #define RC4_MECH_INFO_TYPE 0
62 /*
63 * Mechanism info structure passed to KCF during registration.
64 */
65 static crypto_mech_info_t rc4_mech_info_tab[] = {
66 {SUN_CKM_RC4, RC4_MECH_INFO_TYPE,
67 CRYPTO_FG_ENCRYPT | CRYPTO_FG_ENCRYPT_ATOMIC |
68 CRYPTO_FG_DECRYPT | CRYPTO_FG_DECRYPT_ATOMIC,
69 ARCFOUR_MIN_KEY_BITS, ARCFOUR_MAX_KEY_BITS,
70 CRYPTO_KEYSIZE_UNIT_IN_BITS | CRYPTO_CAN_SHARE_OPSTATE}
71 };
72
73 static void rc4_provider_status(crypto_provider_handle_t, uint_t *);
74
75 static crypto_control_ops_t rc4_control_ops = {
76 rc4_provider_status
77 };
78
79 static int rc4_common_init(crypto_ctx_t *, crypto_mechanism_t *,
80 crypto_key_t *, crypto_spi_ctx_template_t, crypto_req_handle_t);
81
82 static int rc4_crypt_update(crypto_ctx_t *, crypto_data_t *, crypto_data_t *,
83 crypto_req_handle_t);
84
85 static int rc4_crypt_final(crypto_ctx_t *, crypto_data_t *,
86 crypto_req_handle_t);
87
88 static int rc4_crypt(crypto_ctx_t *, crypto_data_t *, crypto_data_t *,
89 crypto_req_handle_t);
90
91 static int rc4_crypt_atomic(crypto_provider_handle_t, crypto_session_id_t,
92 crypto_mechanism_t *, crypto_key_t *, crypto_data_t *,
93 crypto_data_t *, crypto_spi_ctx_template_t, crypto_req_handle_t);
94
95
96 static crypto_cipher_ops_t rc4_cipher_ops = {
97 rc4_common_init,
98 rc4_crypt,
99 rc4_crypt_update,
100 rc4_crypt_final,
101 rc4_crypt_atomic,
102 rc4_common_init,
103 rc4_crypt,
104 rc4_crypt_update,
105 rc4_crypt_final,
106 rc4_crypt_atomic
107 };
108
109 static int rc4_free_context(crypto_ctx_t *);
110
111 static crypto_ctx_ops_t rc4_ctx_ops = {
112 NULL,
113 rc4_free_context
114 };
115
116 static crypto_ops_t rc4_crypto_ops = {
117 &rc4_control_ops,
118 NULL,
119 &rc4_cipher_ops,
120 NULL,
121 NULL,
122 NULL,
123 NULL,
124 NULL,
125 NULL,
126 NULL,
127 NULL,
128 NULL,
129 NULL,
130 &rc4_ctx_ops
131 };
132
133 static crypto_provider_info_t rc4_prov_info = {
134 CRYPTO_SPI_VERSION_1,
135 "RC4 Software Provider",
136 CRYPTO_SW_PROVIDER,
137 {&modlinkage},
138 NULL,
139 &rc4_crypto_ops,
140 sizeof (rc4_mech_info_tab)/sizeof (crypto_mech_info_t),
141 rc4_mech_info_tab
142 };
143
144 static crypto_kcf_provider_handle_t rc4_prov_handle = NULL;
145
146 static mblk_t *advance_position(mblk_t *, off_t, uchar_t **);
147 static int crypto_arcfour_crypt(ARCFour_key *, uchar_t *, crypto_data_t *,
148 int);
149
150 int
_init(void)151 _init(void)
152 {
153 int ret;
154
155 if ((ret = mod_install(&modlinkage)) != 0)
156 return (ret);
157
158 /* Register with KCF. If the registration fails, remove the module. */
159 if (crypto_register_provider(&rc4_prov_info, &rc4_prov_handle)) {
160 (void) mod_remove(&modlinkage);
161 return (EACCES);
162 }
163
164 return (0);
165 }
166
167 int
_fini(void)168 _fini(void)
169 {
170 /* Unregister from KCF if module is registered */
171 if (rc4_prov_handle != NULL) {
172 if (crypto_unregister_provider(rc4_prov_handle))
173 return (EBUSY);
174
175 rc4_prov_handle = NULL;
176 }
177
178 return (mod_remove(&modlinkage));
179 }
180
181 int
_info(struct modinfo * modinfop)182 _info(struct modinfo *modinfop)
183 {
184 return (mod_info(&modlinkage, modinfop));
185 }
186
187
188 /*
189 * KCF software provider control entry points.
190 */
191 /* ARGSUSED */
192 static void
rc4_provider_status(crypto_provider_handle_t provider,uint_t * status)193 rc4_provider_status(crypto_provider_handle_t provider, uint_t *status)
194 {
195 *status = CRYPTO_PROVIDER_READY;
196 }
197
198 /* ARGSUSED */
199 static int
rc4_common_init(crypto_ctx_t * ctx,crypto_mechanism_t * mechanism,crypto_key_t * key,crypto_spi_ctx_template_t template,crypto_req_handle_t req)200 rc4_common_init(crypto_ctx_t *ctx, crypto_mechanism_t *mechanism,
201 crypto_key_t *key, crypto_spi_ctx_template_t template,
202 crypto_req_handle_t req)
203 {
204 ARCFour_key *keystream;
205
206 if ((mechanism)->cm_type != RC4_MECH_INFO_TYPE)
207 return (CRYPTO_MECHANISM_INVALID);
208
209 if (key->ck_format != CRYPTO_KEY_RAW)
210 return (CRYPTO_KEY_TYPE_INCONSISTENT);
211
212 if (key->ck_length < ARCFOUR_MIN_KEY_BITS ||
213 key->ck_length > ARCFOUR_MAX_KEY_BITS) {
214 return (CRYPTO_KEY_SIZE_RANGE);
215 }
216
217 /*
218 * Allocate an RC4 key stream.
219 */
220 if ((keystream = kmem_alloc(sizeof (ARCFour_key),
221 crypto_kmflag(req))) == NULL)
222 return (CRYPTO_HOST_MEMORY);
223
224 arcfour_key_init(keystream, key->ck_data,
225 CRYPTO_BITS2BYTES(key->ck_length));
226
227 ctx->cc_provider_private = keystream;
228
229 return (CRYPTO_SUCCESS);
230 }
231
232 static int
rc4_crypt(crypto_ctx_t * ctx,crypto_data_t * input,crypto_data_t * output,crypto_req_handle_t req)233 rc4_crypt(crypto_ctx_t *ctx, crypto_data_t *input, crypto_data_t *output,
234 crypto_req_handle_t req)
235 {
236 int ret;
237
238 ret = rc4_crypt_update(ctx, input, output, req);
239
240 if (ret != CRYPTO_BUFFER_TOO_SMALL)
241 (void) rc4_free_context(ctx);
242
243 return (ret);
244 }
245
246 /* ARGSUSED */
247 static int
rc4_crypt_update(crypto_ctx_t * ctx,crypto_data_t * input,crypto_data_t * output,crypto_req_handle_t req)248 rc4_crypt_update(crypto_ctx_t *ctx, crypto_data_t *input, crypto_data_t *output,
249 crypto_req_handle_t req)
250 {
251 int ret = CRYPTO_SUCCESS;
252
253 ARCFour_key *key;
254 off_t saveoffset;
255
256 ASSERT(ctx->cc_provider_private != NULL);
257
258 if ((ctx->cc_flags & CRYPTO_USE_OPSTATE) && ctx->cc_opstate != NULL)
259 key = ctx->cc_opstate;
260 else
261 key = ctx->cc_provider_private;
262
263 /* Simple case: in-line encipherment */
264
265 if (output == NULL) {
266 switch (input->cd_format) {
267 case CRYPTO_DATA_RAW: {
268 char *start, *end;
269 start = input->cd_raw.iov_base + input->cd_offset;
270
271 end = input->cd_raw.iov_base + input->cd_raw.iov_len;
272
273 if (start + input->cd_length > end)
274 return (CRYPTO_DATA_INVALID);
275
276 arcfour_crypt(key, (uchar_t *)start, (uchar_t *)start,
277 input->cd_length);
278 break;
279 }
280 case CRYPTO_DATA_MBLK: {
281 uchar_t *start, *end;
282 size_t len, left;
283 mblk_t *mp = input->cd_mp, *mp1, *mp2;
284
285 ASSERT(mp != NULL);
286
287 mp1 = advance_position(mp, input->cd_offset, &start);
288
289 if (mp1 == NULL)
290 return (CRYPTO_DATA_LEN_RANGE);
291
292 mp2 = advance_position(mp, input->cd_offset +
293 input->cd_length, &end);
294
295 if (mp2 == NULL)
296 return (CRYPTO_DATA_LEN_RANGE);
297
298 left = input->cd_length;
299 while (mp1 != NULL) {
300 if (_PTRDIFF(mp1->b_wptr, start) > left) {
301 len = left;
302 arcfour_crypt(key, start, start, len);
303 mp1 = NULL;
304 } else {
305 len = _PTRDIFF(mp1->b_wptr, start);
306 arcfour_crypt(key, start, start, len);
307 mp1 = mp1->b_cont;
308 start = mp1->b_rptr;
309 left -= len;
310 }
311 }
312 break;
313 }
314 case CRYPTO_DATA_UIO: {
315 uio_t *uiop = input->cd_uio;
316 off_t offset = input->cd_offset;
317 size_t length = input->cd_length;
318 uint_t vec_idx;
319 size_t cur_len;
320
321 /*
322 * Jump to the first iovec containing data to be
323 * processed.
324 */
325 for (vec_idx = 0; vec_idx < uiop->uio_iovcnt &&
326 offset >= uiop->uio_iov[vec_idx].iov_len;
327 offset -= uiop->uio_iov[vec_idx++].iov_len)
328 ;
329 if (vec_idx == uiop->uio_iovcnt) {
330 return (CRYPTO_DATA_LEN_RANGE);
331 }
332
333 /*
334 * Now process the iovecs.
335 */
336 while (vec_idx < uiop->uio_iovcnt && length > 0) {
337 uchar_t *start;
338 iovec_t *iovp = &(uiop->uio_iov[vec_idx]);
339
340 cur_len = MIN(iovp->iov_len - offset, length);
341
342 start = (uchar_t *)(iovp->iov_base + offset);
343 arcfour_crypt(key, start + offset,
344 start + offset, cur_len);
345
346 length -= cur_len;
347 vec_idx++;
348 offset = 0;
349 }
350
351 if (vec_idx == uiop->uio_iovcnt && length > 0) {
352
353 return (CRYPTO_DATA_LEN_RANGE);
354 }
355 break;
356 }
357 }
358 return (CRYPTO_SUCCESS);
359 }
360
361 /*
362 * We need to just return the length needed to store the output.
363 * We should not destroy the context for the following case.
364 */
365
366 if (input->cd_length > output->cd_length) {
367 output->cd_length = input->cd_length;
368 return (CRYPTO_BUFFER_TOO_SMALL);
369 }
370
371 saveoffset = output->cd_offset;
372
373 switch (input->cd_format) {
374 case CRYPTO_DATA_RAW: {
375 char *start, *end;
376 start = input->cd_raw.iov_base + input->cd_offset;
377
378 end = input->cd_raw.iov_base + input->cd_raw.iov_len;
379
380 if (start + input->cd_length > end)
381 return (CRYPTO_DATA_LEN_RANGE);
382
383 ret = crypto_arcfour_crypt(key, (uchar_t *)start, output,
384 input->cd_length);
385
386 if (ret != CRYPTO_SUCCESS)
387 return (ret);
388 break;
389 }
390 case CRYPTO_DATA_MBLK: {
391 uchar_t *start, *end;
392 size_t len, left;
393 mblk_t *mp = input->cd_mp, *mp1, *mp2;
394
395 ASSERT(mp != NULL);
396
397 mp1 = advance_position(mp, input->cd_offset, &start);
398
399 if (mp1 == NULL)
400 return (CRYPTO_DATA_LEN_RANGE);
401
402 mp2 = advance_position(mp, input->cd_offset + input->cd_length,
403 &end);
404
405 if (mp2 == NULL)
406 return (CRYPTO_DATA_LEN_RANGE);
407
408 left = input->cd_length;
409 while (mp1 != NULL) {
410 if (_PTRDIFF(mp1->b_wptr, start) > left) {
411 len = left;
412 ret = crypto_arcfour_crypt(key, start, output,
413 len);
414 if (ret != CRYPTO_SUCCESS)
415 return (ret);
416 mp1 = NULL;
417 } else {
418 len = _PTRDIFF(mp1->b_wptr, start);
419 ret = crypto_arcfour_crypt(key, start, output,
420 len);
421 if (ret != CRYPTO_SUCCESS)
422 return (ret);
423 mp1 = mp1->b_cont;
424 start = mp1->b_rptr;
425 left -= len;
426 output->cd_offset += len;
427 }
428 }
429 break;
430 }
431 case CRYPTO_DATA_UIO: {
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 /*
439 * Jump to the first iovec containing data to be
440 * processed.
441 */
442 for (vec_idx = 0; vec_idx < uiop->uio_iovcnt &&
443 offset >= uiop->uio_iov[vec_idx].iov_len;
444 offset -= uiop->uio_iov[vec_idx++].iov_len)
445 ;
446 if (vec_idx == uiop->uio_iovcnt) {
447 return (CRYPTO_DATA_LEN_RANGE);
448 }
449
450 /*
451 * Now process the iovecs.
452 */
453 while (vec_idx < uiop->uio_iovcnt && length > 0) {
454 uchar_t *start;
455 iovec_t *iovp = &(uiop->uio_iov[vec_idx]);
456 cur_len = MIN(iovp->iov_len - offset, length);
457
458 start = (uchar_t *)(iovp->iov_base + offset);
459 ret = crypto_arcfour_crypt(key, start + offset,
460 output, cur_len);
461 if (ret != CRYPTO_SUCCESS)
462 return (ret);
463
464 length -= cur_len;
465 vec_idx++;
466 offset = 0;
467 output->cd_offset += cur_len;
468 }
469
470 if (vec_idx == uiop->uio_iovcnt && length > 0) {
471
472 return (CRYPTO_DATA_LEN_RANGE);
473 }
474 }
475 }
476
477 output->cd_offset = saveoffset;
478 output->cd_length = input->cd_length;
479
480 return (ret);
481 }
482
483 /* ARGSUSED */
rc4_crypt_final(crypto_ctx_t * ctx,crypto_data_t * data,crypto_req_handle_t req)484 static int rc4_crypt_final(crypto_ctx_t *ctx, crypto_data_t *data,
485 crypto_req_handle_t req)
486 {
487 /* No final part for streams ciphers. Just free the context */
488 if (data != NULL)
489 data->cd_length = 0;
490
491 return (rc4_free_context(ctx));
492 }
493
494 /* ARGSUSED */
495 static int
rc4_crypt_atomic(crypto_provider_handle_t handle,crypto_session_id_t session,crypto_mechanism_t * mechanism,crypto_key_t * key,crypto_data_t * input,crypto_data_t * output,crypto_spi_ctx_template_t template,crypto_req_handle_t req)496 rc4_crypt_atomic(crypto_provider_handle_t handle, crypto_session_id_t session,
497 crypto_mechanism_t *mechanism, crypto_key_t *key, crypto_data_t *input,
498 crypto_data_t *output, crypto_spi_ctx_template_t template,
499 crypto_req_handle_t req)
500 {
501 crypto_ctx_t ctx;
502 int ret;
503
504 bzero(&ctx, sizeof (crypto_ctx_t));
505 ret = rc4_common_init(&ctx, mechanism, key, template, req);
506
507 if (ret != CRYPTO_SUCCESS)
508 return (ret);
509
510 ret = rc4_crypt_update(&ctx, input, output, req);
511
512 (void) rc4_free_context(&ctx);
513
514 return (ret);
515 }
516
517 /* ARGSUSED */
518 static int
rc4_free_context(crypto_ctx_t * ctx)519 rc4_free_context(crypto_ctx_t *ctx)
520 {
521 ARCFour_key *keystream = ctx->cc_provider_private;
522
523 if (keystream != NULL) {
524 bzero(keystream, sizeof (ARCFour_key));
525 kmem_free(keystream, sizeof (ARCFour_key));
526 ctx->cc_provider_private = NULL;
527 }
528
529 return (CRYPTO_SUCCESS);
530 }
531
532 /* Encrypts a contiguous input 'in' into the 'out' crypto_data_t */
533
534 static int
crypto_arcfour_crypt(ARCFour_key * key,uchar_t * in,crypto_data_t * out,int length)535 crypto_arcfour_crypt(ARCFour_key *key, uchar_t *in, crypto_data_t *out,
536 int length)
537 {
538 switch (out->cd_format) {
539 case CRYPTO_DATA_RAW: {
540 uchar_t *start, *end;
541 start = (uchar_t *)(out->cd_raw.iov_base +
542 out->cd_offset);
543
544 end = (uchar_t *)(out->cd_raw.iov_base +
545 out->cd_raw.iov_len);
546
547 if (start + out->cd_length > end)
548 return (CRYPTO_DATA_LEN_RANGE);
549
550 arcfour_crypt(key, in, start, length);
551
552 return (CRYPTO_SUCCESS);
553 }
554 case CRYPTO_DATA_MBLK: {
555 uchar_t *start, *end;
556 size_t len, left;
557 mblk_t *mp = out->cd_mp, *mp1, *mp2;
558
559 ASSERT(mp != NULL);
560
561 mp1 = advance_position(mp, out->cd_offset, &start);
562
563 if (mp1 == NULL)
564 return (CRYPTO_DATA_LEN_RANGE);
565
566 mp2 = advance_position(mp, out->cd_offset +
567 out->cd_length, &end);
568
569 if (mp2 == NULL)
570 return (CRYPTO_DATA_LEN_RANGE);
571
572 left = length;
573 while (mp1 != NULL) {
574 if (_PTRDIFF(mp1->b_wptr, start) > left) {
575 len = left;
576 arcfour_crypt(key, in, start, len);
577 mp1 = NULL;
578 } else {
579 len = _PTRDIFF(mp1->b_wptr, start);
580 arcfour_crypt(key, in, start, len);
581 mp1 = mp1->b_cont;
582 start = mp1->b_rptr;
583 left -= len;
584 }
585 }
586 break;
587 }
588 case CRYPTO_DATA_UIO: {
589 uio_t *uiop = out->cd_uio;
590 off_t offset = out->cd_offset;
591 size_t len = length;
592 uint_t vec_idx;
593 size_t cur_len;
594
595 /*
596 * Jump to the first iovec containing data to be
597 * processed.
598 */
599 for (vec_idx = 0; vec_idx < uiop->uio_iovcnt &&
600 offset >= uiop->uio_iov[vec_idx].iov_len;
601 offset -= uiop->uio_iov[vec_idx++].iov_len)
602 ;
603 if (vec_idx == uiop->uio_iovcnt) {
604 return (CRYPTO_DATA_LEN_RANGE);
605 }
606
607 /*
608 * Now process the iovecs.
609 */
610 while (vec_idx < uiop->uio_iovcnt && len > 0) {
611 uchar_t *start;
612 iovec_t *iovp = &(uiop->uio_iov[vec_idx]);
613 cur_len = MIN(iovp->iov_len - offset, len);
614
615 start = (uchar_t *)(iovp->iov_base + offset);
616 arcfour_crypt(key, start + offset,
617 start + offset, cur_len);
618
619 len -= cur_len;
620 vec_idx++;
621 offset = 0;
622 }
623
624 if (vec_idx == uiop->uio_iovcnt && len > 0) {
625 return (CRYPTO_DATA_LEN_RANGE);
626 }
627 break;
628 }
629 default:
630 return (CRYPTO_DATA_INVALID);
631 }
632 return (CRYPTO_SUCCESS);
633 }
634
635 /*
636 * Advances 'offset' bytes from the beginning of the first block in 'mp',
637 * possibly jumping across b_cont boundary
638 * '*cpp' is set to the position of the byte we want, and the block where
639 * 'cpp' is returned.
640 */
641 static mblk_t *
advance_position(mblk_t * mp,off_t offset,uchar_t ** cpp)642 advance_position(mblk_t *mp, off_t offset, uchar_t **cpp)
643 {
644 mblk_t *mp1 = mp;
645 size_t l;
646 off_t o = offset;
647
648 while (mp1 != NULL) {
649 l = MBLKL(mp1);
650
651 if (l <= o) {
652 o -= l;
653 mp1 = mp1->b_cont;
654 } else {
655 *cpp = (uchar_t *)(mp1->b_rptr + o);
656 break;
657 }
658 }
659 return (mp1);
660 }
661