xref: /optee_os/core/lib/libtomcrypt/src/prngs/rc4.c (revision 8411e6ad673d20c4742ed30c785e3f5cdea54dfa)
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