Skip to content

Commit b47d8f3

Browse files
committed
batch, ecmult: Add batch_verify and refactor strauss_batch
This commit refactors ecmult_strauss_batch and adds _batch_verify API. The current ecmult_strauss_batch only works on empty scratch space. To make batch_verify compatible, we need ecmult_strauss_batch to support a scratch space pre-filled with scalars and points. So, it was refactored to do exactly that. The batch_verify API always uses the Strauss algorithm. It doesn't switch to Pippenger (unlike ecmult_multi_var). ecmult_pippenger_batch represents points as secp256k1_ge whereas ecmult_strauss_batch represents points as secp256k1_gej. This makes supporting both Pippenger and Strauss difficult (at least with the current batch object design). Hence, batch_verify only supports Strauss for simplicity.
1 parent 535a94b commit b47d8f3

File tree

3 files changed

+113
-19
lines changed

3 files changed

+113
-19
lines changed

include/secp256k1_batch.h

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,21 @@ SECP256K1_API void secp256k1_batch_destroy(
6363
secp256k1_batch* batch
6464
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2);
6565

66+
/** Verify the set of schnorr signatures or tweaked pubkeys present in the secp256k1_batch.
67+
*
68+
* Returns: 1: every schnorrsig/tweak (in batch) is valid
69+
* 0: atleaset one of the schnorrsig/tweak (in batch) is invalid
70+
*
71+
* In particular, returns 1 if the batch object is empty (does not contain any schnorrsigs/tweaks).
72+
*
73+
* Args: ctx: a secp256k1 context object (can be initialized for none).
74+
* batch: a secp256k1 batch object that contains a set of schnorrsigs/tweaks.
75+
*/
76+
SECP256K1_API int secp256k1_batch_verify(
77+
const secp256k1_context *ctx,
78+
secp256k1_batch *batch
79+
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2);
80+
6681
#ifdef __cplusplus
6782
}
6883
#endif

src/ecmult_impl.h

Lines changed: 54 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -358,33 +358,58 @@ static void secp256k1_ecmult(secp256k1_gej *r, const secp256k1_gej *a, const sec
358358
secp256k1_ecmult_strauss_wnaf(&state, r, 1, a, na, ng);
359359
}
360360

361-
static size_t secp256k1_strauss_scratch_size(size_t n_points) {
362-
static const size_t point_size = (sizeof(secp256k1_ge) + sizeof(secp256k1_fe)) * ECMULT_TABLE_SIZE(WINDOW_A) + sizeof(struct secp256k1_strauss_point_state) + sizeof(secp256k1_gej) + sizeof(secp256k1_scalar);
363-
return n_points*point_size;
361+
/** Allocate strauss state on the scratch space */
362+
static int secp256k1_strauss_scratch_alloc_state(const secp256k1_callback* error_callback, secp256k1_scratch *scratch, struct secp256k1_strauss_state *state, size_t n_points) {
363+
const size_t scratch_checkpoint = secp256k1_scratch_checkpoint(error_callback, scratch);
364+
365+
/* We allocate three objects on the scratch space. If these allocations
366+
* change, make sure to check if this affects STRAUSS_SCRATCH_OBJECTS
367+
* constant and strauss_scratch_size. */
368+
state->aux = (secp256k1_fe*)secp256k1_scratch_alloc(error_callback, scratch, n_points * ECMULT_TABLE_SIZE(WINDOW_A) * sizeof(secp256k1_fe));
369+
state->pre_a = (secp256k1_ge*)secp256k1_scratch_alloc(error_callback, scratch, n_points * ECMULT_TABLE_SIZE(WINDOW_A) * sizeof(secp256k1_ge));
370+
state->ps = (struct secp256k1_strauss_point_state*)secp256k1_scratch_alloc(error_callback, scratch, n_points * sizeof(struct secp256k1_strauss_point_state));
371+
372+
if (state->aux == NULL || state->pre_a == NULL || state->ps == NULL) {
373+
secp256k1_scratch_apply_checkpoint(error_callback, scratch, scratch_checkpoint);
374+
return 0;
375+
}
376+
return 1;
364377
}
365378

366-
static int secp256k1_ecmult_strauss_batch(const secp256k1_callback* error_callback, secp256k1_scratch *scratch, secp256k1_gej *r, const secp256k1_scalar *inp_g_sc, secp256k1_ecmult_multi_callback cb, void *cbdata, size_t n_points, size_t cb_offset) {
367-
secp256k1_gej* points;
368-
secp256k1_scalar* scalars;
379+
/** Run ecmult_strauss_wnaf on the given points and scalars */
380+
static int secp256k1_ecmult_strauss_batch_internal(const secp256k1_callback* error_callback, secp256k1_scratch *scratch, secp256k1_gej *r, secp256k1_scalar *scalars, secp256k1_gej *points, const secp256k1_scalar *inp_g_sc, size_t n_points) {
369381
struct secp256k1_strauss_state state;
370-
size_t i;
371382
const size_t scratch_checkpoint = secp256k1_scratch_checkpoint(error_callback, scratch);
372383

373384
secp256k1_gej_set_infinity(r);
374385
if (inp_g_sc == NULL && n_points == 0) {
375386
return 1;
376387
}
377388

378-
/* We allocate STRAUSS_SCRATCH_OBJECTS objects on the scratch space. If these
379-
* allocations change, make sure to update the STRAUSS_SCRATCH_OBJECTS
380-
* constant and strauss_scratch_size accordingly. */
389+
if(!secp256k1_strauss_scratch_alloc_state(error_callback, scratch, &state, n_points)) {
390+
return 0;
391+
}
392+
393+
secp256k1_ecmult_strauss_wnaf(&state, r, n_points, points, scalars, inp_g_sc);
394+
secp256k1_scratch_apply_checkpoint(error_callback, scratch, scratch_checkpoint);
395+
return 1;
396+
}
397+
398+
/** Run ecmult_strauss_wnaf on the given points and scalars. Returns 0 if the
399+
* scratch space is empty. `n_points` number of scalars and points are
400+
* extracted from `cbdata` using `cb` and stored on the scratch space.
401+
*/
402+
static int secp256k1_ecmult_strauss_batch(const secp256k1_callback* error_callback, secp256k1_scratch *scratch, secp256k1_gej *r, const secp256k1_scalar *inp_g_sc, secp256k1_ecmult_multi_callback cb, void *cbdata, size_t n_points, size_t cb_offset) {
403+
secp256k1_gej* points;
404+
secp256k1_scalar* scalars;
405+
size_t i;
406+
const size_t scratch_checkpoint = secp256k1_scratch_checkpoint(error_callback, scratch);
407+
/* We allocate STRAUSS_SCRATCH_OBJECTS objects on the scratch space in
408+
* total. If these allocations change, make sure to update the
409+
* STRAUSS_SCRATCH_OBJECTS constant and strauss_scratch_size accordingly. */
381410
points = (secp256k1_gej*)secp256k1_scratch_alloc(error_callback, scratch, n_points * sizeof(secp256k1_gej));
382411
scalars = (secp256k1_scalar*)secp256k1_scratch_alloc(error_callback, scratch, n_points * sizeof(secp256k1_scalar));
383-
state.aux = (secp256k1_fe*)secp256k1_scratch_alloc(error_callback, scratch, n_points * ECMULT_TABLE_SIZE(WINDOW_A) * sizeof(secp256k1_fe));
384-
state.pre_a = (secp256k1_ge*)secp256k1_scratch_alloc(error_callback, scratch, n_points * ECMULT_TABLE_SIZE(WINDOW_A) * sizeof(secp256k1_ge));
385-
state.ps = (struct secp256k1_strauss_point_state*)secp256k1_scratch_alloc(error_callback, scratch, n_points * sizeof(struct secp256k1_strauss_point_state));
386-
387-
if (points == NULL || scalars == NULL || state.aux == NULL || state.pre_a == NULL || state.ps == NULL) {
412+
if (points == NULL || scalars == NULL) {
388413
secp256k1_scratch_apply_checkpoint(error_callback, scratch, scratch_checkpoint);
389414
return 0;
390415
}
@@ -397,20 +422,30 @@ static int secp256k1_ecmult_strauss_batch(const secp256k1_callback* error_callba
397422
}
398423
secp256k1_gej_set_ge(&points[i], &point);
399424
}
400-
secp256k1_ecmult_strauss_wnaf(&state, r, n_points, points, scalars, inp_g_sc);
425+
426+
secp256k1_ecmult_strauss_batch_internal(error_callback, scratch, r, scalars, points, inp_g_sc, n_points);
401427
secp256k1_scratch_apply_checkpoint(error_callback, scratch, scratch_checkpoint);
402428
return 1;
403429
}
404430

405-
/* Wrapper for secp256k1_ecmult_multi_func interface */
406-
static int secp256k1_ecmult_strauss_batch_single(const secp256k1_callback* error_callback, secp256k1_scratch *scratch, secp256k1_gej *r, const secp256k1_scalar *inp_g_sc, secp256k1_ecmult_multi_callback cb, void *cbdata, size_t n) {
407-
return secp256k1_ecmult_strauss_batch(error_callback, scratch, r, inp_g_sc, cb, cbdata, n, 0);
431+
/** Return the scratch size that is allocated by a call to strauss_batch
432+
* (ignoring padding required for alignment). */
433+
static size_t secp256k1_strauss_scratch_size(size_t n_points) {
434+
static const size_t point_size = (sizeof(secp256k1_ge) + sizeof(secp256k1_fe)) * ECMULT_TABLE_SIZE(WINDOW_A) + sizeof(struct secp256k1_strauss_point_state) + sizeof(secp256k1_gej) + sizeof(secp256k1_scalar);
435+
return n_points*point_size;
408436
}
409437

438+
/** Return the maximum number of points that can be provided to strauss_batch
439+
* with a given scratch space. */
410440
static size_t secp256k1_strauss_max_points(const secp256k1_callback* error_callback, secp256k1_scratch *scratch) {
411441
return secp256k1_scratch_max_allocation(error_callback, scratch, STRAUSS_SCRATCH_OBJECTS) / secp256k1_strauss_scratch_size(1);
412442
}
413443

444+
/* Wrapper for secp256k1_ecmult_multi_func interface */
445+
static int secp256k1_ecmult_strauss_batch_single(const secp256k1_callback* error_callback, secp256k1_scratch *scratch, secp256k1_gej *r, const secp256k1_scalar *inp_g_sc, secp256k1_ecmult_multi_callback cb, void *cbdata, size_t n) {
446+
return secp256k1_ecmult_strauss_batch(error_callback, scratch, r, inp_g_sc, cb, cbdata, n, 0);
447+
}
448+
414449
/** Convert a number to WNAF notation.
415450
* The number becomes represented by sum(2^{wi} * wnaf[i], i=0..WNAF_SIZE(w)+1) - return_val.
416451
* It has the following guarantees:

src/modules/batch/main_impl.h

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,14 @@ static size_t secp256k1_batch_scratch_size(int max_terms) {
4545
return ret;
4646
}
4747

48+
/** Clears the scalar and points allocated on the batch object's scratch space */
49+
static void secp256k1_batch_scratch_clear(secp256k1_batch* batch) {
50+
secp256k1_scalar_clear(&batch->sc_g);
51+
/* setting the len = 0 will suffice (instead of clearing the memory)
52+
* since, there are no secrets stored on the scratch space */
53+
batch->len = 0;
54+
}
55+
4856
/** Allocates space for `batch->capacity` number of scalars and points on batch
4957
* object's scratch space */
5058
static int secp256k1_batch_scratch_alloc(const secp256k1_callback* error_callback, secp256k1_batch* batch) {
@@ -138,4 +146,40 @@ void secp256k1_batch_destroy(const secp256k1_context *ctx, secp256k1_batch *batc
138146
}
139147
}
140148

149+
/** verifies the inputs (schnorrsig or tweak_check) by performing multi-scalar point
150+
* multiplication on the scalars (`batch->scalars`) and points (`batch->points`)
151+
* present in the batch. Uses `secp256k1_ecmult_strauss_batch_internal` to perform
152+
* the multi-multiplication.
153+
*
154+
* Fails if:
155+
* 0 != -(s1 + a2*s2 + ... + au*su)G
156+
* + R1 + a2*R2 + ... + au*Ru + e1*P1 + (a2*e2)P2 + ... + (au*eu)Pu.
157+
*/
158+
int secp256k1_batch_verify(const secp256k1_context *ctx, secp256k1_batch *batch) {
159+
secp256k1_gej resj;
160+
161+
VERIFY_CHECK(ctx != NULL);
162+
ARG_CHECK(batch != NULL);
163+
164+
if(batch->result == 0) {
165+
return 0;
166+
}
167+
168+
if (batch->len > 0) {
169+
int strauss_ret = secp256k1_ecmult_strauss_batch_internal(&ctx->error_callback, batch->data, &resj, batch->scalars, batch->points, &batch->sc_g, batch->len);
170+
int mid_res = secp256k1_gej_is_infinity(&resj);
171+
172+
/* `_strauss_batch_internal` should not fail due to insufficient memory.
173+
* `batch_create` will allocate memeory needed by `_strauss_batch_internal`. */
174+
VERIFY_CHECK(strauss_ret != 0);
175+
/* Silence −Wunused-variable when VERIFY is off */
176+
(void)strauss_ret;
177+
178+
batch->result = batch->result && mid_res;
179+
secp256k1_batch_scratch_clear(batch);
180+
}
181+
182+
return batch->result;
183+
}
184+
141185
#endif /* SECP256K1_MODULE_BATCH_MAIN_H */

0 commit comments

Comments
 (0)