1/* LibTomCrypt, modular cryptographic library -- Tom St Denis
2 *
3 * LibTomCrypt is a library that provides various cryptographic
4 * algorithms in a highly modular and flexible manner.
5 *
6 * The library is free for all purposes without any express
7 * guarantee it works.
8 *
9 * Tom St Denis, tomstdenis@gmail.com, http://libtomcrypt.com
10 */
11#include "tomcrypt.h"
12
13/**
14  @file rc4.c
15  RC4 PRNG, Tom St Denis
16*/
17
18#ifdef RC4
19
20const struct ltc_prng_descriptor rc4_desc =
21{
22   "rc4", 32,
23    &rc4_start,
24    &rc4_add_entropy,
25    &rc4_ready,
26    &rc4_read,
27    &rc4_done,
28    &rc4_export,
29    &rc4_import,
30    &rc4_test
31};
32
33/**
34  Start the PRNG
35  @param prng     [out] The PRNG state to initialize
36  @return CRYPT_OK if successful
37*/
38int rc4_start(prng_state *prng)
39{
40    LTC_ARGCHK(prng != NULL);
41
42    /* set keysize to zero */
43    prng->rc4.x = 0;
44
45    return CRYPT_OK;
46}
47
48/**
49  Add entropy to the PRNG state
50  @param in       The data to add
51  @param inlen    Length of the data to add
52  @param prng     PRNG state to update
53  @return CRYPT_OK if successful
54*/
55int rc4_add_entropy(const unsigned char *in, unsigned long inlen, prng_state *prng)
56{
57    LTC_ARGCHK(in  != NULL);
58    LTC_ARGCHK(prng != NULL);
59
60    /* trim as required */
61    if (prng->rc4.x + inlen > 256) {
62       if (prng->rc4.x == 256) {
63          /* I can't possibly accept another byte, ok maybe a mint wafer... */
64          return CRYPT_OK;
65       } else {
66          /* only accept part of it */
67          inlen = 256 - prng->rc4.x;
68       }
69    }
70
71    while (inlen--) {
72       prng->rc4.buf[prng->rc4.x++] = *in++;
73    }
74
75    return CRYPT_OK;
76
77}
78
79/**
80  Make the PRNG ready to read from
81  @param prng   The PRNG to make active
82  @return CRYPT_OK if successful
83*/
84int rc4_ready(prng_state *prng)
85{
86    unsigned char key[256], tmp, *s;
87    int keylen, x, y, j;
88
89    LTC_ARGCHK(prng != NULL);
90
91    /* extract the key */
92    s = prng->rc4.buf;
93    XMEMCPY(key, s, 256);
94    keylen = prng->rc4.x;
95
96    /* make RC4 perm and shuffle */
97    for (x = 0; x < 256; x++) {
98        s[x] = x;
99    }
100
101    for (j = x = y = 0; x < 256; x++) {
102        y = (y + prng->rc4.buf[x] + key[j++]) & 255;
103        if (j == keylen) {
104           j = 0;
105        }
106        tmp = s[x]; s[x] = s[y]; s[y] = tmp;
107    }
108    prng->rc4.x = 0;
109    prng->rc4.y = 0;
110
111#ifdef LTC_CLEAN_STACK
112    zeromem(key, sizeof(key));
113#endif
114
115    return CRYPT_OK;
116}
117
118/**
119  Read from the PRNG
120  @param out      Destination
121  @param outlen   Length of output
122  @param prng     The active PRNG to read from
123  @return Number of octets read
124*/
125unsigned long rc4_read(unsigned char *out, unsigned long outlen, prng_state *prng)
126{
127   unsigned char x, y, *s, tmp;
128   unsigned long n;
129
130   LTC_ARGCHK(out != NULL);
131   LTC_ARGCHK(prng != NULL);
132
133#ifdef LTC_VALGRIND
134   zeromem(out, outlen);
135#endif
136
137   n = outlen;
138   x = prng->rc4.x;
139   y = prng->rc4.y;
140   s = prng->rc4.buf;
141   while (outlen--) {
142      x = (x + 1) & 255;
143      y = (y + s[x]) & 255;
144      tmp = s[x]; s[x] = s[y]; s[y] = tmp;
145      tmp = (s[x] + s[y]) & 255;
146      *out++ ^= s[tmp];
147   }
148   prng->rc4.x = x;
149   prng->rc4.y = y;
150   return n;
151}
152
153/**
154  Terminate the PRNG
155  @param prng   The PRNG to terminate
156  @return CRYPT_OK if successful
157*/
158int rc4_done(prng_state *prng)
159{
160   LTC_ARGCHK(prng != NULL);
161   return CRYPT_OK;
162}
163
164/**
165  Export the PRNG state
166  @param out       [out] Destination
167  @param outlen    [in/out] Max size and resulting size of the state
168  @param prng      The PRNG to export
169  @return CRYPT_OK if successful
170*/
171int rc4_export(unsigned char *out, unsigned long *outlen, prng_state *prng)
172{
173   LTC_ARGCHK(outlen != NULL);
174   LTC_ARGCHK(out    != NULL);
175   LTC_ARGCHK(prng   != NULL);
176
177   if (*outlen < 32) {
178      *outlen = 32;
179      return CRYPT_BUFFER_OVERFLOW;
180   }
181
182   if (rc4_read(out, 32, prng) != 32) {
183      return CRYPT_ERROR_READPRNG;
184   }
185   *outlen = 32;
186
187   return CRYPT_OK;
188}
189
190/**
191  Import a PRNG state
192  @param in       The PRNG state
193  @param inlen    Size of the state
194  @param prng     The PRNG to import
195  @return CRYPT_OK if successful
196*/
197int rc4_import(const unsigned char *in, unsigned long inlen, prng_state *prng)
198{
199   int err;
200   LTC_ARGCHK(in   != NULL);
201   LTC_ARGCHK(prng != NULL);
202
203   if (inlen != 32) {
204      return CRYPT_INVALID_ARG;
205   }
206
207   if ((err = rc4_start(prng)) != CRYPT_OK) {
208      return err;
209   }
210   return rc4_add_entropy(in, 32, prng);
211}
212
213/**
214  PRNG self-test
215  @return CRYPT_OK if successful, CRYPT_NOP if self-testing has been disabled
216*/
217int rc4_test(void)
218{
219#if !defined(LTC_TEST) || defined(LTC_VALGRIND)
220   return CRYPT_NOP;
221#else
222   static const struct {
223      unsigned char key[8], pt[8], ct[8];
224   } tests[] = {
225{
226   { 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef },
227   { 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef },
228   { 0x75, 0xb7, 0x87, 0x80, 0x99, 0xe0, 0xc5, 0x96 }
229}
230};
231   prng_state prng;
232   unsigned char dst[8];
233   int err, x;
234
235   for (x = 0; x < (int)(sizeof(tests)/sizeof(tests[0])); x++) {
236       if ((err = rc4_start(&prng)) != CRYPT_OK) {
237          return err;
238       }
239       if ((err = rc4_add_entropy(tests[x].key, 8, &prng)) != CRYPT_OK) {
240          return err;
241       }
242       if ((err = rc4_ready(&prng)) != CRYPT_OK) {
243          return err;
244       }
245       XMEMCPY(dst, tests[x].pt, 8);
246       if (rc4_read(dst, 8, &prng) != 8) {
247          return CRYPT_ERROR_READPRNG;
248       }
249       rc4_done(&prng);
250       if (XMEMCMP(dst, tests[x].ct, 8)) {
251#if 0
252          int y;
253          printf("\n\nRC4 failed, I got:\n");
254          for (y = 0; y < 8; y++) printf("%02x ", dst[y]);
255          printf("\n");
256#endif
257          return CRYPT_FAIL_TESTVECTOR;
258       }
259   }
260   return CRYPT_OK;
261#endif
262}
263
264#endif
265
266
267/* $Source: /cvs/libtom/libtomcrypt/src/prngs/rc4.c,v $ */
268/* $Revision: 1.9 $ */
269/* $Date: 2006/11/16 00:32:18 $ */
270