1 /* chacha.c
2  *
3  * Copyright (C) 2006-2021 wolfSSL Inc.
4  *
5  * This file is part of wolfSSL.
6  *
7  * wolfSSL is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version.
11  *
12  * wolfSSL is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA
20  */
21 /*
22 
23 DESCRIPTION
24 This library contains implementation for the ChaCha20 stream cipher and
25 the Poly1305 authenticator, both as as combined-mode,
26 or Authenticated Encryption with Additional Data (AEAD) algorithm.
27 
28 */
29 
30 #ifdef HAVE_CONFIG_H
31     #include <config.h>
32 #endif
33 
34 #include <wolfssl/wolfcrypt/settings.h>
35 
36 #if defined(HAVE_CHACHA) && defined(HAVE_POLY1305)
37 
38 #include <wolfssl/wolfcrypt/chacha20_poly1305.h>
39 #include <wolfssl/wolfcrypt/error-crypt.h>
40 #include <wolfssl/wolfcrypt/logging.h>
41 
42 #ifdef NO_INLINE
43 #include <wolfssl/wolfcrypt/misc.h>
44 #else
45 #define WOLFSSL_MISC_INCLUDED
46 #include <wolfcrypt/src/misc.c>
47 #endif
48 
49 #define CHACHA20_POLY1305_AEAD_INITIAL_COUNTER  0
wc_ChaCha20Poly1305_Encrypt(const byte inKey[CHACHA20_POLY1305_AEAD_KEYSIZE],const byte inIV[CHACHA20_POLY1305_AEAD_IV_SIZE],const byte * inAAD,const word32 inAADLen,const byte * inPlaintext,const word32 inPlaintextLen,byte * outCiphertext,byte outAuthTag[CHACHA20_POLY1305_AEAD_AUTHTAG_SIZE])50 int wc_ChaCha20Poly1305_Encrypt(
51                 const byte inKey[CHACHA20_POLY1305_AEAD_KEYSIZE],
52                 const byte inIV[CHACHA20_POLY1305_AEAD_IV_SIZE],
53                 const byte* inAAD, const word32 inAADLen,
54                 const byte* inPlaintext, const word32 inPlaintextLen,
55                 byte* outCiphertext,
56                 byte outAuthTag[CHACHA20_POLY1305_AEAD_AUTHTAG_SIZE])
57 {
58     int ret;
59     ChaChaPoly_Aead aead;
60 
61     /* Validate function arguments */
62     if (!inKey || !inIV ||
63         !inPlaintext || !inPlaintextLen ||
64         !outCiphertext ||
65         !outAuthTag)
66     {
67         return BAD_FUNC_ARG;
68     }
69 
70     ret = wc_ChaCha20Poly1305_Init(&aead, inKey, inIV,
71         CHACHA20_POLY1305_AEAD_ENCRYPT);
72     if (ret == 0)
73         ret = wc_ChaCha20Poly1305_UpdateAad(&aead, inAAD, inAADLen);
74     if (ret == 0)
75         ret = wc_ChaCha20Poly1305_UpdateData(&aead, inPlaintext, outCiphertext,
76             inPlaintextLen);
77     if (ret == 0)
78         ret = wc_ChaCha20Poly1305_Final(&aead, outAuthTag);
79     return ret;
80 }
81 
wc_ChaCha20Poly1305_Decrypt(const byte inKey[CHACHA20_POLY1305_AEAD_KEYSIZE],const byte inIV[CHACHA20_POLY1305_AEAD_IV_SIZE],const byte * inAAD,const word32 inAADLen,const byte * inCiphertext,const word32 inCiphertextLen,const byte inAuthTag[CHACHA20_POLY1305_AEAD_AUTHTAG_SIZE],byte * outPlaintext)82 int wc_ChaCha20Poly1305_Decrypt(
83                 const byte inKey[CHACHA20_POLY1305_AEAD_KEYSIZE],
84                 const byte inIV[CHACHA20_POLY1305_AEAD_IV_SIZE],
85                 const byte* inAAD, const word32 inAADLen,
86                 const byte* inCiphertext, const word32 inCiphertextLen,
87                 const byte inAuthTag[CHACHA20_POLY1305_AEAD_AUTHTAG_SIZE],
88                 byte* outPlaintext)
89 {
90     int ret;
91     ChaChaPoly_Aead aead;
92     byte calculatedAuthTag[CHACHA20_POLY1305_AEAD_AUTHTAG_SIZE];
93 
94     /* Validate function arguments */
95     if (!inKey || !inIV ||
96         !inCiphertext || !inCiphertextLen ||
97         !inAuthTag ||
98         !outPlaintext)
99     {
100         return BAD_FUNC_ARG;
101     }
102 
103     XMEMSET(calculatedAuthTag, 0, sizeof(calculatedAuthTag));
104 
105     ret = wc_ChaCha20Poly1305_Init(&aead, inKey, inIV,
106         CHACHA20_POLY1305_AEAD_DECRYPT);
107     if (ret == 0)
108         ret = wc_ChaCha20Poly1305_UpdateAad(&aead, inAAD, inAADLen);
109     if (ret == 0)
110         ret = wc_ChaCha20Poly1305_UpdateData(&aead, inCiphertext, outPlaintext,
111             inCiphertextLen);
112     if (ret == 0)
113         ret = wc_ChaCha20Poly1305_Final(&aead, calculatedAuthTag);
114     if (ret == 0)
115         ret = wc_ChaCha20Poly1305_CheckTag(inAuthTag, calculatedAuthTag);
116     return ret;
117 }
118 
wc_ChaCha20Poly1305_CheckTag(const byte authTag[CHACHA20_POLY1305_AEAD_AUTHTAG_SIZE],const byte authTagChk[CHACHA20_POLY1305_AEAD_AUTHTAG_SIZE])119 int wc_ChaCha20Poly1305_CheckTag(
120     const byte authTag[CHACHA20_POLY1305_AEAD_AUTHTAG_SIZE],
121     const byte authTagChk[CHACHA20_POLY1305_AEAD_AUTHTAG_SIZE])
122 {
123     int ret = 0;
124     if (authTag == NULL || authTagChk == NULL) {
125         return BAD_FUNC_ARG;
126     }
127     if (ConstantCompare(authTag, authTagChk,
128             CHACHA20_POLY1305_AEAD_AUTHTAG_SIZE) != 0) {
129         ret = MAC_CMP_FAILED_E;
130     }
131     return ret;
132 }
133 
wc_ChaCha20Poly1305_Init(ChaChaPoly_Aead * aead,const byte inKey[CHACHA20_POLY1305_AEAD_KEYSIZE],const byte inIV[CHACHA20_POLY1305_AEAD_IV_SIZE],int isEncrypt)134 int wc_ChaCha20Poly1305_Init(ChaChaPoly_Aead* aead,
135     const byte inKey[CHACHA20_POLY1305_AEAD_KEYSIZE],
136     const byte inIV[CHACHA20_POLY1305_AEAD_IV_SIZE],
137     int isEncrypt)
138 {
139     int ret;
140     byte authKey[CHACHA20_POLY1305_AEAD_KEYSIZE];
141 
142     /* check arguments */
143     if (aead == NULL || inKey == NULL || inIV == NULL) {
144         return BAD_FUNC_ARG;
145     }
146 
147     /* setup aead context */
148     XMEMSET(aead, 0, sizeof(ChaChaPoly_Aead));
149     XMEMSET(authKey, 0, sizeof(authKey));
150     aead->isEncrypt = (byte)isEncrypt;
151 
152     /* Initialize the ChaCha20 context (key and iv) */
153     ret = wc_Chacha_SetKey(&aead->chacha, inKey,
154         CHACHA20_POLY1305_AEAD_KEYSIZE);
155     if (ret == 0) {
156         ret = wc_Chacha_SetIV(&aead->chacha, inIV,
157             CHACHA20_POLY1305_AEAD_INITIAL_COUNTER);
158     }
159 
160     /* Create the Poly1305 key */
161     if (ret == 0) {
162         ret = wc_Chacha_Process(&aead->chacha, authKey, authKey,
163             CHACHA20_POLY1305_AEAD_KEYSIZE);
164     }
165 
166     /* Initialize Poly1305 context */
167     if (ret == 0) {
168         ret = wc_Poly1305SetKey(&aead->poly, authKey,
169             CHACHA20_POLY1305_AEAD_KEYSIZE);
170     }
171 
172     /* advance counter by 1 after creating Poly1305 key */
173     if (ret == 0) {
174         ret = wc_Chacha_SetIV(&aead->chacha, inIV,
175             CHACHA20_POLY1305_AEAD_INITIAL_COUNTER + 1);
176     }
177 
178     if (ret == 0) {
179         aead->state = CHACHA20_POLY1305_STATE_READY;
180     }
181 
182     return ret;
183 }
184 
185 /* optional additional authentication data */
wc_ChaCha20Poly1305_UpdateAad(ChaChaPoly_Aead * aead,const byte * inAAD,word32 inAADLen)186 int wc_ChaCha20Poly1305_UpdateAad(ChaChaPoly_Aead* aead,
187     const byte* inAAD, word32 inAADLen)
188 {
189     int ret = 0;
190 
191     if (aead == NULL || (inAAD == NULL && inAADLen > 0)) {
192         return BAD_FUNC_ARG;
193     }
194     if (aead->state != CHACHA20_POLY1305_STATE_READY &&
195         aead->state != CHACHA20_POLY1305_STATE_AAD) {
196         return BAD_STATE_E;
197     }
198     if (inAADLen > CHACHA20_POLY1305_MAX - aead->aadLen)
199         return CHACHA_POLY_OVERFLOW;
200 
201     if (inAAD && inAADLen > 0) {
202         ret = wc_Poly1305Update(&aead->poly, inAAD, inAADLen);
203         if (ret == 0) {
204             aead->aadLen += inAADLen;
205             aead->state = CHACHA20_POLY1305_STATE_AAD;
206         }
207     }
208 
209     return ret;
210 }
211 
212 /* inData and outData can be same pointer (inline) */
wc_ChaCha20Poly1305_UpdateData(ChaChaPoly_Aead * aead,const byte * inData,byte * outData,word32 dataLen)213 int wc_ChaCha20Poly1305_UpdateData(ChaChaPoly_Aead* aead,
214     const byte* inData, byte* outData, word32 dataLen)
215 {
216     int ret = 0;
217 
218     if (aead == NULL || inData == NULL || outData == NULL) {
219         return BAD_FUNC_ARG;
220     }
221     if (aead->state != CHACHA20_POLY1305_STATE_READY &&
222         aead->state != CHACHA20_POLY1305_STATE_AAD &&
223         aead->state != CHACHA20_POLY1305_STATE_DATA) {
224         return BAD_STATE_E;
225     }
226     if (dataLen > CHACHA20_POLY1305_MAX - aead->dataLen)
227         return CHACHA_POLY_OVERFLOW;
228 
229     /* Pad the AAD */
230     if (aead->state == CHACHA20_POLY1305_STATE_AAD) {
231         ret = wc_Poly1305_Pad(&aead->poly, aead->aadLen);
232     }
233 
234     /* advance state */
235     aead->state = CHACHA20_POLY1305_STATE_DATA;
236 
237     /* Perform ChaCha20 encrypt/decrypt and Poly1305 auth calc */
238     if (ret == 0) {
239         if (aead->isEncrypt) {
240             ret = wc_Chacha_Process(&aead->chacha, outData, inData, dataLen);
241             if (ret == 0)
242                 ret = wc_Poly1305Update(&aead->poly, outData, dataLen);
243         }
244         else {
245             ret = wc_Poly1305Update(&aead->poly, inData, dataLen);
246             if (ret == 0)
247                 ret = wc_Chacha_Process(&aead->chacha, outData, inData, dataLen);
248         }
249     }
250     if (ret == 0) {
251         aead->dataLen += dataLen;
252     }
253     return ret;
254 }
255 
wc_ChaCha20Poly1305_Final(ChaChaPoly_Aead * aead,byte outAuthTag[CHACHA20_POLY1305_AEAD_AUTHTAG_SIZE])256 int wc_ChaCha20Poly1305_Final(ChaChaPoly_Aead* aead,
257     byte outAuthTag[CHACHA20_POLY1305_AEAD_AUTHTAG_SIZE])
258 {
259     int ret = 0;
260 
261     if (aead == NULL || outAuthTag == NULL) {
262         return BAD_FUNC_ARG;
263     }
264     if (aead->state != CHACHA20_POLY1305_STATE_AAD &&
265         aead->state != CHACHA20_POLY1305_STATE_DATA) {
266         return BAD_STATE_E;
267     }
268 
269     /* Pad the AAD - Make sure it is done */
270     if (aead->state == CHACHA20_POLY1305_STATE_AAD) {
271         ret = wc_Poly1305_Pad(&aead->poly, aead->aadLen);
272     }
273 
274     /* Pad the plaintext/ciphertext to 16 bytes */
275     if (ret == 0) {
276         ret = wc_Poly1305_Pad(&aead->poly, aead->dataLen);
277     }
278 
279     /* Add the aad length and plaintext/ciphertext length */
280     if (ret == 0) {
281         ret = wc_Poly1305_EncodeSizes(&aead->poly, aead->aadLen,
282             aead->dataLen);
283     }
284 
285     /* Finalize the auth tag */
286     if (ret == 0) {
287         ret = wc_Poly1305Final(&aead->poly, outAuthTag);
288     }
289 
290     /* reset and cleanup sensitive context */
291     ForceZero(aead, sizeof(ChaChaPoly_Aead));
292 
293     return ret;
294 }
295 
296 #ifdef HAVE_XCHACHA
297 
wc_XChaCha20Poly1305_Init(ChaChaPoly_Aead * aead,const byte * ad,word32 ad_len,const byte * nonce,word32 nonce_len,const byte * key,word32 key_len,int isEncrypt)298 int wc_XChaCha20Poly1305_Init(
299     ChaChaPoly_Aead *aead,
300     const byte *ad, word32 ad_len,
301     const byte *nonce, word32 nonce_len,
302     const byte *key, word32 key_len,
303     int isEncrypt)
304 {
305     byte authKey[CHACHA20_POLY1305_AEAD_KEYSIZE];
306     int ret;
307 
308     if ((ad == NULL) || (nonce == NULL) || (key == NULL))
309         return BAD_FUNC_ARG;
310 
311     if ((key_len != CHACHA20_POLY1305_AEAD_KEYSIZE) ||
312         (nonce_len != XCHACHA20_POLY1305_AEAD_NONCE_SIZE))
313         return BAD_FUNC_ARG;
314 
315     if ((ret = wc_XChacha_SetKey(&aead->chacha,
316                                  key, key_len,
317                                  nonce, nonce_len,
318                                  0 /* counter */)) < 0)
319         return ret;
320 
321     XMEMSET(authKey, 0, sizeof authKey);
322 
323     /* Create the Poly1305 key */
324     if ((ret = wc_Chacha_Process(&aead->chacha, authKey, authKey,
325                                  (word32)sizeof authKey)) < 0)
326         return ret;
327     /* advance to start of the next ChaCha block. */
328     wc_Chacha_purge_current_block(&aead->chacha);
329 
330     /* Initialize Poly1305 context */
331     if ((ret = wc_Poly1305SetKey(&aead->poly, authKey,
332                                  (word32)sizeof authKey)) < 0)
333         return ret;
334 
335     if ((ret = wc_Poly1305Update(&aead->poly, ad, (word32)ad_len)) < 0)
336         return ret;
337 
338     if ((ret = wc_Poly1305_Pad(&aead->poly, (word32)ad_len)) < 0)
339         return ret;
340 
341     aead->isEncrypt = (byte)isEncrypt;
342     aead->state = CHACHA20_POLY1305_STATE_AAD;
343 
344     return 0;
345 }
346 
wc_XChaCha20Poly1305_crypt_oneshot(byte * dst,const size_t dst_space,const byte * src,const size_t src_len,const byte * ad,const size_t ad_len,const byte * nonce,const size_t nonce_len,const byte * key,const size_t key_len,int isEncrypt)347 static WC_INLINE int wc_XChaCha20Poly1305_crypt_oneshot(
348     byte *dst, const size_t dst_space,
349     const byte *src, const size_t src_len,
350     const byte *ad, const size_t ad_len,
351     const byte *nonce, const size_t nonce_len,
352     const byte *key, const size_t key_len,
353     int isEncrypt)
354 {
355     int ret;
356     ssize_t dst_len = isEncrypt ?
357         (ssize_t)src_len + POLY1305_DIGEST_SIZE :
358         (ssize_t)src_len - POLY1305_DIGEST_SIZE;
359     const byte *src_i;
360     byte *dst_i;
361     size_t src_len_rem;
362 #if defined(WOLFSSL_SMALL_STACK) && !defined(WOLFSSL_NO_MALLOC)
363     ChaChaPoly_Aead *aead = (ChaChaPoly_Aead *)XMALLOC(sizeof *aead, NULL, DYNAMIC_TYPE_TMP_BUFFER);
364 
365     if (aead == NULL)
366         return MEMORY_E;
367 #else
368     ChaChaPoly_Aead aead_buf, *aead = &aead_buf;
369 #endif
370 
371     if ((dst == NULL) || (src == NULL)) {
372         ret = BAD_FUNC_ARG;
373         goto out;
374     }
375 
376     if ((ssize_t)dst_space < dst_len) {
377         ret = BUFFER_E;
378         goto out;
379     }
380 
381     if ((ret = wc_XChaCha20Poly1305_Init(aead, ad, (word32)ad_len,
382                                          nonce, (word32)nonce_len,
383                                          key, (word32)key_len, 1)) < 0)
384         goto out;
385 
386     /* process the input in 16k pieces to accommodate src_lens that don't fit in a word32,
387      * and to exploit hot cache for the input data.
388      */
389     src_i = src;
390     src_len_rem = isEncrypt ? src_len : (size_t)dst_len;
391     dst_i = dst;
392     while (src_len_rem > 0) {
393         word32 this_src_len =
394             (src_len_rem > 16384) ?
395             16384 :
396             (word32)src_len_rem;
397 
398         if ((ret = wc_Chacha_Process(&aead->chacha, dst_i, src_i, this_src_len)) < 0)
399             goto out;
400 
401         if ((ret = wc_Poly1305Update(&aead->poly, isEncrypt ? dst_i : src_i, this_src_len)) < 0)
402             goto out;
403 
404         src_len_rem -= (size_t)this_src_len;
405         src_i += this_src_len;
406         dst_i += this_src_len;
407     }
408 
409     if (aead->poly.leftover) {
410         if ((ret = wc_Poly1305_Pad(&aead->poly, (word32)aead->poly.leftover)) < 0)
411             return ret;
412     }
413 
414 #ifdef WORD64_AVAILABLE
415     ret = wc_Poly1305_EncodeSizes64(&aead->poly, ad_len, isEncrypt ? src_len : (size_t)dst_len);
416 #else
417     ret = wc_Poly1305_EncodeSizes(&aead->poly, ad_len, isEncrypt ? src_len : (size_t)dst_len);
418 #endif
419     if (ret < 0)
420         goto out;
421 
422     if (isEncrypt)
423         ret = wc_Poly1305Final(&aead->poly, dst + src_len);
424     else {
425         byte outAuthTag[POLY1305_DIGEST_SIZE];
426 
427         if ((ret = wc_Poly1305Final(&aead->poly, outAuthTag)) < 0)
428             goto out;
429 
430         if (ConstantCompare(outAuthTag, src + dst_len, POLY1305_DIGEST_SIZE) != 0) {
431             ret = MAC_CMP_FAILED_E;
432             goto out;
433         }
434     }
435 
436   out:
437 
438     ForceZero(aead, sizeof *aead);
439 
440 #if defined(WOLFSSL_SMALL_STACK) && !defined(WOLFSSL_NO_MALLOC)
441     XFREE(aead, NULL, DYNAMIC_TYPE_TMP_BUFFER);
442 #endif
443 
444     return ret;
445 }
446 
wc_XChaCha20Poly1305_Encrypt(byte * dst,const size_t dst_space,const byte * src,const size_t src_len,const byte * ad,const size_t ad_len,const byte * nonce,const size_t nonce_len,const byte * key,const size_t key_len)447 int wc_XChaCha20Poly1305_Encrypt(
448     byte *dst, const size_t dst_space,
449     const byte *src, const size_t src_len,
450     const byte *ad, const size_t ad_len,
451     const byte *nonce, const size_t nonce_len,
452     const byte *key, const size_t key_len)
453 {
454     return wc_XChaCha20Poly1305_crypt_oneshot(dst, dst_space, src, src_len, ad, ad_len, nonce, nonce_len, key, key_len, 1);
455 }
456 
wc_XChaCha20Poly1305_Decrypt(byte * dst,const size_t dst_space,const byte * src,const size_t src_len,const byte * ad,const size_t ad_len,const byte * nonce,const size_t nonce_len,const byte * key,const size_t key_len)457 int wc_XChaCha20Poly1305_Decrypt(
458     byte *dst, const size_t dst_space,
459     const byte *src, const size_t src_len,
460     const byte *ad, const size_t ad_len,
461     const byte *nonce, const size_t nonce_len,
462     const byte *key, const size_t key_len)
463 {
464     return wc_XChaCha20Poly1305_crypt_oneshot(dst, dst_space, src, src_len, ad, ad_len, nonce, nonce_len, key, key_len, 0);
465 }
466 
467 #endif /* HAVE_XCHACHA */
468 
469 #endif /* HAVE_CHACHA && HAVE_POLY1305 */
470