@@ -21,34 +21,112 @@ class ModArith
21
21
// / The modulus inversion, i.e. the number N' such that mod⋅N' = 2⁶⁴-1.
22
22
const uint64_t m_mod_inv;
23
23
24
+ // / Compute the modulus inverse for Montgomery multiplication, i.e. N': mod⋅N' = 2⁶⁴-1.
25
+ // /
26
+ // / @param mod0 The least significant word of the modulus.
27
+ static constexpr uint64_t compute_mod_inv (uint64_t mod0) noexcept
28
+ {
29
+ // TODO: Find what is this algorithm and why it works.
30
+ uint64_t base = 0 - mod0;
31
+ uint64_t result = 1 ;
32
+ for (auto i = 0 ; i < 64 ; ++i)
33
+ {
34
+ result *= base;
35
+ base *= base;
36
+ }
37
+ return result;
38
+ }
39
+
40
+ // / Compute R² % mod.
41
+ static constexpr UintT compute_r_squared (const UintT& mod) noexcept
42
+ {
43
+ // R is 2^num_bits, R² is 2^(2*num_bits) and needs 2*num_bits+1 bits to represent,
44
+ // rounded to 2*num_bits+64) for intx requirements.
45
+ constexpr auto r2 = intx::uint <UintT::num_bits * 2 + 64 >{1 } << (UintT::num_bits * 2 );
46
+ return intx::udivrem (r2, mod).rem ;
47
+ }
48
+
49
+ static constexpr std::pair<uint64_t , uint64_t > addmul (
50
+ uint64_t t, uint64_t a, uint64_t b, uint64_t c) noexcept
51
+ {
52
+ const auto p = intx::umul (a, b) + t + c;
53
+ return {p[1 ], p[0 ]};
54
+ }
55
+
24
56
public:
25
- explicit ModArith (const UintT& modulus) noexcept ;
57
+ constexpr explicit ModArith (const UintT& modulus) noexcept
58
+ : mod{modulus},
59
+ m_r_squared{compute_r_squared (modulus)},
60
+ m_mod_inv{compute_mod_inv (modulus[0 ])}
61
+ {}
26
62
27
63
// / Converts a value to Montgomery form.
28
64
// /
29
65
// / This is done by using Montgomery multiplication mul(x, R²)
30
66
// / what gives aR²R⁻¹ % mod = aR % mod.
31
- UintT to_mont (const UintT& x) const noexcept ;
67
+ constexpr UintT to_mont (const UintT& x) const noexcept { return mul (x, m_r_squared); }
32
68
33
69
// / Converts a value in Montgomery form back to normal value.
34
70
// /
35
71
// / Given the x is the Montgomery form x = aR, the conversion is done by using
36
72
// / Montgomery multiplication mul(x, 1) what gives aRR⁻¹ % mod = a % mod.
37
- UintT from_mont (const UintT& x) const noexcept ;
73
+ constexpr UintT from_mont (const UintT& x) const noexcept { return mul (x, 1 ); }
38
74
39
75
// / Performs a Montgomery modular multiplication.
40
76
// /
41
77
// / Inputs must be in Montgomery form: x = aR, y = bR.
42
78
// / This computes Montgomery multiplication xyR⁻¹ % mod what gives aRbRR⁻¹ % mod = abR % mod.
43
79
// / The result (abR) is in Montgomery form.
44
- UintT mul (const UintT& x, const UintT& y) const noexcept ;
80
+ constexpr UintT mul (const UintT& x, const UintT& y) const noexcept
81
+ {
82
+ // Coarsely Integrated Operand Scanning (CIOS) Method
83
+ // Based on 2.3.2 from
84
+ // High-Speed Algorithms & Architectures For Number-Theoretic Cryptosystems
85
+ // https://www.microsoft.com/en-us/research/wp-content/uploads/1998/06/97Acar.pdf
86
+
87
+ constexpr auto S = UintT::num_words; // TODO(C++23): Make it static
88
+
89
+ intx::uint <UintT::num_bits + 64 > t;
90
+ for (size_t i = 0 ; i != S; ++i)
91
+ {
92
+ uint64_t c = 0 ;
93
+ for (size_t j = 0 ; j != S; ++j)
94
+ std::tie (c, t[j]) = addmul (t[j], x[j], y[i], c);
95
+ auto tmp = intx::addc (t[S], c);
96
+ t[S] = tmp.value ;
97
+ const auto d = tmp.carry ; // TODO: Carry is 0 for sparse modulus.
98
+
99
+ const auto m = t[0 ] * m_mod_inv;
100
+ std::tie (c, std::ignore) = addmul (t[0 ], m, mod[0 ], 0 );
101
+ for (size_t j = 1 ; j != S; ++j)
102
+ std::tie (c, t[j - 1 ]) = addmul (t[j], m, mod[j], c);
103
+ tmp = intx::addc (t[S], c);
104
+ t[S - 1 ] = tmp.value ;
105
+ t[S] = d + tmp.carry ; // TODO: Carry is 0 for sparse modulus.
106
+ }
107
+
108
+ if (t >= mod)
109
+ t -= mod;
110
+
111
+ return static_cast <UintT>(t);
112
+ }
45
113
46
114
// / Performs a modular addition. It is required that x < mod and y < mod, but x and y may be
47
115
// / but are not required to be in Montgomery form.
48
- UintT add (const UintT& x, const UintT& y) const noexcept ;
116
+ constexpr UintT add (const UintT& x, const UintT& y) const noexcept
117
+ {
118
+ const auto s = addc (x, y); // TODO: cannot overflow if modulus is sparse (e.g. 255 bits).
119
+ const auto d = subc (s.value , mod);
120
+ return (!s.carry && d.carry ) ? s.value : d.value ;
121
+ }
49
122
50
123
// / Performs a modular subtraction. It is required that x < mod and y < mod, but x and y may be
51
124
// / but are not required to be in Montgomery form.
52
- UintT sub (const UintT& x, const UintT& y) const noexcept ;
125
+ constexpr UintT sub (const UintT& x, const UintT& y) const noexcept
126
+ {
127
+ const auto d = subc (x, y);
128
+ const auto s = d.value + mod;
129
+ return (d.carry ) ? s : d.value ;
130
+ }
53
131
};
54
132
} // namespace evmmax
0 commit comments