Skip to content

Commit c9eefe8

Browse files
committed
Optimize EC point doubling for cases a=0 & a=-3
1 parent 031361e commit c9eefe8

File tree

3 files changed

+59
-17
lines changed

3 files changed

+59
-17
lines changed

include/ack/ec.hpp

+43-15
Original file line numberDiff line numberDiff line change
@@ -836,7 +836,17 @@ namespace ack {
836836
return ec_point_fp_proj(); // identity
837837
}
838838

839-
auto t = p.x.sqr() * 3 + this->curve().a * p.z.sqr();
839+
const auto t = []( const ec_point_fp_proj& p) {
840+
const auto x2 = p.x.sqr();
841+
if ( p.curve().a_is_zero ) {
842+
return 3 * x2;
843+
}
844+
if ( p.curve().a_is_minus_3 ) {
845+
return 3 * ( x2 - p.z.sqr() );
846+
}
847+
return 3 * x2 + p.curve().a * p.z.sqr();
848+
}( p );
849+
840850
const auto dy = 2 * p.y;
841851
const auto u = dy * p.z;
842852
const auto v = u * p.x * dy;
@@ -1179,16 +1189,30 @@ namespace ack {
11791189
return ec_point_fp_jacobi(); // identity
11801190
}
11811191

1182-
// https://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian.html#doubling-dbl-1998-cmo-2
1183-
// note: faster than https://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian.html#doubling-dbl-2007-bl
1184-
auto y2 = p.y.sqr();
1185-
auto z2 = p.z.sqr();
1186-
auto S = 4 * p.x * y2;
1187-
auto M = 3 * p.x.sqr() + this->curve().a * z2.sqr();
1188-
auto RX = M.sqr() - 2 * S;
1189-
auto RY = M * ( S - RX ) - 8 * y2.sqr();
1190-
auto RZ = 2 * p.y * p.z;
1191-
return make_point( std::move(RX), std::move(RY), std::move(RZ) );
1192+
// https://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian.html#doubling-dbl-1986-cc
1193+
// note: this algo was measured to be the most efficient of them all.
1194+
1195+
const auto M = [](const auto& p) {
1196+
const bool bZIsOne = p.z.is_one();
1197+
if ( p.curve().a_is_zero ) {
1198+
return 3 * p.x.sqr();
1199+
}
1200+
else if ( p.curve().a_is_minus_3 ) {
1201+
const auto z2 = bZIsOne ? p.z : p.z.sqr();
1202+
return 3 * ( p.x - z2 ) * ( p.x + z2 );
1203+
}
1204+
else {
1205+
const auto z4 = bZIsOne ? p.z : p.z.sqr().sqr();
1206+
return 3 * p.x.sqr() + p.curve().a * z4;
1207+
}
1208+
}( p );
1209+
1210+
const auto y2 = p.y.sqr();
1211+
const auto S = 4 * p.x * y2;
1212+
auto X3 = M.sqr() - 2 * S;
1213+
auto Y3 = M * ( S - X3 ) - 8 * y2.sqr();
1214+
auto Z3 = 2 * p.y * p.z;
1215+
return make_point( std::move(X3), std::move(Y3), std::move(Z3) );
11921216
}
11931217

11941218
/**
@@ -1314,7 +1338,7 @@ namespace ack {
13141338
}
13151339

13161340
[[nodiscard]]
1317-
__attribute__((always_inline)) // note: forced inline produces a little more efficient computation. [[clang::always_inline]] doesn't work.
1341+
__attribute__((always_inline)) // note: forced inline produces slightly more efficient computation. [[clang::always_inline]] doesn't work.
13181342
static ec_point_fp_jacobi addex(const ec_point_fp_jacobi& p, const ec_point_fp_jacobi& q,
13191343
const field_element_type& U1, const field_element_type& U2,
13201344
const field_element_type& S1, const field_element_type& S2)
@@ -1334,8 +1358,8 @@ namespace ack {
13341358
const auto H3 = H2 * H;
13351359
const auto V = U1 * H2;
13361360

1337-
const auto X3 = R.sqr() - H3 - 2 * V;
1338-
const auto Y3 = R * ( V - X3 ) - S1 * H3;
1361+
auto X3 = R.sqr() - H3 - 2 * V;
1362+
auto Y3 = R * ( V - X3 ) - S1 * H3;
13391363
auto Z3 = std::move( H );
13401364
if ( !p.z.is_one() ) {
13411365
Z3 *= p.z;
@@ -1506,6 +1530,8 @@ namespace ack {
15061530
const IntT n; // order of g
15071531
const uint32_t h; // cofactor, i.e.: h = #E(Fp) / n
15081532
// #E(Fp) - number of points on the curve
1533+
const bool a_is_minus_3; // cached a == p - 3
1534+
const bool a_is_zero; // cached a == 0
15091535

15101536
/**
15111537
* Creates a curve from the given parameters.
@@ -1522,7 +1548,9 @@ namespace ack {
15221548
b( std::move(b) ),
15231549
g( make_point( std::move(g.first), std::move(g.second) )),
15241550
n( std::move(n) ),
1525-
h( h )
1551+
h( h ),
1552+
a_is_minus_3( a == ( p - 3) ),
1553+
a_is_zero( a.is_zero() )
15261554
{}
15271555

15281556
/**

tests/include/ack/tests/ec_test.hpp

+15-1
Original file line numberDiff line numberDiff line change
@@ -2916,6 +2916,13 @@ namespace ack::tests {
29162916
using point_proj_type = secp256k1_point_proj;
29172917
using point_jacobi_type = secp256k1_point_jacobi;
29182918

2919+
// Just making sure variables are correctly calculated and cached
2920+
static_assert( curve.a_is_minus_3 == false );
2921+
REQUIRE_EQUAL( curve.a_is_minus_3 == false, true )
2922+
2923+
static_assert( curve.a_is_zero == true );
2924+
REQUIRE_EQUAL( curve.a_is_zero == true, true )
2925+
29192926
// Custom test generated with python
29202927
{
29212928
auto k = bn_t( "C6047F9441ED7D6D3045406E95C07CD85C778E4B8CEF3" );
@@ -3584,10 +3591,17 @@ namespace ack::tests {
35843591
using namespace detail;
35853592
using namespace std::string_view_literals;
35863593
using bn_t = typename secp256r1_t::int_type;
3587-
const auto& curve = secp256r1;
3594+
constexpr auto& curve = secp256r1;
35883595
using point_proj_type = secp256r1_point_proj;
35893596
using point_jacobi_type = secp256r1_point_jacobi;
35903597

3598+
// Just making sure variables are correctly calculated and cached
3599+
static_assert( curve.a_is_minus_3 == true );
3600+
REQUIRE_EQUAL( curve.a_is_minus_3 == true, true )
3601+
3602+
static_assert( curve.a_is_zero == false );
3603+
REQUIRE_EQUAL( curve.a_is_zero == false, true )
3604+
35913605
// Test vectors from Botan library
35923606
// src: https://github.com/randombit/botan/blob/321a50789e6eeda6898af114492445f0882ee70f/src/tests/data/pubkey/ecc_var_point_mul.vec
35933607
{

tests/main.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -45,8 +45,8 @@ int main(int argc, char** argv)
4545

4646
EOSIO_TEST( utils_test )
4747
EOSIO_TEST( bigint_test )
48-
EOSIO_TEST( keccak_test )
4948
EOSIO_TEST( sha_test )
49+
EOSIO_TEST( keccak_test )
5050
EOSIO_TEST( public_key_test )
5151
EOSIO_TEST( mgf1_test )
5252
EOSIO_TEST( rsa_test )

0 commit comments

Comments
 (0)