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 */
chacha20_prng_start(prng_state * prng)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 */
chacha20_prng_add_entropy(const unsigned char * in,unsigned long inlen,prng_state * prng)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 */
chacha20_prng_ready(prng_state * prng)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 */
chacha20_prng_read(unsigned char * out,unsigned long outlen,prng_state * prng)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 */
chacha20_prng_done(prng_state * prng)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 */
LTC_PRNG_EXPORT(chacha20_prng)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 */
chacha20_prng_test(void)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