Coverage Report

Created: 2024-06-28 06:19

/src/cryptofuzz/expmod.cpp
Line
Count
Source (jump to first uncovered line)
1
#include "expmod.h"
2
#include <boost/random.hpp>
3
#include <fuzzing/datasource/id.hpp>
4
#include <cryptofuzz/util.h>
5
#include <cryptofuzz/repository.h>
6
#include "repository_tbl.h"
7
#include "config.h"
8
9
uint32_t PRNG(void);
10
std::string getBignum(bool mustBePositive = false);
11
std::string getPrime(void);
12
13
namespace cryptofuzz {
14
namespace mutator {
15
namespace ExpModGenerator {
16
    using namespace boost::multiprecision;
17
    using namespace boost::random;
18
19
    static mt19937 mt;
20
21
0
    inline bool is_even(const cpp_int& v) {
22
0
        return (v & 1) == 0;
23
0
    }
24
25
0
    inline bool is_odd(const cpp_int& v) {
26
0
        return (v & 1) == 1;
27
0
    }
28
29
0
    inline nlohmann::json to_json(const cpp_int& B, const cpp_int& E, const cpp_int& M) {
30
0
        nlohmann::json ret;
31
32
0
        ret["modifier"] = "";
33
0
        ret["calcOp"] = CF_CALCOP("ExpMod(A,B,C)");
34
0
        ret["bn1"] = B.str();
35
0
        ret["bn2"] = E.str();
36
0
        ret["bn3"] = M.str();
37
0
        ret["bn4"] = "";
38
39
0
        return ret;
40
0
    }
41
42
0
    inline const cpp_int& max(void) {
43
0
        static const cpp_int max(std::string(cryptofuzz::config::kMaxBignumSize, '9'));
44
0
        return max;
45
0
    }
46
47
0
    inline cpp_int max_multiplier(const cpp_int& v, const cpp_int& m) {
48
0
        return v == 0 ? cpp_int(0) : m / v;
49
0
    }
50
51
0
    inline cpp_int max_multiplier(const cpp_int& v) {
52
0
        return max_multiplier(v, max());
53
0
    }
54
55
    /* Multiply 'v' by a random positive integer m such that v * m < max() */
56
    /* If that is not possible, return false, else return true.
57
     */
58
0
    inline bool multiply_random(cpp_int& v, const bool odd = false) {
59
0
        const cpp_int max_mul = max_multiplier(v);
60
61
0
        if ( max_mul == 0 ) {
62
0
            return false;
63
0
        }
64
65
0
        cpp_int mul = uniform_int_distribution<cpp_int>(1, max_mul)(mt);
66
67
0
        if ( odd && is_even(mul) ) {
68
0
            mul--;
69
            //assert(mul > 0);
70
0
        }
71
72
0
        v *= mul;
73
74
0
        return true;
75
0
    }
76
77
    /* If 'a' and 'b' are such that a * b < max(), then
78
     * perform the multiplication on 'a' and return true.
79
     * Return false otherwise.
80
     */
81
0
    inline bool multiply(cpp_int& a, const cpp_int& b) {
82
        /* XXX return a <= max() / b; */
83
0
        const cpp_int max_mul = max_multiplier(a);
84
85
0
        if ( max_mul < b ) {
86
0
            return false;
87
0
        }
88
89
0
        a *= b;
90
91
0
        return true;
92
0
    }
93
94
0
    inline cpp_int get_prime(void) {
95
0
        const cpp_int p(getPrime());
96
97
0
        if ( p > 0 ) {
98
0
            return p;
99
0
        }
100
101
        /* Prime pool is empty, fall back to regular number */
102
0
        return cpp_int(getBignum(true));
103
0
    }
104
105
0
    inline std::vector<size_t> prime_factors(const cpp_int& v) noexcept {
106
0
        static const std::vector<size_t> primes{2, 3, 5, 7, 11, 13, 17, 19};
107
108
0
        std::vector<size_t> ret;
109
110
0
        for (const auto& p : primes) {
111
0
            if ( v % p == 0 ) {
112
0
                ret.push_back(p);
113
0
            }
114
0
        }
115
116
0
        return ret;
117
0
    }
118
119
    /* Compute max E such that base^E <= v */
120
0
    inline size_t log(const cpp_int& v, const cpp_int& base) {
121
0
        size_t i = 0;
122
0
        auto x = base;
123
0
        while ( x <= v ) {
124
0
            x *= base;
125
0
            i++;
126
0
        }
127
0
        return i;
128
0
    }
129
130
    /* This is slightly faster than native boost::multiprecision::pow */
131
0
    inline cpp_int pow(const cpp_int& v, const size_t exponent) {
132
0
        if ( exponent == 0 ) {
133
0
            static const cpp_int one(1);
134
0
            return one;
135
0
        } else if ( exponent == 1 ) {
136
0
            return v;
137
0
        } else {
138
0
            return boost::multiprecision::pow(v, exponent);
139
0
        }
140
0
    }
141
142
    /* Create an ExpMod operation where base, exp and mod are of size 'bits'
143
     * and base^exp%mod == 0.
144
     *
145
     * To find certain modexp reduction bugs such as:
146
     * https://boringssl.googlesource.com/boringssl/+/13c9d5c69d04485a7a8840c12185c832026c8315
147
     * https://boringssl.googlesource.com/boringssl/+/801a801024febe1a33add5ddaa719e257d97aba5
148
     */
149
    static std::optional<nlohmann::json> generate_exp_mod_is_0(const size_t bits)
150
0
    {
151
0
        CF_ASSERT(bits > 0, "Bits must be non-zero");
152
153
        /* For some reason this crashes with high optimization */
154
        /*
155
        const auto min = cpp_int(1) << (bits - 1);
156
        const auto max = (cpp_int(1) << bits) - 1;
157
        */
158
159
0
        auto min = cpp_int(1);
160
0
        min <<= (bits - 1);
161
162
0
        auto max = cpp_int(1);
163
0
        max <<= bits;
164
0
        max--;
165
166
        /* Create random base/exponent */
167
0
        const cpp_int BE = uniform_int_distribution<cpp_int>(min, max)(mt);
168
169
        /* Compute some prime factors of base/exponent */
170
0
        const auto factors = prime_factors(BE);
171
0
        if ( factors.empty() ) {
172
0
            return std::nullopt;
173
0
        }
174
175
        /* Pick two random prime factors */
176
0
        const auto P1 = cpp_int(factors[mt() % factors.size()]);
177
0
        const auto P2 = factors[mt() % factors.size()];
178
179
        /* I = P1^[0..P2] */
180
0
        const cpp_int I = pow(P1, mt() % (P2+1));
181
182
        /* Any J = P2^[0..I] would yield a valid modulus.
183
         *
184
         * However, we want I*J to not exceed the bitsize.
185
         * Therefore, compute the max J.
186
         */
187
0
        const cpp_int maxJ = max / I;
188
189
        //assert(I * maxJ <= max);
190
191
        /* Compute the max exponent such that
192
         * J = P2^exp does not exceed the bitsize.
193
         */
194
0
        const size_t maxJExp = log(maxJ, P2);
195
196
0
        const cpp_int J = pow(cpp_int(P2), maxJExp);
197
198
0
        const cpp_int M = I * J;
199
200
        //assert(M <= max);
201
        //assert(powm(BE, BE, M) == 0);
202
203
0
        return to_json(BE, BE, M);
204
0
    }
205
206
    /* For all odd values base, pow(base, base*mul, be+1) == base) where mul is any odd value. */
207
0
    static std::optional<nlohmann::json> generate_exp_mod_is_odd_base_1(const cpp_int& B) {
208
0
        if ( !is_odd(B) ) {
209
0
            return std::nullopt;
210
0
        }
211
212
0
        cpp_int E = B;
213
0
        if ( !multiply_random(E, true) ) {
214
0
            return std::nullopt;
215
0
        }
216
217
0
        const cpp_int M = B + 1;
218
219
        //assert(powm(B, E, M) == B);
220
221
0
        return to_json(B, E, M);
222
0
    }
223
224
    /* For all odd values base, pow(base, base*mul, base*2) == base) where mul is any positive integer. */
225
0
    static std::optional<nlohmann::json> generate_exp_mod_is_odd_base_2(const cpp_int& B) {
226
0
        if ( !is_odd(B) ) {
227
0
            return std::nullopt;
228
0
        }
229
230
0
        cpp_int E = B;
231
0
        if ( !multiply_random(E) ) {
232
0
            return std::nullopt;
233
0
        }
234
235
0
        const cpp_int M = B * 2;
236
237
        //assert(powm(B, E, M) == B);
238
239
0
        return to_json(B, E, M);
240
0
    }
241
242
    /* For all even values base >= 4, pow(base, base*mul, base*2-2) == base) where mul is any positive integer. */
243
0
    static std::optional<nlohmann::json> generate_exp_mod_is_even_base_1(const cpp_int& B) {
244
0
        if ( !is_even(B) ) {
245
0
            return std::nullopt;
246
0
        }
247
0
        if ( B < 4 ) {
248
0
            return std::nullopt;
249
0
        }
250
251
0
        cpp_int E = B;
252
0
        if ( !multiply_random(E) ) {
253
0
            return std::nullopt;
254
0
        }
255
256
0
        const cpp_int M = B * 2 - 2;
257
258
        //assert(powm(B, E, M) == B);
259
260
0
        return to_json(B, E, M);
261
0
    }
262
263
    /* For all prime values me > base, pow(base, me, me) == base). */
264
0
    static std::optional<nlohmann::json> generate_exp_mod_is_base_1(const cpp_int& B) {
265
0
        const cpp_int p = get_prime();
266
267
0
        if ( B <= p ) {
268
0
            return std::nullopt;
269
0
        }
270
271
0
        const cpp_int E = p;
272
0
        const cpp_int M = p;
273
274
        /* This only holds for prime p */
275
        //assert(powm(B, E, M) == B);
276
277
0
        return to_json(B, E, M);
278
0
    }
279
280
    /* For all prime values p and any positive e, pow(p+1, e, (p+1)*p) == p+1. */
281
0
    static std::optional<nlohmann::json> generate_exp_mod_is_base_2(void) {
282
0
        const cpp_int p = get_prime();
283
284
0
        if ( p < 3 ) {
285
0
            return std::nullopt;
286
0
        }
287
288
0
        const cpp_int B = p + 1;
289
0
        cpp_int E = 1;
290
0
        if ( !multiply_random(E) ) {
291
0
            return std::nullopt;
292
0
        }
293
0
        cpp_int M = p + 1;
294
0
        if ( !multiply(M, p) ) {
295
0
            return std::nullopt;
296
0
        }
297
298
        /* This only holds for prime p */
299
        //assert(powm(B, E, M) == B);
300
301
0
        return to_json(B, E, M);
302
0
    }
303
304
    /* For all odd values exp, pow(exp*mul, exp, exp*2) == exp) where mul is any odd integer. */
305
0
    static std::optional<nlohmann::json> generate_exp_mod_is_odd_exp_1(const cpp_int& E) {
306
0
        if ( !is_odd(E) ) {
307
0
            return std::nullopt;
308
0
        }
309
310
0
        cpp_int B = E;
311
0
        if ( !multiply_random(B, true) ) {
312
0
            return std::nullopt;
313
0
        }
314
0
        const cpp_int M = E * 2;
315
316
        //assert(powm(B, E, M) == E);
317
318
0
        return to_json(B, E, M);
319
0
    }
320
321
    /* 1. Let p be a prime such that p*2-1 >= v.
322
     * 2. Let exp = p*2-1.
323
     * 3. Let base = exp + (exp * (exp - v)).
324
     * 4. Let mod = exp + 1.
325
     * 5. Let k be any positive integer.
326
     *
327
     * Now pow(base, exp**k, mod) == v.
328
     */
329
0
    static std::optional<nlohmann::json> generate_exp_mod_is_v_1(const cpp_int& v) {
330
        /* Step 1 */
331
0
        const cpp_int p = get_prime();
332
0
        if ( p < 1 ) {
333
0
            return std::nullopt;
334
0
        }
335
336
        /* Step 2 */
337
0
        const cpp_int E = 2 * p - 1;
338
0
        if ( E > max() || E < v ) {
339
0
            return std::nullopt;
340
0
        }
341
342
        /* Step 3 */
343
0
        cpp_int B = E;
344
0
        {
345
0
            const cpp_int delta = E - v + 1;
346
            /* B *= delta */
347
0
            if ( !multiply(B, delta) ) {
348
0
                return std::nullopt;
349
0
            }
350
0
        }
351
352
        /* Step 4 */
353
0
        const cpp_int M = E + 1;
354
355
        /* TODO exponentiate exp */
356
357
        /* This only holds for prime p */
358
        //assert(powm(B, E, M) == v);
359
360
0
        return to_json(B, E, M);
361
0
    }
362
363
    /* 1. Let p be a prime such that p-1 >= v.
364
     * 2. Let exp = p-1.
365
     * 3. Let base = exp + (exp * (exp - v)).
366
     * 4. Let mod = exp + 1.
367
     * 5. Let k be any positive integer.
368
     *
369
     * Now pow(base, (exp*k)+1, mod) == v.
370
     */
371
0
    static std::optional<nlohmann::json> generate_exp_mod_is_v_2(const cpp_int& v) {
372
        /* Step 1 */
373
0
        const cpp_int p = get_prime();
374
0
        if ( p < 1 ) {
375
0
            return std::nullopt;
376
0
        }
377
378
        /* Step 2 */
379
0
        cpp_int E = p - 1;
380
0
        if ( E > max() || E < v ) {
381
0
            return std::nullopt;
382
0
        }
383
384
        /* Step 3 */
385
0
        cpp_int B = E;
386
0
        {
387
0
            const cpp_int delta = E - v + 1;
388
            /* B *= delta */
389
0
            if ( !multiply(B, delta) ) {
390
0
                return std::nullopt;
391
0
            }
392
0
        }
393
394
        /* Step 4 */
395
0
        const cpp_int M = E + 1;
396
397
        /* E = E*K+1 */
398
0
        if ( !multiply_random(E) ) {
399
0
            return std::nullopt;
400
0
        }
401
0
        E++;
402
403
        /* This only holds for prime p */
404
        //assert(powm(B, E, M) == v);
405
406
0
        return to_json(B, E, M);
407
0
    }
408
409
0
    std::optional<nlohmann::json> generate_exp_mod(const std::string& _result) {
410
0
        const uint8_t which = PRNG() % 3;
411
0
        if ( which == 0 ) {
412
0
            static const std::array<size_t, 6> bitsizes{256, 512, 1024, 2048, 4096, 8192};
413
414
0
            return generate_exp_mod_is_0(bitsizes[PRNG() % bitsizes.size()]);
415
0
        } else if ( which == 1 ) {
416
0
            const cpp_int result = cpp_int(_result);
417
418
0
            const uint8_t which = PRNG() % 2;
419
420
0
            if ( which == 0 ) {
421
0
                return generate_exp_mod_is_v_1(result);
422
0
            } else if ( which == 1 ) {
423
0
                return generate_exp_mod_is_v_2(result);
424
0
            } else {
425
0
                CF_UNREACHABLE();
426
0
            }
427
0
        } else if ( which == 2 ) {
428
0
            const cpp_int result = cpp_int(_result);
429
430
0
            if ( is_odd(result) ) {
431
0
                const uint8_t which = PRNG() % 5;
432
433
0
                if ( which == 0 ) {
434
0
                    return generate_exp_mod_is_odd_base_1(result);
435
0
                } else if ( which == 1 ) {
436
0
                    return generate_exp_mod_is_odd_base_2(result);
437
0
                } else if ( which == 2 ) {
438
0
                    return generate_exp_mod_is_odd_exp_1(result);
439
0
                } else if ( which == 3 ) {
440
0
                    return generate_exp_mod_is_base_1(result);
441
0
                } else if ( which == 4 ) {
442
0
                    return generate_exp_mod_is_base_2();
443
0
                } else {
444
0
                    CF_UNREACHABLE();
445
0
                }
446
0
            } else {
447
0
                const uint8_t which = PRNG() % 3;
448
449
0
                if ( which == 0 ) {
450
0
                    return generate_exp_mod_is_even_base_1(result);
451
0
                } else if ( which == 1 ) {
452
0
                    return generate_exp_mod_is_base_1(result);
453
0
                } else if ( which == 2 ) {
454
0
                    return generate_exp_mod_is_base_2();
455
0
                } else {
456
0
                    CF_UNREACHABLE();
457
0
                }
458
0
            }
459
0
        } else {
460
0
            CF_UNREACHABLE();
461
0
        }
462
0
    }
463
} /* ExpModGenerator */
464
} /* mutator */
465
} /* cryptofuzz */