Coverage Report

Created: 2026-05-18 06:53

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