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