]> pd.if.org Git - zpackage/blob - libtomcrypt/src/prngs/chacha20.c
commit files needed for zpm-fetchurl
[zpackage] / libtomcrypt / src / prngs / chacha20.c
1 /* LibTomCrypt, modular cryptographic library -- Tom St Denis
2  *
3  * LibTomCrypt is a library that provides various cryptographic
4  * algorithms in a highly modular and flexible manner.
5  *
6  * The library is free for all purposes without any express
7  * guarantee it works.
8  */
9
10  /* the idea of re-keying loosely follows the approach used in:
11   * http://bxr.su/OpenBSD/lib/libc/crypt/arc4random.c
12   */
13
14 #include "tomcrypt.h"
15
16 #ifdef LTC_CHACHA20_PRNG
17
18 const struct ltc_prng_descriptor chacha20_prng_desc =
19 {
20    "chacha20",
21    40,
22    &chacha20_prng_start,
23    &chacha20_prng_add_entropy,
24    &chacha20_prng_ready,
25    &chacha20_prng_read,
26    &chacha20_prng_done,
27    &chacha20_prng_export,
28    &chacha20_prng_import,
29    &chacha20_prng_test
30 };
31
32 /**
33   Start the PRNG
34   @param prng The PRNG state to initialize
35   @return CRYPT_OK if successful
36 */
37 int chacha20_prng_start(prng_state *prng)
38 {
39    LTC_ARGCHK(prng != NULL);
40    prng->ready = 0;
41    XMEMSET(&prng->chacha.ent, 0, sizeof(prng->chacha.ent));
42    prng->chacha.idx = 0;
43    LTC_MUTEX_INIT(&prng->lock)
44    return CRYPT_OK;
45 }
46
47 /**
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
53 */
54 int chacha20_prng_add_entropy(const unsigned char *in, unsigned long inlen, prng_state *prng)
55 {
56    unsigned char buf[40];
57    unsigned long i;
58    int err;
59
60    LTC_ARGCHK(prng != NULL);
61    LTC_ARGCHK(in != NULL);
62    LTC_ARGCHK(inlen > 0);
63
64    LTC_MUTEX_LOCK(&prng->lock);
65    if (prng->ready) {
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;
71       /* iv 8 bytes */
72       if ((err = chacha_ivctr64(&prng->chacha.s, buf + 32, 8, 0)) != CRYPT_OK) goto LBL_UNLOCK;
73       /* clear KEY + IV */
74       zeromem(buf, sizeof(buf));
75    }
76    else {
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++;
79    }
80    err = CRYPT_OK;
81 LBL_UNLOCK:
82    LTC_MUTEX_UNLOCK(&prng->lock);
83    return err;
84 }
85
86 /**
87   Make the PRNG ready to read from
88   @param prng   The PRNG to make active
89   @return CRYPT_OK if successful
90 */
91 int chacha20_prng_ready(prng_state *prng)
92 {
93    int err;
94
95    LTC_ARGCHK(prng != NULL);
96
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;
101    /* iv 8 bytes */
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;
105    prng->ready = 1;
106 LBL_UNLOCK:
107    LTC_MUTEX_UNLOCK(&prng->lock);
108    return err;
109 }
110
111 /**
112   Read from the PRNG
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
117 */
118 unsigned long chacha20_prng_read(unsigned char *out, unsigned long outlen, prng_state *prng)
119 {
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;
124 LBL_UNLOCK:
125    LTC_MUTEX_UNLOCK(&prng->lock);
126    return outlen;
127 }
128
129 /**
130   Terminate the PRNG
131   @param prng   The PRNG to terminate
132   @return CRYPT_OK if successful
133 */
134 int chacha20_prng_done(prng_state *prng)
135 {
136    int err;
137    LTC_ARGCHK(prng != NULL);
138    LTC_MUTEX_LOCK(&prng->lock);
139    prng->ready = 0;
140    err = chacha_done(&prng->chacha.s);
141    LTC_MUTEX_UNLOCK(&prng->lock);
142    LTC_MUTEX_DESTROY(&prng->lock);
143    return err;
144 }
145
146 /**
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
152 */
153 int chacha20_prng_export(unsigned char *out, unsigned long *outlen, prng_state *prng)
154 {
155    unsigned long len = chacha20_prng_desc.export_size;
156
157    LTC_ARGCHK(prng   != NULL);
158    LTC_ARGCHK(out    != NULL);
159    LTC_ARGCHK(outlen != NULL);
160
161    if (*outlen < len) {
162       *outlen = len;
163       return CRYPT_BUFFER_OVERFLOW;
164    }
165
166    if (chacha20_prng_read(out, len, prng) != len) {
167       return CRYPT_ERROR_READPRNG;
168    }
169
170    *outlen = len;
171    return CRYPT_OK;
172 }
173
174 /**
175   Import a PRNG state
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
180 */
181 int chacha20_prng_import(const unsigned char *in, unsigned long inlen, prng_state *prng)
182 {
183    int err;
184
185    LTC_ARGCHK(prng != NULL);
186    LTC_ARGCHK(in   != NULL);
187    if (inlen < (unsigned long)chacha20_prng_desc.export_size) return CRYPT_INVALID_ARG;
188
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;
191    return CRYPT_OK;
192 }
193
194 /**
195   PRNG self-test
196   @return CRYPT_OK if successful, CRYPT_NOP if self-testing has been disabled
197 */
198 int chacha20_prng_test(void)
199 {
200 #ifndef LTC_TEST
201    return CRYPT_NOP;
202 #else
203    prng_state st;
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 };
215    int err;
216
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;
238
239    return CRYPT_OK;
240 #endif
241 }
242
243 #endif
244
245 /* ref:         $Format:%D$ */
246 /* git commit:  $Format:%H$ */
247 /* commit time: $Format:%ai$ */