Coverage Report

Created: 2025-11-16 07:15

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