xref: /illumos-gate/usr/src/common/crypto/arcfour/arcfour_crypt.c (revision 46b592853d0f4f11781b6b0a7533f267c6aee132)
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 #define	ARCFOUR_LOOP_OPTIMIZED
27 
28 #include "arcfour.h"
29 
30 #if defined(__amd64)
31 /* ARCFour_key.flag values */
32 #define	ARCFOUR_ON_INTEL	1
33 #define	ARCFOUR_ON_AMD64	0
34 
35 #ifdef _KERNEL
36 #include <sys/x86_archext.h>
37 #include <sys/cpuvar.h>
38 
39 #else
40 #include <sys/auxv.h>
41 #endif	/* _KERNEL */
42 #endif	/* __amd64 */
43 
44 #ifndef __amd64
45 /*
46  * Initialize the key stream 'key' using the key value.
47  *
48  * Input:
49  * keyval	User-provided key
50  * keyvallen	Length, in bytes, of keyval
51  * Output:
52  * key		Initialized ARCFOUR key schedule, based on keyval
53  */
54 void
55 arcfour_key_init(ARCFour_key *key, uchar_t *keyval, int keyvallen)
56 {
57 /* EXPORT DELETE START */
58 
59 	uchar_t ext_keyval[256];
60 	uchar_t tmp;
61 	int i, j;
62 
63 	/* Normalize key length to 256 */
64 	for (i = j = 0; i < 256; i++, j++) {
65 		if (j == keyvallen)
66 			j = 0;
67 		ext_keyval[i] = keyval[j];
68 	}
69 
70 	for (i = 0; i < 256; i++)
71 		key->arr[i] = (uchar_t)i;
72 
73 	j = 0;
74 	for (i = 0; i < 256; i++) {
75 		j = (j + key->arr[i] + ext_keyval[i]) & 0xff;
76 		tmp = key->arr[i];
77 		key->arr[i] = key->arr[j];
78 		key->arr[j] = tmp;
79 	}
80 	key->i = 0;
81 	key->j = 0;
82 
83 /* EXPORT DELETE END */
84 }
85 #endif	/* !__amd64 */
86 
87 
88 /*
89  * Encipher 'in' using 'key'.
90  *
91  * Input:
92  * key		ARCFOUR key, initialized by arcfour_key_init()
93  * in		Input text
94  * out		Buffer to contain output text
95  * len		Length, in bytes, of the in and out buffers
96  *
97  * Output:
98  * out		Buffer containing output text
99  *
100  * Note: in and out can point to the same location
101  */
102 void
103 arcfour_crypt(ARCFour_key *key, uchar_t *in, uchar_t *out, size_t len)
104 {
105 /* EXPORT DELETE START */
106 #ifdef	__amd64
107 	if (key->flag == ARCFOUR_ON_AMD64) {
108 		arcfour_crypt_asm(key, in, out, len);
109 	} else { /* Intel EM64T */
110 #endif	/* amd64 */
111 
112 	size_t		ii;
113 	uchar_t		i, j, ti, tj;
114 #ifdef ARCFOUR_LOOP_OPTIMIZED
115 	uchar_t		arr_ij;
116 #endif
117 #ifdef __amd64
118 	uint32_t	*arr;
119 #else
120 	uchar_t		*arr;
121 #endif
122 
123 #ifdef	sun4u
124 	/*
125 	 * The sun4u has a version of arcfour_crypt_aligned() hand-tuned for
126 	 * the cases where the input and output buffers are aligned on
127 	 * a multiple of 8-byte boundary.
128 	 */
129 	int		index;
130 	uchar_t		tmp;
131 
132 	index = (((uint64_t)(uintptr_t)in) & 0x7);
133 
134 	/* Get the 'in' on an 8-byte alignment */
135 	if (index > 0) {
136 		i = key->i;
137 		j = key->j;
138 		for (index = 8 - (uint64_t)(uintptr_t)in & 0x7;
139 		    (index-- > 0) && len > 0;
140 		    len--, in++, out++) {
141 			++i;
142 			j = j + key->arr[i];
143 			tmp = key->arr[i];
144 			key->arr[i] = key->arr[j];
145 			key->arr[j] = tmp;
146 			tmp = key->arr[i] + key->arr[j];
147 			*out = *in ^ key->arr[tmp];
148 		}
149 		key->i = i;
150 		key->j = j;
151 	}
152 
153 	if (len == 0)
154 		return;
155 
156 	/* See if we're fortunate and 'out' got aligned as well */
157 
158 	if ((((uint64_t)(uintptr_t)out) & 7) != 0) {
159 #endif	/* sun4u */
160 
161 	i = key->i;
162 	j = key->j;
163 	arr = key->arr;
164 
165 #ifndef ARCFOUR_LOOP_OPTIMIZED
166 	/*
167 	 * This loop is hasn't been reordered, but is kept for reference
168 	 * purposes as it's more readable
169 	 */
170 	for (ii = 0; ii < len; ++ii) {
171 		++i;
172 		ti = arr[i];
173 		j = j + ti;
174 		tj = arr[j];
175 		arr[j] = ti;
176 		arr[i] = tj;
177 		out[ii] = in[ii] ^ arr[(ti + tj) & 0xff];
178 	}
179 
180 #else
181 	/*
182 	 * This for loop is optimized by carefully spreading out
183 	 * memory access and storage to avoid conflicts,
184 	 * allowing the processor to process operations in parallel
185 	 */
186 
187 	/* for loop setup */
188 	++i;
189 	ti = arr[i];
190 	j = j + ti;
191 	tj = arr[j];
192 	arr[j] = ti;
193 	arr[i] = tj;
194 	arr_ij = arr[(ti + tj) & 0xff];
195 	--len;
196 
197 	for (ii = 0; ii < len; ) {
198 		++i;
199 		ti = arr[i];
200 		j = j + ti;
201 		tj = arr[j];
202 		arr[j] = ti;
203 		arr[i] = tj;
204 
205 		/* save result from previous loop: */
206 		out[ii] = in[ii] ^ arr_ij;
207 
208 		++ii;
209 		arr_ij = arr[(ti + tj) & 0xff];
210 	}
211 	/* save result from last loop: */
212 	out[ii] = in[ii] ^ arr_ij;
213 #endif
214 
215 	key->i = i;
216 	key->j = j;
217 
218 #ifdef	sun4u
219 	} else {
220 		arcfour_crypt_aligned(key, len, in, out);
221 	}
222 #endif	/* sun4u */
223 #ifdef	__amd64
224 	}
225 #endif	/* amd64 */
226 
227 /* EXPORT DELETE END */
228 }
229 
230 
231 #ifdef	__amd64
232 /*
233  * Return 1 if executing on Intel, otherwise 0 (e.g., AMD64).
234  */
235 int
236 arcfour_crypt_on_intel(void)
237 {
238 #ifdef _KERNEL
239 	return (cpuid_getvendor(CPU) == X86_VENDOR_Intel);
240 #else
241 	uint_t	ui;
242 	(void) getisax(&ui, 1);
243 	return ((ui & AV_386_AMD_MMX) == 0);
244 #endif	/* _KERNEL */
245 }
246 #endif	/* __amd64 */
247