Skip to content

Commit 6521d9d

Browse files
authored
crypto: Optimize computing mod inversion for Montgomery (#1359)
Compute the regular modular inverse for 2⁶⁴ using the Newton–Raphson numeric method. Then transform the result to the Montgomery modular multiplication requirements.
1 parent 1098f42 commit 6521d9d

1 file changed

Lines changed: 29 additions & 15 deletions

File tree

include/evmmax/evmmax.hpp

Lines changed: 29 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -4,24 +4,30 @@
44
#pragma once
55

66
#include <intx/intx.hpp>
7+
#include <cassert>
78

89
namespace evmmax
910
{
10-
11-
/// Compute the modulus inverse for Montgomery multiplication, i.e. N': mod⋅N' = 2⁶⁴-1.
12-
///
13-
/// @param mod0 The least significant word of the modulus.
14-
constexpr uint64_t compute_mod_inv(uint64_t mod0) noexcept
11+
/// Compute the modular inverse of the number modulo 2⁶⁴: inv⋅a = 1 mod 2⁶⁴.
12+
constexpr uint64_t inv_mod(uint64_t a) noexcept
1513
{
16-
// TODO: Find what is this algorithm and why it works.
17-
uint64_t base = 0 - mod0;
18-
uint64_t result = 1;
19-
for (auto i = 0; i < 64; ++i)
20-
{
21-
result *= base;
22-
base *= base;
23-
}
24-
return result;
14+
assert(a % 2 == 1); // The argument must be odd, otherwise the inverse does not exist.
15+
16+
// Use the Newton–Raphson numeric method, see e.g.
17+
// https://gmplib.org/~tege/divcnst-pldi94.pdf#page=9, formula (9.2)
18+
// Each iteration doubles the number of correct bits:
19+
// 2, 4, 8, ..., so for 64-bit value we need 6 iterations.
20+
// TODO(C++23): static
21+
constexpr auto ITERATIONS = std::countr_zero(sizeof(a) * 8);
22+
23+
// Start with inversion mod 2.
24+
// TODO: This can be further accelerated by:
25+
// - computing the inversion with smaller type (e.g. uint32_t) first,
26+
// - using a better initial approximation (e.g. via lookup table for 4 bits).
27+
uint64_t inv = 1;
28+
for (auto i = 0; i < ITERATIONS; ++i)
29+
inv *= 2 - a * inv; // Overflows are fine because they wrap around modulo 2⁶⁴.
30+
return inv;
2531
}
2632

2733
/// The modular arithmetic operations for EVMMAX (EVM Modular Arithmetic Extensions).
@@ -44,6 +50,14 @@ class ModArith
4450
return intx::udivrem(RR, mod).rem;
4551
}
4652

53+
/// Compute the modulus inverse for Montgomery multiplication, i.e., N': mod⋅N' = 2⁶⁴-1.
54+
static constexpr uint64_t compute_mont_mod_inv(const UintT& mod) noexcept
55+
{
56+
// Compute the inversion mod[0]⁻¹ mod 2⁶⁴, then the final result is N' = -mod[0]⁻¹
57+
// because this gives mod⋅N' = -1 mod 2⁶⁴ = 2⁶⁴-1.
58+
return -inv_mod(mod[0]);
59+
}
60+
4761
static constexpr std::pair<uint64_t, uint64_t> addmul(
4862
uint64_t t, uint64_t a, uint64_t b, uint64_t c) noexcept
4963
{
@@ -53,7 +67,7 @@ class ModArith
5367

5468
public:
5569
constexpr explicit ModArith(const UintT& mod) noexcept
56-
: mod_{mod}, r_squared_{compute_r_squared(mod)}, mod_inv_{compute_mod_inv(mod[0])}
70+
: mod_{mod}, r_squared_{compute_r_squared(mod)}, mod_inv_{compute_mont_mod_inv(mod)}
5771
{}
5872

5973
/// Returns the modulus.

0 commit comments

Comments
 (0)