Coverage Report

Created: 2022-08-24 06:37

/src/cryptofuzz-sp-math-all-8bit/modules/wolfcrypt/bn_helper.cpp
Line
Count
Source (jump to first uncovered line)
1
#include "bn_ops.h"
2
#include <iostream>
3
4
namespace cryptofuzz {
5
namespace module {
6
7
namespace wolfCrypt_detail {
8
#if defined(CRYPTOFUZZ_WOLFCRYPT_ALLOCATION_FAILURES)
9
    extern bool disableAllocationFailures;
10
    extern bool haveAllocFailure;
11
#endif
12
} /* namespace wolfCrypt_detail */
13
14
namespace wolfCrypt_bignum {
15
16
80.9k
Bignum::read_radix_error_t Bignum::read_radix(mp_int* dest, const std::string& str, const size_t base) {
17
80.9k
    return read_radix(dest, str.c_str(), base);
18
80.9k
}
19
20
80.9k
Bignum::read_radix_error_t Bignum::read_radix(mp_int* dest, const char* str, const size_t base) {
21
80.9k
    Bignum::read_radix_error_t ret;
22
23
    /* Create a temporary variable for storing the result of mp_read_radix,
24
     * because if mp_read_radix fails (e.g. due to allocation failure),
25
     * it will set the value of the destination variable to 0.
26
     *
27
     * See OSS-Fuzz 31709 / ZD 11834 for discussion. */
28
80.9k
    auto newMp = (mp_int*)util::malloc(sizeof(mp_int));
29
80.9k
    if ( mp_init(newMp) != MP_OKAY ) {
30
0
        util::free(newMp);
31
0
        return READ_RADIX_FAIL_MEMORY;
32
0
    }
33
34
80.9k
    wolfCrypt_detail::haveAllocFailure = false;
35
80.9k
    if ( mp_read_radix(newMp, str, base) != MP_OKAY ) {
36
326
        ret = wolfCrypt_detail::haveAllocFailure ? READ_RADIX_FAIL_MEMORY : READ_RADIX_FAIL_OTHER;
37
326
        goto end;
38
326
    }
39
40
80.6k
    wolfCrypt_detail::haveAllocFailure = false;
41
80.6k
    if ( mp_copy(newMp, dest) != MP_OKAY ) {
42
0
        ret = wolfCrypt_detail::haveAllocFailure ? READ_RADIX_FAIL_MEMORY : READ_RADIX_FAIL_OTHER;
43
0
        goto end;
44
0
    }
45
46
80.6k
    ret = READ_RADIX_OK;
47
48
80.9k
end:
49
80.9k
    CF_NORET(mp_clear(newMp));
50
80.9k
    util::free(newMp);
51
52
80.9k
    return ret;
53
80.6k
}
54
55
8.85k
void Bignum::baseConversion(void) const {
56
8.85k
#if !defined(WOLFSSL_SP_MATH)
57
8.85k
    uint8_t base = 2;
58
8.85k
    char* str = nullptr;
59
60
8.85k
    try { base = ds.Get<uint8_t>(); } catch ( fuzzing::datasource::Datasource::OutOfData ) { }
61
62
#if defined(CRYPTOFUZZ_WOLFCRYPT_DEBUG)
63
    std::cout << "Convert to base " << std::to_string(base) << " and back" << std::endl;
64
#endif
65
8.85k
    {
66
8.85k
        int size;
67
8.85k
        CF_CHECK_EQ(mp_radix_size(mp, base, &size), MP_OKAY);
68
90
        CF_ASSERT(size > 0, "Output of mp_radix_size is 0 or less");
69
70
90
        str = (char*)util::malloc(size);
71
72
90
#if defined(WOLFSSL_SP_MATH) || defined(WOLFSSL_SP_MATH_ALL) || !defined(USE_FAST_MATH)
73
90
        CF_CHECK_EQ(mp_toradix(mp, str, base), MP_OKAY);
74
#else
75
        wolfCrypt_detail::haveAllocFailure = false;
76
        CF_ASSERT(
77
                    mp_toradix(mp, str, base) == MP_OKAY ||
78
                    wolfCrypt_detail::haveAllocFailure ||
79
                    base < 2 ||
80
                    base > 64,
81
                    "wolfCrypt cannot convert mp to string");
82
83
        /* If allocation failure occurred, then do not use 'str' */
84
        CF_CHECK_FALSE(wolfCrypt_detail::haveAllocFailure);
85
#endif
86
87
84
        {
88
84
            const auto ret = read_radix(mp, str, base);
89
84
            CF_ASSERT(ret == READ_RADIX_OK || ret == READ_RADIX_FAIL_MEMORY, "wolfCrypt cannot parse the output of mp_toradix");
90
84
        }
91
84
    }
92
93
8.85k
end:
94
8.85k
    util::free(str);
95
8.85k
#endif
96
8.85k
}
97
98
8.28k
void Bignum::binaryConversion(void) const {
99
8.28k
    uint8_t* data = nullptr;
100
101
8.28k
    CF_CHECK_EQ(mp_isneg(mp), 0);
102
103
7.92k
    {
104
7.92k
        const auto size = mp_unsigned_bin_size(mp);
105
7.92k
        CF_ASSERT(size >= 0, "mp_unsigned_bin_size returned negative value");
106
107
7.92k
        data = util::malloc(size);
108
7.92k
        CF_CHECK_EQ(mp_to_unsigned_bin_len(mp, data, size), MP_OKAY);
109
110
        /* Ensure no allocation failure occurs in mp_read_unsigned_bin
111
         * because this can leave the mp in a corrupted state
112
         */
113
5.15k
        const auto cached_disableAllocationFailures = wolfCrypt_detail::disableAllocationFailures;
114
5.15k
        wolfCrypt_detail::disableAllocationFailures = true;
115
116
5.15k
        CF_ASSERT(mp_read_unsigned_bin(mp, data, size) == MP_OKAY, "Cannot parse output of mp_to_unsigned_bin_len");
117
118
5.15k
        wolfCrypt_detail::disableAllocationFailures = cached_disableAllocationFailures;
119
5.15k
    }
120
121
8.28k
end:
122
8.28k
    util::free(data);
123
8.28k
}
124
125
Bignum::Bignum(Datasource& ds) :
126
103k
    ds(ds) {
127
103k
    mp = (mp_int*)util::malloc(sizeof(mp_int));
128
103k
    if ( mp_init(mp) != MP_OKAY ) {
129
0
        util::free(mp);
130
0
        throw std::exception();
131
0
    }
132
103k
}
133
134
Bignum::Bignum(mp_int* mp, Datasource& ds) :
135
    mp(mp),
136
    ds(ds),
137
    noFree(true)
138
3.18k
{ }
139
140
Bignum::Bignum(const Bignum& other) :
141
62.4k
    ds(other.ds) {
142
62.4k
    mp = (mp_int*)util::malloc(sizeof(mp_int));
143
62.4k
    if ( mp_init(mp) != MP_OKAY ) {
144
0
        util::free(mp);
145
0
        throw std::exception();
146
0
    }
147
62.4k
    if ( mp_copy(other.mp, mp) != MP_OKAY ) {
148
0
        util::free(mp);
149
0
        throw std::exception();
150
0
    }
151
62.4k
}
152
153
Bignum::Bignum(const Bignum&& other) :
154
62.4k
    ds(other.ds) {
155
62.4k
    mp = (mp_int*)util::malloc(sizeof(mp_int));
156
62.4k
    if ( mp_init(mp) != MP_OKAY ) {
157
0
        util::free(mp);
158
0
        throw std::exception();
159
0
    }
160
62.4k
    if ( mp_copy(other.mp, mp) != MP_OKAY ) {
161
0
        util::free(mp);
162
0
        throw std::exception();
163
0
    }
164
62.4k
}
165
166
231k
Bignum::~Bignum() {
167
231k
    if ( noFree == false ) {
168
228k
        CF_NORET(mp_clear(mp));
169
228k
        util::free(mp);
170
228k
    }
171
231k
}
172
173
0
void Bignum::SetNoFree(void) {
174
0
    noFree = true;
175
0
}
176
177
80.9k
bool Bignum::Set(const std::string s) {
178
80.9k
    bool ret = false;
179
180
80.9k
    bool hex = false;
181
80.9k
    try {
182
80.9k
        hex = ds.Get<bool>();
183
80.9k
    } catch ( ... ) { }
184
185
#if defined(WOLFSSL_SP_MATH)
186
    hex = true;
187
#endif
188
189
80.9k
    if ( hex == true ) {
190
7.72k
        CF_CHECK_EQ(read_radix(mp, util::DecToHex(s), 16), READ_RADIX_OK);
191
73.1k
    } else {
192
73.1k
        CF_CHECK_EQ(read_radix(mp, s, 10), READ_RADIX_OK);
193
73.0k
    }
194
195
80.5k
    ret = true;
196
80.9k
end:
197
80.9k
    return ret;
198
80.5k
}
199
200
12.4k
bool Bignum::Set(const component::Bignum i) {
201
12.4k
    bool ret = false;
202
203
12.4k
    CF_CHECK_EQ(Set(i.ToString()), true);
204
205
12.3k
    ret = true;
206
12.4k
end:
207
12.4k
    return ret;
208
12.3k
}
209
210
70.2k
mp_int* Bignum::GetPtr(void) const {
211
70.2k
    {
212
70.2k
#if defined(WOLFSSL_SP_MATH) || defined(WOLFSSL_SP_MATH_ALL)
213
70.2k
        CF_ASSERT(mp->used <= mp->size, "used is larger than size");
214
#elif !defined(USE_FAST_MATH)
215
        CF_ASSERT(mp->used <= mp->alloc, "used is larger than size");
216
#endif
217
70.2k
    }
218
219
0
    {
220
        /* Optionally clamp the bignum. This should not affect its value. */
221
222
70.2k
        bool clamp = false;
223
224
70.2k
        try { clamp = ds.Get<bool>(); } catch ( fuzzing::datasource::Datasource::OutOfData ) { }
225
226
70.2k
        if ( clamp ) {
227
            /* Implemented as a macro so CF_NORET cannot be used here */
228
9.06k
            /* noret */ mp_clamp(mp);
229
9.06k
        }
230
70.2k
    }
231
232
70.2k
    {
233
        /* Optionally convert to a random base and back */
234
235
70.2k
        bool convert = false;
236
237
70.2k
        try { convert = ds.Get<bool>(); } catch ( fuzzing::datasource::Datasource::OutOfData ) { }
238
239
70.2k
        if ( convert ) {
240
8.85k
            baseConversion();
241
8.85k
        }
242
70.2k
    }
243
244
70.2k
    {
245
        /* Optionally convert to bytes and back */
246
247
70.2k
        bool convert = false;
248
249
70.2k
        try { convert = ds.Get<bool>(); } catch ( fuzzing::datasource::Datasource::OutOfData ) { }
250
251
70.2k
        if ( convert ) {
252
8.28k
            binaryConversion();
253
8.28k
        }
254
70.2k
    }
255
256
70.2k
    return mp;
257
70.2k
}
258
259
77.7k
mp_int* Bignum::GetPtrDirect(void) const {
260
77.7k
    return mp;
261
77.7k
}
262
263
1.50k
std::optional<uint64_t> Bignum::AsUint64(void) const {
264
1.50k
    std::optional<uint64_t> ret = std::nullopt;
265
1.50k
    uint64_t v = 0;
266
267
1.50k
#if !defined(WOLFSSL_SP_MATH)
268
1.50k
    CF_CHECK_EQ(mp_isneg(mp), 0);
269
1.45k
#endif
270
1.45k
    CF_CHECK_LTE(mp_count_bits(mp), (int)(sizeof(v) * 8));
271
1.35k
    CF_CHECK_EQ(mp_to_unsigned_bin_len(mp, (uint8_t*)&v, sizeof(v)), MP_OKAY);
272
1.35k
    v =
273
1.35k
        ((v & 0xFF00000000000000) >> 56) |
274
1.35k
        ((v & 0x00FF000000000000) >> 40) |
275
1.35k
        ((v & 0x0000FF0000000000) >> 24) |
276
1.35k
        ((v & 0x000000FF00000000) >>  8) |
277
1.35k
        ((v & 0x00000000FF000000) <<  8) |
278
1.35k
        ((v & 0x0000000000FF0000) << 24) |
279
1.35k
        ((v & 0x000000000000FF00) << 40) |
280
1.35k
        ((v & 0x00000000000000FF) << 56);
281
282
1.35k
    ret = v;
283
1.50k
end:
284
1.50k
    return ret;
285
1.35k
}
286
287
14.0k
std::optional<std::string> Bignum::ToDecString(void) {
288
14.0k
    std::optional<std::string> ret = std::nullopt;
289
14.0k
    char* str = nullptr;
290
291
#if defined(WOLFSSL_SP_MATH)
292
    str = (char*)util::malloc(8192);
293
294
    CF_CHECK_EQ(mp_tohex(mp, str), MP_OKAY);
295
    ret = { util::HexToDec(str) };
296
#else
297
14.0k
    bool hex = false;
298
14.0k
    int size;
299
300
14.0k
    try {
301
14.0k
        hex = ds.Get<bool>();
302
14.0k
    } catch ( ... ) { }
303
304
305
14.0k
    if ( hex == true ) {
306
1.11k
        CF_CHECK_EQ(mp_radix_size(mp, 16, &size), MP_OKAY);
307
1.11k
        CF_ASSERT(size > 0, "Output of mp_radix_size is 0 or less");
308
309
1.11k
        str = (char*)util::malloc(size+1);
310
311
1.11k
        CF_CHECK_EQ(mp_tohex(mp, str), MP_OKAY);
312
1.11k
        ret = { util::HexToDec(str) };
313
12.9k
    } else {
314
12.9k
        CF_CHECK_EQ(mp_radix_size(mp, 10, &size), MP_OKAY);
315
12.6k
        CF_ASSERT(size > 0, "Output of mp_radix_size is 0 or less");
316
317
12.6k
        str = (char*)util::malloc(size);
318
319
12.6k
        CF_CHECK_EQ(mp_toradix(mp, str, 10), MP_OKAY);
320
12.5k
        ret = std::string(str);
321
12.5k
    }
322
13.6k
#endif
323
324
14.0k
end:
325
14.0k
    free(str);
326
327
14.0k
    return ret;
328
14.0k
}
329
330
6.78k
std::optional<component::Bignum> Bignum::ToComponentBignum(void) {
331
6.78k
    std::optional<component::Bignum> ret = std::nullopt;
332
333
6.78k
    auto str = ToDecString();
334
6.78k
    CF_CHECK_NE(str, std::nullopt);
335
6.59k
    ret = { str };
336
6.78k
end:
337
6.78k
    return ret;
338
6.59k
}
339
340
10.0k
bool Bignum::ToBin(uint8_t* dest, const size_t size) {
341
10.0k
    bool ret = false;
342
343
10.0k
    const auto required = mp_unsigned_bin_size(GetPtr());
344
10.0k
    CF_ASSERT(required >= 0, "mp_unsigned_bin_size returned negative value");
345
346
10.0k
    CF_CHECK_GTE(size, static_cast<size_t>(required));
347
9.90k
    CF_CHECK_EQ(mp_to_unsigned_bin_len(GetPtr(), dest, size), MP_OKAY);
348
349
9.51k
    ret = true;
350
10.0k
end:
351
10.0k
    return ret;
352
9.51k
}
353
354
355
4.73k
std::optional<std::vector<uint8_t>> Bignum::ToBin(Datasource& ds, const component::Bignum b, std::optional<size_t> size) {
356
4.73k
    std::optional<std::vector<uint8_t>> ret = std::nullopt;
357
4.73k
    std::vector<uint8_t> v;
358
4.73k
    Bignum bn(ds);
359
4.73k
    uint16_t padding = 0;
360
361
4.73k
    CF_CHECK_EQ(bn.Set(b), true);
362
4.71k
    if ( size != std::nullopt ) {
363
0
        v.resize(*size);
364
4.71k
    } else {
365
4.71k
        try {
366
4.71k
            padding = ds.Get<uint16_t>();
367
4.71k
        } catch ( fuzzing::datasource::Datasource::OutOfData ) { }
368
369
4.71k
        v.resize( mp_unsigned_bin_size(bn.GetPtr()) + padding );
370
4.71k
    }
371
372
4.71k
    CF_CHECK_EQ(bn.ToBin(v.data(), v.size()), true);
373
374
4.32k
    ret = v;
375
4.73k
end:
376
4.73k
    return ret;
377
4.32k
}
378
379
5.05k
bool Bignum::ToBin(Datasource& ds, const component::Bignum b, uint8_t* dest, const size_t size) {
380
5.05k
    bool ret = false;
381
5.05k
    Bignum bn(ds);
382
383
5.05k
    CF_CHECK_EQ(bn.Set(b), true);
384
5.00k
    CF_CHECK_EQ(bn.ToBin(dest, size), true);
385
386
4.90k
    ret = true;
387
5.05k
end:
388
5.05k
    return ret;
389
4.90k
}
390
391
1.30k
bool Bignum::ToBin(Datasource& ds, const component::BignumPair b, uint8_t* dest, const size_t size) {
392
1.30k
    CF_ASSERT((size % 2) == 0, "Input size is not multiple of 2 in Bignum::ToBin");
393
394
1.30k
    bool ret = false;
395
1.30k
    const auto halfSize = size / 2;
396
397
1.30k
    CF_CHECK_EQ(ToBin(ds, b.first, dest, halfSize), true);
398
1.28k
    CF_CHECK_EQ(ToBin(ds, b.second, dest + halfSize, halfSize), true);
399
400
1.26k
    ret = true;
401
1.30k
end:
402
1.30k
    return ret;
403
1.26k
}
404
405
1.15k
std::optional<component::Bignum> Bignum::BinToBignum(Datasource& ds, const uint8_t* src, const size_t size) {
406
1.15k
    std::optional<component::Bignum> ret = std::nullopt;
407
408
1.15k
    wolfCrypt_bignum::Bignum bn(ds);
409
1.15k
    CF_CHECK_EQ(mp_read_unsigned_bin(bn.GetPtr(), src, size), MP_OKAY);
410
411
1.15k
    ret = bn.ToComponentBignum();
412
413
1.15k
end:
414
1.15k
    return ret;
415
1.15k
}
416
417
776
std::optional<component::BignumPair> Bignum::BinToBignumPair(Datasource& ds, const uint8_t* src, const size_t size) {
418
776
    CF_ASSERT((size % 2) == 0, "Input size is not multiple of 2 in Bignum::BinToBignumPair");
419
420
776
    std::optional<component::BignumPair> ret = std::nullopt;
421
776
    std::optional<component::Bignum> A, B;
422
776
    const auto halfSize = size / 2;
423
424
776
    {
425
776
        wolfCrypt_bignum::Bignum bn(ds);
426
776
        CF_CHECK_EQ(mp_read_unsigned_bin(bn.GetPtr(), src, halfSize), MP_OKAY);
427
776
        CF_CHECK_NE(A = bn.ToComponentBignum(), std::nullopt);
428
755
    }
429
430
0
    {
431
755
        wolfCrypt_bignum::Bignum bn(ds);
432
755
        CF_CHECK_EQ(mp_read_unsigned_bin(bn.GetPtr(), src + halfSize, halfSize), MP_OKAY);
433
755
        CF_CHECK_NE(B = bn.ToComponentBignum(), std::nullopt);
434
738
    }
435
436
437
0
    ret = {A->ToTrimmedString(), B->ToTrimmedString()};
438
439
776
end:
440
776
    return ret;
441
738
}
442
443
15.6k
void Bignum::Randomize(void) {
444
15.6k
    std::vector<uint8_t> data;
445
15.6k
    try {
446
15.6k
        data = ds.GetData(0, 1, 1024);
447
15.6k
    } catch ( fuzzing::datasource::Datasource::OutOfData ) { }
448
449
15.6k
    if ( !data.empty() ) {
450
        /* ignore return value */ mp_read_unsigned_bin(GetPtrDirect(), data.data(), data.size());
451
313
    }
452
15.6k
}
453
454
1.72k
bool Bignum::operator==(const Bignum& rhs) const {
455
1.72k
    return mp_cmp(GetPtr(), rhs.GetPtr()) == MP_EQ;
456
1.72k
}
457
458
BignumCluster::BignumCluster(Datasource& ds, Bignum bn0, Bignum bn1, Bignum bn2, Bignum bn3) :
459
    ds(ds),
460
    bn({bn0, bn1, bn2, bn3})
461
15.6k
{ }
462
463
15.6k
BignumCluster::~BignumCluster() {
464
78.1k
    for (size_t i = 0; i < 4; i++) {
465
62.4k
        if ( cache.bn[i] == nullptr ) {
466
616
            continue;
467
616
        }
468
469
61.8k
        mp_clear(cache.bn[i]);
470
61.8k
        util::free(cache.bn[i]);
471
61.8k
    }
472
15.6k
}
473
474
18.8k
Bignum& BignumCluster::operator[](const size_t index) {
475
18.8k
    CF_ASSERT(index < bn.size(), "Invalid index requested in BignumCluster::operator[]");
476
477
18.8k
    try {
478
        /* Rewire? */
479
18.8k
        if ( ds.Get<bool>() == true ) {
480
            /* Pick a random bignum */
481
1.83k
            const auto newIndex = ds.Get<uint8_t>() % 4;
482
483
            /* Same value? */
484
1.83k
            if ( bn[newIndex] == bn[index] ) {
485
                /* Then return reference to other bignum */
486
718
                return bn[newIndex];
487
718
            }
488
489
            /* Fall through */
490
1.83k
        }
491
18.8k
    } catch ( fuzzing::datasource::Datasource::OutOfData ) { }
492
493
18.1k
    return bn[index];
494
18.8k
}
495
496
62.3k
bool BignumCluster::Set(const size_t index, const std::string s) {
497
62.3k
    CF_ASSERT(index < bn.size(), "Invalid index requested in BignumCluster::Set");
498
499
62.3k
    return bn[index].Set(s);
500
62.3k
}
501
502
115
mp_int* BignumCluster::GetDestPtr(const size_t index) {
503
    /* Because it is requested as a destination pointer,
504
     * this bignum will be altered, hence invalidate
505
     * the cache
506
     */
507
115
    InvalidateCache();
508
509
115
    return bn[index].GetPtr();
510
115
}
511
512
15.4k
void BignumCluster::Save(void) {
513
77.3k
    for (size_t i = 0; i < 4; i++) {
514
61.8k
        mp_int* cached_mp = (mp_int*)util::malloc(sizeof(mp_int));
515
516
61.8k
        wolfCrypt_detail::disableAllocationFailures = true;
517
518
61.8k
        CF_ASSERT(mp_init(cached_mp) == MP_OKAY, "mp_init failed unexpectedly");
519
61.8k
        CF_ASSERT(mp_copy(bn[i].GetPtrDirect(), cached_mp) == MP_OKAY, "mp_copy failed unexpectedly");
520
521
61.8k
        wolfCrypt_detail::disableAllocationFailures = false;
522
523
61.8k
        cache.bn[i] = cached_mp;
524
61.8k
    }
525
15.4k
}
526
527
257
void BignumCluster::InvalidateCache(void) {
528
257
    cache.invalid = true;
529
257
}
530
531
4.10k
bool BignumCluster::EqualsCache(void) const {
532
4.10k
    if ( cache.invalid == true ) {
533
202
        return true;
534
202
    }
535
536
19.5k
    for (size_t i = 0; i < 4; i++) {
537
15.6k
        if ( cache.bn[i] == nullptr ) {
538
0
            continue;
539
0
        }
540
541
15.6k
        wolfCrypt_detail::disableAllocationFailures = true;
542
543
15.6k
        if ( mp_cmp(bn[i].GetPtrDirect(), cache.bn[i]) != MP_EQ ) {
544
#if defined(CRYPTOFUZZ_WOLFCRYPT_DEBUG)
545
            char str[8192];
546
547
            std::cout << "Bignum with index " << std::to_string(i) << " was changed" << std::endl;
548
549
            wolfCrypt_detail::disableAllocationFailures = true;
550
551
            CF_ASSERT(mp_tohex(cache.bn[i], str) == MP_OKAY, "mp_tohex failed unexpectedly");
552
            printf("it was: %s\n", str);
553
554
            CF_ASSERT(mp_tohex(bn[i].GetPtrDirect(), str) == MP_OKAY, "mp_tohex failed unexpectedly");
555
            printf("it is now %s\n", str);
556
557
#endif
558
0
            wolfCrypt_detail::disableAllocationFailures = false;
559
560
0
            return false;
561
0
        }
562
563
15.6k
        wolfCrypt_detail::disableAllocationFailures = false;
564
15.6k
    }
565
566
3.90k
    return true;
567
3.90k
}
568
569
} /* namespace wolfCrypt_bignum */
570
} /* namespace module */
571
} /* namespace cryptofuzz */