1*8411e6adSJerome Forissier /* LibTomCrypt, modular cryptographic library -- Tom St Denis */
2*8411e6adSJerome Forissier /* SPDX-License-Identifier: Unlicense */
35a913ee7SJerome Forissier
45a913ee7SJerome Forissier /* the idea of re-keying loosely follows the approach used in:
55a913ee7SJerome Forissier * http://bxr.su/OpenBSD/lib/libc/crypt/arc4random.c
65a913ee7SJerome Forissier */
75a913ee7SJerome Forissier
85a913ee7SJerome Forissier #include "tomcrypt_private.h"
95a913ee7SJerome Forissier
105a913ee7SJerome Forissier #ifdef LTC_CHACHA20_PRNG
115a913ee7SJerome Forissier
125a913ee7SJerome Forissier const struct ltc_prng_descriptor chacha20_prng_desc =
135a913ee7SJerome Forissier {
145a913ee7SJerome Forissier "chacha20",
155a913ee7SJerome Forissier 40,
165a913ee7SJerome Forissier &chacha20_prng_start,
175a913ee7SJerome Forissier &chacha20_prng_add_entropy,
185a913ee7SJerome Forissier &chacha20_prng_ready,
195a913ee7SJerome Forissier &chacha20_prng_read,
205a913ee7SJerome Forissier &chacha20_prng_done,
215a913ee7SJerome Forissier &chacha20_prng_export,
225a913ee7SJerome Forissier &chacha20_prng_import,
235a913ee7SJerome Forissier &chacha20_prng_test
245a913ee7SJerome Forissier };
255a913ee7SJerome Forissier
265a913ee7SJerome Forissier /**
275a913ee7SJerome Forissier Start the PRNG
285a913ee7SJerome Forissier @param prng The PRNG state to initialize
295a913ee7SJerome Forissier @return CRYPT_OK if successful
305a913ee7SJerome Forissier */
chacha20_prng_start(prng_state * prng)315a913ee7SJerome Forissier int chacha20_prng_start(prng_state *prng)
325a913ee7SJerome Forissier {
335a913ee7SJerome Forissier LTC_ARGCHK(prng != NULL);
345a913ee7SJerome Forissier prng->ready = 0;
355a913ee7SJerome Forissier XMEMSET(&prng->u.chacha.ent, 0, sizeof(prng->u.chacha.ent));
365a913ee7SJerome Forissier prng->u.chacha.idx = 0;
375a913ee7SJerome Forissier LTC_MUTEX_INIT(&prng->lock)
385a913ee7SJerome Forissier return CRYPT_OK;
395a913ee7SJerome Forissier }
405a913ee7SJerome Forissier
415a913ee7SJerome Forissier /**
425a913ee7SJerome Forissier Add entropy to the PRNG state
435a913ee7SJerome Forissier @param in The data to add
445a913ee7SJerome Forissier @param inlen Length of the data to add
455a913ee7SJerome Forissier @param prng PRNG state to update
465a913ee7SJerome Forissier @return CRYPT_OK if successful
475a913ee7SJerome Forissier */
chacha20_prng_add_entropy(const unsigned char * in,unsigned long inlen,prng_state * prng)485a913ee7SJerome Forissier int chacha20_prng_add_entropy(const unsigned char *in, unsigned long inlen, prng_state *prng)
495a913ee7SJerome Forissier {
505a913ee7SJerome Forissier unsigned char buf[40];
515a913ee7SJerome Forissier unsigned long i;
525a913ee7SJerome Forissier int err;
535a913ee7SJerome Forissier
545a913ee7SJerome Forissier LTC_ARGCHK(prng != NULL);
555a913ee7SJerome Forissier LTC_ARGCHK(in != NULL);
565a913ee7SJerome Forissier LTC_ARGCHK(inlen > 0);
575a913ee7SJerome Forissier
585a913ee7SJerome Forissier LTC_MUTEX_LOCK(&prng->lock);
595a913ee7SJerome Forissier if (prng->ready) {
605a913ee7SJerome Forissier /* chacha20_prng_ready() was already called, do "rekey" operation */
615a913ee7SJerome Forissier if ((err = chacha_keystream(&prng->u.chacha.s, buf, sizeof(buf))) != CRYPT_OK) goto LBL_UNLOCK;
625a913ee7SJerome Forissier for(i = 0; i < inlen; i++) buf[i % sizeof(buf)] ^= in[i];
635a913ee7SJerome Forissier /* key 32 bytes, 20 rounds */
645a913ee7SJerome Forissier if ((err = chacha_setup(&prng->u.chacha.s, buf, 32, 20)) != CRYPT_OK) goto LBL_UNLOCK;
655a913ee7SJerome Forissier /* iv 8 bytes */
665a913ee7SJerome Forissier if ((err = chacha_ivctr64(&prng->u.chacha.s, buf + 32, 8, 0)) != CRYPT_OK) goto LBL_UNLOCK;
675a913ee7SJerome Forissier /* clear KEY + IV */
685a913ee7SJerome Forissier zeromem(buf, sizeof(buf));
695a913ee7SJerome Forissier }
705a913ee7SJerome Forissier else {
715a913ee7SJerome Forissier /* chacha20_prng_ready() was not called yet, add entropy to ent buffer */
725a913ee7SJerome Forissier while (inlen--) prng->u.chacha.ent[prng->u.chacha.idx++ % sizeof(prng->u.chacha.ent)] ^= *in++;
735a913ee7SJerome Forissier }
745a913ee7SJerome Forissier err = CRYPT_OK;
755a913ee7SJerome Forissier LBL_UNLOCK:
765a913ee7SJerome Forissier LTC_MUTEX_UNLOCK(&prng->lock);
775a913ee7SJerome Forissier return err;
785a913ee7SJerome Forissier }
795a913ee7SJerome Forissier
805a913ee7SJerome Forissier /**
815a913ee7SJerome Forissier Make the PRNG ready to read from
825a913ee7SJerome Forissier @param prng The PRNG to make active
835a913ee7SJerome Forissier @return CRYPT_OK if successful
845a913ee7SJerome Forissier */
chacha20_prng_ready(prng_state * prng)855a913ee7SJerome Forissier int chacha20_prng_ready(prng_state *prng)
865a913ee7SJerome Forissier {
875a913ee7SJerome Forissier int err;
885a913ee7SJerome Forissier
895a913ee7SJerome Forissier LTC_ARGCHK(prng != NULL);
905a913ee7SJerome Forissier
915a913ee7SJerome Forissier LTC_MUTEX_LOCK(&prng->lock);
925a913ee7SJerome Forissier if (prng->ready) { err = CRYPT_OK; goto LBL_UNLOCK; }
935a913ee7SJerome Forissier /* key 32 bytes, 20 rounds */
945a913ee7SJerome Forissier if ((err = chacha_setup(&prng->u.chacha.s, prng->u.chacha.ent, 32, 20)) != CRYPT_OK) goto LBL_UNLOCK;
955a913ee7SJerome Forissier /* iv 8 bytes */
965a913ee7SJerome Forissier if ((err = chacha_ivctr64(&prng->u.chacha.s, prng->u.chacha.ent + 32, 8, 0)) != CRYPT_OK) goto LBL_UNLOCK;
975a913ee7SJerome Forissier XMEMSET(&prng->u.chacha.ent, 0, sizeof(prng->u.chacha.ent));
985a913ee7SJerome Forissier prng->u.chacha.idx = 0;
995a913ee7SJerome Forissier prng->ready = 1;
1005a913ee7SJerome Forissier LBL_UNLOCK:
1015a913ee7SJerome Forissier LTC_MUTEX_UNLOCK(&prng->lock);
1025a913ee7SJerome Forissier return err;
1035a913ee7SJerome Forissier }
1045a913ee7SJerome Forissier
1055a913ee7SJerome Forissier /**
1065a913ee7SJerome Forissier Read from the PRNG
1075a913ee7SJerome Forissier @param out Destination
1085a913ee7SJerome Forissier @param outlen Length of output
1095a913ee7SJerome Forissier @param prng The active PRNG to read from
1105a913ee7SJerome Forissier @return Number of octets read
1115a913ee7SJerome Forissier */
chacha20_prng_read(unsigned char * out,unsigned long outlen,prng_state * prng)1125a913ee7SJerome Forissier unsigned long chacha20_prng_read(unsigned char *out, unsigned long outlen, prng_state *prng)
1135a913ee7SJerome Forissier {
1145a913ee7SJerome Forissier if (outlen == 0 || prng == NULL || out == NULL) return 0;
1155a913ee7SJerome Forissier LTC_MUTEX_LOCK(&prng->lock);
1165a913ee7SJerome Forissier if (!prng->ready) { outlen = 0; goto LBL_UNLOCK; }
1175a913ee7SJerome Forissier if (chacha_keystream(&prng->u.chacha.s, out, outlen) != CRYPT_OK) outlen = 0;
1185a913ee7SJerome Forissier LBL_UNLOCK:
1195a913ee7SJerome Forissier LTC_MUTEX_UNLOCK(&prng->lock);
1205a913ee7SJerome Forissier return outlen;
1215a913ee7SJerome Forissier }
1225a913ee7SJerome Forissier
1235a913ee7SJerome Forissier /**
1245a913ee7SJerome Forissier Terminate the PRNG
1255a913ee7SJerome Forissier @param prng The PRNG to terminate
1265a913ee7SJerome Forissier @return CRYPT_OK if successful
1275a913ee7SJerome Forissier */
chacha20_prng_done(prng_state * prng)1285a913ee7SJerome Forissier int chacha20_prng_done(prng_state *prng)
1295a913ee7SJerome Forissier {
1305a913ee7SJerome Forissier int err;
1315a913ee7SJerome Forissier LTC_ARGCHK(prng != NULL);
1325a913ee7SJerome Forissier LTC_MUTEX_LOCK(&prng->lock);
1335a913ee7SJerome Forissier prng->ready = 0;
1345a913ee7SJerome Forissier err = chacha_done(&prng->u.chacha.s);
1355a913ee7SJerome Forissier LTC_MUTEX_UNLOCK(&prng->lock);
1365a913ee7SJerome Forissier LTC_MUTEX_DESTROY(&prng->lock);
1375a913ee7SJerome Forissier return err;
1385a913ee7SJerome Forissier }
1395a913ee7SJerome Forissier
1405a913ee7SJerome Forissier /**
1415a913ee7SJerome Forissier Export the PRNG state
1425a913ee7SJerome Forissier @param out [out] Destination
1435a913ee7SJerome Forissier @param outlen [in/out] Max size and resulting size of the state
1445a913ee7SJerome Forissier @param prng The PRNG to export
1455a913ee7SJerome Forissier @return CRYPT_OK if successful
1465a913ee7SJerome Forissier */
LTC_PRNG_EXPORT(chacha20_prng)147*8411e6adSJerome Forissier LTC_PRNG_EXPORT(chacha20_prng)
1485a913ee7SJerome Forissier
1495a913ee7SJerome Forissier /**
1505a913ee7SJerome Forissier Import a PRNG state
1515a913ee7SJerome Forissier @param in The PRNG state
1525a913ee7SJerome Forissier @param inlen Size of the state
1535a913ee7SJerome Forissier @param prng The PRNG to import
1545a913ee7SJerome Forissier @return CRYPT_OK if successful
1555a913ee7SJerome Forissier */
1565a913ee7SJerome Forissier int chacha20_prng_import(const unsigned char *in, unsigned long inlen, prng_state *prng)
1575a913ee7SJerome Forissier {
1585a913ee7SJerome Forissier int err;
1595a913ee7SJerome Forissier
1605a913ee7SJerome Forissier LTC_ARGCHK(prng != NULL);
1615a913ee7SJerome Forissier LTC_ARGCHK(in != NULL);
1625a913ee7SJerome Forissier if (inlen < (unsigned long)chacha20_prng_desc.export_size) return CRYPT_INVALID_ARG;
1635a913ee7SJerome Forissier
1645a913ee7SJerome Forissier if ((err = chacha20_prng_start(prng)) != CRYPT_OK) return err;
1655a913ee7SJerome Forissier if ((err = chacha20_prng_add_entropy(in, inlen, prng)) != CRYPT_OK) return err;
1665a913ee7SJerome Forissier return CRYPT_OK;
1675a913ee7SJerome Forissier }
1685a913ee7SJerome Forissier
1695a913ee7SJerome Forissier /**
1705a913ee7SJerome Forissier PRNG self-test
1715a913ee7SJerome Forissier @return CRYPT_OK if successful, CRYPT_NOP if self-testing has been disabled
1725a913ee7SJerome Forissier */
chacha20_prng_test(void)1735a913ee7SJerome Forissier int chacha20_prng_test(void)
1745a913ee7SJerome Forissier {
1755a913ee7SJerome Forissier #ifndef LTC_TEST
1765a913ee7SJerome Forissier return CRYPT_NOP;
1775a913ee7SJerome Forissier #else
1785a913ee7SJerome Forissier prng_state st;
1795a913ee7SJerome Forissier unsigned char en[] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a,
1805a913ee7SJerome Forissier 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14,
1815a913ee7SJerome Forissier 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e,
1825a913ee7SJerome Forissier 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28,
1835a913ee7SJerome Forissier 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32 };
1845a913ee7SJerome Forissier unsigned char dmp[300];
1855a913ee7SJerome Forissier unsigned long dmplen = sizeof(dmp);
1865a913ee7SJerome Forissier unsigned char out[500];
1875a913ee7SJerome Forissier unsigned char t1[] = { 0x59, 0xB2, 0x26, 0x95, 0x2B, 0x01, 0x8F, 0x05, 0xBE, 0xD8 };
1885a913ee7SJerome Forissier unsigned char t2[] = { 0x47, 0xC9, 0x0D, 0x03, 0xE4, 0x75, 0x34, 0x27, 0xBD, 0xDE };
1895a913ee7SJerome Forissier unsigned char t3[] = { 0xBC, 0xFA, 0xEF, 0x59, 0x37, 0x7F, 0x1A, 0x91, 0x1A, 0xA6 };
1905a913ee7SJerome Forissier int err;
1915a913ee7SJerome Forissier
1925a913ee7SJerome Forissier if ((err = chacha20_prng_start(&st)) != CRYPT_OK) return err;
1935a913ee7SJerome Forissier /* add entropy to uninitialized prng */
1945a913ee7SJerome Forissier if ((err = chacha20_prng_add_entropy(en, sizeof(en), &st)) != CRYPT_OK) return err;
1955a913ee7SJerome Forissier if ((err = chacha20_prng_ready(&st)) != CRYPT_OK) return err;
1965a913ee7SJerome Forissier if (chacha20_prng_read(out, 10, &st) != 10) return CRYPT_ERROR_READPRNG; /* 10 bytes for testing */
1975a913ee7SJerome Forissier if (compare_testvector(out, 10, t1, sizeof(t1), "CHACHA-PRNG", 1)) return CRYPT_FAIL_TESTVECTOR;
1985a913ee7SJerome Forissier if (chacha20_prng_read(out, 500, &st) != 500) return CRYPT_ERROR_READPRNG; /* skip 500 bytes */
1995a913ee7SJerome Forissier /* add entropy to already initialized prng */
2005a913ee7SJerome Forissier if ((err = chacha20_prng_add_entropy(en, sizeof(en), &st)) != CRYPT_OK) return err;
2015a913ee7SJerome Forissier if (chacha20_prng_read(out, 500, &st) != 500) return CRYPT_ERROR_READPRNG; /* skip 500 bytes */
2025a913ee7SJerome Forissier if ((err = chacha20_prng_export(dmp, &dmplen, &st)) != CRYPT_OK) return err;
2035a913ee7SJerome Forissier if (chacha20_prng_read(out, 500, &st) != 500) return CRYPT_ERROR_READPRNG; /* skip 500 bytes */
2045a913ee7SJerome Forissier if (chacha20_prng_read(out, 10, &st) != 10) return CRYPT_ERROR_READPRNG; /* 10 bytes for testing */
2055a913ee7SJerome Forissier if (compare_testvector(out, 10, t2, sizeof(t2), "CHACHA-PRNG", 2)) return CRYPT_FAIL_TESTVECTOR;
2065a913ee7SJerome Forissier if ((err = chacha20_prng_done(&st)) != CRYPT_OK) return err;
2075a913ee7SJerome Forissier if ((err = chacha20_prng_import(dmp, dmplen, &st)) != CRYPT_OK) return err;
2085a913ee7SJerome Forissier if ((err = chacha20_prng_ready(&st)) != CRYPT_OK) return err;
2095a913ee7SJerome Forissier if (chacha20_prng_read(out, 500, &st) != 500) return CRYPT_ERROR_READPRNG; /* skip 500 bytes */
2105a913ee7SJerome Forissier if (chacha20_prng_read(out, 10, &st) != 10) return CRYPT_ERROR_READPRNG; /* 10 bytes for testing */
2115a913ee7SJerome Forissier if (compare_testvector(out, 10, t3, sizeof(t3), "CHACHA-PRNG", 3)) return CRYPT_FAIL_TESTVECTOR;
2125a913ee7SJerome Forissier if ((err = chacha20_prng_done(&st)) != CRYPT_OK) return err;
2135a913ee7SJerome Forissier
2145a913ee7SJerome Forissier return CRYPT_OK;
2155a913ee7SJerome Forissier #endif
2165a913ee7SJerome Forissier }
2175a913ee7SJerome Forissier
2185a913ee7SJerome Forissier #endif
219