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