Coverage Report

Created: 2025-07-23 06:59

/src/wolfssl-sp-math-all-8bit/wolfcrypt/src/chacha20_poly1305.c
Line
Count
Source (jump to first uncovered line)
1
/* chacha.c
2
 *
3
 * Copyright (C) 2006-2025 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 3 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
#include <wolfssl/wolfcrypt/libwolfssl_sources.h>
31
32
#if defined(HAVE_CHACHA) && defined(HAVE_POLY1305)
33
34
#include <wolfssl/wolfcrypt/chacha20_poly1305.h>
35
36
#ifdef NO_INLINE
37
#include <wolfssl/wolfcrypt/misc.h>
38
#else
39
#define WOLFSSL_MISC_INCLUDED
40
#include <wolfcrypt/src/misc.c>
41
#endif
42
43
0
#define CHACHA20_POLY1305_AEAD_INITIAL_COUNTER  0
44
WOLFSSL_ABI
45
int wc_ChaCha20Poly1305_Encrypt(
46
                const byte inKey[CHACHA20_POLY1305_AEAD_KEYSIZE],
47
                const byte inIV[CHACHA20_POLY1305_AEAD_IV_SIZE],
48
                const byte* inAAD, const word32 inAADLen,
49
                const byte* inPlaintext, const word32 inPlaintextLen,
50
                byte* outCiphertext,
51
                byte outAuthTag[CHACHA20_POLY1305_AEAD_AUTHTAG_SIZE])
52
0
{
53
0
    int ret;
54
0
#ifdef WOLFSSL_SMALL_STACK
55
0
    ChaChaPoly_Aead *aead = NULL;
56
#else
57
    ChaChaPoly_Aead aead[1];
58
#endif
59
60
    /* Validate function arguments */
61
0
    if (!inKey || !inIV ||
62
0
        (inPlaintextLen > 0 && inPlaintext == NULL) ||
63
0
        !outCiphertext ||
64
0
        !outAuthTag)
65
0
    {
66
0
        return BAD_FUNC_ARG;
67
0
    }
68
69
0
#ifdef WOLFSSL_SMALL_STACK
70
0
    aead = (ChaChaPoly_Aead *)XMALLOC(sizeof(*aead), NULL,
71
0
                                      DYNAMIC_TYPE_TMP_BUFFER);
72
0
    if (aead == NULL)
73
0
        return MEMORY_E;
74
0
#endif
75
76
0
    ret = wc_ChaCha20Poly1305_Init(aead, inKey, inIV,
77
0
        CHACHA20_POLY1305_AEAD_ENCRYPT);
78
0
    if (ret == 0)
79
0
        ret = wc_ChaCha20Poly1305_UpdateAad(aead, inAAD, inAADLen);
80
0
    if (ret == 0)
81
0
        ret = wc_ChaCha20Poly1305_UpdateData(aead, inPlaintext, outCiphertext,
82
0
            inPlaintextLen);
83
0
    if (ret == 0)
84
0
        ret = wc_ChaCha20Poly1305_Final(aead, outAuthTag);
85
86
0
#ifdef WOLFSSL_SMALL_STACK
87
0
    XFREE(aead, NULL, DYNAMIC_TYPE_TMP_BUFFER);
88
0
#endif
89
90
0
    return ret;
91
0
}
92
93
WOLFSSL_ABI
94
int wc_ChaCha20Poly1305_Decrypt(
95
                const byte inKey[CHACHA20_POLY1305_AEAD_KEYSIZE],
96
                const byte inIV[CHACHA20_POLY1305_AEAD_IV_SIZE],
97
                const byte* inAAD, const word32 inAADLen,
98
                const byte* inCiphertext, const word32 inCiphertextLen,
99
                const byte inAuthTag[CHACHA20_POLY1305_AEAD_AUTHTAG_SIZE],
100
                byte* outPlaintext)
101
0
{
102
0
    int ret;
103
0
#ifdef WOLFSSL_SMALL_STACK
104
0
    ChaChaPoly_Aead *aead = NULL;
105
#else
106
    ChaChaPoly_Aead aead[1];
107
#endif
108
0
    byte calculatedAuthTag[CHACHA20_POLY1305_AEAD_AUTHTAG_SIZE];
109
110
    /* Validate function arguments */
111
0
    if (!inKey || !inIV ||
112
0
        (inCiphertextLen > 0 && inCiphertext == NULL) ||
113
0
        !inAuthTag ||
114
0
        !outPlaintext)
115
0
    {
116
0
        return BAD_FUNC_ARG;
117
0
    }
118
119
0
#ifdef WOLFSSL_SMALL_STACK
120
0
    aead = (ChaChaPoly_Aead *)XMALLOC(sizeof(*aead), NULL,
121
0
                                      DYNAMIC_TYPE_TMP_BUFFER);
122
0
    if (aead == NULL)
123
0
        return MEMORY_E;
124
0
#endif
125
126
0
    XMEMSET(calculatedAuthTag, 0, sizeof(calculatedAuthTag));
127
128
0
    ret = wc_ChaCha20Poly1305_Init(aead, inKey, inIV,
129
0
        CHACHA20_POLY1305_AEAD_DECRYPT);
130
0
    if (ret == 0)
131
0
        ret = wc_ChaCha20Poly1305_UpdateAad(aead, inAAD, inAADLen);
132
0
    if (ret == 0)
133
0
        ret = wc_ChaCha20Poly1305_UpdateData(aead, inCiphertext, outPlaintext,
134
0
            inCiphertextLen);
135
0
    if (ret == 0)
136
0
        ret = wc_ChaCha20Poly1305_Final(aead, calculatedAuthTag);
137
0
    if (ret == 0)
138
0
        ret = wc_ChaCha20Poly1305_CheckTag(inAuthTag, calculatedAuthTag);
139
140
0
#ifdef WOLFSSL_SMALL_STACK
141
0
    XFREE(aead, NULL, DYNAMIC_TYPE_TMP_BUFFER);
142
0
#endif
143
144
0
    return ret;
145
0
}
146
147
int wc_ChaCha20Poly1305_CheckTag(
148
    const byte authTag[CHACHA20_POLY1305_AEAD_AUTHTAG_SIZE],
149
    const byte authTagChk[CHACHA20_POLY1305_AEAD_AUTHTAG_SIZE])
150
0
{
151
0
    int ret = 0;
152
0
    if (authTag == NULL || authTagChk == NULL) {
153
0
        return BAD_FUNC_ARG;
154
0
    }
155
0
    if (ConstantCompare(authTag, authTagChk,
156
0
            CHACHA20_POLY1305_AEAD_AUTHTAG_SIZE) != 0) {
157
0
        ret = MAC_CMP_FAILED_E;
158
0
    }
159
0
    return ret;
160
0
}
161
162
int wc_ChaCha20Poly1305_Init(ChaChaPoly_Aead* aead,
163
    const byte inKey[CHACHA20_POLY1305_AEAD_KEYSIZE],
164
    const byte inIV[CHACHA20_POLY1305_AEAD_IV_SIZE],
165
    int isEncrypt)
166
0
{
167
0
    int ret;
168
0
    byte authKey[CHACHA20_POLY1305_AEAD_KEYSIZE];
169
170
    /* check arguments */
171
0
    if (aead == NULL || inKey == NULL || inIV == NULL) {
172
0
        return BAD_FUNC_ARG;
173
0
    }
174
175
    /* setup aead context */
176
0
    XMEMSET(aead, 0, sizeof(ChaChaPoly_Aead));
177
0
    XMEMSET(authKey, 0, sizeof(authKey));
178
0
    aead->isEncrypt = isEncrypt ? 1 : 0;
179
180
    /* Initialize the ChaCha20 context (key and iv) */
181
0
    ret = wc_Chacha_SetKey(&aead->chacha, inKey,
182
0
        CHACHA20_POLY1305_AEAD_KEYSIZE);
183
0
    if (ret == 0) {
184
0
        ret = wc_Chacha_SetIV(&aead->chacha, inIV,
185
0
            CHACHA20_POLY1305_AEAD_INITIAL_COUNTER);
186
0
    }
187
188
    /* Create the Poly1305 key */
189
0
    if (ret == 0) {
190
0
        ret = wc_Chacha_Process(&aead->chacha, authKey, authKey,
191
0
            CHACHA20_POLY1305_AEAD_KEYSIZE);
192
0
    }
193
194
    /* Initialize Poly1305 context */
195
0
    if (ret == 0) {
196
0
        ret = wc_Poly1305SetKey(&aead->poly, authKey,
197
0
            CHACHA20_POLY1305_AEAD_KEYSIZE);
198
0
    }
199
200
    /* advance counter by 1 after creating Poly1305 key */
201
0
    if (ret == 0) {
202
0
        ret = wc_Chacha_SetIV(&aead->chacha, inIV,
203
0
            CHACHA20_POLY1305_AEAD_INITIAL_COUNTER + 1);
204
0
    }
205
206
0
    if (ret == 0) {
207
0
        aead->state = CHACHA20_POLY1305_STATE_READY;
208
0
    }
209
210
0
    return ret;
211
0
}
212
213
/* optional additional authentication data */
214
int wc_ChaCha20Poly1305_UpdateAad(ChaChaPoly_Aead* aead,
215
    const byte* inAAD, word32 inAADLen)
216
0
{
217
0
    int ret = 0;
218
219
0
    if (aead == NULL || (inAAD == NULL && inAADLen > 0)) {
220
0
        return BAD_FUNC_ARG;
221
0
    }
222
0
    if (aead->state != CHACHA20_POLY1305_STATE_READY &&
223
0
        aead->state != CHACHA20_POLY1305_STATE_AAD) {
224
0
        return BAD_STATE_E;
225
0
    }
226
0
    if (inAADLen > CHACHA20_POLY1305_MAX - aead->aadLen)
227
0
        return CHACHA_POLY_OVERFLOW;
228
229
0
    if (inAAD && inAADLen > 0) {
230
0
        ret = wc_Poly1305Update(&aead->poly, inAAD, inAADLen);
231
0
        if (ret == 0) {
232
0
            aead->aadLen += inAADLen;
233
0
            aead->state = CHACHA20_POLY1305_STATE_AAD;
234
0
        }
235
0
    }
236
237
0
    return ret;
238
0
}
239
240
/* inData and outData can be same pointer (inline) */
241
int wc_ChaCha20Poly1305_UpdateData(ChaChaPoly_Aead* aead,
242
    const byte* inData, byte* outData, word32 dataLen)
243
0
{
244
0
    int ret = 0;
245
246
0
    if (aead == NULL || inData == NULL || outData == NULL) {
247
0
        return BAD_FUNC_ARG;
248
0
    }
249
0
    if (aead->state != CHACHA20_POLY1305_STATE_READY &&
250
0
        aead->state != CHACHA20_POLY1305_STATE_AAD &&
251
0
        aead->state != CHACHA20_POLY1305_STATE_DATA) {
252
0
        return BAD_STATE_E;
253
0
    }
254
0
    if (dataLen > CHACHA20_POLY1305_MAX - aead->dataLen)
255
0
        return CHACHA_POLY_OVERFLOW;
256
257
    /* Pad the AAD */
258
0
    if (aead->state == CHACHA20_POLY1305_STATE_AAD) {
259
0
        ret = wc_Poly1305_Pad(&aead->poly, aead->aadLen);
260
0
    }
261
262
    /* advance state */
263
0
    aead->state = CHACHA20_POLY1305_STATE_DATA;
264
265
    /* Perform ChaCha20 encrypt/decrypt and Poly1305 auth calc */
266
0
    if (ret == 0) {
267
0
        if (aead->isEncrypt) {
268
0
            ret = wc_Chacha_Process(&aead->chacha, outData, inData, dataLen);
269
0
            if (ret == 0)
270
0
                ret = wc_Poly1305Update(&aead->poly, outData, dataLen);
271
0
        }
272
0
        else {
273
0
            ret = wc_Poly1305Update(&aead->poly, inData, dataLen);
274
0
            if (ret == 0)
275
0
                ret = wc_Chacha_Process(&aead->chacha, outData, inData, dataLen);
276
0
        }
277
0
    }
278
0
    if (ret == 0) {
279
0
        aead->dataLen += dataLen;
280
0
    }
281
0
    return ret;
282
0
}
283
284
int wc_ChaCha20Poly1305_Final(ChaChaPoly_Aead* aead,
285
    byte outAuthTag[CHACHA20_POLY1305_AEAD_AUTHTAG_SIZE])
286
0
{
287
0
    int ret = 0;
288
289
0
    if (aead == NULL || outAuthTag == NULL) {
290
0
        return BAD_FUNC_ARG;
291
0
    }
292
0
    if (aead->state != CHACHA20_POLY1305_STATE_AAD &&
293
0
        aead->state != CHACHA20_POLY1305_STATE_DATA) {
294
0
        return BAD_STATE_E;
295
0
    }
296
297
    /* Pad the AAD - Make sure it is done */
298
0
    if (aead->state == CHACHA20_POLY1305_STATE_AAD) {
299
0
        ret = wc_Poly1305_Pad(&aead->poly, aead->aadLen);
300
0
    }
301
302
    /* Pad the plaintext/ciphertext to 16 bytes */
303
0
    if (ret == 0) {
304
0
        ret = wc_Poly1305_Pad(&aead->poly, aead->dataLen);
305
0
    }
306
307
    /* Add the aad length and plaintext/ciphertext length */
308
0
    if (ret == 0) {
309
0
        ret = wc_Poly1305_EncodeSizes(&aead->poly, aead->aadLen,
310
0
            aead->dataLen);
311
0
    }
312
313
    /* Finalize the auth tag */
314
0
    if (ret == 0) {
315
0
        ret = wc_Poly1305Final(&aead->poly, outAuthTag);
316
0
    }
317
318
    /* reset and cleanup sensitive context */
319
0
    ForceZero(aead, sizeof(ChaChaPoly_Aead));
320
321
0
    return ret;
322
0
}
323
324
#ifdef HAVE_XCHACHA
325
326
int wc_XChaCha20Poly1305_Init(
327
    ChaChaPoly_Aead *aead,
328
    const byte *ad, word32 ad_len,
329
    const byte *nonce, word32 nonce_len,
330
    const byte *key, word32 key_len,
331
    int isEncrypt)
332
0
{
333
0
    byte authKey[CHACHA20_POLY1305_AEAD_KEYSIZE];
334
0
    int ret;
335
336
0
    if ((ad == NULL) || (nonce == NULL) || (key == NULL))
337
0
        return BAD_FUNC_ARG;
338
339
0
    if ((key_len != CHACHA20_POLY1305_AEAD_KEYSIZE) ||
340
0
        (nonce_len != XCHACHA20_POLY1305_AEAD_NONCE_SIZE))
341
0
        return BAD_FUNC_ARG;
342
343
0
    if ((ret = wc_XChacha_SetKey(&aead->chacha,
344
0
                                 key, key_len,
345
0
                                 nonce, nonce_len,
346
0
                                 0 /* counter */)) < 0)
347
0
        return ret;
348
349
0
    XMEMSET(authKey, 0, sizeof authKey);
350
351
    /* Create the Poly1305 key */
352
0
    if ((ret = wc_Chacha_Process(&aead->chacha, authKey, authKey,
353
0
                                 (word32)sizeof authKey)) < 0)
354
0
        return ret;
355
    /* advance to start of the next ChaCha block. */
356
0
    wc_Chacha_purge_current_block(&aead->chacha);
357
358
    /* Initialize Poly1305 context */
359
0
    if ((ret = wc_Poly1305SetKey(&aead->poly, authKey,
360
0
                                 (word32)sizeof authKey)) < 0)
361
0
        return ret;
362
363
0
    if ((ret = wc_Poly1305Update(&aead->poly, ad, (word32)ad_len)) < 0)
364
0
        return ret;
365
366
0
    if ((ret = wc_Poly1305_Pad(&aead->poly, (word32)ad_len)) < 0)
367
0
        return ret;
368
369
0
    aead->isEncrypt = isEncrypt ? 1 : 0;
370
0
    aead->state = CHACHA20_POLY1305_STATE_AAD;
371
372
0
    return 0;
373
0
}
374
375
static WC_INLINE int wc_XChaCha20Poly1305_crypt_oneshot(
376
    byte *dst, const size_t dst_space,
377
    const byte *src, const size_t src_len,
378
    const byte *ad, const size_t ad_len,
379
    const byte *nonce, const size_t nonce_len,
380
    const byte *key, const size_t key_len,
381
    int isEncrypt)
382
0
{
383
0
    int ret;
384
0
    long int dst_len = isEncrypt ?
385
0
        (long int)src_len + POLY1305_DIGEST_SIZE :
386
0
        (long int)src_len - POLY1305_DIGEST_SIZE;
387
0
    const byte *src_i;
388
0
    byte *dst_i;
389
0
    size_t src_len_rem;
390
0
#if defined(WOLFSSL_SMALL_STACK) && !defined(WOLFSSL_NO_MALLOC)
391
0
    ChaChaPoly_Aead *aead = (ChaChaPoly_Aead *)XMALLOC(sizeof *aead, NULL, DYNAMIC_TYPE_TMP_BUFFER);
392
393
0
    if (aead == NULL)
394
0
        return MEMORY_E;
395
#else
396
    ChaChaPoly_Aead aead_buf, *aead = &aead_buf;
397
#endif
398
399
0
    if ((dst == NULL) || (src == NULL)) {
400
0
        ret = BAD_FUNC_ARG;
401
0
        goto out;
402
0
    }
403
404
0
    if ((long int)dst_space < dst_len) {
405
0
        ret = BUFFER_E;
406
0
        goto out;
407
0
    }
408
409
0
    if ((ret = wc_XChaCha20Poly1305_Init(aead, ad, (word32)ad_len,
410
0
                                         nonce, (word32)nonce_len,
411
0
                                         key, (word32)key_len, 1)) < 0)
412
0
        goto out;
413
414
#ifdef WOLFSSL_CHECK_MEM_ZERO
415
    wc_MemZero_Add("wc_XChaCha20Poly1305_crypt_oneshot aead", aead,
416
        sizeof(ChaChaPoly_Aead));
417
#endif
418
419
    /* process the input in 16k pieces to accommodate src_lens that don't fit in a word32,
420
     * and to exploit hot cache for the input data.
421
     */
422
0
    src_i = src;
423
0
    src_len_rem = isEncrypt ? src_len : (size_t)dst_len;
424
0
    dst_i = dst;
425
0
    while (src_len_rem > 0) {
426
0
        word32 this_src_len =
427
0
            (src_len_rem > 16384) ?
428
0
            16384 :
429
0
            (word32)src_len_rem;
430
431
0
        if ((ret = wc_Chacha_Process(&aead->chacha, dst_i, src_i, this_src_len)) < 0)
432
0
            goto out;
433
434
0
        if ((ret = wc_Poly1305Update(&aead->poly, isEncrypt ? dst_i : src_i, this_src_len)) < 0)
435
0
            goto out;
436
437
0
        src_len_rem -= (size_t)this_src_len;
438
0
        src_i += this_src_len;
439
0
        dst_i += this_src_len;
440
0
    }
441
442
0
    if (aead->poly.leftover) {
443
0
        if ((ret = wc_Poly1305_Pad(&aead->poly, (word32)aead->poly.leftover)) < 0)
444
0
            return ret;
445
0
    }
446
447
0
#ifdef WORD64_AVAILABLE
448
0
    ret = wc_Poly1305_EncodeSizes64(&aead->poly, ad_len, isEncrypt ? src_len : (size_t)dst_len);
449
#else
450
    ret = wc_Poly1305_EncodeSizes(&aead->poly, ad_len, isEncrypt ? src_len : (size_t)dst_len);
451
#endif
452
0
    if (ret < 0)
453
0
        goto out;
454
455
0
    if (isEncrypt)
456
0
        ret = wc_Poly1305Final(&aead->poly, dst + src_len);
457
0
    else {
458
0
        byte outAuthTag[POLY1305_DIGEST_SIZE];
459
460
0
        if ((ret = wc_Poly1305Final(&aead->poly, outAuthTag)) < 0)
461
0
            goto out;
462
463
0
        if (ConstantCompare(outAuthTag, src + dst_len, POLY1305_DIGEST_SIZE) != 0) {
464
0
            ret = MAC_CMP_FAILED_E;
465
0
            goto out;
466
0
        }
467
0
    }
468
469
0
  out:
470
471
0
    ForceZero(aead, sizeof *aead);
472
473
0
#if defined(WOLFSSL_SMALL_STACK) && !defined(WOLFSSL_NO_MALLOC)
474
0
    XFREE(aead, NULL, DYNAMIC_TYPE_TMP_BUFFER);
475
#elif defined(WOLFSSL_CHECK_MEM_ZERO)
476
    wc_MemZero_Check(aead, sizeof(ChaChaPoly_Aead));
477
#endif
478
479
0
    return ret;
480
0
}
481
482
int wc_XChaCha20Poly1305_Encrypt(
483
    byte *dst, const size_t dst_space,
484
    const byte *src, const size_t src_len,
485
    const byte *ad, const size_t ad_len,
486
    const byte *nonce, const size_t nonce_len,
487
    const byte *key, const size_t key_len)
488
0
{
489
0
    return wc_XChaCha20Poly1305_crypt_oneshot(dst, dst_space, src, src_len, ad, ad_len, nonce, nonce_len, key, key_len, 1);
490
0
}
491
492
int wc_XChaCha20Poly1305_Decrypt(
493
    byte *dst, const size_t dst_space,
494
    const byte *src, const size_t src_len,
495
    const byte *ad, const size_t ad_len,
496
    const byte *nonce, const size_t nonce_len,
497
    const byte *key, const size_t key_len)
498
0
{
499
0
    return wc_XChaCha20Poly1305_crypt_oneshot(dst, dst_space, src, src_len, ad, ad_len, nonce, nonce_len, key, key_len, 0);
500
0
}
501
502
#endif /* HAVE_XCHACHA */
503
504
#endif /* HAVE_CHACHA && HAVE_POLY1305 */