1 /* LibTomCrypt, modular cryptographic library -- Tom St Denis */ 2 /* SPDX-License-Identifier: Unlicense */ 3 4 /* the idea of re-keying loosely follows the approach used in: 5 * http://bxr.su/OpenBSD/lib/libc/crypt/arc4random.c 6 */ 7 8 #include "tomcrypt_private.h" 9 10 #ifdef LTC_CHACHA20_PRNG 11 12 const struct ltc_prng_descriptor chacha20_prng_desc = 13 { 14 "chacha20", 15 40, 16 &chacha20_prng_start, 17 &chacha20_prng_add_entropy, 18 &chacha20_prng_ready, 19 &chacha20_prng_read, 20 &chacha20_prng_done, 21 &chacha20_prng_export, 22 &chacha20_prng_import, 23 &chacha20_prng_test 24 }; 25 26 /** 27 Start the PRNG 28 @param prng The PRNG state to initialize 29 @return CRYPT_OK if successful 30 */ 31 int chacha20_prng_start(prng_state *prng) 32 { 33 LTC_ARGCHK(prng != NULL); 34 prng->ready = 0; 35 XMEMSET(&prng->u.chacha.ent, 0, sizeof(prng->u.chacha.ent)); 36 prng->u.chacha.idx = 0; 37 LTC_MUTEX_INIT(&prng->lock) 38 return CRYPT_OK; 39 } 40 41 /** 42 Add entropy to the PRNG state 43 @param in The data to add 44 @param inlen Length of the data to add 45 @param prng PRNG state to update 46 @return CRYPT_OK if successful 47 */ 48 int chacha20_prng_add_entropy(const unsigned char *in, unsigned long inlen, prng_state *prng) 49 { 50 unsigned char buf[40]; 51 unsigned long i; 52 int err; 53 54 LTC_ARGCHK(prng != NULL); 55 LTC_ARGCHK(in != NULL); 56 LTC_ARGCHK(inlen > 0); 57 58 LTC_MUTEX_LOCK(&prng->lock); 59 if (prng->ready) { 60 /* chacha20_prng_ready() was already called, do "rekey" operation */ 61 if ((err = chacha_keystream(&prng->u.chacha.s, buf, sizeof(buf))) != CRYPT_OK) goto LBL_UNLOCK; 62 for(i = 0; i < inlen; i++) buf[i % sizeof(buf)] ^= in[i]; 63 /* key 32 bytes, 20 rounds */ 64 if ((err = chacha_setup(&prng->u.chacha.s, buf, 32, 20)) != CRYPT_OK) goto LBL_UNLOCK; 65 /* iv 8 bytes */ 66 if ((err = chacha_ivctr64(&prng->u.chacha.s, buf + 32, 8, 0)) != CRYPT_OK) goto LBL_UNLOCK; 67 /* clear KEY + IV */ 68 zeromem(buf, sizeof(buf)); 69 } 70 else { 71 /* chacha20_prng_ready() was not called yet, add entropy to ent buffer */ 72 while (inlen--) prng->u.chacha.ent[prng->u.chacha.idx++ % sizeof(prng->u.chacha.ent)] ^= *in++; 73 } 74 err = CRYPT_OK; 75 LBL_UNLOCK: 76 LTC_MUTEX_UNLOCK(&prng->lock); 77 return err; 78 } 79 80 /** 81 Make the PRNG ready to read from 82 @param prng The PRNG to make active 83 @return CRYPT_OK if successful 84 */ 85 int chacha20_prng_ready(prng_state *prng) 86 { 87 int err; 88 89 LTC_ARGCHK(prng != NULL); 90 91 LTC_MUTEX_LOCK(&prng->lock); 92 if (prng->ready) { err = CRYPT_OK; goto LBL_UNLOCK; } 93 /* key 32 bytes, 20 rounds */ 94 if ((err = chacha_setup(&prng->u.chacha.s, prng->u.chacha.ent, 32, 20)) != CRYPT_OK) goto LBL_UNLOCK; 95 /* iv 8 bytes */ 96 if ((err = chacha_ivctr64(&prng->u.chacha.s, prng->u.chacha.ent + 32, 8, 0)) != CRYPT_OK) goto LBL_UNLOCK; 97 XMEMSET(&prng->u.chacha.ent, 0, sizeof(prng->u.chacha.ent)); 98 prng->u.chacha.idx = 0; 99 prng->ready = 1; 100 LBL_UNLOCK: 101 LTC_MUTEX_UNLOCK(&prng->lock); 102 return err; 103 } 104 105 /** 106 Read from the PRNG 107 @param out Destination 108 @param outlen Length of output 109 @param prng The active PRNG to read from 110 @return Number of octets read 111 */ 112 unsigned long chacha20_prng_read(unsigned char *out, unsigned long outlen, prng_state *prng) 113 { 114 if (outlen == 0 || prng == NULL || out == NULL) return 0; 115 LTC_MUTEX_LOCK(&prng->lock); 116 if (!prng->ready) { outlen = 0; goto LBL_UNLOCK; } 117 if (chacha_keystream(&prng->u.chacha.s, out, outlen) != CRYPT_OK) outlen = 0; 118 LBL_UNLOCK: 119 LTC_MUTEX_UNLOCK(&prng->lock); 120 return outlen; 121 } 122 123 /** 124 Terminate the PRNG 125 @param prng The PRNG to terminate 126 @return CRYPT_OK if successful 127 */ 128 int chacha20_prng_done(prng_state *prng) 129 { 130 int err; 131 LTC_ARGCHK(prng != NULL); 132 LTC_MUTEX_LOCK(&prng->lock); 133 prng->ready = 0; 134 err = chacha_done(&prng->u.chacha.s); 135 LTC_MUTEX_UNLOCK(&prng->lock); 136 LTC_MUTEX_DESTROY(&prng->lock); 137 return err; 138 } 139 140 /** 141 Export the PRNG state 142 @param out [out] Destination 143 @param outlen [in/out] Max size and resulting size of the state 144 @param prng The PRNG to export 145 @return CRYPT_OK if successful 146 */ 147 LTC_PRNG_EXPORT(chacha20_prng) 148 149 /** 150 Import a PRNG state 151 @param in The PRNG state 152 @param inlen Size of the state 153 @param prng The PRNG to import 154 @return CRYPT_OK if successful 155 */ 156 int chacha20_prng_import(const unsigned char *in, unsigned long inlen, prng_state *prng) 157 { 158 int err; 159 160 LTC_ARGCHK(prng != NULL); 161 LTC_ARGCHK(in != NULL); 162 if (inlen < (unsigned long)chacha20_prng_desc.export_size) return CRYPT_INVALID_ARG; 163 164 if ((err = chacha20_prng_start(prng)) != CRYPT_OK) return err; 165 if ((err = chacha20_prng_add_entropy(in, inlen, prng)) != CRYPT_OK) return err; 166 return CRYPT_OK; 167 } 168 169 /** 170 PRNG self-test 171 @return CRYPT_OK if successful, CRYPT_NOP if self-testing has been disabled 172 */ 173 int chacha20_prng_test(void) 174 { 175 #ifndef LTC_TEST 176 return CRYPT_NOP; 177 #else 178 prng_state st; 179 unsigned char en[] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 180 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 181 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 182 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 183 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32 }; 184 unsigned char dmp[300]; 185 unsigned long dmplen = sizeof(dmp); 186 unsigned char out[500]; 187 unsigned char t1[] = { 0x59, 0xB2, 0x26, 0x95, 0x2B, 0x01, 0x8F, 0x05, 0xBE, 0xD8 }; 188 unsigned char t2[] = { 0x47, 0xC9, 0x0D, 0x03, 0xE4, 0x75, 0x34, 0x27, 0xBD, 0xDE }; 189 unsigned char t3[] = { 0xBC, 0xFA, 0xEF, 0x59, 0x37, 0x7F, 0x1A, 0x91, 0x1A, 0xA6 }; 190 int err; 191 192 if ((err = chacha20_prng_start(&st)) != CRYPT_OK) return err; 193 /* add entropy to uninitialized prng */ 194 if ((err = chacha20_prng_add_entropy(en, sizeof(en), &st)) != CRYPT_OK) return err; 195 if ((err = chacha20_prng_ready(&st)) != CRYPT_OK) return err; 196 if (chacha20_prng_read(out, 10, &st) != 10) return CRYPT_ERROR_READPRNG; /* 10 bytes for testing */ 197 if (compare_testvector(out, 10, t1, sizeof(t1), "CHACHA-PRNG", 1)) return CRYPT_FAIL_TESTVECTOR; 198 if (chacha20_prng_read(out, 500, &st) != 500) return CRYPT_ERROR_READPRNG; /* skip 500 bytes */ 199 /* add entropy to already initialized prng */ 200 if ((err = chacha20_prng_add_entropy(en, sizeof(en), &st)) != CRYPT_OK) return err; 201 if (chacha20_prng_read(out, 500, &st) != 500) return CRYPT_ERROR_READPRNG; /* skip 500 bytes */ 202 if ((err = chacha20_prng_export(dmp, &dmplen, &st)) != CRYPT_OK) return err; 203 if (chacha20_prng_read(out, 500, &st) != 500) return CRYPT_ERROR_READPRNG; /* skip 500 bytes */ 204 if (chacha20_prng_read(out, 10, &st) != 10) return CRYPT_ERROR_READPRNG; /* 10 bytes for testing */ 205 if (compare_testvector(out, 10, t2, sizeof(t2), "CHACHA-PRNG", 2)) return CRYPT_FAIL_TESTVECTOR; 206 if ((err = chacha20_prng_done(&st)) != CRYPT_OK) return err; 207 if ((err = chacha20_prng_import(dmp, dmplen, &st)) != CRYPT_OK) return err; 208 if ((err = chacha20_prng_ready(&st)) != CRYPT_OK) return err; 209 if (chacha20_prng_read(out, 500, &st) != 500) return CRYPT_ERROR_READPRNG; /* skip 500 bytes */ 210 if (chacha20_prng_read(out, 10, &st) != 10) return CRYPT_ERROR_READPRNG; /* 10 bytes for testing */ 211 if (compare_testvector(out, 10, t3, sizeof(t3), "CHACHA-PRNG", 3)) return CRYPT_FAIL_TESTVECTOR; 212 if ((err = chacha20_prng_done(&st)) != CRYPT_OK) return err; 213 214 return CRYPT_OK; 215 #endif 216 } 217 218 #endif 219