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.
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.
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]
22 * Copyright 2010 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
27 * RC4 provider for the Kernel Cryptographic Framework (KCF)
30 #include <sys/types.h>
31 #include <sys/systm.h>
32 #include <sys/modctl.h>
33 #include <sys/cmn_err.h>
35 #include <sys/crypto/common.h>
36 #include <sys/crypto/spi.h>
37 #include <sys/sysmacros.h>
38 #include <sys/strsun.h>
41 extern struct mod_ops mod_cryptoops
;
44 * Module linkage information for the kernel.
46 static struct modlcrypto modlcrypto
= {
48 "RC4 Kernel SW Provider"
51 static struct modlinkage modlinkage
= {
58 * CSPI information (entry points, provider info, etc.)
61 #define RC4_MECH_INFO_TYPE 0
63 * Mechanism info structure passed to KCF during registration.
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
}
73 static void rc4_provider_status(crypto_provider_handle_t
, uint_t
*);
75 static crypto_control_ops_t rc4_control_ops
= {
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
);
82 static int rc4_crypt_update(crypto_ctx_t
*, crypto_data_t
*, crypto_data_t
*,
85 static int rc4_crypt_final(crypto_ctx_t
*, crypto_data_t
*,
88 static int rc4_crypt(crypto_ctx_t
*, crypto_data_t
*, crypto_data_t
*,
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
);
96 static crypto_cipher_ops_t rc4_cipher_ops
= {
109 static int rc4_free_context(crypto_ctx_t
*);
111 static crypto_ctx_ops_t rc4_ctx_ops
= {
116 static crypto_ops_t rc4_crypto_ops
= {
133 static crypto_provider_info_t rc4_prov_info
= {
134 CRYPTO_SPI_VERSION_1
,
135 "RC4 Software Provider",
140 sizeof (rc4_mech_info_tab
)/sizeof (crypto_mech_info_t
),
144 static crypto_kcf_provider_handle_t rc4_prov_handle
= 0;
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
*,
155 if ((ret
= mod_install(&modlinkage
)) != 0)
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
);
170 /* Unregister from KCF if module is registered */
171 if (rc4_prov_handle
!= 0) {
172 if (crypto_unregister_provider(rc4_prov_handle
))
178 return (mod_remove(&modlinkage
));
182 _info(struct modinfo
*modinfop
)
184 return (mod_info(&modlinkage
, modinfop
));
189 * KCF software provider control entry points.
193 rc4_provider_status(crypto_provider_handle_t provider
, uint_t
*status
)
195 *status
= CRYPTO_PROVIDER_READY
;
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
)
204 ARCFour_key
*keystream
;
206 if ((mechanism
)->cm_type
!= RC4_MECH_INFO_TYPE
)
207 return (CRYPTO_MECHANISM_INVALID
);
209 if (key
->ck_format
!= CRYPTO_KEY_RAW
)
210 return (CRYPTO_KEY_TYPE_INCONSISTENT
);
212 if (key
->ck_length
< ARCFOUR_MIN_KEY_BITS
||
213 key
->ck_length
> ARCFOUR_MAX_KEY_BITS
) {
214 return (CRYPTO_KEY_SIZE_RANGE
);
218 * Allocate an RC4 key stream.
220 if ((keystream
= kmem_alloc(sizeof (ARCFour_key
),
221 crypto_kmflag(req
))) == NULL
)
222 return (CRYPTO_HOST_MEMORY
);
224 arcfour_key_init(keystream
, key
->ck_data
,
225 CRYPTO_BITS2BYTES(key
->ck_length
));
227 ctx
->cc_provider_private
= keystream
;
229 return (CRYPTO_SUCCESS
);
233 rc4_crypt(crypto_ctx_t
*ctx
, crypto_data_t
*input
, crypto_data_t
*output
,
234 crypto_req_handle_t req
)
238 ret
= rc4_crypt_update(ctx
, input
, output
, req
);
240 if (ret
!= CRYPTO_BUFFER_TOO_SMALL
)
241 (void) rc4_free_context(ctx
);
248 rc4_crypt_update(crypto_ctx_t
*ctx
, crypto_data_t
*input
, crypto_data_t
*output
,
249 crypto_req_handle_t req
)
251 int ret
= CRYPTO_SUCCESS
;
256 ASSERT(ctx
->cc_provider_private
!= NULL
);
258 if ((ctx
->cc_flags
& CRYPTO_USE_OPSTATE
) && ctx
->cc_opstate
!= NULL
)
259 key
= ctx
->cc_opstate
;
261 key
= ctx
->cc_provider_private
;
263 /* Simple case: in-line encipherment */
265 if (output
== NULL
) {
266 switch (input
->cd_format
) {
267 case CRYPTO_DATA_RAW
: {
269 start
= input
->cd_raw
.iov_base
+ input
->cd_offset
;
271 end
= input
->cd_raw
.iov_base
+ input
->cd_raw
.iov_len
;
273 if (start
+ input
->cd_length
> end
)
274 return (CRYPTO_DATA_INVALID
);
276 arcfour_crypt(key
, (uchar_t
*)start
, (uchar_t
*)start
,
280 case CRYPTO_DATA_MBLK
: {
281 uchar_t
*start
, *end
;
283 mblk_t
*mp
= input
->cd_mp
, *mp1
, *mp2
;
287 mp1
= advance_position(mp
, input
->cd_offset
, &start
);
290 return (CRYPTO_DATA_LEN_RANGE
);
292 mp2
= advance_position(mp
, input
->cd_offset
+
293 input
->cd_length
, &end
);
296 return (CRYPTO_DATA_LEN_RANGE
);
298 left
= input
->cd_length
;
299 while (mp1
!= NULL
) {
300 if (_PTRDIFF(mp1
->b_wptr
, start
) > left
) {
302 arcfour_crypt(key
, start
, start
, len
);
305 len
= _PTRDIFF(mp1
->b_wptr
, start
);
306 arcfour_crypt(key
, start
, start
, len
);
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
;
322 * Jump to the first iovec containing data to be
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
)
329 if (vec_idx
== uiop
->uio_iovcnt
) {
330 return (CRYPTO_DATA_LEN_RANGE
);
334 * Now process the iovecs.
336 while (vec_idx
< uiop
->uio_iovcnt
&& length
> 0) {
338 iovec_t
*iovp
= &(uiop
->uio_iov
[vec_idx
]);
340 cur_len
= MIN(iovp
->iov_len
- offset
, length
);
342 start
= (uchar_t
*)(iovp
->iov_base
+ offset
);
343 arcfour_crypt(key
, start
+ offset
,
344 start
+ offset
, cur_len
);
351 if (vec_idx
== uiop
->uio_iovcnt
&& length
> 0) {
353 return (CRYPTO_DATA_LEN_RANGE
);
358 return (CRYPTO_SUCCESS
);
362 * We need to just return the length needed to store the output.
363 * We should not destroy the context for the following case.
366 if (input
->cd_length
> output
->cd_length
) {
367 output
->cd_length
= input
->cd_length
;
368 return (CRYPTO_BUFFER_TOO_SMALL
);
371 saveoffset
= output
->cd_offset
;
373 switch (input
->cd_format
) {
374 case CRYPTO_DATA_RAW
: {
376 start
= input
->cd_raw
.iov_base
+ input
->cd_offset
;
378 end
= input
->cd_raw
.iov_base
+ input
->cd_raw
.iov_len
;
380 if (start
+ input
->cd_length
> end
)
381 return (CRYPTO_DATA_LEN_RANGE
);
383 ret
= crypto_arcfour_crypt(key
, (uchar_t
*)start
, output
,
386 if (ret
!= CRYPTO_SUCCESS
)
390 case CRYPTO_DATA_MBLK
: {
391 uchar_t
*start
, *end
;
393 mblk_t
*mp
= input
->cd_mp
, *mp1
, *mp2
;
397 mp1
= advance_position(mp
, input
->cd_offset
, &start
);
400 return (CRYPTO_DATA_LEN_RANGE
);
402 mp2
= advance_position(mp
, input
->cd_offset
+ input
->cd_length
,
406 return (CRYPTO_DATA_LEN_RANGE
);
408 left
= input
->cd_length
;
409 while (mp1
!= NULL
) {
410 if (_PTRDIFF(mp1
->b_wptr
, start
) > left
) {
412 ret
= crypto_arcfour_crypt(key
, start
, output
,
414 if (ret
!= CRYPTO_SUCCESS
)
418 len
= _PTRDIFF(mp1
->b_wptr
, start
);
419 ret
= crypto_arcfour_crypt(key
, start
, output
,
421 if (ret
!= CRYPTO_SUCCESS
)
426 output
->cd_offset
+= len
;
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
;
439 * Jump to the first iovec containing data to be
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
)
446 if (vec_idx
== uiop
->uio_iovcnt
) {
447 return (CRYPTO_DATA_LEN_RANGE
);
451 * Now process the iovecs.
453 while (vec_idx
< uiop
->uio_iovcnt
&& length
> 0) {
455 iovec_t
*iovp
= &(uiop
->uio_iov
[vec_idx
]);
456 cur_len
= MIN(iovp
->iov_len
- offset
, length
);
458 start
= (uchar_t
*)(iovp
->iov_base
+ offset
);
459 ret
= crypto_arcfour_crypt(key
, start
+ offset
,
461 if (ret
!= CRYPTO_SUCCESS
)
467 output
->cd_offset
+= cur_len
;
470 if (vec_idx
== uiop
->uio_iovcnt
&& length
> 0) {
472 return (CRYPTO_DATA_LEN_RANGE
);
477 output
->cd_offset
= saveoffset
;
478 output
->cd_length
= input
->cd_length
;
484 static int rc4_crypt_final(crypto_ctx_t
*ctx
, crypto_data_t
*data
,
485 crypto_req_handle_t req
)
487 /* No final part for streams ciphers. Just free the context */
491 return (rc4_free_context(ctx
));
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
)
504 bzero(&ctx
, sizeof (crypto_ctx_t
));
505 ret
= rc4_common_init(&ctx
, mechanism
, key
, template, req
);
507 if (ret
!= CRYPTO_SUCCESS
)
510 ret
= rc4_crypt_update(&ctx
, input
, output
, req
);
512 (void) rc4_free_context(&ctx
);
519 rc4_free_context(crypto_ctx_t
*ctx
)
521 ARCFour_key
*keystream
= ctx
->cc_provider_private
;
523 if (keystream
!= NULL
) {
524 bzero(keystream
, sizeof (ARCFour_key
));
525 kmem_free(keystream
, sizeof (ARCFour_key
));
526 ctx
->cc_provider_private
= NULL
;
529 return (CRYPTO_SUCCESS
);
532 /* Encrypts a contiguous input 'in' into the 'out' crypto_data_t */
535 crypto_arcfour_crypt(ARCFour_key
*key
, uchar_t
*in
, crypto_data_t
*out
,
538 switch (out
->cd_format
) {
539 case CRYPTO_DATA_RAW
: {
540 uchar_t
*start
, *end
;
541 start
= (uchar_t
*)(out
->cd_raw
.iov_base
+
544 end
= (uchar_t
*)(out
->cd_raw
.iov_base
+
545 out
->cd_raw
.iov_len
);
547 if (start
+ out
->cd_length
> end
)
548 return (CRYPTO_DATA_LEN_RANGE
);
550 arcfour_crypt(key
, in
, start
, length
);
552 return (CRYPTO_SUCCESS
);
554 case CRYPTO_DATA_MBLK
: {
555 uchar_t
*start
, *end
;
557 mblk_t
*mp
= out
->cd_mp
, *mp1
, *mp2
;
561 mp1
= advance_position(mp
, out
->cd_offset
, &start
);
564 return (CRYPTO_DATA_LEN_RANGE
);
566 mp2
= advance_position(mp
, out
->cd_offset
+
567 out
->cd_length
, &end
);
570 return (CRYPTO_DATA_LEN_RANGE
);
573 while (mp1
!= NULL
) {
574 if (_PTRDIFF(mp1
->b_wptr
, start
) > left
) {
576 arcfour_crypt(key
, in
, start
, len
);
579 len
= _PTRDIFF(mp1
->b_wptr
, start
);
580 arcfour_crypt(key
, in
, start
, len
);
588 case CRYPTO_DATA_UIO
: {
589 uio_t
*uiop
= out
->cd_uio
;
590 off_t offset
= out
->cd_offset
;
596 * Jump to the first iovec containing data to be
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
)
603 if (vec_idx
== uiop
->uio_iovcnt
) {
604 return (CRYPTO_DATA_LEN_RANGE
);
608 * Now process the iovecs.
610 while (vec_idx
< uiop
->uio_iovcnt
&& len
> 0) {
612 iovec_t
*iovp
= &(uiop
->uio_iov
[vec_idx
]);
613 cur_len
= MIN(iovp
->iov_len
- offset
, len
);
615 start
= (uchar_t
*)(iovp
->iov_base
+ offset
);
616 arcfour_crypt(key
, start
+ offset
,
617 start
+ offset
, cur_len
);
624 if (vec_idx
== uiop
->uio_iovcnt
&& len
> 0) {
625 return (CRYPTO_DATA_LEN_RANGE
);
630 return (CRYPTO_DATA_INVALID
);
632 return (CRYPTO_SUCCESS
);
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
642 advance_position(mblk_t
*mp
, off_t offset
, uchar_t
**cpp
)
648 while (mp1
!= NULL
) {
655 *cpp
= (uchar_t
*)(mp1
->b_rptr
+ o
);