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 */
rc4_start(prng_state * prng)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 */
rc4_add_entropy(const unsigned char * in,unsigned long inlen,prng_state * prng)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 */
rc4_ready(prng_state * prng)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 */
rc4_read(unsigned char * out,unsigned long outlen,prng_state * prng)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 */
rc4_done(prng_state * prng)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 */
rc4_export(unsigned char * out,unsigned long * outlen,prng_state * prng)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 */
rc4_import(const unsigned char * in,unsigned long inlen,prng_state * prng)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 */
rc4_test(void)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: HEAD -> master, tag: v1.18.2 */
249 /* git commit: 7e7eb695d581782f04b24dc444cbfde86af59853 */
250 /* commit time: 2018-07-01 22:49:01 +0200 */
251