Skip to content

Commit fde1dfc

Browse files
peterdettmansipa
authored andcommitted
Signed-digit multi-comb ecmult_gen algorithm
This introduces the signed-digit multi-comb multiplication algorithm for constant-time G multiplications (ecmult_gen). It is based on section 3.3 of "Fast and compact elliptic-curve cryptography" by Mike Hamburg (see https://eprint.iacr.org/2012/309). Original implementation by Peter Dettman, with changes by Pieter Wuille to use scalars for recoding, and additional comments.
1 parent 486518b commit fde1dfc

8 files changed

+754
-9868
lines changed

src/ecmult_gen.h

+89-15
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/***********************************************************************
2-
* Copyright (c) 2013, 2014 Pieter Wuille *
2+
* Copyright (c) Pieter Wuille, Peter Dettman *
33
* Distributed under the MIT software license, see the accompanying *
44
* file COPYING or https://www.opensource.org/licenses/mit-license.php.*
55
***********************************************************************/
@@ -10,31 +10,105 @@
1010
#include "scalar.h"
1111
#include "group.h"
1212

13-
#ifndef ECMULT_GEN_PREC_BITS
14-
# define ECMULT_GEN_PREC_BITS 4
15-
# ifdef DEBUG_CONFIG
16-
# pragma message DEBUG_CONFIG_MSG("ECMULT_GEN_PREC_BITS undefined, assuming default value")
13+
14+
/* Configuration parameters for the signed-digit multi-comb algorithm:
15+
*
16+
* - COMB_BLOCKS is the number of blocks the input is split into. Each
17+
* has a corresponding table.
18+
* - COMB_TEETH is the number of bits simultaneously covered by one table.
19+
*
20+
* The comb's spacing (COMB_SPACING), or the distance between the teeth,
21+
* is defined as ceil(256 / (COMB_BLOCKS * COMB_TEETH)). Each block covers
22+
* COMB_SPACING * COMB_TEETH consecutive bits in the input.
23+
*
24+
* The size of the precomputed table is COMB_BLOCKS * (1 << (COMB_TEETH - 1))
25+
* secp256k1_ge_storages.
26+
*
27+
* The number of point additions equals COMB_BLOCKS * COMB_SPACING. Each point
28+
* addition involves a cmov from (1 << (COMB_TEETH - 1)) table entries and a
29+
* conditional negation.
30+
*
31+
* The number of point doublings is COMB_SPACING - 1. */
32+
33+
#if defined(EXHAUSTIVE_TEST_ORDER)
34+
/* We need to control these values for exhaustive tests because
35+
* the table cannot have infinities in them (secp256k1_ge_storage
36+
* doesn't support infinities) */
37+
# undef COMB_BLOCKS
38+
# undef COMB_TEETH
39+
# if EXHAUSTIVE_TEST_ORDER > 32
40+
# define COMB_BLOCKS 52
41+
# define COMB_TEETH 5
42+
# elif EXHAUSTIVE_TEST_ORDER > 16
43+
# define COMB_BLOCKS 64
44+
# define COMB_TEETH 4
45+
# elif EXHAUSTIVE_TEST_ORDER > 8
46+
# define COMB_BLOCKS 86
47+
# define COMB_TEETH 3
48+
# elif EXHAUSTIVE_TEST_ORDER > 4
49+
# define COMB_BLOCKS 128
50+
# define COMB_TEETH 2
51+
# else
52+
# define COMB_BLOCKS 256
53+
# define COMB_TEETH 1
1754
# endif
18-
#endif
55+
#else /* !defined(EXHAUSTIVE_TEST_ORDER) */
56+
/* Use (11, 6) as default configuration, which results in a 22 kB table. */
57+
# ifndef COMB_BLOCKS
58+
# define COMB_BLOCKS 11
59+
# ifdef DEBUG_CONFIG
60+
# pragma message DEBUG_CONFIG_MSG("COMB_BLOCKS undefined, assuming default value")
61+
# endif
62+
# endif
63+
# ifndef COMB_TEETH
64+
# define COMB_TEETH 6
65+
# ifdef DEBUG_CONFIG
66+
# pragma message DEBUG_CONFIG_MSG("COMB_TEETH undefined, assuming default value")
67+
# endif
68+
# endif
69+
#endif /* defined(EXHAUSTIVE_TEST_ORDER) */
1970

20-
#ifdef DEBUG_CONFIG
21-
# pragma message DEBUG_CONFIG_DEF(ECMULT_GEN_PREC_BITS)
71+
/* Range checks on the parameters. */
72+
#if !(1 <= COMB_BLOCKS && COMB_BLOCKS <= 256)
73+
# error "COMB_BLOCKS must be in the range [1, 256]"
2274
#endif
75+
#if !(1 <= COMB_TEETH && COMB_TEETH <= 8)
76+
# error "COMB_TEETH must be in the range [1, 8]"
77+
#endif
78+
79+
/* The remaining COMB_* parameters are derived values, don't modify these. */
80+
/* - The distance between the teeth of each comb. */
81+
#define COMB_SPACING CEIL_DIV(256, COMB_BLOCKS * COMB_TEETH)
82+
/* - The number of bits covered by all the blocks; must be at least 256. */
83+
#define COMB_BITS (COMB_BLOCKS * COMB_TEETH * COMB_SPACING)
84+
/* - The number of entries per table. */
85+
#define COMB_POINTS (1 << (COMB_TEETH - 1))
2386

24-
#if ECMULT_GEN_PREC_BITS != 2 && ECMULT_GEN_PREC_BITS != 4 && ECMULT_GEN_PREC_BITS != 8
25-
# error "Set ECMULT_GEN_PREC_BITS to 2, 4 or 8."
87+
/* Additional sanity checks. */
88+
#if (COMB_BLOCKS - 1) * COMB_TEETH * COMB_SPACING >= 256
89+
# error "COMB_BLOCKS can be reduced"
90+
#endif
91+
#if COMB_BLOCKS * (COMB_TEETH - 1) * COMB_SPACING >= 256
92+
# error "COMB_TEETH can be reduced"
2693
#endif
2794

28-
#define ECMULT_GEN_PREC_G(bits) (1 << bits)
29-
#define ECMULT_GEN_PREC_N(bits) (256 / bits)
95+
#ifdef DEBUG_CONFIG
96+
# pragma message DEBUG_CONFIG_DEF(COMB_BLOCKS)
97+
# pragma message DEBUG_CONFIG_DEF(COMB_TEETH)
98+
#endif
3099

31100
typedef struct {
32101
/* Whether the context has been built. */
33102
int built;
34103

35-
/* Blinding values used when computing nG as (n-b)G + bG. */
36-
secp256k1_scalar scalar_offset; /* -b */
37-
secp256k1_ge ge_offset; /* bG */
104+
/* Values chosen such that
105+
*
106+
* n*G == comb(n + (2^COMB_BITS-1)/2 + scalar_offset, G/2) + ge_offset.
107+
*
108+
* This expression lets us use scalar blinding and optimize the comb precomputation. See
109+
* ecmult_gen_impl.h for more details. */
110+
secp256k1_scalar scalar_offset;
111+
secp256k1_ge ge_offset;
38112
} secp256k1_ecmult_gen_context;
39113

40114
static void secp256k1_ecmult_gen_context_build(secp256k1_ecmult_gen_context* ctx);

src/ecmult_gen_compute_table.h

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/***********************************************************************
2-
* Copyright (c) 2013, 2014, 2015 Pieter Wuille, Gregory Maxwell *
2+
* Copyright (c) Pieter Wuille, Gregory Maxwell *
33
* Distributed under the MIT software license, see the accompanying *
44
* file COPYING or https://www.opensource.org/licenses/mit-license.php.*
55
***********************************************************************/
@@ -9,6 +9,6 @@
99

1010
#include "ecmult_gen.h"
1111

12-
static void secp256k1_ecmult_gen_compute_table(secp256k1_ge_storage* table, const secp256k1_ge* gen, int bits);
12+
static void secp256k1_ecmult_gen_compute_table(secp256k1_ge_storage* table, const secp256k1_ge* gen, int blocks, int teeth);
1313

1414
#endif /* SECP256K1_ECMULT_GEN_COMPUTE_TABLE_H */

src/ecmult_gen_compute_table_impl.h

+77-55
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/***********************************************************************
2-
* Copyright (c) 2013, 2014, 2015 Pieter Wuille, Gregory Maxwell *
2+
* Copyright (c) Pieter Wuille, Gregory Maxwell, Peter Dettman *
33
* Distributed under the MIT software license, see the accompanying *
44
* file COPYING or https://www.opensource.org/licenses/mit-license.php.*
55
***********************************************************************/
@@ -10,74 +10,96 @@
1010
#include "ecmult_gen_compute_table.h"
1111
#include "group_impl.h"
1212
#include "field_impl.h"
13+
#include "scalar_impl.h"
1314
#include "ecmult_gen.h"
1415
#include "util.h"
1516

16-
static void secp256k1_ecmult_gen_compute_table(secp256k1_ge_storage* table, const secp256k1_ge* gen, int bits) {
17-
int g = ECMULT_GEN_PREC_G(bits);
18-
int n = ECMULT_GEN_PREC_N(bits);
17+
static void secp256k1_ecmult_gen_compute_table(secp256k1_ge_storage* table, const secp256k1_ge* gen, int blocks, int teeth) {
18+
size_t points = ((size_t)1) << (teeth - 1);
19+
size_t points_total = points * blocks;
20+
int spacing = (256 + blocks * teeth - 1) / (blocks * teeth);
21+
secp256k1_ge* prec = checked_malloc(&default_error_callback, points_total * sizeof(*prec));
22+
secp256k1_gej* ds = checked_malloc(&default_error_callback, teeth * sizeof(*ds));
23+
secp256k1_gej* vs = checked_malloc(&default_error_callback, points_total * sizeof(*vs));
24+
secp256k1_gej u;
25+
size_t vs_pos = 0;
26+
secp256k1_scalar half;
27+
int block, i;
1928

20-
secp256k1_ge* prec = checked_malloc(&default_error_callback, n * g * sizeof(*prec));
21-
secp256k1_gej gj;
22-
secp256k1_gej nums_gej;
23-
int i, j;
29+
VERIFY_CHECK(points_total > 0);
2430

25-
VERIFY_CHECK(g > 0);
26-
VERIFY_CHECK(n > 0);
27-
28-
/* get the generator */
29-
secp256k1_gej_set_ge(&gj, gen);
30-
31-
/* Construct a group element with no known corresponding scalar (nothing up my sleeve). */
31+
/* u is the running power of two times gen we're working with, initially gen/2. */
32+
secp256k1_scalar_half(&half, &secp256k1_scalar_one);
33+
secp256k1_gej_set_infinity(&u);
34+
for (i = 255; i >= 0; --i) {
35+
/* Use a very simple multiplication ladder to avoid dependency on ecmult. */
36+
secp256k1_gej_double_var(&u, &u, NULL);
37+
if (secp256k1_scalar_get_bits(&half, i, 1)) {
38+
secp256k1_gej_add_ge_var(&u, &u, gen, NULL);
39+
}
40+
}
41+
#ifdef VERIFY
3242
{
33-
static const unsigned char nums_b32[33] = "The scalar for this x is unknown";
34-
secp256k1_fe nums_x;
35-
secp256k1_ge nums_ge;
36-
int r;
37-
r = secp256k1_fe_set_b32_limit(&nums_x, nums_b32);
38-
(void)r;
39-
VERIFY_CHECK(r);
40-
r = secp256k1_ge_set_xo_var(&nums_ge, &nums_x, 0);
41-
(void)r;
42-
VERIFY_CHECK(r);
43-
secp256k1_gej_set_ge(&nums_gej, &nums_ge);
44-
/* Add G to make the bits in x uniformly distributed. */
45-
secp256k1_gej_add_ge_var(&nums_gej, &nums_gej, gen, NULL);
43+
/* Verify that u*2 = gen. */
44+
secp256k1_gej double_u;
45+
secp256k1_gej_double_var(&double_u, &u, NULL);
46+
VERIFY_CHECK(secp256k1_gej_eq_ge_var(&double_u, gen));
4647
}
48+
#endif
4749

48-
/* compute prec. */
49-
{
50-
secp256k1_gej gbase;
51-
secp256k1_gej numsbase;
52-
secp256k1_gej* precj = checked_malloc(&default_error_callback, n * g * sizeof(*precj)); /* Jacobian versions of prec. */
53-
gbase = gj; /* PREC_G^j * G */
54-
numsbase = nums_gej; /* 2^j * nums. */
55-
for (j = 0; j < n; j++) {
56-
/* Set precj[j*PREC_G .. j*PREC_G+(PREC_G-1)] to (numsbase, numsbase + gbase, ..., numsbase + (PREC_G-1)*gbase). */
57-
precj[j*g] = numsbase;
58-
for (i = 1; i < g; i++) {
59-
secp256k1_gej_add_var(&precj[j*g + i], &precj[j*g + i - 1], &gbase, NULL);
50+
for (block = 0; block < blocks; ++block) {
51+
int tooth;
52+
/* Here u = 2^(block*teeth*spacing) * gen/2. */
53+
secp256k1_gej sum;
54+
secp256k1_gej_set_infinity(&sum);
55+
for (tooth = 0; tooth < teeth; ++tooth) {
56+
/* Here u = 2^((block*teeth + tooth)*spacing) * gen/2. */
57+
int bit_off;
58+
/* Make sum = sum(2^((block*teeth + t)*spacing), t=0..tooth) * gen/2. */
59+
secp256k1_gej_add_var(&sum, &sum, &u, NULL);
60+
/* Make u = 2^((block*teeth + tooth)*spacing + 1) * gen/2. */
61+
secp256k1_gej_double_var(&u, &u, NULL);
62+
/* Make ds[tooth] = u = 2^((block*teeth + tooth)*spacing + 1) * gen/2. */
63+
ds[tooth] = u;
64+
/* Make u = 2^((block*teeth + tooth + 1)*spacing) * gen/2. */
65+
for (bit_off = 1; bit_off < spacing; ++bit_off) {
66+
secp256k1_gej_double_var(&u, &u, NULL);
6067
}
61-
/* Multiply gbase by PREC_G. */
62-
for (i = 0; i < bits; i++) {
63-
secp256k1_gej_double_var(&gbase, &gbase, NULL);
64-
}
65-
/* Multiply numbase by 2. */
66-
secp256k1_gej_double_var(&numsbase, &numsbase, NULL);
67-
if (j == n - 2) {
68-
/* In the last iteration, numsbase is (1 - 2^j) * nums instead. */
69-
secp256k1_gej_neg(&numsbase, &numsbase);
70-
secp256k1_gej_add_var(&numsbase, &numsbase, &nums_gej, NULL);
68+
}
69+
/* Now u = 2^((block*teeth + teeth)*spacing) * gen/2
70+
* = 2^((block+1)*teeth*spacing) * gen/2 */
71+
72+
/* Next, compute the table entries for block number block in Jacobian coordinates.
73+
* The entries will occupy vs[block*points + i] for i=0..points-1.
74+
* We start by computing the first (i=0) value corresponding to all summed
75+
* powers of two times G being negative. */
76+
secp256k1_gej_neg(&vs[vs_pos++], &sum);
77+
/* And then teeth-1 times "double" the range of i values for which the table
78+
* is computed: in each iteration, double the table by taking an existing
79+
* table entry and adding ds[tooth]. */
80+
for (tooth = 0; tooth < teeth - 1; ++tooth) {
81+
size_t stride = ((size_t)1) << tooth;
82+
size_t index;
83+
for (index = 0; index < stride; ++index, ++vs_pos) {
84+
secp256k1_gej_add_var(&vs[vs_pos], &vs[vs_pos - stride], &ds[tooth], NULL);
7185
}
7286
}
73-
secp256k1_ge_set_all_gej_var(prec, precj, n * g);
74-
free(precj);
7587
}
76-
for (j = 0; j < n; j++) {
77-
for (i = 0; i < g; i++) {
78-
secp256k1_ge_to_storage(&table[j*g + i], &prec[j*g + i]);
88+
VERIFY_CHECK(vs_pos == points_total);
89+
90+
/* Convert all points simultaneously from secp256k1_gej to secp256k1_ge. */
91+
secp256k1_ge_set_all_gej_var(prec, vs, points_total);
92+
/* Convert all points from secp256k1_ge to secp256k1_ge_storage output. */
93+
for (block = 0; block < blocks; ++block) {
94+
size_t index;
95+
for (index = 0; index < points; ++index) {
96+
secp256k1_ge_to_storage(&table[block * points + index], &prec[block * points + index]);
7997
}
8098
}
99+
100+
/* Free memory. */
101+
free(vs);
102+
free(ds);
81103
free(prec);
82104
}
83105

0 commit comments

Comments
 (0)