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