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