xref: /onnv-gate/usr/src/common/crypto/arcfour/sun4v/arcfour_crypt.c (revision 6125:38a604bf8269)
1227Skais /*
2227Skais  * CDDL HEADER START
3227Skais  *
4227Skais  * The contents of this file are subject to the terms of the
5*6125Sbubbva  * Common Development and Distribution License (the "License").
6*6125Sbubbva  * You may not use this file except in compliance with the License.
7227Skais  *
8227Skais  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9227Skais  * or http://www.opensolaris.org/os/licensing.
10227Skais  * See the License for the specific language governing permissions
11227Skais  * and limitations under the License.
12227Skais  *
13227Skais  * When distributing Covered Code, include this CDDL HEADER in each
14227Skais  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15227Skais  * If applicable, add the following below this CDDL HEADER, with the
16227Skais  * fields enclosed by brackets "[]" replaced with your own identifying
17227Skais  * information: Portions Copyright [yyyy] [name of copyright owner]
18227Skais  *
19227Skais  * CDDL HEADER END
20227Skais  */
21227Skais /*
22*6125Sbubbva  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
23227Skais  * Use is subject to license terms.
24227Skais  */
25227Skais 
26227Skais #pragma ident	"%Z%%M%	%I%	%E% SMI"
27227Skais 
28227Skais #include "../arcfour.h"
29227Skais 
30227Skais /* Initialize the key stream 'key' using the key value */
31227Skais void
32227Skais arcfour_key_init(ARCFour_key *key, uchar_t *keyval, int keyvallen)
33227Skais {
34227Skais /* EXPORT DELETE START */
35227Skais 
36227Skais 	uchar_t ext_keyval[256];
37227Skais 	uchar_t tmp;
38227Skais 	int i, j;
39227Skais 
40227Skais 	for (i = j = 0; i < 256; i++, j++) {
41227Skais 		if (j == keyvallen)
42227Skais 			j = 0;
43227Skais 
44227Skais 		ext_keyval[i] = keyval[j];
45227Skais 	}
46227Skais 	for (i = 0; i < 256; i++)
47227Skais 		key->arr[i] = (uchar_t)i;
48227Skais 
49227Skais 	j = 0;
50227Skais 	for (i = 0; i < 256; i++) {
51227Skais 		j = (j + key->arr[i] + ext_keyval[i]) % 256;
52227Skais 		tmp = key->arr[i];
53227Skais 		key->arr[i] = key->arr[j];
54227Skais 		key->arr[j] = tmp;
55227Skais 	}
56227Skais 	key->i = 0;
57227Skais 	key->j = 0;
58227Skais 
59227Skais /* EXPORT DELETE END */
60227Skais }
61227Skais 
62227Skais 
63227Skais /*
64227Skais  * Encipher 'in' using 'key.
65227Skais  * in and out can point to the same location
66227Skais  */
67227Skais void
68227Skais arcfour_crypt(ARCFour_key *key, uchar_t *in, uchar_t *out, size_t len)
69227Skais {
70227Skais 	size_t ii, it;
71227Skais 	unsigned long long in0, out0, merge = 0, merge0 = 0, merge1, mask = 0;
72227Skais 	uchar_t i, j, *base, jj, *base1, tmp;
73227Skais 	unsigned int tmp0, tmp1, i_accum, count = 0, shift = 0, i1;
74227Skais 
75227Skais 
76227Skais /* EXPORT DELETE START */
77227Skais 	int index;
78227Skais 
79227Skais 	base = key->arr;
80227Skais 
81*6125Sbubbva 	index = (((uintptr_t)in) & 0x7);
82227Skais 
83227Skais 	/* Get the 'in' on an 8-byte alignment */
84227Skais 	if (index > 0) {
85227Skais 		i = key->i;
86227Skais 		j = key->j;
87*6125Sbubbva 
88*6125Sbubbva 		for (index = 8 - index; (index-- > 0) && len > 0;
89227Skais 		    len--, in++, out++) {
90*6125Sbubbva 
91227Skais 			i = i + 1;
92227Skais 			j = j + key->arr[i];
93227Skais 			tmp = key->arr[i];
94227Skais 			key->arr[i] = key->arr[j];
95227Skais 			key->arr[j] = tmp;
96227Skais 			tmp = key->arr[i] + key->arr[j];
97227Skais 			*out = *in ^ key->arr[tmp];
98227Skais 		}
99227Skais 		key->i = i;
100227Skais 		key->j = j;
101227Skais 
102227Skais 	}
103227Skais 	if (len == 0)
104227Skais 		return;
105227Skais 
106227Skais 	/* See if we're fortunate and 'out' got aligned as well */
107227Skais 
108227Skais 
109227Skais 	/*
110227Skais 	 * Niagara optimized version for
111227Skais 	 * the cases where the input and output  buffers are aligned on
112227Skais 	 * a multiple of 8-byte boundary.
113227Skais 	 */
114227Skais #ifdef	sun4v
115*6125Sbubbva 	if ((((uintptr_t)out) & 7) != 0) {
116227Skais #endif	/* sun4v */
117227Skais 		i = key->i;
118227Skais 		j = key->j;
119227Skais 		for (ii = 0; ii < len; ii++) {
120227Skais 			i = i + 1;
121227Skais 			tmp0 = base[i];
122227Skais 			j = j + tmp0;
123227Skais 			tmp1 = base[j];
124227Skais 			base[i] = tmp1;
125227Skais 			base[j] = tmp0;
126227Skais 			tmp0 += tmp1;
127227Skais 			tmp0 = tmp0 & 0xff;
128227Skais 			out[ii] = in[ii] ^ base[tmp0];
129227Skais 		}
130227Skais 		key->i = i;
131227Skais 		key->j = j;
132227Skais #ifdef	sun4v
133227Skais 	} else {
134227Skais 		i = key->i;
135227Skais 		j = key->j;
136227Skais 
137227Skais 		/*
138227Skais 		 * Want to align base[i] on a 2B boundary -- allows updates
139227Skais 		 * via [i] to be performed in 2B chunks (reducing # of stores).
140227Skais 		 * Requires appropriate alias detection.
141227Skais 		 */
142227Skais 
143227Skais 		if (((i+1) % 2) != 0) {
144227Skais 			i = i + 1;
145227Skais 			tmp0 = base[i];
146227Skais 			j = j + tmp0;
147227Skais 			tmp1 = base[j];
148227Skais 
149227Skais 			base[i] = tmp1;
150227Skais 			base[j] = tmp0;
151227Skais 
152227Skais 			tmp0 += tmp1;
153227Skais 			tmp0 = tmp0 & 0xff;
154227Skais 
155227Skais 			merge0 = (unsigned long long)(base[tmp0]) << 56;
156227Skais 			shift = 8; mask = 0xff;
157227Skais 		}
158227Skais 
159227Skais 		/*
160227Skais 		 * Note - in and out may now be misaligned -
161227Skais 		 * as updating [out] in 8B chunks need to handle this
162227Skais 		 * possibility. Also could have a 1B overrun.
163227Skais 		 * Need to drop out of loop early as a result.
164227Skais 		 */
165227Skais 
166227Skais 		for (ii = 0, i1 = i; ii < ((len-1)  & (~7));
167227Skais 		    ii += 8, i1 = i1&0xff) {
168227Skais 
169227Skais 			/*
170227Skais 			 * If i < less than 248, know wont wrap around
171227Skais 			 * (i % 256), so don't need to bother with masking i
172227Skais 			 * after each increment
173227Skais 			 */
174227Skais 			if (i1 < 248) {
175227Skais 
176227Skais 				/* BYTE 0 */
177227Skais 				i1 = (i1 + 1);
178227Skais 
179227Skais 				/*
180227Skais 				 * Creating this base pointer reduces subsequent
181227Skais 				 * arihmetic ops required to load [i]
182227Skais 				 *
183227Skais 				 * N.B. don't need to check if [j] aliases.
184227Skais 				 * [i] and [j] end up with the same values
185227Skais 				 * anyway.
186227Skais 				 */
187227Skais 				base1 = &base[i1];
188227Skais 
189227Skais 				tmp0 = base1[0];
190227Skais 				j = j + tmp0;
191227Skais 
192227Skais 				tmp1 = base[j];
193227Skais 				/*
194227Skais 				 * Don't store [i] yet
195227Skais 				 */
196227Skais 				i_accum = tmp1;
197227Skais 				base[j] = tmp0;
198227Skais 
199227Skais 				tmp0 += tmp1;
200227Skais 				tmp0 = tmp0 & 0xff;
201227Skais 
202227Skais 				/*
203227Skais 				 * Check [tmp0] doesn't alias with [i]
204227Skais 				 */
205227Skais 
206227Skais 				/*
207227Skais 				 * Updating [out] in 8B chunks
208227Skais 				 */
209227Skais 				if (i1 == tmp0) {
210227Skais 					merge =
211227Skais 					    (unsigned long long)(i_accum) << 56;
212227Skais 				} else {
213227Skais 					merge =
214227Skais 					    (unsigned long long)(base[tmp0]) <<
215227Skais 					    56;
216227Skais 				}
217227Skais 
218227Skais 				/* BYTE 1 */
219227Skais 				tmp0 = base1[1];
220227Skais 
221227Skais 				j = j + tmp0;
222227Skais 
223227Skais 				/*
224227Skais 				 * [j] can now alias with [i] and [i-1]
225227Skais 				 * If alias abort speculation
226227Skais 				 */
227227Skais 				if ((i1 ^ j) < 2) {
228227Skais 					base1[0] = i_accum;
229227Skais 
230227Skais 					tmp1 = base[j];
231227Skais 
232227Skais 					base1[1] = tmp1;
233227Skais 					base[j] = tmp0;
234227Skais 
235227Skais 					tmp0 += tmp1;
236227Skais 					tmp0 = tmp0 & 0xff;
237227Skais 
238227Skais 					merge |= (unsigned long long)
239227Skais 					    (base[tmp0]) << 48;
240227Skais 				} else {
241227Skais 
242227Skais 					tmp1 = base[j];
243227Skais 
244227Skais 					i_accum = i_accum << 8;
245227Skais 					i_accum |= tmp1;
246227Skais 
247227Skais 					base[j] = tmp0;
248227Skais 
249227Skais 					tmp0 += tmp1;
250227Skais 					tmp0 = tmp0 & 0xff;
251227Skais 
252227Skais 					/*
253227Skais 					 * Speculation suceeded! Update [i]
254227Skais 					 * in 2B chunk
255227Skais 					 */
256227Skais 					*((unsigned short *) &base[i1]) =
257227Skais 					    i_accum;
258227Skais 
259227Skais 					merge |=
260227Skais 					    (unsigned long long)(base[tmp0]) <<
261227Skais 					    48;
262227Skais 				}
263227Skais 
264227Skais 
265227Skais 				/*
266227Skais 				 * Too expensive to perform [i] speculation for
267227Skais 				 * every byte. Just need to reduce frequency
268227Skais 				 * of stores until store buffer full stalls
269227Skais 				 * are not the bottleneck.
270227Skais 				 */
271227Skais 
272227Skais 				/* BYTE 2 */
273227Skais 				tmp0 = base1[2];
274227Skais 				j = j + tmp0;
275227Skais 				tmp1 = base[j];
276227Skais 				base1[2] = tmp1;
277227Skais 				base[j] = tmp0;
278227Skais 				tmp1 += tmp0;
279227Skais 				tmp1 = tmp1 & 0xff;
280227Skais 				merge |= (unsigned long long)(base[tmp1]) << 40;
281227Skais 
282227Skais 				/* BYTE 3 */
283227Skais 				tmp0 = base1[3];
284227Skais 				j = j + tmp0;
285227Skais 				tmp1 = base[j];
286227Skais 				base1[3] = tmp1;
287227Skais 				base[j] = tmp0;
288227Skais 				tmp0 += tmp1;
289227Skais 				tmp0 = tmp0 & 0xff;
290227Skais 				merge |= (unsigned long long)(base[tmp0]) << 32;
291227Skais 
292227Skais 				/* BYTE 4 */
293227Skais 				tmp0 = base1[4];
294227Skais 				j = j + tmp0;
295227Skais 				tmp1 = base[j];
296227Skais 				base1[4] = tmp1;
297227Skais 				base[j] = tmp0;
298227Skais 				tmp0 += tmp1;
299227Skais 				tmp0 = tmp0 & 0xff;
300227Skais 				merge |= (unsigned long long)(base[tmp0]) << 24;
301227Skais 
302227Skais 				/* BYTE 5 */
303227Skais 				tmp0 = base1[5];
304227Skais 				j = j + tmp0;
305227Skais 				tmp1 = base[j];
306227Skais 				base1[5] = tmp1;
307227Skais 				base[j] = tmp0;
308227Skais 				tmp0 += tmp1;
309227Skais 				tmp0 = tmp0 & 0xff;
310227Skais 				merge |= (unsigned long long)(base[tmp0]) << 16;
311227Skais 
312227Skais 				/* BYTE 6 */
313227Skais 				i1 = (i1+6);
314227Skais 				tmp0 = base1[6];
315227Skais 				j = j + tmp0;
316227Skais 				tmp1 = base[j];
317227Skais 				i_accum = tmp1;
318227Skais 				base[j] = tmp0;
319227Skais 
320227Skais 				tmp0 += tmp1;
321227Skais 				tmp0 = tmp0 & 0xff;
322227Skais 
323227Skais 				if (i1 == tmp0) {
324227Skais 					merge |=
325227Skais 					    (unsigned long long)(i_accum) << 8;
326227Skais 				} else {
327227Skais 					merge |=
328227Skais 					    (unsigned long long)(base[tmp0]) <<
329227Skais 					    8;
330227Skais 				}
331227Skais 
332227Skais 				/* BYTE 7 */
333227Skais 				tmp0 = base1[7];
334227Skais 
335227Skais 				/*
336227Skais 				 * Perform [i] speculation again. Indentical
337227Skais 				 * to that performed for BYTE0 and BYTE1.
338227Skais 				 */
339227Skais 				j = j + tmp0;
340227Skais 				if ((i1 ^ j) < 2) {
341227Skais 					base1[6] = i_accum;
342227Skais 					tmp1 = base[j];
343227Skais 
344227Skais 					base1[7] = tmp1;
345227Skais 					base[j] = tmp0;
346227Skais 
347227Skais 					tmp0 += tmp1;
348227Skais 					tmp0 = tmp0 & 0xff;
349227Skais 
350227Skais 					merge |=
351227Skais 					    (unsigned long long)(base[tmp0]);
352227Skais 
353227Skais 				} else {
354227Skais 					tmp1 = base[j];
355227Skais 
356227Skais 					i_accum = i_accum << 8;
357227Skais 					i_accum |= tmp1;
358227Skais 
359227Skais 					base[j] = tmp0;
360227Skais 
361227Skais 					tmp0 += tmp1;
362227Skais 					tmp0 = tmp0 & 0xff;
363227Skais 
364227Skais 					*((unsigned short *) &base[i1]) =
365227Skais 					    i_accum;
366227Skais 
367227Skais 					merge |=
368227Skais 					    (unsigned long long)(base[tmp0]);
369227Skais 				}
370227Skais 				i1++;
371227Skais 			} else {
372227Skais 				/*
373227Skais 				 * i is too close to wrap-around to allow
374227Skais 				 * masking to be disregarded
375227Skais 				 */
376227Skais 
377227Skais 				/*
378227Skais 				 * Same old speculation for BYTE 0 and BYTE 1
379227Skais 				 */
380227Skais 
381227Skais 				/* BYTE 0 */
382227Skais 				i1 = (i1 + 1) & 0xff;
383227Skais 				jj = i1;
384227Skais 
385227Skais 				tmp0 = base[i1];
386227Skais 				j = j + tmp0;
387227Skais 
388227Skais 				tmp1 = base[j];
389227Skais 				i_accum = tmp1;
390227Skais 				base[j] = tmp0;
391227Skais 
392227Skais 				tmp0 += tmp1;
393227Skais 				tmp0 = tmp0 & 0xff;
394227Skais 
395227Skais 				if (i1 == tmp0) {
396227Skais 					merge =
397227Skais 					    (unsigned long long)(i_accum) << 56;
398227Skais 				} else {
399227Skais 					merge =
400227Skais 					    (unsigned long long)(base[tmp0]) <<
401227Skais 					    56;
402227Skais 				}
403227Skais 
404227Skais 				/* BYTE 1 */
405227Skais 				tmp0 = base[i1+1];
406227Skais 
407227Skais 				j = j + tmp0;
408227Skais 
409227Skais 				if ((jj ^ j) < 2) {
410227Skais 					base[jj] = i_accum;
411227Skais 
412227Skais 					tmp1 = base[j];
413227Skais 
414227Skais 					base[i1+1] = tmp1;
415227Skais 					base[j] = tmp0;
416227Skais 
417227Skais 					tmp0 += tmp1;
418227Skais 					tmp0 = tmp0 & 0xff;
419227Skais 
420227Skais 					merge |=
421227Skais 					    (unsigned long long)(base[tmp0]) <<
422227Skais 					    48;
423227Skais 				} else {
424227Skais 
425227Skais 					tmp1 = base[j];
426227Skais 
427227Skais 					i_accum = i_accum << 8;
428227Skais 					i_accum |= tmp1;
429227Skais 
430227Skais 					base[j] = tmp0;
431227Skais 
432227Skais 					tmp0 += tmp1;
433227Skais 					tmp0 = tmp0 & 0xff;
434227Skais 
435227Skais 					*((unsigned short *) &base[jj]) =
436227Skais 					    i_accum;
437227Skais 
438227Skais 					merge |=
439227Skais 					    (unsigned long long)(base[tmp0]) <<
440227Skais 					    48;
441227Skais 				}
442227Skais 
443227Skais 				/* BYTE 2 */
444227Skais 				/*
445227Skais 				 * As know i must be even when enter loop (to
446227Skais 				 * satisfy alignment), can only wrap around
447227Skais 				 * on the even bytes. So just need to perform
448227Skais 				 * mask every 2nd byte
449227Skais 				 */
450227Skais 				i1 = (i1 + 2) & 0xff;
451227Skais 				tmp0 = base[i1];
452227Skais 				j = j + tmp0;
453227Skais 				tmp1 = base[j];
454227Skais 				base[i1] = tmp1;
455227Skais 				base[j] = tmp0;
456227Skais 				tmp0 += tmp1;
457227Skais 				tmp0 = tmp0 & 0xff;
458227Skais 				merge |= (unsigned long long)(base[tmp0]) << 40;
459227Skais 
460227Skais 				/* BYTE 3 */
461227Skais 				tmp0 = base[i1+1];
462227Skais 				j = j + tmp0;
463227Skais 				tmp1 = base[j];
464227Skais 				base[i1+1] = tmp1;
465227Skais 				base[j] = tmp0;
466227Skais 				tmp0 += tmp1;
467227Skais 				tmp0 = tmp0 & 0xff;
468227Skais 				merge |= (unsigned long long)(base[tmp0]) << 32;
469227Skais 
470227Skais 				/* BYTE 4 */
471227Skais 				i1 = (i1 + 2) & 0xff;
472227Skais 				tmp0 = base[i1];
473227Skais 				j = j + tmp0;
474227Skais 				tmp1 = base[j];
475227Skais 				base[i1] = tmp1;
476227Skais 				base[j] = tmp0;
477227Skais 				tmp0 += tmp1;
478227Skais 				tmp0 = tmp0 & 0xff;
479227Skais 				merge |= (unsigned long long)(base[tmp0]) << 24;
480227Skais 
481227Skais 				/* BYTE 5 */
482227Skais 				tmp0 = base[i1+1];
483227Skais 				j = j + tmp0;
484227Skais 				tmp1 = base[j];
485227Skais 				base[i1+1] = tmp1;
486227Skais 				base[j] = tmp0;
487227Skais 				tmp0 += tmp1;
488227Skais 				tmp0 = tmp0 & 0xff;
489227Skais 				merge |= (unsigned long long)(base[tmp0]) << 16;
490227Skais 
491227Skais 				/* BYTE 6 */
492227Skais 				i1 = (i1+2) &0xff;
493227Skais 				jj = i1;
494227Skais 				tmp0 = base[i1];
495227Skais 
496227Skais 				j = j + tmp0;
497227Skais 
498227Skais 				tmp1 = base[j];
499227Skais 				i_accum = tmp1;
500227Skais 				base[j] = tmp0;
501227Skais 
502227Skais 
503227Skais 				tmp0 += tmp1;
504227Skais 				tmp0 = tmp0 & 0xff;
505227Skais 
506227Skais 				if (i1 == tmp0) {
507227Skais 					merge |=
508227Skais 					    (unsigned long long)(i_accum) << 8;
509227Skais 				} else {
510227Skais 					merge |=
511227Skais 					    (unsigned long long)(base[tmp0]) <<
512227Skais 					    8;
513227Skais 				}
514227Skais 
515227Skais 				/* BYTE 7 */
516227Skais 				i1++;
517227Skais 				tmp0 = base[i1];
518227Skais 
519227Skais 				j = j + tmp0;
520227Skais 				if ((jj ^ j) < 2) {
521227Skais 					base[jj] = i_accum;
522227Skais 					tmp1 = base[j];
523227Skais 
524227Skais 					base[i1] = tmp1;
525227Skais 					base[j] = tmp0;
526227Skais 
527227Skais 					tmp0 += tmp1;
528227Skais 					tmp0 = tmp0 & 0xff;
529227Skais 
530227Skais 					merge |=
531227Skais 					    (unsigned long long)(base[tmp0]);
532227Skais 
533227Skais 				} else {
534227Skais 					tmp1 = base[j];
535227Skais 
536227Skais 					i_accum = i_accum << 8;
537227Skais 					i_accum |= tmp1;
538227Skais 
539227Skais 					base[j] = tmp0;
540227Skais 
541227Skais 					tmp0 += tmp1;
542227Skais 					tmp0 = tmp0 & 0xff;
543227Skais 
544227Skais 					*((unsigned short *) &base[jj]) =
545227Skais 					    i_accum;
546227Skais 
547227Skais 					merge |=
548227Skais 					    (unsigned long long)(base[tmp0]);
549227Skais 				}
550227Skais 			}
551227Skais 
552227Skais 			/*
553227Skais 			 * Perform update to [out]
554227Skais 			 * Remember could be alignment issues
555227Skais 			 */
556227Skais 			in0 = *((unsigned long long *) (&in[ii]));
557227Skais 
558227Skais 			merge1 = merge0 | (merge >> shift);
559227Skais 
560227Skais 			merge0 = (merge & mask) << 56;
561227Skais 
562227Skais 			in0 = in0 ^ merge1;
563227Skais 
564227Skais 			*((unsigned long long *) (&out[ii])) = in0;
565227Skais 		}
566227Skais 
567227Skais 		i = i1;
568227Skais 
569227Skais 		/*
570227Skais 		 * Handle any overrun
571227Skais 		 */
572227Skais 		if (shift) {
573227Skais 			out[ii] = in[ii] ^ (merge0 >> 56);
574227Skais 			ii++;
575227Skais 		}
576227Skais 
577227Skais 		/*
578227Skais 		 * Handle final few bytes
579227Skais 		 */
580227Skais 		for (; ii < len; ii++) {
581227Skais 			i = i + 1;
582227Skais 			tmp0 = base[i];
583227Skais 			j = j + tmp0;
584227Skais 			tmp1 = base[j];
585227Skais 
586227Skais 			base[i] = tmp1;
587227Skais 			base[j] = tmp0;
588227Skais 
589227Skais 			tmp0 += tmp1;
590227Skais 			tmp0 = tmp0 & 0xff;
591227Skais 			out[ii] = in[ii] ^ base[tmp0];
592227Skais 		}
593227Skais 		key->i = i;
594227Skais 		key->j = j;
595227Skais 	}
596227Skais #endif /* sun4v */
597227Skais 
598227Skais /* EXPORT DELETE END */
599227Skais }
600