Coverage Report

Created: 2026-08-25 19:22

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/root/bitcoin/src/test/fuzz/crypto_diff_fuzz_chacha20.cpp
Line
Count
Source
1
// Copyright (c) 2020-present The Bitcoin Core developers
2
// Distributed under the MIT software license, see the accompanying
3
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
4
5
#include <crypto/chacha20.h>
6
#include <test/fuzz/FuzzedDataProvider.h>
7
#include <test/fuzz/fuzz.h>
8
#include <test/fuzz/util.h>
9
10
#include <cstdint>
11
#include <vector>
12
13
/*
14
From https://cr.yp.to/chacha.html
15
chacha-merged.c version 20080118
16
D. J. Bernstein
17
Public domain.
18
*/
19
20
typedef unsigned int u32;
21
typedef unsigned char u8;
22
23
331M
#define U8C(v) (v##U)
24
3.40G
#define U32C(v) (v##U)
25
26
331M
#define U8V(v) ((u8)(v)&U8C(0xFF))
27
3.40G
#define U32V(v) ((u32)(v)&U32C(0xFFFFFFFF))
28
29
1.65G
#define ROTL32(v, n) (U32V((v) << (n)) | ((v) >> (32 - (n))))
30
31
#define U8TO32_LITTLE(p)                                              \
32
113k
    (((u32)((p)[0])) | ((u32)((p)[1]) << 8) | ((u32)((p)[2]) << 16) | \
33
113k
     ((u32)((p)[3]) << 24))
34
35
#define U32TO8_LITTLE(p, v)      \
36
82.9M
    do {                         \
37
82.9M
        (p)[0] = U8V((v));       \
38
82.9M
        (p)[1] = U8V((v) >> 8);  \
39
82.9M
        (p)[2] = U8V((v) >> 16); \
40
82.9M
        (p)[3] = U8V((v) >> 24); \
41
82.9M
    } while (0)
42
43
/* ------------------------------------------------------------------------- */
44
/* Data structures */
45
46
typedef struct
47
{
48
    u32 input[16];
49
} ECRYPT_ctx;
50
51
/* ------------------------------------------------------------------------- */
52
/* Mandatory functions */
53
54
void ECRYPT_keysetup(
55
    ECRYPT_ctx* ctx,
56
    const u8* key,
57
    u32 keysize, /* Key size in bits. */
58
    u32 ivsize); /* IV size in bits. */
59
60
void ECRYPT_ivsetup(
61
    ECRYPT_ctx* ctx,
62
    const u8* iv);
63
64
void ECRYPT_encrypt_bytes(
65
    ECRYPT_ctx* ctx,
66
    const u8* plaintext,
67
    u8* ciphertext,
68
    u32 msglen); /* Message length in bytes. */
69
70
/* ------------------------------------------------------------------------- */
71
72
/* Optional features */
73
74
void ECRYPT_keystream_bytes(
75
    ECRYPT_ctx* ctx,
76
    u8* keystream,
77
    u32 length); /* Length of keystream in bytes. */
78
79
/* ------------------------------------------------------------------------- */
80
81
1.65G
#define ROTATE(v, c) (ROTL32(v, c))
82
82.9M
#define XOR(v, w) ((v) ^ (w))
83
1.74G
#define PLUS(v, w) (U32V((v) + (w)))
84
5.18M
#define PLUSONE(v) (PLUS((v), 1))
85
86
#define QUARTERROUND(a, b, c, d) \
87
414M
    a = PLUS(a, b); d = ROTATE(XOR(d, a), 16);   \
88
414M
    c = PLUS(c, d); b = ROTATE(XOR(b, c), 12);   \
89
414M
    a = PLUS(a, b); d = ROTATE(XOR(d, a), 8);    \
90
414M
    c = PLUS(c, d); b = ROTATE(XOR(b, c), 7);
91
92
static const char sigma[] = "expand 32-byte k";
93
static const char tau[] = "expand 16-byte k";
94
95
void ECRYPT_keysetup(ECRYPT_ctx* x, const u8* k, u32 kbits, u32 ivbits)
96
8.10k
{
97
8.10k
    const char* constants;
98
99
8.10k
    x->input[4] = U8TO32_LITTLE(k + 0);
100
8.10k
    x->input[5] = U8TO32_LITTLE(k + 4);
101
8.10k
    x->input[6] = U8TO32_LITTLE(k + 8);
102
8.10k
    x->input[7] = U8TO32_LITTLE(k + 12);
103
8.10k
    if (kbits == 256) { /* recommended */
  Branch (103:9): [True: 8.10k, False: 0]
104
8.10k
        k += 16;
105
8.10k
        constants = sigma;
106
8.10k
    } else { /* kbits == 128 */
107
0
        constants = tau;
108
0
    }
109
8.10k
    x->input[8] = U8TO32_LITTLE(k + 0);
110
8.10k
    x->input[9] = U8TO32_LITTLE(k + 4);
111
8.10k
    x->input[10] = U8TO32_LITTLE(k + 8);
112
8.10k
    x->input[11] = U8TO32_LITTLE(k + 12);
113
8.10k
    x->input[0] = U8TO32_LITTLE(constants + 0);
114
8.10k
    x->input[1] = U8TO32_LITTLE(constants + 4);
115
8.10k
    x->input[2] = U8TO32_LITTLE(constants + 8);
116
8.10k
    x->input[3] = U8TO32_LITTLE(constants + 12);
117
8.10k
}
118
119
void ECRYPT_ivsetup(ECRYPT_ctx* x, const u8* iv)
120
8.10k
{
121
8.10k
    x->input[12] = 0;
122
8.10k
    x->input[13] = 0;
123
8.10k
    x->input[14] = U8TO32_LITTLE(iv + 0);
124
8.10k
    x->input[15] = U8TO32_LITTLE(iv + 4);
125
8.10k
}
126
127
void ECRYPT_encrypt_bytes(ECRYPT_ctx* x, const u8* m, u8* c, u32 bytes)
128
220k
{
129
220k
    u32 x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15;
130
220k
    u32 j0, j1, j2, j3, j4, j5, j6, j7, j8, j9, j10, j11, j12, j13, j14, j15;
131
220k
    u8* ctarget = nullptr;
132
220k
    u8 tmp[64];
133
220k
    uint32_t i;
134
135
220k
    if (!bytes) return;
  Branch (135:9): [True: 34.2k, False: 186k]
136
137
186k
    j0 = x->input[0];
138
186k
    j1 = x->input[1];
139
186k
    j2 = x->input[2];
140
186k
    j3 = x->input[3];
141
186k
    j4 = x->input[4];
142
186k
    j5 = x->input[5];
143
186k
    j6 = x->input[6];
144
186k
    j7 = x->input[7];
145
186k
    j8 = x->input[8];
146
186k
    j9 = x->input[9];
147
186k
    j10 = x->input[10];
148
186k
    j11 = x->input[11];
149
186k
    j12 = x->input[12];
150
186k
    j13 = x->input[13];
151
186k
    j14 = x->input[14];
152
186k
    j15 = x->input[15];
153
154
5.18M
    for (;;) {
155
5.18M
        if (bytes < 64) {
  Branch (155:13): [True: 166k, False: 5.01M]
156
3.74M
            for (i = 0; i < bytes; ++i)
  Branch (156:25): [True: 3.58M, False: 166k]
157
3.58M
                tmp[i] = m[i];
158
166k
            m = tmp;
159
166k
            ctarget = c;
160
166k
            c = tmp;
161
166k
        }
162
5.18M
        x0 = j0;
163
5.18M
        x1 = j1;
164
5.18M
        x2 = j2;
165
5.18M
        x3 = j3;
166
5.18M
        x4 = j4;
167
5.18M
        x5 = j5;
168
5.18M
        x6 = j6;
169
5.18M
        x7 = j7;
170
5.18M
        x8 = j8;
171
5.18M
        x9 = j9;
172
5.18M
        x10 = j10;
173
5.18M
        x11 = j11;
174
5.18M
        x12 = j12;
175
5.18M
        x13 = j13;
176
5.18M
        x14 = j14;
177
5.18M
        x15 = j15;
178
56.9M
        for (i = 20; i > 0; i -= 2) {
  Branch (178:22): [True: 51.8M, False: 5.18M]
179
51.8M
            QUARTERROUND(x0, x4, x8, x12)
180
51.8M
            QUARTERROUND(x1, x5, x9, x13)
181
51.8M
            QUARTERROUND(x2, x6, x10, x14)
182
51.8M
            QUARTERROUND(x3, x7, x11, x15)
183
51.8M
            QUARTERROUND(x0, x5, x10, x15)
184
51.8M
            QUARTERROUND(x1, x6, x11, x12)
185
51.8M
            QUARTERROUND(x2, x7, x8, x13)
186
51.8M
            QUARTERROUND(x3, x4, x9, x14)
187
51.8M
        }
188
5.18M
        x0 = PLUS(x0, j0);
189
5.18M
        x1 = PLUS(x1, j1);
190
5.18M
        x2 = PLUS(x2, j2);
191
5.18M
        x3 = PLUS(x3, j3);
192
5.18M
        x4 = PLUS(x4, j4);
193
5.18M
        x5 = PLUS(x5, j5);
194
5.18M
        x6 = PLUS(x6, j6);
195
5.18M
        x7 = PLUS(x7, j7);
196
5.18M
        x8 = PLUS(x8, j8);
197
5.18M
        x9 = PLUS(x9, j9);
198
5.18M
        x10 = PLUS(x10, j10);
199
5.18M
        x11 = PLUS(x11, j11);
200
5.18M
        x12 = PLUS(x12, j12);
201
5.18M
        x13 = PLUS(x13, j13);
202
5.18M
        x14 = PLUS(x14, j14);
203
5.18M
        x15 = PLUS(x15, j15);
204
205
5.18M
        x0 = XOR(x0, U8TO32_LITTLE(m + 0));
206
5.18M
        x1 = XOR(x1, U8TO32_LITTLE(m + 4));
207
5.18M
        x2 = XOR(x2, U8TO32_LITTLE(m + 8));
208
5.18M
        x3 = XOR(x3, U8TO32_LITTLE(m + 12));
209
5.18M
        x4 = XOR(x4, U8TO32_LITTLE(m + 16));
210
5.18M
        x5 = XOR(x5, U8TO32_LITTLE(m + 20));
211
5.18M
        x6 = XOR(x6, U8TO32_LITTLE(m + 24));
212
5.18M
        x7 = XOR(x7, U8TO32_LITTLE(m + 28));
213
5.18M
        x8 = XOR(x8, U8TO32_LITTLE(m + 32));
214
5.18M
        x9 = XOR(x9, U8TO32_LITTLE(m + 36));
215
5.18M
        x10 = XOR(x10, U8TO32_LITTLE(m + 40));
216
5.18M
        x11 = XOR(x11, U8TO32_LITTLE(m + 44));
217
5.18M
        x12 = XOR(x12, U8TO32_LITTLE(m + 48));
218
5.18M
        x13 = XOR(x13, U8TO32_LITTLE(m + 52));
219
5.18M
        x14 = XOR(x14, U8TO32_LITTLE(m + 56));
220
5.18M
        x15 = XOR(x15, U8TO32_LITTLE(m + 60));
221
222
5.18M
        j12 = PLUSONE(j12);
223
5.18M
        if (!j12) {
  Branch (223:13): [True: 4.34k, False: 5.17M]
224
4.34k
            j13 = PLUSONE(j13);
225
            /* stopping at 2^70 bytes per nonce is user's responsibility */
226
4.34k
        }
227
228
5.18M
        U32TO8_LITTLE(c + 0, x0);
229
5.18M
        U32TO8_LITTLE(c + 4, x1);
230
5.18M
        U32TO8_LITTLE(c + 8, x2);
231
5.18M
        U32TO8_LITTLE(c + 12, x3);
232
5.18M
        U32TO8_LITTLE(c + 16, x4);
233
5.18M
        U32TO8_LITTLE(c + 20, x5);
234
5.18M
        U32TO8_LITTLE(c + 24, x6);
235
5.18M
        U32TO8_LITTLE(c + 28, x7);
236
5.18M
        U32TO8_LITTLE(c + 32, x8);
237
5.18M
        U32TO8_LITTLE(c + 36, x9);
238
5.18M
        U32TO8_LITTLE(c + 40, x10);
239
5.18M
        U32TO8_LITTLE(c + 44, x11);
240
5.18M
        U32TO8_LITTLE(c + 48, x12);
241
5.18M
        U32TO8_LITTLE(c + 52, x13);
242
5.18M
        U32TO8_LITTLE(c + 56, x14);
243
5.18M
        U32TO8_LITTLE(c + 60, x15);
244
245
5.18M
        if (bytes <= 64) {
  Branch (245:13): [True: 186k, False: 4.99M]
246
186k
            if (bytes < 64) {
  Branch (246:17): [True: 166k, False: 19.8k]
247
3.74M
                for (i = 0; i < bytes; ++i)
  Branch (247:29): [True: 3.58M, False: 166k]
248
3.58M
                    ctarget[i] = c[i];
249
166k
            }
250
186k
            x->input[12] = j12;
251
186k
            x->input[13] = j13;
252
186k
            return;
253
186k
        }
254
4.99M
        bytes -= 64;
255
4.99M
        c += 64;
256
4.99M
        m += 64;
257
4.99M
    }
258
186k
}
259
260
void ECRYPT_keystream_bytes(ECRYPT_ctx* x, u8* stream, u32 bytes)
261
165k
{
262
165k
    u32 i;
263
171M
    for (i = 0; i < bytes; ++i)
  Branch (263:17): [True: 171M, False: 165k]
264
171M
        stream[i] = 0;
265
165k
    ECRYPT_encrypt_bytes(x, stream, stream, bytes);
266
165k
}
267
268
FUZZ_TARGET(crypto_diff_fuzz_chacha20)
269
558
{
270
558
    FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()};
271
272
558
    ECRYPT_ctx ctx;
273
274
558
    const std::vector<unsigned char> key = ConsumeFixedLengthByteVector(fuzzed_data_provider, 32);
275
558
    ChaCha20 chacha20{MakeByteSpan(key)};
276
558
    ECRYPT_keysetup(&ctx, key.data(), key.size() * 8, 0);
277
278
    // ECRYPT_keysetup() doesn't set the counter and nonce to 0 while SetKey() does
279
558
    static const uint8_t iv[8] = {0, 0, 0, 0, 0, 0, 0, 0};
280
558
    ChaCha20::Nonce96 nonce{0, 0};
281
558
    uint32_t counter{0};
282
558
    ECRYPT_ivsetup(&ctx, iv);
283
284
266k
    LIMITED_WHILE (fuzzed_data_provider.ConsumeBool(), 3000) {
285
266k
        CallOneOf(
286
266k
            fuzzed_data_provider,
287
266k
            [&] {
288
7.55k
                const std::vector<unsigned char> key = ConsumeFixedLengthByteVector(fuzzed_data_provider, 32);
289
7.55k
                chacha20.SetKey(MakeByteSpan(key));
290
7.55k
                nonce = {0, 0};
291
7.55k
                counter = 0;
292
7.55k
                ECRYPT_keysetup(&ctx, key.data(), key.size() * 8, 0);
293
                // ECRYPT_keysetup() doesn't set the counter and nonce to 0 while SetKey() does
294
7.55k
                uint8_t iv[8] = {0, 0, 0, 0, 0, 0, 0, 0};
295
7.55k
                ECRYPT_ivsetup(&ctx, iv);
296
7.55k
            },
297
266k
            [&] {
298
38.2k
                uint32_t iv_prefix = fuzzed_data_provider.ConsumeIntegral<uint32_t>();
299
38.2k
                uint64_t iv = fuzzed_data_provider.ConsumeIntegral<uint64_t>();
300
38.2k
                nonce = {iv_prefix, iv};
301
38.2k
                counter = fuzzed_data_provider.ConsumeIntegral<uint32_t>();
302
38.2k
                chacha20.Seek(nonce, counter);
303
38.2k
                ctx.input[12] = counter;
304
38.2k
                ctx.input[13] = iv_prefix;
305
38.2k
                ctx.input[14] = iv;
306
38.2k
                ctx.input[15] = iv >> 32;
307
38.2k
            },
308
266k
            [&] {
309
165k
                uint32_t integralInRange = fuzzed_data_provider.ConsumeIntegralInRange<size_t>(0, 4096);
310
165k
                std::vector<uint8_t> output(integralInRange);
311
165k
                chacha20.Keystream(MakeWritableByteSpan(output));
312
165k
                std::vector<uint8_t> djb_output(integralInRange);
313
165k
                ECRYPT_keystream_bytes(&ctx, djb_output.data(), djb_output.size());
314
165k
                assert(output == djb_output);
  Branch (314:17): [True: 165k, False: 0]
315
                // DJB's version seeks forward to a multiple of 64 bytes after every operation. Correct for that.
316
165k
                uint32_t old_counter = counter;
317
165k
                counter += (integralInRange + 63) >> 6;
318
165k
                if (counter < old_counter) ++nonce.first;
  Branch (318:21): [True: 1.39k, False: 163k]
319
165k
                if (integralInRange & 63) {
  Branch (319:21): [True: 121k, False: 43.6k]
320
121k
                    chacha20.Seek(nonce, counter);
321
121k
                }
322
165k
                assert(counter == ctx.input[12]);
  Branch (322:17): [True: 165k, False: 0]
323
165k
            },
324
266k
            [&] {
325
55.1k
                uint32_t integralInRange = fuzzed_data_provider.ConsumeIntegralInRange<size_t>(0, 4096);
326
55.1k
                std::vector<uint8_t> output(integralInRange);
327
55.1k
                const std::vector<uint8_t> input = ConsumeFixedLengthByteVector(fuzzed_data_provider, output.size());
328
55.1k
                chacha20.Crypt(MakeByteSpan(input), MakeWritableByteSpan(output));
329
55.1k
                std::vector<uint8_t> djb_output(integralInRange);
330
55.1k
                ECRYPT_encrypt_bytes(&ctx, input.data(), djb_output.data(), input.size());
331
55.1k
                assert(output == djb_output);
  Branch (331:17): [True: 55.1k, False: 0]
332
                // DJB's version seeks forward to a multiple of 64 bytes after every operation. Correct for that.
333
55.1k
                uint32_t old_counter = counter;
334
55.1k
                counter += (integralInRange + 63) >> 6;
335
55.1k
                if (counter < old_counter) ++nonce.first;
  Branch (335:21): [True: 2.94k, False: 52.1k]
336
55.1k
                if (integralInRange & 63) {
  Branch (336:21): [True: 44.5k, False: 10.5k]
337
44.5k
                    chacha20.Seek(nonce, counter);
338
44.5k
                }
339
                assert(counter == ctx.input[12]);
  Branch (339:17): [True: 55.1k, False: 0]
340
55.1k
            });
341
266k
    }
342
558
}