1*8411e6adSJerome Forissier /* LibTomCrypt, modular cryptographic library -- Tom St Denis */
2*8411e6adSJerome Forissier /* SPDX-License-Identifier: Unlicense */
35a913ee7SJerome Forissier #include "tomcrypt_private.h"
47018ae01SSY Chiu
57018ae01SSY Chiu /**
65a913ee7SJerome Forissier @file prngs/rc4.c
75a913ee7SJerome Forissier RC4 PRNG, Tom St Denis
87018ae01SSY Chiu */
97018ae01SSY Chiu
107018ae01SSY Chiu #ifdef LTC_RC4
117018ae01SSY Chiu
127018ae01SSY Chiu const struct ltc_prng_descriptor rc4_desc =
137018ae01SSY Chiu {
145a913ee7SJerome Forissier "rc4",
155a913ee7SJerome Forissier 32,
167018ae01SSY Chiu &rc4_start,
177018ae01SSY Chiu &rc4_add_entropy,
187018ae01SSY Chiu &rc4_ready,
197018ae01SSY Chiu &rc4_read,
207018ae01SSY Chiu &rc4_done,
217018ae01SSY Chiu &rc4_export,
227018ae01SSY Chiu &rc4_import,
237018ae01SSY Chiu &rc4_test
247018ae01SSY Chiu };
257018ae01SSY Chiu
267018ae01SSY Chiu /**
277018ae01SSY Chiu Start the PRNG
287018ae01SSY Chiu @param prng [out] The PRNG state to initialize
297018ae01SSY Chiu @return CRYPT_OK if successful
307018ae01SSY Chiu */
rc4_start(prng_state * prng)317018ae01SSY Chiu int rc4_start(prng_state *prng)
327018ae01SSY Chiu {
337018ae01SSY Chiu LTC_ARGCHK(prng != NULL);
345a913ee7SJerome Forissier prng->ready = 0;
355a913ee7SJerome Forissier /* set entropy (key) size to zero */
365a913ee7SJerome Forissier prng->u.rc4.s.x = 0;
375a913ee7SJerome Forissier /* clear entropy (key) buffer */
385a913ee7SJerome Forissier XMEMSET(&prng->u.rc4.s.buf, 0, sizeof(prng->u.rc4.s.buf));
395a913ee7SJerome Forissier LTC_MUTEX_INIT(&prng->lock)
407018ae01SSY Chiu return CRYPT_OK;
417018ae01SSY Chiu }
427018ae01SSY Chiu
437018ae01SSY Chiu /**
447018ae01SSY Chiu Add entropy to the PRNG state
457018ae01SSY Chiu @param in The data to add
467018ae01SSY Chiu @param inlen Length of the data to add
477018ae01SSY Chiu @param prng PRNG state to update
487018ae01SSY Chiu @return CRYPT_OK if successful
497018ae01SSY Chiu */
rc4_add_entropy(const unsigned char * in,unsigned long inlen,prng_state * prng)507018ae01SSY Chiu int rc4_add_entropy(const unsigned char *in, unsigned long inlen, prng_state *prng)
517018ae01SSY Chiu {
525a913ee7SJerome Forissier unsigned char buf[256];
535a913ee7SJerome Forissier unsigned long i;
545a913ee7SJerome Forissier int err;
555a913ee7SJerome Forissier
567018ae01SSY Chiu LTC_ARGCHK(prng != NULL);
575a913ee7SJerome Forissier LTC_ARGCHK(in != NULL);
585a913ee7SJerome Forissier LTC_ARGCHK(inlen > 0);
597018ae01SSY Chiu
605a913ee7SJerome Forissier LTC_MUTEX_LOCK(&prng->lock);
615a913ee7SJerome Forissier if (prng->ready) {
625a913ee7SJerome Forissier /* rc4_ready() was already called, do "rekey" operation */
635a913ee7SJerome Forissier if ((err = rc4_stream_keystream(&prng->u.rc4.s, buf, sizeof(buf))) != CRYPT_OK) goto LBL_UNLOCK;
645a913ee7SJerome Forissier for(i = 0; i < inlen; i++) buf[i % sizeof(buf)] ^= in[i];
655a913ee7SJerome Forissier /* initialize RC4 */
665a913ee7SJerome Forissier if ((err = rc4_stream_setup(&prng->u.rc4.s, buf, sizeof(buf))) != CRYPT_OK) goto LBL_UNLOCK;
675a913ee7SJerome Forissier /* drop first 3072 bytes - https://en.wikipedia.org/wiki/RC4#Fluhrer.2C_Mantin_and_Shamir_attack */
685a913ee7SJerome Forissier for (i = 0; i < 12; i++) rc4_stream_keystream(&prng->u.rc4.s, buf, sizeof(buf));
695a913ee7SJerome Forissier zeromem(buf, sizeof(buf));
707018ae01SSY Chiu }
715a913ee7SJerome Forissier else {
725a913ee7SJerome Forissier /* rc4_ready() was not called yet, add entropy to the buffer */
735a913ee7SJerome Forissier while (inlen--) prng->u.rc4.s.buf[prng->u.rc4.s.x++ % sizeof(prng->u.rc4.s.buf)] ^= *in++;
747018ae01SSY Chiu }
755a913ee7SJerome Forissier err = CRYPT_OK;
765a913ee7SJerome Forissier LBL_UNLOCK:
775a913ee7SJerome Forissier LTC_MUTEX_UNLOCK(&prng->lock);
785a913ee7SJerome Forissier return err;
797018ae01SSY Chiu }
807018ae01SSY Chiu
817018ae01SSY Chiu /**
827018ae01SSY Chiu Make the PRNG ready to read from
837018ae01SSY Chiu @param prng The PRNG to make active
847018ae01SSY Chiu @return CRYPT_OK if successful
857018ae01SSY Chiu */
rc4_ready(prng_state * prng)867018ae01SSY Chiu int rc4_ready(prng_state *prng)
877018ae01SSY Chiu {
885a913ee7SJerome Forissier unsigned char buf[256] = { 0 };
895a913ee7SJerome Forissier unsigned long len;
905a913ee7SJerome Forissier int err, i;
917018ae01SSY Chiu
927018ae01SSY Chiu LTC_ARGCHK(prng != NULL);
937018ae01SSY Chiu
945a913ee7SJerome Forissier LTC_MUTEX_LOCK(&prng->lock);
955a913ee7SJerome Forissier if (prng->ready) { err = CRYPT_OK; goto LBL_UNLOCK; }
965a913ee7SJerome Forissier XMEMCPY(buf, prng->u.rc4.s.buf, sizeof(buf));
975a913ee7SJerome Forissier /* initialize RC4 */
985a913ee7SJerome Forissier len = MIN(prng->u.rc4.s.x, 256); /* TODO: we can perhaps always use all 256 bytes */
995a913ee7SJerome Forissier if ((err = rc4_stream_setup(&prng->u.rc4.s, buf, len)) != CRYPT_OK) goto LBL_UNLOCK;
1005a913ee7SJerome Forissier /* drop first 3072 bytes - https://en.wikipedia.org/wiki/RC4#Fluhrer.2C_Mantin_and_Shamir_attack */
1015a913ee7SJerome Forissier for (i = 0; i < 12; i++) rc4_stream_keystream(&prng->u.rc4.s, buf, sizeof(buf));
1025a913ee7SJerome Forissier prng->ready = 1;
1035a913ee7SJerome Forissier LBL_UNLOCK:
1045a913ee7SJerome Forissier LTC_MUTEX_UNLOCK(&prng->lock);
1055a913ee7SJerome Forissier return err;
1067018ae01SSY Chiu }
1077018ae01SSY Chiu
1087018ae01SSY Chiu /**
1097018ae01SSY Chiu Read from the PRNG
1107018ae01SSY Chiu @param out Destination
1117018ae01SSY Chiu @param outlen Length of output
1127018ae01SSY Chiu @param prng The active PRNG to read from
1137018ae01SSY Chiu @return Number of octets read
1147018ae01SSY Chiu */
rc4_read(unsigned char * out,unsigned long outlen,prng_state * prng)1157018ae01SSY Chiu unsigned long rc4_read(unsigned char *out, unsigned long outlen, prng_state *prng)
1167018ae01SSY Chiu {
1175a913ee7SJerome Forissier if (outlen == 0 || prng == NULL || out == NULL) return 0;
1185a913ee7SJerome Forissier LTC_MUTEX_LOCK(&prng->lock);
1195a913ee7SJerome Forissier if (!prng->ready) { outlen = 0; goto LBL_UNLOCK; }
1205a913ee7SJerome Forissier if (rc4_stream_keystream(&prng->u.rc4.s, out, outlen) != CRYPT_OK) outlen = 0;
1215a913ee7SJerome Forissier LBL_UNLOCK:
1225a913ee7SJerome Forissier LTC_MUTEX_UNLOCK(&prng->lock);
1235a913ee7SJerome Forissier return outlen;
1247018ae01SSY Chiu }
1257018ae01SSY Chiu
1267018ae01SSY Chiu /**
1277018ae01SSY Chiu Terminate the PRNG
1287018ae01SSY Chiu @param prng The PRNG to terminate
1297018ae01SSY Chiu @return CRYPT_OK if successful
1307018ae01SSY Chiu */
rc4_done(prng_state * prng)1317018ae01SSY Chiu int rc4_done(prng_state *prng)
1327018ae01SSY Chiu {
1335a913ee7SJerome Forissier int err;
1347018ae01SSY Chiu LTC_ARGCHK(prng != NULL);
1355a913ee7SJerome Forissier LTC_MUTEX_LOCK(&prng->lock);
1365a913ee7SJerome Forissier prng->ready = 0;
1375a913ee7SJerome Forissier err = rc4_stream_done(&prng->u.rc4.s);
1385a913ee7SJerome Forissier LTC_MUTEX_UNLOCK(&prng->lock);
1395a913ee7SJerome Forissier LTC_MUTEX_DESTROY(&prng->lock);
1405a913ee7SJerome Forissier return err;
1417018ae01SSY Chiu }
1427018ae01SSY Chiu
1437018ae01SSY Chiu /**
1447018ae01SSY Chiu Export the PRNG state
1457018ae01SSY Chiu @param out [out] Destination
1467018ae01SSY Chiu @param outlen [in/out] Max size and resulting size of the state
1477018ae01SSY Chiu @param prng The PRNG to export
1487018ae01SSY Chiu @return CRYPT_OK if successful
1497018ae01SSY Chiu */
LTC_PRNG_EXPORT(rc4)150*8411e6adSJerome Forissier LTC_PRNG_EXPORT(rc4)
1517018ae01SSY Chiu
1527018ae01SSY Chiu /**
1537018ae01SSY Chiu Import a PRNG state
1547018ae01SSY Chiu @param in The PRNG state
1557018ae01SSY Chiu @param inlen Size of the state
1567018ae01SSY Chiu @param prng The PRNG to import
1577018ae01SSY Chiu @return CRYPT_OK if successful
1587018ae01SSY Chiu */
1597018ae01SSY Chiu int rc4_import(const unsigned char *in, unsigned long inlen, prng_state *prng)
1607018ae01SSY Chiu {
1617018ae01SSY Chiu int err;
1625a913ee7SJerome Forissier
1637018ae01SSY Chiu LTC_ARGCHK(prng != NULL);
1645a913ee7SJerome Forissier LTC_ARGCHK(in != NULL);
1655a913ee7SJerome Forissier if (inlen < (unsigned long)rc4_desc.export_size) return CRYPT_INVALID_ARG;
1667018ae01SSY Chiu
1675a913ee7SJerome Forissier if ((err = rc4_start(prng)) != CRYPT_OK) return err;
1685a913ee7SJerome Forissier if ((err = rc4_add_entropy(in, inlen, prng)) != CRYPT_OK) return err;
1695a913ee7SJerome Forissier return CRYPT_OK;
1707018ae01SSY Chiu }
1717018ae01SSY Chiu
1727018ae01SSY Chiu /**
1737018ae01SSY Chiu PRNG self-test
1747018ae01SSY Chiu @return CRYPT_OK if successful, CRYPT_NOP if self-testing has been disabled
1757018ae01SSY Chiu */
rc4_test(void)1767018ae01SSY Chiu int rc4_test(void)
1777018ae01SSY Chiu {
1785a913ee7SJerome Forissier #ifndef LTC_TEST
1797018ae01SSY Chiu return CRYPT_NOP;
1807018ae01SSY Chiu #else
1815a913ee7SJerome Forissier prng_state st;
1825a913ee7SJerome Forissier unsigned char en[] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a,
1835a913ee7SJerome Forissier 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14,
1845a913ee7SJerome Forissier 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e,
1855a913ee7SJerome Forissier 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28,
1865a913ee7SJerome Forissier 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32 };
1875a913ee7SJerome Forissier unsigned char dmp[500];
1885a913ee7SJerome Forissier unsigned long dmplen = sizeof(dmp);
1895a913ee7SJerome Forissier unsigned char out[1000];
1905a913ee7SJerome Forissier unsigned char t1[] = { 0xE0, 0x4D, 0x9A, 0xF6, 0xA8, 0x9D, 0x77, 0x53, 0xAE, 0x09 };
1915a913ee7SJerome Forissier unsigned char t2[] = { 0xEF, 0x80, 0xA2, 0xE6, 0x50, 0x91, 0xF3, 0x17, 0x4A, 0x8A };
1925a913ee7SJerome Forissier unsigned char t3[] = { 0x4B, 0xD6, 0x5C, 0x67, 0x99, 0x03, 0x56, 0x12, 0x80, 0x48 };
1935a913ee7SJerome Forissier int err;
1947018ae01SSY Chiu
1955a913ee7SJerome Forissier if ((err = rc4_start(&st)) != CRYPT_OK) return err;
1965a913ee7SJerome Forissier /* add entropy to uninitialized prng */
1975a913ee7SJerome Forissier if ((err = rc4_add_entropy(en, sizeof(en), &st)) != CRYPT_OK) return err;
1985a913ee7SJerome Forissier if ((err = rc4_ready(&st)) != CRYPT_OK) return err;
1995a913ee7SJerome Forissier if (rc4_read(out, 10, &st) != 10) return CRYPT_ERROR_READPRNG; /* 10 bytes for testing */
2005a913ee7SJerome Forissier if (compare_testvector(out, 10, t1, sizeof(t1), "RC4-PRNG", 1)) return CRYPT_FAIL_TESTVECTOR;
2015a913ee7SJerome Forissier if (rc4_read(out, 500, &st) != 500) return CRYPT_ERROR_READPRNG; /* skip 500 bytes */
2025a913ee7SJerome Forissier /* add entropy to already initialized prng */
2035a913ee7SJerome Forissier if ((err = rc4_add_entropy(en, sizeof(en), &st)) != CRYPT_OK) return err;
2045a913ee7SJerome Forissier if (rc4_read(out, 500, &st) != 500) return CRYPT_ERROR_READPRNG; /* skip 500 bytes */
2055a913ee7SJerome Forissier if ((err = rc4_export(dmp, &dmplen, &st)) != CRYPT_OK) return err;
2065a913ee7SJerome Forissier if (rc4_read(out, 500, &st) != 500) return CRYPT_ERROR_READPRNG; /* skip 500 bytes */
2075a913ee7SJerome Forissier if (rc4_read(out, 10, &st) != 10) return CRYPT_ERROR_READPRNG; /* 10 bytes for testing */
2085a913ee7SJerome Forissier if (compare_testvector(out, 10, t2, sizeof(t2), "RC4-PRNG", 2)) return CRYPT_FAIL_TESTVECTOR;
2095a913ee7SJerome Forissier if ((err = rc4_done(&st)) != CRYPT_OK) return err;
2105a913ee7SJerome Forissier if ((err = rc4_import(dmp, dmplen, &st)) != CRYPT_OK) return err;
2115a913ee7SJerome Forissier if ((err = rc4_ready(&st)) != CRYPT_OK) return err;
2125a913ee7SJerome Forissier if (rc4_read(out, 500, &st) != 500) return CRYPT_ERROR_READPRNG; /* skip 500 bytes */
2135a913ee7SJerome Forissier if (rc4_read(out, 10, &st) != 10) return CRYPT_ERROR_READPRNG; /* 10 bytes for testing */
2145a913ee7SJerome Forissier if (compare_testvector(out, 10, t3, sizeof(t3), "RC4-PRNG", 3)) return CRYPT_FAIL_TESTVECTOR;
2155a913ee7SJerome Forissier if ((err = rc4_done(&st)) != CRYPT_OK) return err;
2165a913ee7SJerome Forissier
2177018ae01SSY Chiu return CRYPT_OK;
2187018ae01SSY Chiu #endif
2197018ae01SSY Chiu }
2207018ae01SSY Chiu
2217018ae01SSY Chiu #endif
222