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