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