Skip to content

Commit 4fb4dac

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 7688a4f commit 4fb4dac

8 files changed

+16917
-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

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

0 commit comments

Comments
 (0)