1 /* LibTomCrypt, modular cryptographic library -- Tom St Denis
3 * LibTomCrypt is a library that provides various cryptographic
4 * algorithms in a highly modular and flexible manner.
6 * The library is free for all purposes without any express
10 /* the idea of re-keying loosely follows the approach used in:
11 * http://bxr.su/OpenBSD/lib/libc/crypt/arc4random.c
16 #ifdef LTC_CHACHA20_PRNG
18 const struct ltc_prng_descriptor chacha20_prng_desc =
23 &chacha20_prng_add_entropy,
27 &chacha20_prng_export,
28 &chacha20_prng_import,
34 @param prng The PRNG state to initialize
35 @return CRYPT_OK if successful
37 int chacha20_prng_start(prng_state *prng)
39 LTC_ARGCHK(prng != NULL);
41 XMEMSET(&prng->chacha.ent, 0, sizeof(prng->chacha.ent));
43 LTC_MUTEX_INIT(&prng->lock)
48 Add entropy to the PRNG state
49 @param in The data to add
50 @param inlen Length of the data to add
51 @param prng PRNG state to update
52 @return CRYPT_OK if successful
54 int chacha20_prng_add_entropy(const unsigned char *in, unsigned long inlen, prng_state *prng)
56 unsigned char buf[40];
60 LTC_ARGCHK(prng != NULL);
61 LTC_ARGCHK(in != NULL);
62 LTC_ARGCHK(inlen > 0);
64 LTC_MUTEX_LOCK(&prng->lock);
66 /* chacha20_prng_ready() was already called, do "rekey" operation */
67 if ((err = chacha_keystream(&prng->chacha.s, buf, sizeof(buf))) != CRYPT_OK) goto LBL_UNLOCK;
68 for(i = 0; i < inlen; i++) buf[i % sizeof(buf)] ^= in[i];
69 /* key 32 bytes, 20 rounds */
70 if ((err = chacha_setup(&prng->chacha.s, buf, 32, 20)) != CRYPT_OK) goto LBL_UNLOCK;
72 if ((err = chacha_ivctr64(&prng->chacha.s, buf + 32, 8, 0)) != CRYPT_OK) goto LBL_UNLOCK;
74 zeromem(buf, sizeof(buf));
77 /* chacha20_prng_ready() was not called yet, add entropy to ent buffer */
78 while (inlen--) prng->chacha.ent[prng->chacha.idx++ % sizeof(prng->chacha.ent)] ^= *in++;
82 LTC_MUTEX_UNLOCK(&prng->lock);
87 Make the PRNG ready to read from
88 @param prng The PRNG to make active
89 @return CRYPT_OK if successful
91 int chacha20_prng_ready(prng_state *prng)
95 LTC_ARGCHK(prng != NULL);
97 LTC_MUTEX_LOCK(&prng->lock);
98 if (prng->ready) { err = CRYPT_OK; goto LBL_UNLOCK; }
99 /* key 32 bytes, 20 rounds */
100 if ((err = chacha_setup(&prng->chacha.s, prng->chacha.ent, 32, 20)) != CRYPT_OK) goto LBL_UNLOCK;
102 if ((err = chacha_ivctr64(&prng->chacha.s, prng->chacha.ent + 32, 8, 0)) != CRYPT_OK) goto LBL_UNLOCK;
103 XMEMSET(&prng->chacha.ent, 0, sizeof(prng->chacha.ent));
104 prng->chacha.idx = 0;
107 LTC_MUTEX_UNLOCK(&prng->lock);
113 @param out Destination
114 @param outlen Length of output
115 @param prng The active PRNG to read from
116 @return Number of octets read
118 unsigned long chacha20_prng_read(unsigned char *out, unsigned long outlen, prng_state *prng)
120 if (outlen == 0 || prng == NULL || out == NULL) return 0;
121 LTC_MUTEX_LOCK(&prng->lock);
122 if (!prng->ready) { outlen = 0; goto LBL_UNLOCK; }
123 if (chacha_keystream(&prng->chacha.s, out, outlen) != CRYPT_OK) outlen = 0;
125 LTC_MUTEX_UNLOCK(&prng->lock);
131 @param prng The PRNG to terminate
132 @return CRYPT_OK if successful
134 int chacha20_prng_done(prng_state *prng)
137 LTC_ARGCHK(prng != NULL);
138 LTC_MUTEX_LOCK(&prng->lock);
140 err = chacha_done(&prng->chacha.s);
141 LTC_MUTEX_UNLOCK(&prng->lock);
142 LTC_MUTEX_DESTROY(&prng->lock);
147 Export the PRNG state
148 @param out [out] Destination
149 @param outlen [in/out] Max size and resulting size of the state
150 @param prng The PRNG to export
151 @return CRYPT_OK if successful
153 int chacha20_prng_export(unsigned char *out, unsigned long *outlen, prng_state *prng)
155 unsigned long len = chacha20_prng_desc.export_size;
157 LTC_ARGCHK(prng != NULL);
158 LTC_ARGCHK(out != NULL);
159 LTC_ARGCHK(outlen != NULL);
163 return CRYPT_BUFFER_OVERFLOW;
166 if (chacha20_prng_read(out, len, prng) != len) {
167 return CRYPT_ERROR_READPRNG;
176 @param in The PRNG state
177 @param inlen Size of the state
178 @param prng The PRNG to import
179 @return CRYPT_OK if successful
181 int chacha20_prng_import(const unsigned char *in, unsigned long inlen, prng_state *prng)
185 LTC_ARGCHK(prng != NULL);
186 LTC_ARGCHK(in != NULL);
187 if (inlen < (unsigned long)chacha20_prng_desc.export_size) return CRYPT_INVALID_ARG;
189 if ((err = chacha20_prng_start(prng)) != CRYPT_OK) return err;
190 if ((err = chacha20_prng_add_entropy(in, inlen, prng)) != CRYPT_OK) return err;
196 @return CRYPT_OK if successful, CRYPT_NOP if self-testing has been disabled
198 int chacha20_prng_test(void)
204 unsigned char en[] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a,
205 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14,
206 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e,
207 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28,
208 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32 };
209 unsigned char dmp[300];
210 unsigned long dmplen = sizeof(dmp);
211 unsigned char out[500];
212 unsigned char t1[] = { 0x59, 0xB2, 0x26, 0x95, 0x2B, 0x01, 0x8F, 0x05, 0xBE, 0xD8 };
213 unsigned char t2[] = { 0x47, 0xC9, 0x0D, 0x03, 0xE4, 0x75, 0x34, 0x27, 0xBD, 0xDE };
214 unsigned char t3[] = { 0xBC, 0xFA, 0xEF, 0x59, 0x37, 0x7F, 0x1A, 0x91, 0x1A, 0xA6 };
217 if ((err = chacha20_prng_start(&st)) != CRYPT_OK) return err;
218 /* add entropy to uninitialized prng */
219 if ((err = chacha20_prng_add_entropy(en, sizeof(en), &st)) != CRYPT_OK) return err;
220 if ((err = chacha20_prng_ready(&st)) != CRYPT_OK) return err;
221 if (chacha20_prng_read(out, 10, &st) != 10) return CRYPT_ERROR_READPRNG; /* 10 bytes for testing */
222 if (compare_testvector(out, 10, t1, sizeof(t1), "CHACHA-PRNG", 1)) return CRYPT_FAIL_TESTVECTOR;
223 if (chacha20_prng_read(out, 500, &st) != 500) return CRYPT_ERROR_READPRNG; /* skip 500 bytes */
224 /* add entropy to already initialized prng */
225 if ((err = chacha20_prng_add_entropy(en, sizeof(en), &st)) != CRYPT_OK) return err;
226 if (chacha20_prng_read(out, 500, &st) != 500) return CRYPT_ERROR_READPRNG; /* skip 500 bytes */
227 if ((err = chacha20_prng_export(dmp, &dmplen, &st)) != CRYPT_OK) return err;
228 if (chacha20_prng_read(out, 500, &st) != 500) return CRYPT_ERROR_READPRNG; /* skip 500 bytes */
229 if (chacha20_prng_read(out, 10, &st) != 10) return CRYPT_ERROR_READPRNG; /* 10 bytes for testing */
230 if (compare_testvector(out, 10, t2, sizeof(t2), "CHACHA-PRNG", 2)) return CRYPT_FAIL_TESTVECTOR;
231 if ((err = chacha20_prng_done(&st)) != CRYPT_OK) return err;
232 if ((err = chacha20_prng_import(dmp, dmplen, &st)) != CRYPT_OK) return err;
233 if ((err = chacha20_prng_ready(&st)) != CRYPT_OK) return err;
234 if (chacha20_prng_read(out, 500, &st) != 500) return CRYPT_ERROR_READPRNG; /* skip 500 bytes */
235 if (chacha20_prng_read(out, 10, &st) != 10) return CRYPT_ERROR_READPRNG; /* 10 bytes for testing */
236 if (compare_testvector(out, 10, t3, sizeof(t3), "CHACHA-PRNG", 3)) return CRYPT_FAIL_TESTVECTOR;
237 if ((err = chacha20_prng_done(&st)) != CRYPT_OK) return err;
245 /* ref: $Format:%D$ */
246 /* git commit: $Format:%H$ */
247 /* commit time: $Format:%ai$ */