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