Skip to content

Commit 89a378d

Browse files
authored
crypto: Rework prime/order specification for FieldElement (#1415)
Decouple the `Curve::FIELD_PRIME` from the prime/order specification for the `FieldElement`. This allows at least multiple different FieldElements for the same curve.
1 parent 6521d9d commit 89a378d

7 files changed

Lines changed: 81 additions & 68 deletions

File tree

lib/evmone_precompiles/bn254.hpp

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -18,27 +18,29 @@ struct Curve
1818
/// The field/scalar unsigned int type.
1919
using uint_type = uint256;
2020

21-
/// The field prime number (P).
22-
static constexpr auto FIELD_PRIME =
23-
0x30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47_u256;
24-
2521
/// The order of the curve (N).
2622
static constexpr auto ORDER =
2723
0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001_u256;
2824

29-
/// The modular arithmetic for the field.
30-
static constexpr ModArith Fp{FIELD_PRIME};
25+
struct FpSpec
26+
{
27+
/// The field prime number (P).
28+
static constexpr auto ORDER =
29+
0x30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47_u256;
30+
};
31+
using Fp = ecc::FieldElement<FpSpec>;
32+
33+
static constexpr auto& FIELD_PRIME = Fp::ORDER;
3134

3235
static constexpr auto A = 0;
33-
static constexpr auto B = ecc::FieldElement<Curve>(3);
36+
static constexpr auto B = Fp{3};
3437

3538
/// Endomorphism parameters. See ecc::decompose().
3639
/// @{
3740
/// λ
3841
static constexpr auto LAMBDA = 0xb3c4d79d41a917585bfc41088d8daaa78b17ea66b99c90dd_u256;
3942
/// β
40-
static constexpr ecc::FieldElement<Curve> BETA{
41-
0x59e26bcea0d48bacd4f263f1acdb5c4f5763473177fffffe_u256};
43+
static constexpr Fp BETA{0x59e26bcea0d48bacd4f263f1acdb5c4f5763473177fffffe_u256};
4244
/// x₁
4345
static constexpr auto X1 = 0x6f4d8248eeb859fd95b806bca6f338ee_u256;
4446
/// -y₁

lib/evmone_precompiles/ecc.hpp

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -21,14 +21,19 @@ struct Constant : std::integral_constant<int, N>
2121
using zero_t = Constant<0>;
2222
using one_t = Constant<1>;
2323

24+
/// The order specification (prime number) for a finite field.
25+
template <typename T>
26+
concept FieldSpec = requires { T::ORDER; };
27+
2428
/// A representation of an element in a prime field.
2529
///
2630
/// TODO: Combine with BaseFieldElem.
27-
template <typename Curve>
31+
template <FieldSpec Spec>
2832
struct FieldElement
2933
{
30-
using uint_type = Curve::uint_type;
31-
static constexpr auto& Fp = Curve::Fp;
34+
using uint_type = std::remove_const_t<decltype(Spec::ORDER)>;
35+
static constexpr auto& ORDER = Spec::ORDER;
36+
static constexpr ModArith<uint_type> Fp{ORDER};
3237

3338
// TODO: Make this private.
3439
uint_type value_{};
@@ -44,7 +49,7 @@ struct FieldElement
4449
{
4550
// TODO: Add intx::load from std::span.
4651
const auto x = intx::be::unsafe::load<uint_type>(b.data());
47-
if (x >= Curve::FIELD_PRIME) [[unlikely]]
52+
if (x >= ORDER) [[unlikely]]
4853
return std::nullopt;
4954
return FieldElement{x};
5055
}
@@ -122,7 +127,7 @@ struct Point
122127
template <typename Curve>
123128
struct AffinePoint
124129
{
125-
using FE = FieldElement<Curve>;
130+
using FE = Curve::Fp;
126131

127132
FE x;
128133
FE y;
@@ -165,7 +170,7 @@ struct AffinePoint
165170
template <typename Curve>
166171
struct ProjPoint
167172
{
168-
using FE = FieldElement<Curve>;
173+
using FE = Curve::Fp;
169174
FE x;
170175
FE y{1}; // TODO: Make sure this is compile-time constant.
171176
FE z;
@@ -264,7 +269,7 @@ AffinePoint<Curve> add_affine(const AffinePoint<Curve>& p, const AffinePoint<Cur
264269
const auto xx = x1 * x1;
265270
dy = xx + xx + xx;
266271
if constexpr (Curve::A != 0)
267-
dy += FieldElement<Curve>{Curve::A};
272+
dy += typename Curve::Fp{Curve::A};
268273
dx = y1 + y1;
269274
}
270275
const auto slope = dy / dx;

lib/evmone_precompiles/pairing/bn254/fields.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ using namespace intx;
1515
struct BaseFieldConfig
1616
{
1717
using ValueT = uint256;
18-
static constexpr auto& MOD_ARITH = Curve::Fp;
18+
static constexpr auto MOD_ARITH = ModArith{Curve::FIELD_PRIME};
1919
static constexpr auto ONE = MOD_ARITH.to_mont(1);
2020
};
2121
using Fq = ecc::BaseFieldElem<BaseFieldConfig>;

lib/evmone_precompiles/secp256k1.cpp

Lines changed: 24 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -8,15 +8,14 @@ namespace evmmax::secp256k1
88
{
99
namespace
1010
{
11-
constexpr auto B = ecc::FieldElement<Curve>{7};
11+
constexpr auto B = Curve::Fp{7};
1212

1313
constexpr AffinePoint G{0x79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798_u256,
1414
0x483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8_u256};
1515
} // namespace
1616

1717
// FIXME: Change to "uncompress_point".
18-
std::optional<ecc::FieldElement<Curve>> calculate_y(
19-
const ecc::FieldElement<Curve>& x, bool y_parity) noexcept
18+
std::optional<Curve::Fp> calculate_y(const Curve::Fp& x, bool y_parity) noexcept
2019
{
2120
// Calculate y = √(x³ + 7).
2221
const auto xxx = x * x * x;
@@ -50,43 +49,39 @@ std::optional<AffinePoint> secp256k1_ecdsa_recover(std::span<const uint8_t, 32>
5049
// Follows "Elliptic Curve Digital Signature Algorithm - Public key recovery"
5150
// https://en.wikipedia.org/wiki/Elliptic_Curve_Digital_Signature_Algorithm#Public_key_recovery
5251

53-
const auto r = intx::be::unsafe::load<uint256>(r_bytes.data());
54-
const auto s = intx::be::unsafe::load<uint256>(s_bytes.data());
55-
5652
// 1. Validate r and s are within [1, n-1].
57-
if (r == 0 || r >= Curve::ORDER || s == 0 || s >= Curve::ORDER) [[unlikely]]
53+
const auto opt_r = Curve::Fr::from_bytes(r_bytes);
54+
if (!opt_r.has_value() || *opt_r == 0) [[unlikely]]
55+
return std::nullopt;
56+
57+
const auto opt_s = Curve::Fr::from_bytes(s_bytes);
58+
if (!opt_s.has_value() || *opt_s == 0) [[unlikely]]
5859
return std::nullopt;
5960

61+
const auto& r = *opt_r;
62+
const auto& s = *opt_s;
63+
6064
// 3. Hash of the message is already calculated in e.
6165
// 4. Convert hash e to z field element by doing z = e % n.
6266
// https://www.rfc-editor.org/rfc/rfc6979#section-2.3.2
6367
// Converting to Montgomery form performs the e % n reduction.
64-
const ModArith n{Curve::ORDER};
65-
const auto z = intx::be::unsafe::load<uint256>(hash.data());
66-
const auto z_mont = n.to_mont(z);
68+
const auto z = Curve::Fr{intx::be::unsafe::load<uint256>(hash.data())};
6769

6870
// 5. Calculate u1 and u2.
69-
const auto r_n = n.to_mont(r);
70-
const auto r_inv = n.inv(r_n);
71-
72-
const auto z_neg = n.sub(0, z_mont);
73-
const auto u1_mont = n.mul(z_neg, r_inv);
74-
const auto u1 = n.from_mont(u1_mont);
75-
76-
const auto s_mont = n.to_mont(s);
77-
const auto u2_mont = n.mul(s_mont, r_inv);
78-
const auto u2 = n.from_mont(u2_mont);
71+
const auto r_inv = 1 / r;
72+
const auto u1 = -z * r_inv;
73+
const auto u2 = s * r_inv;
7974
assert(u2 != 0); // Because s != 0 and r_inv != 0.
8075

8176
// 2. Calculate y coordinate of R from r and v.
82-
const auto r_mont = ecc::FieldElement<Curve>{r};
77+
const auto r_mont = Curve::Fp{r.value()};
8378
const auto y = calculate_y(r_mont, parity);
84-
if (!y.has_value()) [[unlikely]]
79+
if (!y.has_value())
8580
return std::nullopt;
8681

8782
// 6. Calculate public key point Q = u1×G + u2×R.
8883
const auto R = AffinePoint{r_mont, *y};
89-
const auto Q = msm(u1, G, u2, R);
84+
const auto Q = msm(u1.value(), G, u2.value(), R);
9085

9186
// The public key mustn't be the point at infinity. This check is cheaper on a non-affine point.
9287
if (Q == 0) [[unlikely]]
@@ -107,7 +102,7 @@ std::optional<evmc::address> ecrecover(std::span<const uint8_t, 32> hash,
107102
return to_address(*pubkey);
108103
}
109104

110-
std::optional<ecc::FieldElement<Curve>> field_sqrt(const ecc::FieldElement<Curve>& x) noexcept
105+
std::optional<Curve::Fp> field_sqrt(const Curve::Fp& x) noexcept
111106
{
112107
// Computes modular exponentiation
113108
// x^0x3fffffffffffffffffffffffffffffffffffffffffffffffffffffffbfffff0c
@@ -138,11 +133,11 @@ std::optional<ecc::FieldElement<Curve>> field_sqrt(const ecc::FieldElement<Curve
138133
// return ((x223 << 23 + x22) << 6 + _11) << 2
139134

140135
// Allocate Temporaries.
141-
ecc::FieldElement<Curve> z;
142-
ecc::FieldElement<Curve> t0;
143-
ecc::FieldElement<Curve> t1;
144-
ecc::FieldElement<Curve> t2;
145-
ecc::FieldElement<Curve> t3;
136+
Curve::Fp z;
137+
Curve::Fp t0;
138+
Curve::Fp t1;
139+
Curve::Fp t2;
140+
Curve::Fp t3;
146141

147142

148143
// Step 1: z = x^0x2

lib/evmone_precompiles/secp256k1.hpp

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -16,15 +16,24 @@ struct Curve
1616
{
1717
using uint_type = uint256;
1818

19-
/// The field prime number (P).
20-
static constexpr auto FIELD_PRIME =
21-
0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f_u256;
19+
struct FpSpec
20+
{
21+
/// The field prime number (P).
22+
static constexpr auto ORDER =
23+
0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f_u256;
24+
};
25+
using Fp = ecc::FieldElement<FpSpec>;
2226

23-
/// The secp256k1 curve group order (N).
24-
static constexpr auto ORDER =
25-
0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141_u256;
27+
struct FrSpec
28+
{
29+
/// The secp256k1 curve group order (N).
30+
static constexpr auto ORDER =
31+
0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141_u256;
32+
};
33+
using Fr = ecc::FieldElement<FrSpec>;
2634

27-
static constexpr ModArith Fp{FIELD_PRIME};
35+
static constexpr auto& FIELD_PRIME = Fp::ORDER;
36+
static constexpr auto& ORDER = Fr::ORDER;
2837

2938
static constexpr auto A = 0;
3039
};
@@ -37,11 +46,10 @@ using AffinePoint = ecc::AffinePoint<Curve>;
3746
/// where P is ::FieldPrime.
3847
///
3948
/// @return Square root of x if it exists, std::nullopt otherwise.
40-
std::optional<ecc::FieldElement<Curve>> field_sqrt(const ecc::FieldElement<Curve>& x) noexcept;
49+
std::optional<Curve::Fp> field_sqrt(const Curve::Fp& x) noexcept;
4150

4251
/// Calculate y coordinate of a point having x coordinate and y parity.
43-
std::optional<ecc::FieldElement<Curve>> calculate_y(
44-
const ecc::FieldElement<Curve>& x, bool y_parity) noexcept;
52+
std::optional<Curve::Fp> calculate_y(const Curve::Fp& x, bool y_parity) noexcept;
4553

4654
/// Convert the secp256k1 point (uncompressed public key) to Ethereum address.
4755
evmc::address to_address(const AffinePoint& pt) noexcept;

lib/evmone_precompiles/secp256r1.hpp

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,17 +14,21 @@ struct Curve
1414
{
1515
using uint_type = uint256;
1616

17-
/// The field prime number (P).
18-
static constexpr auto FIELD_PRIME =
19-
0xffffffff00000001000000000000000000000000ffffffffffffffffffffffff_u256;
20-
2117
/// The secp256r1 curve group order (N).
2218
static constexpr auto ORDER =
2319
0xffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551_u256;
2420

25-
static constexpr ModArith Fp{FIELD_PRIME};
21+
struct FpSpec
22+
{
23+
/// The field prime number (P).
24+
static constexpr auto ORDER =
25+
0xffffffff00000001000000000000000000000000ffffffffffffffffffffffff_u256;
26+
};
27+
using Fp = ecc::FieldElement<FpSpec>;
28+
29+
static constexpr auto& FIELD_PRIME = Fp::ORDER;
2630

27-
static constexpr auto A = FIELD_PRIME - 3;
31+
static constexpr auto A = Fp::ORDER - 3;
2832

2933
static constexpr auto B =
3034
0x5ac635d8aa3a93e7b3ebbd55769886bc651d06b0cc53b0f63bce3c3e27d2604b_u256;

test/unittests/evmmax_secp256k1_test.cpp

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
#include <gtest/gtest.h>
77
#include <test/utils/utils.hpp>
88

9-
using namespace evmmax;
109
using namespace evmmax::secp256k1;
1110
using namespace evmc::literals;
1211
using namespace evmone::test;
@@ -42,7 +41,7 @@ TEST(secp256k1, field_sqrt)
4241
Curve::FIELD_PRIME - 1,
4342
})
4443
{
45-
const auto a = ecc::FieldElement<Curve>{t};
44+
const auto a = Curve::Fp{t};
4645
const auto a2_sqrt = field_sqrt(a * a);
4746
ASSERT_TRUE(a2_sqrt.has_value()) << to_string(t);
4847
EXPECT_TRUE(a2_sqrt == a || a2_sqrt == -a) << to_string(t);
@@ -53,13 +52,13 @@ TEST(secp256k1, field_sqrt_invalid)
5352
{
5453
for (const auto& t : {3_u256, Curve::FIELD_PRIME - 1})
5554
{
56-
EXPECT_FALSE(field_sqrt(ecc::FieldElement<Curve>{t}).has_value());
55+
EXPECT_FALSE(field_sqrt(Curve::Fp{t}).has_value());
5756
}
5857
}
5958

6059
TEST(secp256k1, scalar_inv)
6160
{
62-
const ModArith n{Curve::ORDER};
61+
const evmmax::ModArith n{Curve::ORDER};
6362

6463
for (const auto& t : {
6564
1_u256,
@@ -104,7 +103,7 @@ TEST(secp256k1, calculate_y)
104103

105104
for (const auto& t : test_cases)
106105
{
107-
const auto x = ecc::FieldElement<Curve>{t.x};
106+
const auto x = Curve::Fp{t.x};
108107

109108
const auto y_even = calculate_y(x, false);
110109
ASSERT_TRUE(y_even.has_value());
@@ -123,7 +122,7 @@ TEST(secp256k1, calculate_y_invalid)
123122
Curve::FIELD_PRIME - 1,
124123
})
125124
{
126-
const auto x = ecc::FieldElement<Curve>{t};
125+
const auto x = Curve::Fp{t};
127126

128127
const auto y_even = calculate_y(x, false);
129128
ASSERT_FALSE(y_even.has_value());

0 commit comments

Comments
 (0)