Coverage Report

Created: 2026-06-07 07:41

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/serenity/Userland/Libraries/LibCrypto/NumberTheory/ModularFunctions.cpp
Line
Count
Source
1
/*
2
 * Copyright (c) 2020, Ali Mohammad Pur <mpfard@serenityos.org>
3
 *
4
 * SPDX-License-Identifier: BSD-2-Clause
5
 */
6
7
#include <AK/Debug.h>
8
#include <LibCrypto/BigInt/Algorithms/UnsignedBigIntegerAlgorithms.h>
9
#include <LibCrypto/NumberTheory/ModularFunctions.h>
10
11
namespace Crypto::NumberTheory {
12
13
UnsignedBigInteger Mod(UnsignedBigInteger const& a, UnsignedBigInteger const& b)
14
0
{
15
0
    UnsignedBigInteger result;
16
0
    result.set_to(a);
17
0
    result.set_to(result.divided_by(b).remainder);
18
0
    return result;
19
0
}
20
21
UnsignedBigInteger ModularInverse(UnsignedBigInteger const& a_, UnsignedBigInteger const& b)
22
0
{
23
0
    if (b == 1)
24
0
        return { 1 };
25
26
0
    UnsignedBigInteger temp_1;
27
0
    UnsignedBigInteger temp_minus;
28
0
    UnsignedBigInteger temp_quotient;
29
0
    UnsignedBigInteger temp_d;
30
0
    UnsignedBigInteger temp_u;
31
0
    UnsignedBigInteger temp_v;
32
0
    UnsignedBigInteger temp_x;
33
0
    UnsignedBigInteger result;
34
35
0
    UnsignedBigIntegerAlgorithms::modular_inverse_without_allocation(a_, b, temp_1, temp_minus, temp_quotient, temp_d, temp_u, temp_v, temp_x, result);
36
0
    return result;
37
0
}
38
39
UnsignedBigInteger ModularPower(UnsignedBigInteger const& b, UnsignedBigInteger const& e, UnsignedBigInteger const& m)
40
0
{
41
0
    if (m == 1)
42
0
        return 0;
43
44
0
    if (m.is_odd()) {
45
0
        UnsignedBigInteger temp_z0 { 0 };
46
0
        UnsignedBigInteger temp_rr { 0 };
47
0
        UnsignedBigInteger temp_one { 0 };
48
0
        UnsignedBigInteger temp_z { 0 };
49
0
        UnsignedBigInteger temp_zz { 0 };
50
0
        UnsignedBigInteger temp_x { 0 };
51
0
        UnsignedBigInteger temp_extra { 0 };
52
53
0
        UnsignedBigInteger result;
54
0
        UnsignedBigIntegerAlgorithms::montgomery_modular_power_with_minimal_allocations(b, e, m, temp_z0, temp_rr, temp_one, temp_z, temp_zz, temp_x, temp_extra, result);
55
0
        return result;
56
0
    }
57
58
0
    UnsignedBigInteger ep { e };
59
0
    UnsignedBigInteger base { b };
60
61
0
    UnsignedBigInteger result;
62
0
    UnsignedBigInteger temp_1;
63
0
    UnsignedBigInteger temp_2;
64
0
    UnsignedBigInteger temp_3;
65
0
    UnsignedBigInteger temp_multiply;
66
0
    UnsignedBigInteger temp_quotient;
67
0
    UnsignedBigInteger temp_remainder;
68
69
0
    UnsignedBigIntegerAlgorithms::destructive_modular_power_without_allocation(ep, base, m, temp_1, temp_2, temp_3, temp_multiply, temp_quotient, temp_remainder, result);
70
71
0
    return result;
72
0
}
73
74
UnsignedBigInteger GCD(UnsignedBigInteger const& a, UnsignedBigInteger const& b)
75
0
{
76
0
    UnsignedBigInteger temp_a { a };
77
0
    UnsignedBigInteger temp_b { b };
78
0
    UnsignedBigInteger temp_quotient;
79
0
    UnsignedBigInteger temp_remainder;
80
0
    UnsignedBigInteger output;
81
82
0
    UnsignedBigIntegerAlgorithms::destructive_GCD_without_allocation(temp_a, temp_b, temp_quotient, temp_remainder, output);
83
84
0
    return output;
85
0
}
86
87
UnsignedBigInteger LCM(UnsignedBigInteger const& a, UnsignedBigInteger const& b)
88
0
{
89
0
    UnsignedBigInteger temp_a { a };
90
0
    UnsignedBigInteger temp_b { b };
91
0
    UnsignedBigInteger temp_1;
92
0
    UnsignedBigInteger temp_2;
93
0
    UnsignedBigInteger temp_3;
94
0
    UnsignedBigInteger temp_quotient;
95
0
    UnsignedBigInteger temp_remainder;
96
0
    UnsignedBigInteger gcd_output;
97
0
    UnsignedBigInteger output { 0 };
98
99
0
    UnsignedBigIntegerAlgorithms::destructive_GCD_without_allocation(temp_a, temp_b, temp_quotient, temp_remainder, gcd_output);
100
0
    if (gcd_output == 0) {
101
0
        dbgln_if(NT_DEBUG, "GCD is zero");
102
0
        return output;
103
0
    }
104
105
    // output = (a / gcd_output) * b
106
0
    UnsignedBigIntegerAlgorithms::divide_without_allocation(a, gcd_output, temp_quotient, temp_remainder);
107
0
    UnsignedBigIntegerAlgorithms::multiply_without_allocation(temp_quotient, b, temp_1, temp_2, temp_3, output);
108
109
0
    dbgln_if(NT_DEBUG, "quot: {} rem: {} out: {}", temp_quotient, temp_remainder, output);
110
111
0
    return output;
112
0
}
113
114
static bool MR_primality_test(UnsignedBigInteger n, Vector<UnsignedBigInteger, 256> const& tests)
115
0
{
116
    // Written using Wikipedia:
117
    // https://en.wikipedia.org/wiki/Miller%E2%80%93Rabin_primality_test#Miller%E2%80%93Rabin_test
118
0
    VERIFY(!(n < 4));
119
0
    auto predecessor = n.minus({ 1 });
120
0
    auto d = predecessor;
121
0
    size_t r = 0;
122
123
0
    {
124
0
        auto div_result = d.divided_by(2);
125
0
        while (div_result.remainder == 0) {
126
0
            d = div_result.quotient;
127
0
            div_result = d.divided_by(2);
128
0
            ++r;
129
0
        }
130
0
    }
131
0
    if (r == 0) {
132
        // n - 1 is odd, so n was even. But there is only one even prime:
133
0
        return n == 2;
134
0
    }
135
136
0
    for (auto& a : tests) {
137
        // Technically: VERIFY(2 <= a && a <= n - 2)
138
0
        VERIFY(a < n);
139
0
        auto x = ModularPower(a, d, n);
140
0
        if (x == 1 || x == predecessor)
141
0
            continue;
142
0
        bool skip_this_witness = false;
143
        // r − 1 iterations.
144
0
        for (size_t i = 0; i < r - 1; ++i) {
145
0
            x = ModularPower(x, 2, n);
146
0
            if (x == predecessor) {
147
0
                skip_this_witness = true;
148
0
                break;
149
0
            }
150
0
        }
151
0
        if (skip_this_witness)
152
0
            continue;
153
0
        return false; // "composite"
154
0
    }
155
156
0
    return true; // "probably prime"
157
0
}
158
159
UnsignedBigInteger random_number(UnsignedBigInteger const& min, UnsignedBigInteger const& max_excluded)
160
0
{
161
0
    VERIFY(min < max_excluded);
162
0
    auto range = max_excluded.minus(min);
163
0
    UnsignedBigInteger base;
164
0
    auto size = range.trimmed_length() * sizeof(u32) + 2;
165
    // "+2" is intentional (see below).
166
0
    auto buffer = ByteBuffer::create_uninitialized(size).release_value_but_fixme_should_propagate_errors(); // FIXME: Handle possible OOM situation.
167
0
    auto* buf = buffer.data();
168
169
0
    fill_with_random(buffer);
170
0
    UnsignedBigInteger random { buf, size };
171
    // At this point, `random` is a large number, in the range [0, 256^size).
172
    // To get down to the actual range, we could just compute random % range.
173
    // This introduces "modulo bias". However, since we added 2 to `size`,
174
    // we know that the generated range is at least 65536 times as large as the
175
    // required range! This means that the modulo bias is only 0.0015%, if all
176
    // inputs are chosen adversarially. Let's hope this is good enough.
177
0
    auto divmod = random.divided_by(range);
178
    // The proper way to fix this is to restart if `divmod.quotient` is maximal.
179
0
    return divmod.remainder.plus(min);
180
0
}
181
182
bool is_probably_prime(UnsignedBigInteger const& p)
183
0
{
184
    // Is it a small number?
185
0
    if (p < 49) {
186
0
        u32 p_value = p.words()[0];
187
        // Is it a very small prime?
188
0
        if (p_value == 2 || p_value == 3 || p_value == 5 || p_value == 7)
189
0
            return true;
190
        // Is it the multiple of a very small prime?
191
0
        if (p_value % 2 == 0 || p_value % 3 == 0 || p_value % 5 == 0 || p_value % 7 == 0)
192
0
            return false;
193
        // Then it must be a prime, but not a very small prime, like 37.
194
0
        return true;
195
0
    }
196
197
0
    Vector<UnsignedBigInteger, 256> tests;
198
    // Make some good initial guesses that are guaranteed to find all primes < 2^64.
199
0
    tests.append(UnsignedBigInteger(2));
200
0
    tests.append(UnsignedBigInteger(3));
201
0
    tests.append(UnsignedBigInteger(5));
202
0
    tests.append(UnsignedBigInteger(7));
203
0
    tests.append(UnsignedBigInteger(11));
204
0
    tests.append(UnsignedBigInteger(13));
205
0
    UnsignedBigInteger seventeen { 17 };
206
0
    for (size_t i = tests.size(); i < 256; ++i) {
207
0
        tests.append(random_number(seventeen, p.minus(2)));
208
0
    }
209
    // Miller-Rabin's "error" is 8^-k. In adversarial cases, it's 4^-k.
210
    // With 200 random numbers, this would mean an error of about 2^-400.
211
    // So we don't need to worry too much about the quality of the random numbers.
212
213
0
    return MR_primality_test(p, tests);
214
0
}
215
216
UnsignedBigInteger random_big_prime(size_t bits)
217
0
{
218
0
    VERIFY(bits >= 33);
219
0
    UnsignedBigInteger min = "6074001000"_bigint.shift_left(bits - 33);
220
0
    UnsignedBigInteger max = UnsignedBigInteger { 1 }.shift_left(bits).minus(1);
221
0
    for (;;) {
222
0
        auto p = random_number(min, max);
223
0
        if ((p.words()[0] & 1) == 0) {
224
            // An even number is definitely not a large prime.
225
0
            continue;
226
0
        }
227
0
        if (is_probably_prime(p))
228
0
            return p;
229
0
    }
230
0
}
231
232
}