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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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