79f00e712933212db0b71283ae2a6e70d38b3745
[openssl.git] / crypto / rc4 / rc4_enc.c
1 /* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
2  * All rights reserved.
3  *
4  * This package is an SSL implementation written
5  * by Eric Young (eay@cryptsoft.com).
6  * The implementation was written so as to conform with Netscapes SSL.
7  *
8  * This library is free for commercial and non-commercial use as long as
9  * the following conditions are aheared to.  The following conditions
10  * apply to all code found in this distribution, be it the RC4, RSA,
11  * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
12  * included with this distribution is covered by the same copyright terms
13  * except that the holder is Tim Hudson (tjh@cryptsoft.com).
14  *
15  * Copyright remains Eric Young's, and as such any Copyright notices in
16  * the code are not to be removed.
17  * If this package is used in a product, Eric Young should be given attribution
18  * as the author of the parts of the library used.
19  * This can be in the form of a textual message at program startup or
20  * in documentation (online or textual) provided with the package.
21  *
22  * Redistribution and use in source and binary forms, with or without
23  * modification, are permitted provided that the following conditions
24  * are met:
25  * 1. Redistributions of source code must retain the copyright
26  *    notice, this list of conditions and the following disclaimer.
27  * 2. Redistributions in binary form must reproduce the above copyright
28  *    notice, this list of conditions and the following disclaimer in the
29  *    documentation and/or other materials provided with the distribution.
30  * 3. All advertising materials mentioning features or use of this software
31  *    must display the following acknowledgement:
32  *    "This product includes cryptographic software written by
33  *     Eric Young (eay@cryptsoft.com)"
34  *    The word 'cryptographic' can be left out if the rouines from the library
35  *    being used are not cryptographic related :-).
36  * 4. If you include any Windows specific code (or a derivative thereof) from
37  *    the apps directory (application code) you must include an acknowledgement:
38  *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
39  *
40  * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
41  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
42  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
43  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
44  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
45  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
46  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
47  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
48  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
49  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
50  * SUCH DAMAGE.
51  *
52  * The licence and distribution terms for any publically available version or
53  * derivative of this code cannot be changed.  i.e. this code cannot simply be
54  * copied and put under another distribution licence
55  * [including the GNU Public Licence.]
56  */
57
58 #include <openssl/rc4.h>
59 #include "rc4_locl.h"
60
61 /*-
62  * RC4 as implemented from a posting from
63  * Newsgroups: sci.crypt
64  * From: sterndark@netcom.com (David Sterndark)
65  * Subject: RC4 Algorithm revealed.
66  * Message-ID: <sternCvKL4B.Hyy@netcom.com>
67  * Date: Wed, 14 Sep 1994 06:35:31 GMT
68  */
69
70 void RC4(RC4_KEY *key, size_t len, const unsigned char *indata,
71          unsigned char *outdata)
72 {
73     register RC4_INT *d;
74     register RC4_INT x, y, tx, ty;
75     size_t i;
76
77     x = key->x;
78     y = key->y;
79     d = key->data;
80
81 #if defined(RC4_CHUNK) && !defined(PEDANTIC)
82     /*-
83      * The original reason for implementing this(*) was the fact that
84      * pre-21164a Alpha CPUs don't have byte load/store instructions
85      * and e.g. a byte store has to be done with 64-bit load, shift,
86      * and, or and finally 64-bit store. Peaking data and operating
87      * at natural word size made it possible to reduce amount of
88      * instructions as well as to perform early read-ahead without
89      * suffering from RAW (read-after-write) hazard. This resulted
90      * in ~40%(**) performance improvement on 21064 box with gcc.
91      * But it's not only Alpha users who win here:-) Thanks to the
92      * early-n-wide read-ahead this implementation also exhibits
93      * >40% speed-up on SPARC and 20-30% on 64-bit MIPS (depending
94      * on sizeof(RC4_INT)).
95      *
96      * (*)  "this" means code which recognizes the case when input
97      *      and output pointers appear to be aligned at natural CPU
98      *      word boundary
99      * (**) i.e. according to 'apps/openssl speed rc4' benchmark,
100      *      crypto/rc4/rc4speed.c exhibits almost 70% speed-up...
101      *
102      * Cavets.
103      *
104      * - RC4_CHUNK="unsigned long long" should be a #1 choice for
105      *   UltraSPARC. Unfortunately gcc generates very slow code
106      *   (2.5-3 times slower than one generated by Sun's WorkShop
107      *   C) and therefore gcc (at least 2.95 and earlier) should
108      *   always be told that RC4_CHUNK="unsigned long".
109      *
110      *                                      <appro@fy.chalmers.se>
111      */
112
113 # define RC4_STEP       ( \
114                         x=(x+1) &0xff,  \
115                         tx=d[x],        \
116                         y=(tx+y)&0xff,  \
117                         ty=d[y],        \
118                         d[y]=tx,        \
119                         d[x]=ty,        \
120                         (RC4_CHUNK)d[(tx+ty)&0xff]\
121                         )
122
123     if ((((size_t)indata & (sizeof(RC4_CHUNK) - 1)) |
124          ((size_t)outdata & (sizeof(RC4_CHUNK) - 1))) == 0) {
125         RC4_CHUNK ichunk, otp;
126         const union {
127             long one;
128             char little;
129         } is_endian = {
130             1
131         };
132
133         /*-
134          * I reckon we can afford to implement both endian
135          * cases and to decide which way to take at run-time
136          * because the machine code appears to be very compact
137          * and redundant 1-2KB is perfectly tolerable (i.e.
138          * in case the compiler fails to eliminate it:-). By
139          * suggestion from Terrel Larson <terr@terralogic.net>
140          * who also stands for the is_endian union:-)
141          *
142          * Special notes.
143          *
144          * - is_endian is declared automatic as doing otherwise
145          *   (declaring static) prevents gcc from eliminating
146          *   the redundant code;
147          * - compilers (those I've tried) don't seem to have
148          *   problems eliminating either the operators guarded
149          *   by "if (sizeof(RC4_CHUNK)==8)" or the condition
150          *   expressions themselves so I've got 'em to replace
151          *   corresponding #ifdefs from the previous version;
152          * - I chose to let the redundant switch cases when
153          *   sizeof(RC4_CHUNK)!=8 be (were also #ifdefed
154          *   before);
155          * - in case you wonder "&(sizeof(RC4_CHUNK)*8-1)" in
156          *   [LB]ESHFT guards against "shift is out of range"
157          *   warnings when sizeof(RC4_CHUNK)!=8
158          *
159          *                      <appro@fy.chalmers.se>
160          */
161         if (!is_endian.little) { /* BIG-ENDIAN CASE */
162 # define BESHFT(c)      (((sizeof(RC4_CHUNK)-(c)-1)*8)&(sizeof(RC4_CHUNK)*8-1))
163             for (; len & (0 - sizeof(RC4_CHUNK)); len -= sizeof(RC4_CHUNK)) {
164                 ichunk = *(RC4_CHUNK *) indata;
165                 otp = RC4_STEP << BESHFT(0);
166                 otp |= RC4_STEP << BESHFT(1);
167                 otp |= RC4_STEP << BESHFT(2);
168                 otp |= RC4_STEP << BESHFT(3);
169                 if (sizeof(RC4_CHUNK) == 8) {
170                     otp |= RC4_STEP << BESHFT(4);
171                     otp |= RC4_STEP << BESHFT(5);
172                     otp |= RC4_STEP << BESHFT(6);
173                     otp |= RC4_STEP << BESHFT(7);
174                 }
175                 *(RC4_CHUNK *) outdata = otp ^ ichunk;
176                 indata += sizeof(RC4_CHUNK);
177                 outdata += sizeof(RC4_CHUNK);
178             }
179             if (len) {
180                 RC4_CHUNK mask = (RC4_CHUNK) - 1, ochunk;
181
182                 ichunk = *(RC4_CHUNK *) indata;
183                 ochunk = *(RC4_CHUNK *) outdata;
184                 otp = 0;
185                 i = BESHFT(0);
186                 mask <<= (sizeof(RC4_CHUNK) - len) << 3;
187                 switch (len & (sizeof(RC4_CHUNK) - 1)) {
188                 case 7:
189                     otp = RC4_STEP << i, i -= 8;
190                 case 6:
191                     otp |= RC4_STEP << i, i -= 8;
192                 case 5:
193                     otp |= RC4_STEP << i, i -= 8;
194                 case 4:
195                     otp |= RC4_STEP << i, i -= 8;
196                 case 3:
197                     otp |= RC4_STEP << i, i -= 8;
198                 case 2:
199                     otp |= RC4_STEP << i, i -= 8;
200                 case 1:
201                     otp |= RC4_STEP << i, i -= 8;
202                 case 0:;       /*
203                                  * it's never the case,
204                                  * but it has to be here
205                                  * for ultrix?
206                                  */
207                 }
208                 ochunk &= ~mask;
209                 ochunk |= (otp ^ ichunk) & mask;
210                 *(RC4_CHUNK *) outdata = ochunk;
211             }
212             key->x = x;
213             key->y = y;
214             return;
215         } else {                /* LITTLE-ENDIAN CASE */
216 # define LESHFT(c)      (((c)*8)&(sizeof(RC4_CHUNK)*8-1))
217             for (; len & (0 - sizeof(RC4_CHUNK)); len -= sizeof(RC4_CHUNK)) {
218                 ichunk = *(RC4_CHUNK *) indata;
219                 otp = RC4_STEP;
220                 otp |= RC4_STEP << 8;
221                 otp |= RC4_STEP << 16;
222                 otp |= RC4_STEP << 24;
223                 if (sizeof(RC4_CHUNK) == 8) {
224                     otp |= RC4_STEP << LESHFT(4);
225                     otp |= RC4_STEP << LESHFT(5);
226                     otp |= RC4_STEP << LESHFT(6);
227                     otp |= RC4_STEP << LESHFT(7);
228                 }
229                 *(RC4_CHUNK *) outdata = otp ^ ichunk;
230                 indata += sizeof(RC4_CHUNK);
231                 outdata += sizeof(RC4_CHUNK);
232             }
233             if (len) {
234                 RC4_CHUNK mask = (RC4_CHUNK) - 1, ochunk;
235
236                 ichunk = *(RC4_CHUNK *) indata;
237                 ochunk = *(RC4_CHUNK *) outdata;
238                 otp = 0;
239                 i = 0;
240                 mask >>= (sizeof(RC4_CHUNK) - len) << 3;
241                 switch (len & (sizeof(RC4_CHUNK) - 1)) {
242                 case 7:
243                     otp = RC4_STEP, i += 8;
244                 case 6:
245                     otp |= RC4_STEP << i, i += 8;
246                 case 5:
247                     otp |= RC4_STEP << i, i += 8;
248                 case 4:
249                     otp |= RC4_STEP << i, i += 8;
250                 case 3:
251                     otp |= RC4_STEP << i, i += 8;
252                 case 2:
253                     otp |= RC4_STEP << i, i += 8;
254                 case 1:
255                     otp |= RC4_STEP << i, i += 8;
256                 case 0:;       /*
257                                  * it's never the case,
258                                  * but it has to be here
259                                  * for ultrix?
260                                  */
261                 }
262                 ochunk &= ~mask;
263                 ochunk |= (otp ^ ichunk) & mask;
264                 *(RC4_CHUNK *) outdata = ochunk;
265             }
266             key->x = x;
267             key->y = y;
268             return;
269         }
270     }
271 #endif
272 #define LOOP(in,out) \
273                 x=((x+1)&0xff); \
274                 tx=d[x]; \
275                 y=(tx+y)&0xff; \
276                 d[x]=ty=d[y]; \
277                 d[y]=tx; \
278                 (out) = d[(tx+ty)&0xff]^ (in);
279
280 #ifndef RC4_INDEX
281 # define RC4_LOOP(a,b,i) LOOP(*((a)++),*((b)++))
282 #else
283 # define RC4_LOOP(a,b,i) LOOP(a[i],b[i])
284 #endif
285
286     i = len >> 3;
287     if (i) {
288         for (;;) {
289             RC4_LOOP(indata, outdata, 0);
290             RC4_LOOP(indata, outdata, 1);
291             RC4_LOOP(indata, outdata, 2);
292             RC4_LOOP(indata, outdata, 3);
293             RC4_LOOP(indata, outdata, 4);
294             RC4_LOOP(indata, outdata, 5);
295             RC4_LOOP(indata, outdata, 6);
296             RC4_LOOP(indata, outdata, 7);
297 #ifdef RC4_INDEX
298             indata += 8;
299             outdata += 8;
300 #endif
301             if (--i == 0)
302                 break;
303         }
304     }
305     i = len & 0x07;
306     if (i) {
307         for (;;) {
308             RC4_LOOP(indata, outdata, 0);
309             if (--i == 0)
310                 break;
311             RC4_LOOP(indata, outdata, 1);
312             if (--i == 0)
313                 break;
314             RC4_LOOP(indata, outdata, 2);
315             if (--i == 0)
316                 break;
317             RC4_LOOP(indata, outdata, 3);
318             if (--i == 0)
319                 break;
320             RC4_LOOP(indata, outdata, 4);
321             if (--i == 0)
322                 break;
323             RC4_LOOP(indata, outdata, 5);
324             if (--i == 0)
325                 break;
326             RC4_LOOP(indata, outdata, 6);
327             if (--i == 0)
328                 break;
329         }
330     }
331     key->x = x;
332     key->y = y;
333 }