]> pd.if.org Git - zpackage/blob - libtomcrypt/src/prngs/rc4.c
e2aa92175e76bc6255fc3d5ab5729b2ca12e610a
[zpackage] / libtomcrypt / src / prngs / rc4.c
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 #include "tomcrypt.h"
10
11 /**
12   @file prngs/rc4.c
13   RC4 PRNG, Tom St Denis
14 */
15
16 #ifdef LTC_RC4
17
18 const struct ltc_prng_descriptor rc4_desc =
19 {
20    "rc4",
21    32,
22    &rc4_start,
23    &rc4_add_entropy,
24    &rc4_ready,
25    &rc4_read,
26    &rc4_done,
27    &rc4_export,
28    &rc4_import,
29    &rc4_test
30 };
31
32 /**
33   Start the PRNG
34   @param prng     [out] The PRNG state to initialize
35   @return CRYPT_OK if successful
36 */
37 int rc4_start(prng_state *prng)
38 {
39    LTC_ARGCHK(prng != NULL);
40    prng->ready = 0;
41    /* set entropy (key) size to zero */
42    prng->rc4.s.x = 0;
43    /* clear entropy (key) buffer */
44    XMEMSET(&prng->rc4.s.buf, 0, sizeof(prng->rc4.s.buf));
45    LTC_MUTEX_INIT(&prng->lock)
46    return CRYPT_OK;
47 }
48
49 /**
50   Add entropy to the PRNG state
51   @param in       The data to add
52   @param inlen    Length of the data to add
53   @param prng     PRNG state to update
54   @return CRYPT_OK if successful
55 */
56 int rc4_add_entropy(const unsigned char *in, unsigned long inlen, prng_state *prng)
57 {
58    unsigned char buf[256];
59    unsigned long i;
60    int err;
61
62    LTC_ARGCHK(prng != NULL);
63    LTC_ARGCHK(in != NULL);
64    LTC_ARGCHK(inlen > 0);
65
66    LTC_MUTEX_LOCK(&prng->lock);
67    if (prng->ready) {
68       /* rc4_ready() was already called, do "rekey" operation */
69       if ((err = rc4_stream_keystream(&prng->rc4.s, buf, sizeof(buf))) != CRYPT_OK) goto LBL_UNLOCK;
70       for(i = 0; i < inlen; i++) buf[i % sizeof(buf)] ^= in[i];
71       /* initialize RC4 */
72       if ((err = rc4_stream_setup(&prng->rc4.s, buf, sizeof(buf))) != CRYPT_OK) goto LBL_UNLOCK;
73       /* drop first 3072 bytes - https://en.wikipedia.org/wiki/RC4#Fluhrer.2C_Mantin_and_Shamir_attack */
74       for (i = 0; i < 12; i++) rc4_stream_keystream(&prng->rc4.s, buf, sizeof(buf));
75       zeromem(buf, sizeof(buf));
76    }
77    else {
78       /* rc4_ready() was not called yet, add entropy to the buffer */
79       while (inlen--) prng->rc4.s.buf[prng->rc4.s.x++ % sizeof(prng->rc4.s.buf)] ^= *in++;
80    }
81    err = CRYPT_OK;
82 LBL_UNLOCK:
83    LTC_MUTEX_UNLOCK(&prng->lock);
84    return err;
85 }
86
87 /**
88   Make the PRNG ready to read from
89   @param prng   The PRNG to make active
90   @return CRYPT_OK if successful
91 */
92 int rc4_ready(prng_state *prng)
93 {
94    unsigned char buf[256] = { 0 };
95    unsigned long len;
96    int err, i;
97
98    LTC_ARGCHK(prng != NULL);
99
100    LTC_MUTEX_LOCK(&prng->lock);
101    if (prng->ready) { err = CRYPT_OK; goto LBL_UNLOCK; }
102    XMEMCPY(buf, prng->rc4.s.buf, sizeof(buf));
103    /* initialize RC4 */
104    len = MIN(prng->rc4.s.x, 256); /* TODO: we can perhaps always use all 256 bytes */
105    if ((err = rc4_stream_setup(&prng->rc4.s, buf, len)) != CRYPT_OK) goto LBL_UNLOCK;
106    /* drop first 3072 bytes - https://en.wikipedia.org/wiki/RC4#Fluhrer.2C_Mantin_and_Shamir_attack */
107    for (i = 0; i < 12; i++) rc4_stream_keystream(&prng->rc4.s, buf, sizeof(buf));
108    prng->ready = 1;
109 LBL_UNLOCK:
110    LTC_MUTEX_UNLOCK(&prng->lock);
111    return err;
112 }
113
114 /**
115   Read from the PRNG
116   @param out      Destination
117   @param outlen   Length of output
118   @param prng     The active PRNG to read from
119   @return Number of octets read
120 */
121 unsigned long rc4_read(unsigned char *out, unsigned long outlen, prng_state *prng)
122 {
123    if (outlen == 0 || prng == NULL || out == NULL) return 0;
124    LTC_MUTEX_LOCK(&prng->lock);
125    if (!prng->ready) { outlen = 0; goto LBL_UNLOCK; }
126    if (rc4_stream_keystream(&prng->rc4.s, out, outlen) != CRYPT_OK) outlen = 0;
127 LBL_UNLOCK:
128    LTC_MUTEX_UNLOCK(&prng->lock);
129    return outlen;
130 }
131
132 /**
133   Terminate the PRNG
134   @param prng   The PRNG to terminate
135   @return CRYPT_OK if successful
136 */
137 int rc4_done(prng_state *prng)
138 {
139    int err;
140    LTC_ARGCHK(prng != NULL);
141    LTC_MUTEX_LOCK(&prng->lock);
142    prng->ready = 0;
143    err = rc4_stream_done(&prng->rc4.s);
144    LTC_MUTEX_UNLOCK(&prng->lock);
145    LTC_MUTEX_DESTROY(&prng->lock);
146    return err;
147 }
148
149 /**
150   Export the PRNG state
151   @param out       [out] Destination
152   @param outlen    [in/out] Max size and resulting size of the state
153   @param prng      The PRNG to export
154   @return CRYPT_OK if successful
155 */
156 int rc4_export(unsigned char *out, unsigned long *outlen, prng_state *prng)
157 {
158    unsigned long len = rc4_desc.export_size;
159
160    LTC_ARGCHK(prng   != NULL);
161    LTC_ARGCHK(out    != NULL);
162    LTC_ARGCHK(outlen != NULL);
163
164    if (*outlen < len) {
165       *outlen = len;
166       return CRYPT_BUFFER_OVERFLOW;
167    }
168
169    if (rc4_read(out, len, prng) != len) {
170       return CRYPT_ERROR_READPRNG;
171    }
172
173    *outlen = len;
174    return CRYPT_OK;
175 }
176
177 /**
178   Import a PRNG state
179   @param in       The PRNG state
180   @param inlen    Size of the state
181   @param prng     The PRNG to import
182   @return CRYPT_OK if successful
183 */
184 int rc4_import(const unsigned char *in, unsigned long inlen, prng_state *prng)
185 {
186    int err;
187
188    LTC_ARGCHK(prng != NULL);
189    LTC_ARGCHK(in   != NULL);
190    if (inlen < (unsigned long)rc4_desc.export_size) return CRYPT_INVALID_ARG;
191
192    if ((err = rc4_start(prng)) != CRYPT_OK)                  return err;
193    if ((err = rc4_add_entropy(in, inlen, prng)) != CRYPT_OK) return err;
194    return CRYPT_OK;
195 }
196
197 /**
198   PRNG self-test
199   @return CRYPT_OK if successful, CRYPT_NOP if self-testing has been disabled
200 */
201 int rc4_test(void)
202 {
203 #ifndef LTC_TEST
204    return CRYPT_NOP;
205 #else
206    prng_state st;
207    unsigned char en[] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a,
208                           0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14,
209                           0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e,
210                           0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28,
211                           0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32 };
212    unsigned char dmp[500];
213    unsigned long dmplen = sizeof(dmp);
214    unsigned char out[1000];
215    unsigned char t1[] = { 0xE0, 0x4D, 0x9A, 0xF6, 0xA8, 0x9D, 0x77, 0x53, 0xAE, 0x09 };
216    unsigned char t2[] = { 0xEF, 0x80, 0xA2, 0xE6, 0x50, 0x91, 0xF3, 0x17, 0x4A, 0x8A };
217    unsigned char t3[] = { 0x4B, 0xD6, 0x5C, 0x67, 0x99, 0x03, 0x56, 0x12, 0x80, 0x48 };
218    int err;
219
220    if ((err = rc4_start(&st)) != CRYPT_OK)                         return err;
221    /* add entropy to uninitialized prng */
222    if ((err = rc4_add_entropy(en, sizeof(en), &st)) != CRYPT_OK)   return err;
223    if ((err = rc4_ready(&st)) != CRYPT_OK)                         return err;
224    if (rc4_read(out, 10, &st) != 10)                               return CRYPT_ERROR_READPRNG; /* 10 bytes for testing */
225    if (compare_testvector(out, 10, t1, sizeof(t1), "RC4-PRNG", 1)) return CRYPT_FAIL_TESTVECTOR;
226    if (rc4_read(out, 500, &st) != 500)                             return CRYPT_ERROR_READPRNG; /* skip 500 bytes */
227    /* add entropy to already initialized prng */
228    if ((err = rc4_add_entropy(en, sizeof(en), &st)) != CRYPT_OK)   return err;
229    if (rc4_read(out, 500, &st) != 500)                             return CRYPT_ERROR_READPRNG; /* skip 500 bytes */
230    if ((err = rc4_export(dmp, &dmplen, &st)) != CRYPT_OK)          return err;
231    if (rc4_read(out, 500, &st) != 500)                             return CRYPT_ERROR_READPRNG; /* skip 500 bytes */
232    if (rc4_read(out, 10, &st) != 10)                               return CRYPT_ERROR_READPRNG; /* 10 bytes for testing */
233    if (compare_testvector(out, 10, t2, sizeof(t2), "RC4-PRNG", 2)) return CRYPT_FAIL_TESTVECTOR;
234    if ((err = rc4_done(&st)) != CRYPT_OK)                          return err;
235    if ((err = rc4_import(dmp, dmplen, &st)) != CRYPT_OK)           return err;
236    if ((err = rc4_ready(&st)) != CRYPT_OK)                         return err;
237    if (rc4_read(out, 500, &st) != 500)                             return CRYPT_ERROR_READPRNG; /* skip 500 bytes */
238    if (rc4_read(out, 10, &st) != 10)                               return CRYPT_ERROR_READPRNG; /* 10 bytes for testing */
239    if (compare_testvector(out, 10, t3, sizeof(t3), "RC4-PRNG", 3)) return CRYPT_FAIL_TESTVECTOR;
240    if ((err = rc4_done(&st)) != CRYPT_OK)                          return err;
241
242    return CRYPT_OK;
243 #endif
244 }
245
246 #endif
247
248 /* ref:         $Format:%D$ */
249 /* git commit:  $Format:%H$ */
250 /* commit time: $Format:%ai$ */