2003-02-23 Moritz Schulte <moritz@g10code.com>
[libgcrypt.git] / cipher / arcfour.c
1 /* arcfour.c  -  The arcfour stream cipher
2  *      Copyright (C) 2000, 2001, 2002, 2003 Free Software Foundation, Inc.
3  *
4  * This file is part of Libgcrypt.
5  *
6  * Libgcrypt is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU Lesser general Public License as
8  * published by the Free Software Foundation; either version 2.1 of
9  * the License, or (at your option) any later version.
10  *
11  * Libgcrypt is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
19  *
20  * For a description of the algorithm, see:
21  *   Bruce Schneier: Applied Cryptography. John Wiley & Sons, 1996.
22  *   ISBN 0-471-11709-9. Pages 397 ff.
23  */
24
25
26 #include <config.h>
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <string.h>
30 #include "types.h"
31 #include "g10lib.h"
32 #include "arcfour.h"
33
34 static const char *selftest(void);
35
36
37 typedef struct {
38     int idx_i, idx_j;
39     byte sbox[256];
40 } ARCFOUR_context;
41
42
43 static void
44 do_encrypt_stream( ARCFOUR_context *ctx,
45                 byte *outbuf, const byte *inbuf, unsigned int length )
46 {
47   register int i = ctx->idx_i;
48   register int j = ctx->idx_j;
49   register byte *sbox = ctx->sbox;
50   register int t;  
51
52   while ( length-- )
53     {
54       i++;
55       i = i & 255; /* and seems to be faster than mod */
56       j += sbox[i];
57       j &= 255;
58       t = sbox[i]; sbox[i] = sbox[j]; sbox[j] = t;
59       *outbuf++ = *inbuf++ ^ sbox[(sbox[i] + sbox[j]) & 255];
60     }
61   
62   ctx->idx_i = i;
63   ctx->idx_j = j;
64 }
65
66 static void
67 encrypt_stream( ARCFOUR_context *ctx,
68                 byte *outbuf, const byte *inbuf, unsigned int length )
69 {
70
71     do_encrypt_stream (ctx, outbuf, inbuf, length );
72     _gcry_burn_stack (64);
73 }
74
75
76 static int
77 do_arcfour_setkey( ARCFOUR_context *ctx, const byte *key, unsigned int keylen )
78 {
79     static int initialized;
80     static const char* selftest_failed;
81     int i, j;
82     byte karr[256];
83
84     if( !initialized ) {
85         initialized = 1;
86         selftest_failed = selftest();
87         if( selftest_failed )
88             log_error ("ARCFOUR selftest failed (%s)\n", selftest_failed );
89     }
90     if( selftest_failed )
91         return GCRYERR_SELFTEST;
92
93     if( keylen < 40/8 ) /* we want at least 40 bits */
94         return GCRYERR_INV_KEYLEN; 
95
96     ctx->idx_i = ctx->idx_j = 0;
97     for (i=0; i < 256; i++ )
98         ctx->sbox[i] = i;
99     for (i=0; i < 256; i++ )
100         karr[i] = key[i%keylen];
101     for (i=j=0; i < 256; i++ ) {
102         int t;
103         j = (j + ctx->sbox[i] + karr[i]) % 256;
104         t = ctx->sbox[i];
105         ctx->sbox[i] = ctx->sbox[j];
106         ctx->sbox[j] = t;
107     } 
108     memset( karr, 0, 256 );
109
110     return 0;
111 }
112
113 static int
114 arcfour_setkey ( ARCFOUR_context *ctx, const byte *key, unsigned int keylen )
115 {
116     int rc = do_arcfour_setkey (ctx, key, keylen );
117     _gcry_burn_stack (300);
118     return rc;
119 }
120
121
122 static const char*
123 selftest(void)
124 {
125     ARCFOUR_context ctx;
126     byte scratch[16];      
127     
128     /* Test vector from Cryptlib labeled there:
129      * "from the State/Commerce Department" */
130     static const byte key_1[] =
131         { 0x61, 0x8A, 0x63, 0xD2, 0xFB };
132     static const byte plaintext_1[] =
133         { 0xDC, 0xEE, 0x4C, 0xF9, 0x2C };
134     static const byte ciphertext_1[] =
135         { 0xF1, 0x38, 0x29, 0xC9, 0xDE };
136
137     arcfour_setkey( &ctx, key_1, sizeof(key_1));
138     encrypt_stream( &ctx, scratch, plaintext_1, sizeof(plaintext_1));
139     if (memcmp (scratch, ciphertext_1, sizeof (ciphertext_1)))
140         return "Arcfour encryption test 1 failed.";
141     arcfour_setkey( &ctx, key_1, sizeof(key_1));
142     encrypt_stream(&ctx, scratch, scratch, sizeof(plaintext_1)); /* decrypt */
143     if ( memcmp (scratch, plaintext_1, sizeof (plaintext_1)))
144         return "Arcfour decryption test 1 failed.";
145     return NULL;
146 }
147
148
149 /****************
150  * Return some information about the algorithm.  We need algo here to
151  * distinguish different flavors of the algorithm.
152  * Returns: A pointer to string describing the algorithm or NULL if
153  *          the ALGO is invalid.
154  * NOTE: This is a special get_info function which is different from all
155  * others because arcfour is a stream cipher.  We use this hack until
156  * we have redesign the interface.
157  */
158 const char *
159 _gcry_arcfour_get_info( int algo, size_t *keylen, size_t *blocksize,
160                    size_t *contextsize,
161                    int  (**r_setkey)( void *c, byte *key, unsigned keylen ),
162                    void (**r_stencrypt)( void *c, byte *outbuf,
163                                        byte *inbuf, unsigned int nbytes ),
164                    void (**r_stdecrypt)( void *c, byte *outbuf,
165                                        byte *inbuf, unsigned int nbytes )
166                  )
167 {
168     *keylen = 128; /* arbitrary value */
169     *blocksize = 1;
170     *contextsize = sizeof(ARCFOUR_context);
171     *(int  (**)(ARCFOUR_context*, const byte*, unsigned))r_setkey
172                                                         = arcfour_setkey;
173     *(void (**)(ARCFOUR_context*, byte*, const byte*, unsigned))r_stencrypt
174                                                         = encrypt_stream;
175     *(void (**)(ARCFOUR_context*, byte*, const byte*, unsigned))r_stdecrypt
176                                                         = encrypt_stream;
177
178
179     if( algo == GCRY_CIPHER_ARCFOUR )
180         return "ARCFOUR";
181     return NULL;
182 }