Coverage Report

Created: 2025-05-14 12:32

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-2021 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
0
#define U8C(v) (v##U)
24
0
#define U32C(v) (v##U)
25
26
0
#define U8V(v) ((u8)(v)&U8C(0xFF))
27
0
#define U32V(v) ((u32)(v)&U32C(0xFFFFFFFF))
28
29
0
#define ROTL32(v, n) (U32V((v) << (n)) | ((v) >> (32 - (n))))
30
31
#define U8TO32_LITTLE(p)                                              \
32
0
    (((u32)((p)[0])) | ((u32)((p)[1]) << 8) | ((u32)((p)[2]) << 16) | \
33
0
     ((u32)((p)[3]) << 24))
34
35
#define U32TO8_LITTLE(p, v)      \
36
0
    do {                         \
37
0
        (p)[0] = U8V((v));       \
38
0
        (p)[1] = U8V((v) >> 8);  \
39
0
        (p)[2] = U8V((v) >> 16); \
40
0
        (p)[3] = U8V((v) >> 24); \
41
0
    } 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
0
#define ROTATE(v, c) (ROTL32(v, c))
82
0
#define XOR(v, w) ((v) ^ (w))
83
0
#define PLUS(v, w) (U32V((v) + (w)))
84
0
#define PLUSONE(v) (PLUS((v), 1))
85
86
#define QUARTERROUND(a, b, c, d) \
87
0
    a = PLUS(a, b); d = ROTATE(XOR(d, a), 16);   \
88
0
    c = PLUS(c, d); b = ROTATE(XOR(b, c), 12);   \
89
0
    a = PLUS(a, b); d = ROTATE(XOR(d, a), 8);    \
90
0
    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
0
{
97
0
    const char* constants;
98
99
0
    x->input[4] = U8TO32_LITTLE(k + 0);
100
0
    x->input[5] = U8TO32_LITTLE(k + 4);
101
0
    x->input[6] = U8TO32_LITTLE(k + 8);
102
0
    x->input[7] = U8TO32_LITTLE(k + 12);
103
0
    if (kbits == 256) { /* recommended */
104
0
        k += 16;
105
0
        constants = sigma;
106
0
    } else { /* kbits == 128 */
107
0
        constants = tau;
108
0
    }
109
0
    x->input[8] = U8TO32_LITTLE(k + 0);
110
0
    x->input[9] = U8TO32_LITTLE(k + 4);
111
0
    x->input[10] = U8TO32_LITTLE(k + 8);
112
0
    x->input[11] = U8TO32_LITTLE(k + 12);
113
0
    x->input[0] = U8TO32_LITTLE(constants + 0);
114
0
    x->input[1] = U8TO32_LITTLE(constants + 4);
115
0
    x->input[2] = U8TO32_LITTLE(constants + 8);
116
0
    x->input[3] = U8TO32_LITTLE(constants + 12);
117
0
}
118
119
void ECRYPT_ivsetup(ECRYPT_ctx* x, const u8* iv)
120
0
{
121
0
    x->input[12] = 0;
122
0
    x->input[13] = 0;
123
0
    x->input[14] = U8TO32_LITTLE(iv + 0);
124
0
    x->input[15] = U8TO32_LITTLE(iv + 4);
125
0
}
126
127
void ECRYPT_encrypt_bytes(ECRYPT_ctx* x, const u8* m, u8* c, u32 bytes)
128
0
{
129
0
    u32 x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15;
130
0
    u32 j0, j1, j2, j3, j4, j5, j6, j7, j8, j9, j10, j11, j12, j13, j14, j15;
131
0
    u8* ctarget = nullptr;
132
0
    u8 tmp[64];
133
0
    uint32_t i;
134
135
0
    if (!bytes) return;
136
137
0
    j0 = x->input[0];
138
0
    j1 = x->input[1];
139
0
    j2 = x->input[2];
140
0
    j3 = x->input[3];
141
0
    j4 = x->input[4];
142
0
    j5 = x->input[5];
143
0
    j6 = x->input[6];
144
0
    j7 = x->input[7];
145
0
    j8 = x->input[8];
146
0
    j9 = x->input[9];
147
0
    j10 = x->input[10];
148
0
    j11 = x->input[11];
149
0
    j12 = x->input[12];
150
0
    j13 = x->input[13];
151
0
    j14 = x->input[14];
152
0
    j15 = x->input[15];
153
154
0
    for (;;) {
155
0
        if (bytes < 64) {
156
0
            for (i = 0; i < bytes; ++i)
157
0
                tmp[i] = m[i];
158
0
            m = tmp;
159
0
            ctarget = c;
160
0
            c = tmp;
161
0
        }
162
0
        x0 = j0;
163
0
        x1 = j1;
164
0
        x2 = j2;
165
0
        x3 = j3;
166
0
        x4 = j4;
167
0
        x5 = j5;
168
0
        x6 = j6;
169
0
        x7 = j7;
170
0
        x8 = j8;
171
0
        x9 = j9;
172
0
        x10 = j10;
173
0
        x11 = j11;
174
0
        x12 = j12;
175
0
        x13 = j13;
176
0
        x14 = j14;
177
0
        x15 = j15;
178
0
        for (i = 20; i > 0; i -= 2) {
179
0
            QUARTERROUND(x0, x4, x8, x12)
180
0
            QUARTERROUND(x1, x5, x9, x13)
181
0
            QUARTERROUND(x2, x6, x10, x14)
182
0
            QUARTERROUND(x3, x7, x11, x15)
183
0
            QUARTERROUND(x0, x5, x10, x15)
184
0
            QUARTERROUND(x1, x6, x11, x12)
185
0
            QUARTERROUND(x2, x7, x8, x13)
186
0
            QUARTERROUND(x3, x4, x9, x14)
187
0
        }
188
0
        x0 = PLUS(x0, j0);
189
0
        x1 = PLUS(x1, j1);
190
0
        x2 = PLUS(x2, j2);
191
0
        x3 = PLUS(x3, j3);
192
0
        x4 = PLUS(x4, j4);
193
0
        x5 = PLUS(x5, j5);
194
0
        x6 = PLUS(x6, j6);
195
0
        x7 = PLUS(x7, j7);
196
0
        x8 = PLUS(x8, j8);
197
0
        x9 = PLUS(x9, j9);
198
0
        x10 = PLUS(x10, j10);
199
0
        x11 = PLUS(x11, j11);
200
0
        x12 = PLUS(x12, j12);
201
0
        x13 = PLUS(x13, j13);
202
0
        x14 = PLUS(x14, j14);
203
0
        x15 = PLUS(x15, j15);
204
205
0
        x0 = XOR(x0, U8TO32_LITTLE(m + 0));
206
0
        x1 = XOR(x1, U8TO32_LITTLE(m + 4));
207
0
        x2 = XOR(x2, U8TO32_LITTLE(m + 8));
208
0
        x3 = XOR(x3, U8TO32_LITTLE(m + 12));
209
0
        x4 = XOR(x4, U8TO32_LITTLE(m + 16));
210
0
        x5 = XOR(x5, U8TO32_LITTLE(m + 20));
211
0
        x6 = XOR(x6, U8TO32_LITTLE(m + 24));
212
0
        x7 = XOR(x7, U8TO32_LITTLE(m + 28));
213
0
        x8 = XOR(x8, U8TO32_LITTLE(m + 32));
214
0
        x9 = XOR(x9, U8TO32_LITTLE(m + 36));
215
0
        x10 = XOR(x10, U8TO32_LITTLE(m + 40));
216
0
        x11 = XOR(x11, U8TO32_LITTLE(m + 44));
217
0
        x12 = XOR(x12, U8TO32_LITTLE(m + 48));
218
0
        x13 = XOR(x13, U8TO32_LITTLE(m + 52));
219
0
        x14 = XOR(x14, U8TO32_LITTLE(m + 56));
220
0
        x15 = XOR(x15, U8TO32_LITTLE(m + 60));
221
222
0
        j12 = PLUSONE(j12);
223
0
        if (!j12) {
224
0
            j13 = PLUSONE(j13);
225
            /* stopping at 2^70 bytes per nonce is user's responsibility */
226
0
        }
227
228
0
        U32TO8_LITTLE(c + 0, x0);
229
0
        U32TO8_LITTLE(c + 4, x1);
230
0
        U32TO8_LITTLE(c + 8, x2);
231
0
        U32TO8_LITTLE(c + 12, x3);
232
0
        U32TO8_LITTLE(c + 16, x4);
233
0
        U32TO8_LITTLE(c + 20, x5);
234
0
        U32TO8_LITTLE(c + 24, x6);
235
0
        U32TO8_LITTLE(c + 28, x7);
236
0
        U32TO8_LITTLE(c + 32, x8);
237
0
        U32TO8_LITTLE(c + 36, x9);
238
0
        U32TO8_LITTLE(c + 40, x10);
239
0
        U32TO8_LITTLE(c + 44, x11);
240
0
        U32TO8_LITTLE(c + 48, x12);
241
0
        U32TO8_LITTLE(c + 52, x13);
242
0
        U32TO8_LITTLE(c + 56, x14);
243
0
        U32TO8_LITTLE(c + 60, x15);
244
245
0
        if (bytes <= 64) {
246
0
            if (bytes < 64) {
247
0
                for (i = 0; i < bytes; ++i)
248
0
                    ctarget[i] = c[i];
249
0
            }
250
0
            x->input[12] = j12;
251
0
            x->input[13] = j13;
252
0
            return;
253
0
        }
254
0
        bytes -= 64;
255
0
        c += 64;
256
0
        m += 64;
257
0
    }
258
0
}
259
260
void ECRYPT_keystream_bytes(ECRYPT_ctx* x, u8* stream, u32 bytes)
261
0
{
262
0
    u32 i;
263
0
    for (i = 0; i < bytes; ++i)
264
0
        stream[i] = 0;
265
0
    ECRYPT_encrypt_bytes(x, stream, stream, bytes);
266
0
}
267
268
FUZZ_TARGET(crypto_diff_fuzz_chacha20)
269
0
{
270
0
    FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()};
271
272
0
    ECRYPT_ctx ctx;
273
274
0
    const std::vector<unsigned char> key = ConsumeFixedLengthByteVector(fuzzed_data_provider, 32);
275
0
    ChaCha20 chacha20{MakeByteSpan(key)};
276
0
    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
0
    static const uint8_t iv[8] = {0, 0, 0, 0, 0, 0, 0, 0};
280
0
    ChaCha20::Nonce96 nonce{0, 0};
281
0
    uint32_t counter{0};
282
0
    ECRYPT_ivsetup(&ctx, iv);
283
284
0
    LIMITED_WHILE (fuzzed_data_provider.ConsumeBool(), 3000) {
285
0
        CallOneOf(
286
0
            fuzzed_data_provider,
287
0
            [&] {
288
0
                const std::vector<unsigned char> key = ConsumeFixedLengthByteVector(fuzzed_data_provider, 32);
289
0
                chacha20.SetKey(MakeByteSpan(key));
290
0
                nonce = {0, 0};
291
0
                counter = 0;
292
0
                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
0
                uint8_t iv[8] = {0, 0, 0, 0, 0, 0, 0, 0};
295
0
                ECRYPT_ivsetup(&ctx, iv);
296
0
            },
297
0
            [&] {
298
0
                uint32_t iv_prefix = fuzzed_data_provider.ConsumeIntegral<uint32_t>();
299
0
                uint64_t iv = fuzzed_data_provider.ConsumeIntegral<uint64_t>();
300
0
                nonce = {iv_prefix, iv};
301
0
                counter = fuzzed_data_provider.ConsumeIntegral<uint32_t>();
302
0
                chacha20.Seek(nonce, counter);
303
0
                ctx.input[12] = counter;
304
0
                ctx.input[13] = iv_prefix;
305
0
                ctx.input[14] = iv;
306
0
                ctx.input[15] = iv >> 32;
307
0
            },
308
0
            [&] {
309
0
                uint32_t integralInRange = fuzzed_data_provider.ConsumeIntegralInRange<size_t>(0, 4096);
310
0
                std::vector<uint8_t> output(integralInRange);
311
0
                chacha20.Keystream(MakeWritableByteSpan(output));
312
0
                std::vector<uint8_t> djb_output(integralInRange);
313
0
                ECRYPT_keystream_bytes(&ctx, djb_output.data(), djb_output.size());
314
0
                assert(output == djb_output);
315
                // DJB's version seeks forward to a multiple of 64 bytes after every operation. Correct for that.
316
0
                uint32_t old_counter = counter;
317
0
                counter += (integralInRange + 63) >> 6;
318
0
                if (counter < old_counter) ++nonce.first;
319
0
                if (integralInRange & 63) {
320
0
                    chacha20.Seek(nonce, counter);
321
0
                }
322
0
                assert(counter == ctx.input[12]);
323
0
            },
324
0
            [&] {
325
0
                uint32_t integralInRange = fuzzed_data_provider.ConsumeIntegralInRange<size_t>(0, 4096);
326
0
                std::vector<uint8_t> output(integralInRange);
327
0
                const std::vector<uint8_t> input = ConsumeFixedLengthByteVector(fuzzed_data_provider, output.size());
328
0
                chacha20.Crypt(MakeByteSpan(input), MakeWritableByteSpan(output));
329
0
                std::vector<uint8_t> djb_output(integralInRange);
330
0
                ECRYPT_encrypt_bytes(&ctx, input.data(), djb_output.data(), input.size());
331
0
                assert(output == djb_output);
332
                // DJB's version seeks forward to a multiple of 64 bytes after every operation. Correct for that.
333
0
                uint32_t old_counter = counter;
334
0
                counter += (integralInRange + 63) >> 6;
335
0
                if (counter < old_counter) ++nonce.first;
336
0
                if (integralInRange & 63) {
337
0
                    chacha20.Seek(nonce, counter);
338
0
                }
339
                assert(counter == ctx.input[12]);
340
0
            });
341
0
    }
342
0
}