Coverage Report

Created: 2025-11-16 07:15

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/wolfssl-normal-math/wolfcrypt/src/chacha.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.
25
26
Based from chacha-ref.c version 20080118
27
D. J. Bernstein
28
Public domain.
29
30
*/
31
32
#include <wolfssl/wolfcrypt/libwolfssl_sources.h>
33
34
#ifdef HAVE_CHACHA
35
    #include <wolfssl/wolfcrypt/chacha.h>
36
37
    #ifdef NO_INLINE
38
        #include <wolfssl/wolfcrypt/misc.h>
39
    #else
40
        #define WOLFSSL_MISC_INCLUDED
41
        #include <wolfcrypt/src/misc.c>
42
    #endif
43
44
    #ifdef BIG_ENDIAN_ORDER
45
        #define LITTLE32(x) ByteReverseWord32(x)
46
    #else
47
24.2k
        #define LITTLE32(x) (x)
48
    #endif
49
50
    /* Number of rounds */
51
11.6k
    #define ROUNDS  20
52
53
3.91M
    #define U32C(v) (v##U)
54
3.91M
    #define U32V(v) ((word32)(v) & U32C(0xFFFFFFFF))
55
5.19k
    #define U8TO32_LITTLE(p) LITTLE32(((word32*)(p))[0])
56
57
3.72M
    #define ROTATE(v,c) rotlFixed(v, c)
58
    #define XOR(v,w)    ((v) ^ (w))
59
3.91M
    #define PLUS(v,w)   (U32V((v) + (w)))
60
7.64k
    #define PLUSONE(v)  (PLUS((v),1))
61
62
    #define QUARTERROUND(a,b,c,d) \
63
930k
        x[a] = PLUS(x[a],x[b]); x[d] = ROTATE(XOR(x[d],x[a]),16); \
64
930k
        x[c] = PLUS(x[c],x[d]); x[b] = ROTATE(XOR(x[b],x[c]),12); \
65
930k
        x[a] = PLUS(x[a],x[b]); x[d] = ROTATE(XOR(x[d],x[a]), 8); \
66
930k
        x[c] = PLUS(x[c],x[d]); x[b] = ROTATE(XOR(x[b],x[c]), 7);
67
#endif /* HAVE_CHACHA */
68
69
70
#if defined(WOLFSSL_RISCV_ASM) && !defined(NO_CHACHA_ASM)
71
    /* implementation located in wolfcrypt/src/port/riscv/riscv-64-chacha.c */
72
73
#else
74
75
/* BEGIN ChaCha C implementation */
76
#if defined(HAVE_CHACHA)
77
78
#include <wolfssl/wolfcrypt/cpuid.h>
79
80
#ifdef CHACHA_AEAD_TEST
81
    #include <stdio.h>
82
#endif
83
84
#ifdef USE_INTEL_CHACHA_SPEEDUP
85
    #include <emmintrin.h>
86
    #include <immintrin.h>
87
88
    #if defined(__GNUC__) && ((__GNUC__ < 4) || \
89
                              (__GNUC__ == 4 && __GNUC_MINOR__ <= 8))
90
        #undef  NO_AVX2_SUPPORT
91
        #define NO_AVX2_SUPPORT
92
    #endif
93
    #if defined(__clang__) && ((__clang_major__ < 3) || \
94
                               (__clang_major__ == 3 && __clang_minor__ <= 5))
95
        #undef  NO_AVX2_SUPPORT
96
        #define NO_AVX2_SUPPORT
97
    #elif defined(__clang__) && defined(NO_AVX2_SUPPORT)
98
        #undef NO_AVX2_SUPPORT
99
    #endif
100
    #if defined(_MSC_VER) && (_MSC_VER <= 1900)
101
        #undef  NO_AVX2_SUPPORT
102
        #define NO_AVX2_SUPPORT
103
    #endif
104
105
    #ifndef NO_AVX2_SUPPORT
106
        #define HAVE_INTEL_AVX2
107
    #endif
108
109
    static cpuid_flags_t cpuidFlags = WC_CPUID_INITIALIZER;
110
#endif
111
112
/**
113
  * Set up iv(nonce). Earlier versions used 64 bits instead of 96, this version
114
  * uses the typical AEAD 96 bit nonce and can do record sizes of 256 GB.
115
  */
116
int wc_Chacha_SetIV(ChaCha* ctx, const byte* inIv, word32 counter)
117
6.34k
{
118
6.34k
#if !defined(USE_ARM_CHACHA_SPEEDUP)
119
6.34k
    word32 temp[CHACHA_IV_WORDS];/* used for alignment of memory */
120
6.34k
#endif
121
122
6.34k
    if (ctx == NULL || inIv == NULL)
123
0
        return BAD_FUNC_ARG;
124
125
6.34k
    ctx->left = 0; /* resets state */
126
127
6.34k
#if !defined(USE_ARM_CHACHA_SPEEDUP)
128
6.34k
    XMEMCPY(temp, inIv, CHACHA_IV_BYTES);
129
    /* block counter */
130
6.34k
    ctx->X[CHACHA_MATRIX_CNT_IV+0] = counter;
131
    /* fixed variable from nonce */
132
6.34k
    ctx->X[CHACHA_MATRIX_CNT_IV+1] = LITTLE32(temp[0]);
133
    /* counter from nonce */
134
6.34k
    ctx->X[CHACHA_MATRIX_CNT_IV+2] = LITTLE32(temp[1]);
135
    /* counter from nonce */
136
6.34k
    ctx->X[CHACHA_MATRIX_CNT_IV+3] = LITTLE32(temp[2]);
137
#else
138
    wc_chacha_setiv(ctx->X, inIv, counter);
139
#endif
140
141
6.34k
    return 0;
142
6.34k
}
143
144
#if !defined(USE_ARM_CHACHA_SPEEDUP)
145
/* "expand 32-byte k" as unsigned 32 byte */
146
static const word32 sigma[4] = {0x61707865, 0x3320646e, 0x79622d32, 0x6b206574};
147
/* "expand 16-byte k" as unsigned 16 byte */
148
static const word32 tau[4] = {0x61707865, 0x3120646e, 0x79622d36, 0x6b206574};
149
#endif
150
151
/**
152
  * Key setup. 8 word iv (nonce)
153
  */
154
int wc_Chacha_SetKey(ChaCha* ctx, const byte* key, word32 keySz)
155
649
{
156
649
#if !defined(USE_ARM_CHACHA_SPEEDUP)
157
649
    const word32* constants;
158
649
    const byte*   k;
159
#ifdef XSTREAM_ALIGN
160
    word32 alignKey[8];
161
#endif
162
649
#endif
163
164
649
    if (ctx == NULL || key == NULL)
165
0
        return BAD_FUNC_ARG;
166
167
649
    if (keySz != (CHACHA_MAX_KEY_SZ/2) && keySz != CHACHA_MAX_KEY_SZ)
168
0
        return BAD_FUNC_ARG;
169
170
649
#if !defined(USE_ARM_CHACHA_SPEEDUP)
171
#ifdef XSTREAM_ALIGN
172
    if ((wc_ptr_t)key % 4) {
173
        WOLFSSL_MSG("wc_ChachaSetKey unaligned key");
174
        XMEMCPY(alignKey, key, keySz);
175
        k = (byte*)alignKey;
176
    }
177
    else {
178
        k = key;
179
    }
180
#else
181
649
    k = key;
182
649
#endif /* XSTREAM_ALIGN */
183
184
#ifdef CHACHA_AEAD_TEST
185
    word32 i;
186
    printf("ChaCha key used :\n");
187
    for (i = 0; i < keySz; i++) {
188
        printf("%02x", key[i]);
189
        if ((i + 1) % 8 == 0)
190
           printf("\n");
191
    }
192
    printf("\n\n");
193
#endif
194
195
649
    ctx->X[4] = U8TO32_LITTLE(k +  0);
196
649
    ctx->X[5] = U8TO32_LITTLE(k +  4);
197
649
    ctx->X[6] = U8TO32_LITTLE(k +  8);
198
649
    ctx->X[7] = U8TO32_LITTLE(k + 12);
199
649
    if (keySz == CHACHA_MAX_KEY_SZ) {
200
649
        k += 16;
201
649
        constants = sigma;
202
649
    }
203
0
    else {
204
0
        constants = tau;
205
0
    }
206
649
    ctx->X[ 8] = U8TO32_LITTLE(k +  0);
207
649
    ctx->X[ 9] = U8TO32_LITTLE(k +  4);
208
649
    ctx->X[10] = U8TO32_LITTLE(k +  8);
209
649
    ctx->X[11] = U8TO32_LITTLE(k + 12);
210
649
    ctx->X[ 0] = constants[0];
211
649
    ctx->X[ 1] = constants[1];
212
649
    ctx->X[ 2] = constants[2];
213
649
    ctx->X[ 3] = constants[3];
214
#else
215
    wc_chacha_setkey(ctx->X, key, keySz);
216
#endif
217
218
649
    ctx->left = 0; /* resets state */
219
220
649
    return 0;
221
649
}
222
223
#if !defined(USE_INTEL_CHACHA_SPEEDUP) && !defined(USE_ARM_CHACHA_SPEEDUP)
224
/**
225
  * Converts word into bytes with rotations having been done.
226
  */
227
static WC_INLINE void wc_Chacha_wordtobyte(word32 x[CHACHA_CHUNK_WORDS],
228
        word32 state[CHACHA_CHUNK_WORDS])
229
11.6k
{
230
11.6k
    word32 i;
231
232
11.6k
    XMEMCPY(x, state, CHACHA_CHUNK_BYTES);
233
234
127k
    for (i = (ROUNDS); i > 0; i -= 2) {
235
116k
        QUARTERROUND(0, 4,  8, 12)
236
116k
        QUARTERROUND(1, 5,  9, 13)
237
116k
        QUARTERROUND(2, 6, 10, 14)
238
116k
        QUARTERROUND(3, 7, 11, 15)
239
116k
        QUARTERROUND(0, 5, 10, 15)
240
116k
        QUARTERROUND(1, 6, 11, 12)
241
116k
        QUARTERROUND(2, 7,  8, 13)
242
116k
        QUARTERROUND(3, 4,  9, 14)
243
116k
    }
244
245
197k
    for (i = 0; i < CHACHA_CHUNK_WORDS; i++) {
246
186k
        x[i] = PLUS(x[i], state[i]);
247
#ifdef BIG_ENDIAN_ORDER
248
        x[i] = LITTLE32(x[i]);
249
#endif
250
186k
    }
251
11.6k
}
252
#endif /* !USE_INTEL_CHACHA_SPEEDUP */
253
254
#ifdef __cplusplus
255
    extern "C" {
256
#endif
257
258
extern void chacha_encrypt_x64(ChaCha* ctx, const byte* m, byte* c,
259
                               word32 bytes);
260
extern void chacha_encrypt_avx1(ChaCha* ctx, const byte* m, byte* c,
261
                                word32 bytes);
262
extern void chacha_encrypt_avx2(ChaCha* ctx, const byte* m, byte* c,
263
                                word32 bytes);
264
265
#ifdef __cplusplus
266
    }  /* extern "C" */
267
#endif
268
269
270
#if !defined(USE_INTEL_CHACHA_SPEEDUP) && !defined(USE_ARM_CHACHA_SPEEDUP)
271
/**
272
  * Encrypt a stream of bytes
273
  */
274
static void wc_Chacha_encrypt_bytes(ChaCha* ctx, const byte* m, byte* c,
275
                                    word32 bytes)
276
3.98k
{
277
3.98k
    union {
278
3.98k
        byte state[CHACHA_CHUNK_BYTES];
279
3.98k
        word32 state32[CHACHA_CHUNK_WORDS];
280
3.98k
        wolfssl_word align_word; /* align for xorbufout */
281
3.98k
    } tmp;
282
283
    /* handle left overs */
284
3.98k
    if (bytes > 0 && ctx->left > 0) {
285
0
        word32 processed = min(bytes, ctx->left);
286
0
        wc_Chacha_wordtobyte(tmp.state32, ctx->X); /* recreate the stream */
287
0
        xorbufout(c, m, tmp.state + CHACHA_CHUNK_BYTES - ctx->left, processed);
288
0
        ctx->left -= processed;
289
290
        /* Used up all of the stream that was left, increment the counter */
291
0
        if (ctx->left == 0) {
292
0
            ctx->X[CHACHA_MATRIX_CNT_IV] =
293
0
                                          PLUSONE(ctx->X[CHACHA_MATRIX_CNT_IV]);
294
0
        }
295
0
        bytes -= processed;
296
0
        c += processed;
297
0
        m += processed;
298
0
    }
299
300
11.6k
    while (bytes >= CHACHA_CHUNK_BYTES) {
301
7.64k
        wc_Chacha_wordtobyte(tmp.state32, ctx->X);
302
7.64k
        ctx->X[CHACHA_MATRIX_CNT_IV] = PLUSONE(ctx->X[CHACHA_MATRIX_CNT_IV]);
303
7.64k
        xorbufout(c, m, tmp.state, CHACHA_CHUNK_BYTES);
304
7.64k
        bytes -= CHACHA_CHUNK_BYTES;
305
7.64k
        c += CHACHA_CHUNK_BYTES;
306
7.64k
        m += CHACHA_CHUNK_BYTES;
307
7.64k
    }
308
309
3.98k
    if (bytes) {
310
        /* in this case there will always be some left over since bytes is less
311
         * than CHACHA_CHUNK_BYTES, so do not increment counter after getting
312
         * stream in order for the stream to be recreated on next call */
313
3.98k
        wc_Chacha_wordtobyte(tmp.state32, ctx->X);
314
3.98k
        xorbufout(c, m, tmp.state, bytes);
315
3.98k
        ctx->left = CHACHA_CHUNK_BYTES - bytes;
316
3.98k
    }
317
3.98k
}
318
#endif /* !USE_INTEL_CHACHA_SPEEDUP */
319
320
321
/**
322
  * API to encrypt/decrypt a message of any size.
323
  */
324
int wc_Chacha_Process(ChaCha* ctx, byte* output, const byte* input,
325
                      word32 msglen)
326
3.98k
{
327
3.98k
    if (ctx == NULL || input == NULL || output == NULL)
328
0
        return BAD_FUNC_ARG;
329
330
#ifdef USE_INTEL_CHACHA_SPEEDUP
331
    /* handle left overs */
332
    if (msglen > 0 && ctx->left > 0) {
333
        byte*  out;
334
        word32 processed = min(msglen, ctx->left);
335
336
        out = (byte*)ctx->over + CHACHA_CHUNK_BYTES - ctx->left;
337
        xorbufout(output, input, out, processed);
338
        ctx->left -= processed;
339
        msglen -= processed;
340
        output += processed;
341
        input += processed;
342
    }
343
344
    if (msglen == 0) {
345
        return 0;
346
    }
347
348
    cpuid_get_flags_ex(&cpuidFlags);
349
350
    #ifdef HAVE_INTEL_AVX2
351
    if (IS_INTEL_AVX2(cpuidFlags)) {
352
        SAVE_VECTOR_REGISTERS(return _svr_ret;);
353
        chacha_encrypt_avx2(ctx, input, output, msglen);
354
        RESTORE_VECTOR_REGISTERS();
355
        return 0;
356
    }
357
    #endif
358
    if (IS_INTEL_AVX1(cpuidFlags)) {
359
        SAVE_VECTOR_REGISTERS(return _svr_ret;);
360
        chacha_encrypt_avx1(ctx, input, output, msglen);
361
        RESTORE_VECTOR_REGISTERS();
362
        return 0;
363
    }
364
    else {
365
        chacha_encrypt_x64(ctx, input, output, msglen);
366
        return 0;
367
    }
368
#elif defined(USE_ARM_CHACHA_SPEEDUP)
369
    /* Handle left over bytes from last block. */
370
    if ((msglen > 0) && (ctx->left > 0)) {
371
        byte* over = ((byte*)ctx->over) + CHACHA_CHUNK_BYTES - ctx->left;
372
        word32 l = min(msglen, ctx->left);
373
374
        wc_chacha_use_over(over, output, input, l);
375
376
        ctx->left -= l;
377
        input += l;
378
        output += l;
379
        msglen -= l;
380
    }
381
382
    if (msglen != 0) {
383
        wc_chacha_crypt_bytes(ctx, output, input, msglen);
384
    }
385
    return 0;
386
#else
387
3.98k
    wc_Chacha_encrypt_bytes(ctx, input, output, msglen);
388
3.98k
    return 0;
389
3.98k
#endif
390
3.98k
}
391
#endif /* HAVE_CHACHA */
392
#endif /* END ChaCha C implementation */
393
394
#if defined(HAVE_CHACHA) && defined(HAVE_XCHACHA)
395
396
void wc_Chacha_purge_current_block(ChaCha* ctx)
397
0
{
398
0
    if (ctx->left > 0) {
399
0
        byte scratch[CHACHA_CHUNK_BYTES];
400
0
        XMEMSET(scratch, 0, sizeof(scratch));
401
0
        (void)wc_Chacha_Process(ctx, scratch, scratch, CHACHA_CHUNK_BYTES - ctx->left);
402
0
    }
403
0
}
404
405
/*
406
 * wc_HChacha_block - half a ChaCha block, for XChaCha
407
 *
408
 * see https://tools.ietf.org/html/draft-arciszewski-xchacha-03
409
 */
410
static WC_INLINE void wc_HChacha_block(ChaCha* ctx,
411
    word32 stream[CHACHA_CHUNK_WORDS/2], word32 nrounds)
412
0
{
413
0
    word32 x[CHACHA_CHUNK_WORDS];
414
0
    word32 i;
415
416
0
    for (i = 0; i < CHACHA_CHUNK_WORDS; i++) {
417
0
        x[i] = ctx->X[i];
418
0
    }
419
420
0
    for (i = nrounds; i > 0; i -= 2) {
421
0
        QUARTERROUND(0, 4,  8, 12)
422
0
        QUARTERROUND(1, 5,  9, 13)
423
0
        QUARTERROUND(2, 6, 10, 14)
424
0
        QUARTERROUND(3, 7, 11, 15)
425
0
        QUARTERROUND(0, 5, 10, 15)
426
0
        QUARTERROUND(1, 6, 11, 12)
427
0
        QUARTERROUND(2, 7,  8, 13)
428
0
        QUARTERROUND(3, 4,  9, 14)
429
0
    }
430
431
0
    for (i = 0; i < CHACHA_CHUNK_WORDS/4; ++i)
432
0
        stream[i] = x[i];
433
0
    for (i = CHACHA_CHUNK_WORDS/4; i < CHACHA_CHUNK_WORDS/2; ++i)
434
0
        stream[i] = x[i + CHACHA_CHUNK_WORDS/2];
435
0
}
436
437
/* XChaCha -- https://tools.ietf.org/html/draft-arciszewski-xchacha-03 */
438
int wc_XChacha_SetKey(ChaCha *ctx,
439
                      const byte *key, word32 keySz,
440
                      const byte *nonce, word32 nonceSz,
441
                      word32 counter)
442
0
{
443
0
    int ret;
444
0
    word32 k[CHACHA_MAX_KEY_SZ];
445
0
    byte   iv[CHACHA_IV_BYTES];
446
447
0
    if (nonceSz != XCHACHA_NONCE_BYTES)
448
0
        return BAD_FUNC_ARG;
449
450
0
    if ((ret = wc_Chacha_SetKey(ctx, key, keySz)) < 0)
451
0
        return ret;
452
453
    /* form a first chacha IV from the first 16 bytes of the nonce.
454
     * the first word is supplied in the "counter" arg, and
455
     * the result is a full 128 bit nonceful IV for the one-time block
456
     * crypto op that follows.
457
     */
458
0
    if ((ret = wc_Chacha_SetIV(ctx, nonce + 4, U8TO32_LITTLE(nonce))) < 0)
459
0
        return ret;
460
461
0
    wc_HChacha_block(ctx, k, 20); /* 20 rounds, but keeping half the output. */
462
463
    /* the HChacha output is used as a 256 bit key for the main cipher. */
464
0
    XMEMCPY(&ctx->X[4], k, 8 * sizeof(word32));
465
466
    /* use 8 bytes from the end of the 24 byte nonce, padded up to 12 bytes,
467
     * to form the IV for the main cipher.
468
     */
469
0
    XMEMSET(iv, 0, 4);
470
0
    XMEMCPY(iv + 4, nonce + 16, 8);
471
472
0
    if ((ret = wc_Chacha_SetIV(ctx, iv, counter)) < 0)
473
0
        return ret;
474
475
0
    ForceZero(k, sizeof k);
476
0
    ForceZero(iv, sizeof iv);
477
478
0
    return 0;
479
0
}
480
481
#endif /* HAVE_CHACHA && HAVE_XCHACHA */