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