Skip to content

Commit 58cca2c

Browse files
Generate ecmult_static_pre_g.h
This header contains a static array that replaces the ecmult_context pre_g and pre_g_128 tables. The gen_ecmult_static_pre_g program generates this header file.
1 parent 0440945 commit 58cca2c

8 files changed

+16914
-39
lines changed

.gitattributes

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
src/ecmult_static_pre_g.h linguist-generated

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ bench_internal
99
tests
1010
exhaustive_tests
1111
gen_context
12+
gen_ecmult_static_pre_g
1213
valgrind_ctime_test
1314
*.exe
1415
*.so

Makefile.am

+9-2
Original file line numberDiff line numberDiff line change
@@ -127,12 +127,19 @@ exhaustive_tests_LDFLAGS = -static
127127
TESTS += exhaustive_tests
128128
endif
129129

130+
EXTRA_PROGRAMS = gen_ecmult_static_pre_g
131+
gen_ecmult_static_pre_g_SOURCES = src/gen_ecmult_static_pre_g.c
132+
# See Automake manual, Section "Errors with distclean"
133+
src/ecmult_static_pre_g.h:
134+
$(MAKE) $(AM_MAKEFLAGS) gen_ecmult_static_pre_g$(EXEEXT)
135+
./gen_ecmult_static_pre_g$(EXEEXT)
136+
130137
if USE_ECMULT_STATIC_PRECOMPUTATION
131138
CPPFLAGS_FOR_BUILD +=-I$(top_srcdir) -I$(builddir)/src
132139

133140
gen_context_OBJECTS = gen_context.o
134141
gen_context_BIN = gen_context$(BUILD_EXEEXT)
135-
gen_%.o: src/gen_%.c src/libsecp256k1-config.h
142+
$(gen_context_OBJECTS): src/gen_context.c src/libsecp256k1-config.h
136143
$(CC_FOR_BUILD) $(DEFS) $(CPPFLAGS_FOR_BUILD) $(SECP_CFLAGS_FOR_BUILD) $(CFLAGS_FOR_BUILD) -c $< -o $@
137144

138145
$(gen_context_BIN): $(gen_context_OBJECTS)
@@ -149,7 +156,7 @@ src/ecmult_static_context.h: $(gen_context_BIN)
149156
CLEANFILES = $(gen_context_BIN) src/ecmult_static_context.h
150157
endif
151158

152-
EXTRA_DIST = autogen.sh src/gen_context.c src/basic-config.h
159+
EXTRA_DIST = autogen.sh src/gen_context.c src/ecmult_static_pre_g.h src/basic-config.h
153160

154161
if ENABLE_MODULE_ECDH
155162
include src/modules/ecdh/Makefile.am.include

src/ecmult.h

+18
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,24 @@
1111
#include "scalar.h"
1212
#include "scratch.h"
1313

14+
/* Noone will ever need more than a window size of 24. The code might
15+
* be correct for larger values of ECMULT_WINDOW_SIZE but this is not
16+
* not tested.
17+
*
18+
* The following limitations are known, and there are probably more:
19+
* If WINDOW_G > 27 and size_t has 32 bits, then the code is incorrect
20+
* because the size of the memory object that we allocate (in bytes)
21+
* will not fit in a size_t.
22+
* If WINDOW_G > 31 and int has 32 bits, then the code is incorrect
23+
* because certain expressions will overflow.
24+
*/
25+
#if ECMULT_WINDOW_SIZE < 2 || ECMULT_WINDOW_SIZE > 24
26+
# error Set ECMULT_WINDOW_SIZE to an integer in range [2..24].
27+
#endif
28+
29+
/** The number of entries a table with precomputed multiples needs to have. */
30+
#define ECMULT_TABLE_SIZE(w) (1L << ((w)-2))
31+
1432
typedef struct {
1533
/* For accelerating the computation of a*P + b*G: */
1634
secp256k1_ge_storage (*pre_g)[]; /* odd multiples of the generator */

src/ecmult_impl.h

+2-16
Original file line numberDiff line numberDiff line change
@@ -44,28 +44,14 @@
4444
# define WINDOW_G ECMULT_WINDOW_SIZE
4545
#endif
4646

47-
/* Noone will ever need more than a window size of 24. The code might
48-
* be correct for larger values of ECMULT_WINDOW_SIZE but this is not
49-
* not tested.
50-
*
51-
* The following limitations are known, and there are probably more:
52-
* If WINDOW_G > 27 and size_t has 32 bits, then the code is incorrect
53-
* because the size of the memory object that we allocate (in bytes)
54-
* will not fit in a size_t.
55-
* If WINDOW_G > 31 and int has 32 bits, then the code is incorrect
56-
* because certain expressions will overflow.
57-
*/
58-
#if ECMULT_WINDOW_SIZE < 2 || ECMULT_WINDOW_SIZE > 24
59-
# error Set ECMULT_WINDOW_SIZE to an integer in range [2..24].
47+
#if ECMULT_WINDOW_SIZE < WINDOW_G
48+
# error ECMULT_WINDOW_SIZE to small for WINDOW_G.
6049
#endif
6150

6251
#define WNAF_BITS 128
6352
#define WNAF_SIZE_BITS(bits, w) (((bits) + (w) - 1) / (w))
6453
#define WNAF_SIZE(w) WNAF_SIZE_BITS(WNAF_BITS, w)
6554

66-
/** The number of entries a table with precomputed multiples needs to have. */
67-
#define ECMULT_TABLE_SIZE(w) (1 << ((w)-2))
68-
6955
/* The number of objects allocated on the scratch space for ecmult_multi algorithms */
7056
#define PIPPENGER_SCRATCH_OBJECTS 6
7157
#define STRAUSS_SCRATCH_OBJECTS 6

src/ecmult_static_pre_g.h

+16,608
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/gen_ecmult_static_pre_g.c

+249
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,249 @@
1+
/*****************************************************************************************************
2+
* Copyright (c) 2013, 2014, 2017, 2021 Pieter Wuille, Andrew Poelstra, Jonas Nick, Russell O'Connor *
3+
* Distributed under the MIT software license, see the accompanying *
4+
* file COPYING or https://www.opensource.org/licenses/mit-license.php. *
5+
*****************************************************************************************************/
6+
7+
#include <stdio.h>
8+
9+
/* Autotools creates libsecp256k1-config.h, of which ECMULT_WINDOW_SIZE is needed.
10+
ifndef guard so downstream users can define their own if they do not use autotools. */
11+
#if !defined(ECMULT_WINDOW_SIZE)
12+
#include "libsecp256k1-config.h"
13+
#endif
14+
15+
/* In principle we could use external ASM, but this yields only a minor speedup in
16+
build time and it's very complicated. In particular when cross-compiling, we'd
17+
need to build the external ASM for the build and the host machine. */
18+
#undef USE_EXTERNAL_ASM
19+
20+
#include "../include/secp256k1.h"
21+
#include "assumptions.h"
22+
#include "util.h"
23+
#include "field_impl.h"
24+
#include "group_impl.h"
25+
#include "ecmult.h"
26+
27+
static void secp256k1_ecmult_odd_multiples_table_storage(const int n, secp256k1_ge_storage *pre, const secp256k1_gej *a) {
28+
secp256k1_gej d;
29+
secp256k1_ge d_ge, p_ge;
30+
secp256k1_gej pj;
31+
secp256k1_fe zi;
32+
secp256k1_fe zr;
33+
secp256k1_fe dx_over_dz_squared;
34+
int i;
35+
36+
secp256k1_gej_double_var(&d, a, NULL);
37+
38+
/* First, we perform all the additions in an isomorphic curve obtained by multiplying
39+
* all `z` coordinates by 1/`d.z`. In these coordinates `d` is affine so we can use
40+
* `secp256k1_gej_add_ge_var` to perform the additions. For each addition, we store
41+
* the resulting y-coordinate and the z-ratio, since we only have enough memory to
42+
* store two field elements. These are sufficient to efficiently undo the isomorphism
43+
* and recompute all the `x`s.
44+
*/
45+
d_ge.x = d.x;
46+
d_ge.y = d.y;
47+
d_ge.infinity = 0;
48+
49+
secp256k1_ge_set_gej_zinv(&p_ge, a, &d.z);
50+
pj.x = p_ge.x;
51+
pj.y = p_ge.y;
52+
pj.z = a->z;
53+
pj.infinity = 0;
54+
55+
for (i = 0; i < (n - 1); i++) {
56+
secp256k1_fe_normalize_var(&pj.y);
57+
secp256k1_fe_to_storage(&pre[i].y, &pj.y);
58+
secp256k1_gej_add_ge_var(&pj, &pj, &d_ge, &zr);
59+
secp256k1_fe_normalize_var(&zr);
60+
secp256k1_fe_to_storage(&pre[i].x, &zr);
61+
}
62+
63+
/* Invert d.z in the same batch, preserving pj.z so we can extract 1/d.z */
64+
secp256k1_fe_mul(&zi, &pj.z, &d.z);
65+
secp256k1_fe_inv_var(&zi, &zi);
66+
67+
/* Directly set `pre[n - 1]` to `pj`, saving the inverted z-coordinate so
68+
* that we can combine it with the saved z-ratios to compute the other zs
69+
* without any more inversions. */
70+
secp256k1_ge_set_gej_zinv(&p_ge, &pj, &zi);
71+
secp256k1_ge_to_storage(&pre[n - 1], &p_ge);
72+
73+
/* Compute the actual x-coordinate of D, which will be needed below. */
74+
secp256k1_fe_mul(&d.z, &zi, &pj.z); /* d.z = 1/d.z */
75+
secp256k1_fe_sqr(&dx_over_dz_squared, &d.z);
76+
secp256k1_fe_mul(&dx_over_dz_squared, &dx_over_dz_squared, &d.x);
77+
78+
/* Going into the second loop, we have set `pre[n-1]` to its final affine
79+
* form, but still need to set `pre[i]` for `i` in 0 through `n-2`. We
80+
* have `zi = (p.z * d.z)^-1`, where
81+
*
82+
* `p.z` is the z-coordinate of the point on the isomorphic curve
83+
* which was ultimately assigned to `pre[n-1]`.
84+
* `d.z` is the multiplier that must be applied to all z-coordinates
85+
* to move from our isomorphic curve back to secp256k1; so the
86+
* product `p.z * d.z` is the z-coordinate of the secp256k1
87+
* point assigned to `pre[n-1]`.
88+
*
89+
* All subsequent inverse-z-coordinates can be obtained by multiplying this
90+
* factor by successive z-ratios, which is much more efficient than directly
91+
* computing each one.
92+
*
93+
* Importantly, these inverse-zs will be coordinates of points on secp256k1,
94+
* while our other stored values come from computations on the isomorphic
95+
* curve. So in the below loop, we will take care not to actually use `zi`
96+
* or any derived values until we're back on secp256k1.
97+
*/
98+
i = n - 1;
99+
while (i > 0) {
100+
secp256k1_fe zi2, zi3;
101+
const secp256k1_fe *rzr;
102+
i--;
103+
104+
secp256k1_ge_from_storage(&p_ge, &pre[i]);
105+
106+
/* For each remaining point, we extract the z-ratio from the stored
107+
* x-coordinate, compute its z^-1 from that, and compute the full
108+
* point from that. */
109+
rzr = &p_ge.x;
110+
secp256k1_fe_mul(&zi, &zi, rzr);
111+
secp256k1_fe_sqr(&zi2, &zi);
112+
secp256k1_fe_mul(&zi3, &zi2, &zi);
113+
/* To compute the actual x-coordinate, we use the stored z ratio and
114+
* y-coordinate, which we obtained from `secp256k1_gej_add_ge_var`
115+
* in the loop above, as well as the inverse of the square of its
116+
* z-coordinate. We store the latter in the `zi2` variable, which is
117+
* computed iteratively starting from the overall Z inverse then
118+
* multiplying by each z-ratio in turn.
119+
*
120+
* Denoting the z-ratio as `rzr`, we observe that it is equal to `h`
121+
* from the inside of the above `gej_add_ge_var` call. This satisfies
122+
*
123+
* rzr = d_x * z^2 - x * d_z^2
124+
*
125+
* where (`d_x`, `d_z`) are Jacobian coordinates of `D` and `(x, z)`
126+
* are Jacobian coordinates of our desired point -- except both are on
127+
* the isomorphic curve that we were using when we called `gej_add_ge_var`.
128+
* To get back to secp256k1, we must multiply both `z`s by `d_z`, or
129+
* equivalently divide both `x`s by `d_z^2`. Our equation then becomes
130+
*
131+
* rzr = d_x * z^2 / d_z^2 - x
132+
*
133+
* (The left-hand-side, being a ratio of z-coordinates, is unaffected
134+
* by the isomorphism.)
135+
*
136+
* Rearranging to solve for `x`, we have
137+
*
138+
* x = d_x * z^2 / d_z^2 - rzr
139+
*
140+
* But what we actually want is the affine coordinate `X = x/z^2`,
141+
* which will satisfy
142+
*
143+
* X = d_x / d_z^2 - rzr / z^2
144+
* = dx_over_dz_squared - rzr * zi2
145+
*/
146+
secp256k1_fe_mul(&p_ge.x, rzr, &zi2);
147+
secp256k1_fe_negate(&p_ge.x, &p_ge.x, 1);
148+
secp256k1_fe_add(&p_ge.x, &dx_over_dz_squared);
149+
/* y is stored_y/z^3, as we expect */
150+
secp256k1_fe_mul(&p_ge.y, &p_ge.y, &zi3);
151+
/* Store */
152+
secp256k1_ge_to_storage(&pre[i], &p_ge);
153+
}
154+
}
155+
156+
void print_table(FILE* fp, const char * name, int window_g, const secp256k1_gej * gj, int with_conditionals) {
157+
/* This array is only static because it may be unreasonably large to place on the stack. */
158+
static secp256k1_ge_storage pre[ECMULT_TABLE_SIZE(ECMULT_WINDOW_SIZE) < ECMULT_TABLE_SIZE(8)
159+
? ECMULT_TABLE_SIZE(8)
160+
: ECMULT_TABLE_SIZE(ECMULT_WINDOW_SIZE)];
161+
int j;
162+
int i;
163+
164+
secp256k1_ecmult_odd_multiples_table_storage(ECMULT_TABLE_SIZE(window_g), pre, gj);
165+
fprintf(fp, "static const secp256k1_ge_storage %s[ECMULT_TABLE_SIZE(WINDOW_G)] = {\n", name);
166+
fprintf(fp, " S(%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x)\n", SECP256K1_GE_STORAGE_CONST_GET(pre[0]));
167+
j = 1;
168+
for(i = 3; i <= window_g; ++i) {
169+
if (with_conditionals) {
170+
fprintf(fp, "#if ECMULT_TABLE_SIZE(WINDOW_G) > %ld\n", ECMULT_TABLE_SIZE(i-1));
171+
}
172+
for(;j < ECMULT_TABLE_SIZE(i); ++j) {
173+
fprintf(fp, ",S(%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x)\n", SECP256K1_GE_STORAGE_CONST_GET(pre[j]));
174+
}
175+
if (with_conditionals) {
176+
fprintf(fp, "#endif\n");
177+
}
178+
}
179+
fprintf(fp, "};\n");
180+
181+
}
182+
183+
void print_two_tables(FILE* fp, int window_g, secp256k1_gej * gj, int with_conditionals) {
184+
int i;
185+
186+
print_table(fp, "secp256k1_pre_g", window_g, gj, with_conditionals);
187+
for (i = 0; i < 128; ++i) {
188+
secp256k1_gej_double_var(gj, gj, NULL);
189+
}
190+
print_table(fp, "secp256k1_pre_g_128", window_g, gj, with_conditionals);
191+
}
192+
193+
int main(void) {
194+
const secp256k1_ge g = SECP256K1_G;
195+
const secp256k1_ge g_13 = SECP256K1_G_ORDER_13;
196+
const secp256k1_ge g_199 = SECP256K1_G_ORDER_199;
197+
const int window_g_13 = 4;
198+
const int window_g_199 = 8;
199+
FILE* fp;
200+
secp256k1_gej gj;
201+
202+
fp = fopen("src/ecmult_static_pre_g.h","w");
203+
if (fp == NULL) {
204+
fprintf(stderr, "Could not open src/ecmult_static_pre_g.h for writing!\n");
205+
return -1;
206+
}
207+
208+
fprintf(fp, "/* This file was automatically generated by gen_ecmult_static_pre_g. */\n");
209+
fprintf(fp, "#ifndef SECP256K1_ECMULT_STATIC_PRE_G_H\n");
210+
fprintf(fp, "#define SECP256K1_ECMULT_STATIC_PRE_G_H\n");
211+
fprintf(fp, "#include \"src/group.h\"\n");
212+
fprintf(fp, "#ifdef S\n");
213+
fprintf(fp, " #error macro identifier S already in use.\n");
214+
fprintf(fp, "#endif\n");
215+
fprintf(fp, "#define S(a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p) "
216+
"SECP256K1_GE_STORAGE_CONST(0x##a##u,0x##b##u,0x##c##u,0x##d##u,0x##e##u,0x##f##u,0x##g##u,"
217+
"0x##h##u,0x##i##u,0x##j##u,0x##k##u,0x##l##u,0x##m##u,0x##n##u,0x##o##u,0x##p##u)\n");
218+
fprintf(fp, "#if ECMULT_TABLE_SIZE(ECMULT_WINDOW_SIZE) > %ld\n", ECMULT_TABLE_SIZE(ECMULT_WINDOW_SIZE));
219+
fprintf(fp, " #error configuration mismatch, invalid ECMULT_WINDOW_SIZE. Try deleting ecmult_static_pre_g.h before the build.\n");
220+
fprintf(fp, "#endif\n");
221+
fprintf(fp, "#if defined(EXHAUSTIVE_TEST_ORDER)\n");
222+
fprintf(fp, "#if EXHAUSTIVE_TEST_ORDER == 13\n");
223+
fprintf(fp, "#define WINDOW_G %d\n", window_g_13);
224+
225+
secp256k1_gej_set_ge(&gj, &g_13);
226+
print_two_tables(fp, window_g_13, &gj, 0);
227+
228+
fprintf(fp, "#elif EXHAUSTIVE_TEST_ORDER == 199\n");
229+
fprintf(fp, "#define WINDOW_G %d\n", window_g_199);
230+
231+
secp256k1_gej_set_ge(&gj, &g_199);
232+
print_two_tables(fp, window_g_199, &gj, 0);
233+
234+
fprintf(fp, "#else\n");
235+
fprintf(fp, " #error No known generator for the specified exhaustive test group order.\n");
236+
fprintf(fp, "#endif\n");
237+
fprintf(fp, "#else\n");
238+
fprintf(fp, "#define WINDOW_G ECMULT_WINDOW_SIZE\n");
239+
240+
secp256k1_gej_set_ge(&gj, &g);
241+
print_two_tables(fp, ECMULT_WINDOW_SIZE, &gj, 1);
242+
243+
fprintf(fp, "#endif\n");
244+
fprintf(fp, "#undef S\n");
245+
fprintf(fp, "#endif\n");
246+
fclose(fp);
247+
248+
return 0;
249+
}

0 commit comments

Comments
 (0)