Skip to content

Commit b7c7b58

Browse files
committed
batch, ecmult: Add batch_verify API 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 work, 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 e0dbd66 commit b7c7b58

File tree

3 files changed

+111
-19
lines changed

3 files changed

+111
-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
@@ -347,33 +347,58 @@ static void secp256k1_ecmult(secp256k1_gej *r, const secp256k1_gej *a, const sec
347347
secp256k1_ecmult_strauss_wnaf(&state, r, 1, a, na, ng);
348348
}
349349

350-
static size_t secp256k1_strauss_scratch_size(size_t n_points) {
351-
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);
352-
return n_points*point_size;
350+
/** Allocate strauss state on the scratch space */
351+
static int secp256k1_strauss_scratch_alloc_state(const secp256k1_callback* error_callback, secp256k1_scratch *scratch, struct secp256k1_strauss_state *state, size_t n_points) {
352+
const size_t scratch_checkpoint = secp256k1_scratch_checkpoint(error_callback, scratch);
353+
354+
/* We allocate three objects on the scratch space. If these allocations
355+
* change, make sure to check if this affects STRAUSS_SCRATCH_OBJECTS
356+
* constant and strauss_scratch_size. */
357+
state->aux = (secp256k1_fe*)secp256k1_scratch_alloc(error_callback, scratch, n_points * ECMULT_TABLE_SIZE(WINDOW_A) * sizeof(secp256k1_fe));
358+
state->pre_a = (secp256k1_ge*)secp256k1_scratch_alloc(error_callback, scratch, n_points * ECMULT_TABLE_SIZE(WINDOW_A) * sizeof(secp256k1_ge));
359+
state->ps = (struct secp256k1_strauss_point_state*)secp256k1_scratch_alloc(error_callback, scratch, n_points * sizeof(struct secp256k1_strauss_point_state));
360+
361+
if (state->aux == NULL || state->pre_a == NULL || state->ps == NULL) {
362+
secp256k1_scratch_apply_checkpoint(error_callback, scratch, scratch_checkpoint);
363+
return 0;
364+
}
365+
return 1;
353366
}
354367

355-
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) {
356-
secp256k1_gej* points;
357-
secp256k1_scalar* scalars;
368+
/** Run ecmult_strauss_wnaf on the given points and scalars */
369+
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) {
358370
struct secp256k1_strauss_state state;
359-
size_t i;
360371
const size_t scratch_checkpoint = secp256k1_scratch_checkpoint(error_callback, scratch);
361372

362373
secp256k1_gej_set_infinity(r);
363374
if (inp_g_sc == NULL && n_points == 0) {
364375
return 1;
365376
}
366377

367-
/* We allocate STRAUSS_SCRATCH_OBJECTS objects on the scratch space. If these
368-
* allocations change, make sure to update the STRAUSS_SCRATCH_OBJECTS
369-
* constant and strauss_scratch_size accordingly. */
378+
if(!secp256k1_strauss_scratch_alloc_state(error_callback, scratch, &state, n_points)) {
379+
return 0;
380+
}
381+
382+
secp256k1_ecmult_strauss_wnaf(&state, r, n_points, points, scalars, inp_g_sc);
383+
secp256k1_scratch_apply_checkpoint(error_callback, scratch, scratch_checkpoint);
384+
return 1;
385+
}
386+
387+
/** Run ecmult_strauss_wnaf on the given points and scalars. Returns 0 if the
388+
* scratch space is empty. `n_points` number of scalars and points are
389+
* extracted from `cbdata` using `cb` and stored on the scratch space.
390+
*/
391+
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) {
392+
secp256k1_gej* points;
393+
secp256k1_scalar* scalars;
394+
size_t i;
395+
const size_t scratch_checkpoint = secp256k1_scratch_checkpoint(error_callback, scratch);
396+
/* We allocate STRAUSS_SCRATCH_OBJECTS objects on the scratch space in
397+
* total. If these allocations change, make sure to update the
398+
* STRAUSS_SCRATCH_OBJECTS constant and strauss_scratch_size accordingly. */
370399
points = (secp256k1_gej*)secp256k1_scratch_alloc(error_callback, scratch, n_points * sizeof(secp256k1_gej));
371400
scalars = (secp256k1_scalar*)secp256k1_scratch_alloc(error_callback, scratch, n_points * sizeof(secp256k1_scalar));
372-
state.aux = (secp256k1_fe*)secp256k1_scratch_alloc(error_callback, scratch, n_points * ECMULT_TABLE_SIZE(WINDOW_A) * sizeof(secp256k1_fe));
373-
state.pre_a = (secp256k1_ge*)secp256k1_scratch_alloc(error_callback, scratch, n_points * ECMULT_TABLE_SIZE(WINDOW_A) * sizeof(secp256k1_ge));
374-
state.ps = (struct secp256k1_strauss_point_state*)secp256k1_scratch_alloc(error_callback, scratch, n_points * sizeof(struct secp256k1_strauss_point_state));
375-
376-
if (points == NULL || scalars == NULL || state.aux == NULL || state.pre_a == NULL || state.ps == NULL) {
401+
if (points == NULL || scalars == NULL) {
377402
secp256k1_scratch_apply_checkpoint(error_callback, scratch, scratch_checkpoint);
378403
return 0;
379404
}
@@ -386,20 +411,30 @@ static int secp256k1_ecmult_strauss_batch(const secp256k1_callback* error_callba
386411
}
387412
secp256k1_gej_set_ge(&points[i], &point);
388413
}
389-
secp256k1_ecmult_strauss_wnaf(&state, r, n_points, points, scalars, inp_g_sc);
414+
415+
secp256k1_ecmult_strauss_batch_internal(error_callback, scratch, r, scalars, points, inp_g_sc, n_points);
390416
secp256k1_scratch_apply_checkpoint(error_callback, scratch, scratch_checkpoint);
391417
return 1;
392418
}
393419

394-
/* Wrapper for secp256k1_ecmult_multi_func interface */
395-
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) {
396-
return secp256k1_ecmult_strauss_batch(error_callback, scratch, r, inp_g_sc, cb, cbdata, n, 0);
420+
/** Return the scratch size that is allocated by a call to strauss_batch
421+
* (ignoring padding required for alignment). */
422+
static size_t secp256k1_strauss_scratch_size(size_t n_points) {
423+
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);
424+
return n_points*point_size;
397425
}
398426

427+
/** Return the maximum number of points that can be provided to strauss_batch
428+
* with a given scratch space. */
399429
static size_t secp256k1_strauss_max_points(const secp256k1_callback* error_callback, secp256k1_scratch *scratch) {
400430
return secp256k1_scratch_max_allocation(error_callback, scratch, STRAUSS_SCRATCH_OBJECTS) / secp256k1_strauss_scratch_size(1);
401431
}
402432

433+
/* Wrapper for secp256k1_ecmult_multi_func interface */
434+
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) {
435+
return secp256k1_ecmult_strauss_batch(error_callback, scratch, r, inp_g_sc, cb, cbdata, n, 0);
436+
}
437+
403438
/** Convert a number to WNAF notation.
404439
* The number becomes represented by sum(2^{wi} * wnaf[i], i=0..WNAF_SIZE(w)+1) - return_val.
405440
* It has the following guarantees:

src/modules/batch/main_impl.h

Lines changed: 42 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,38 @@ 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+
176+
batch->result = batch->result && mid_res;
177+
secp256k1_batch_scratch_clear(batch);
178+
}
179+
180+
return batch->result;
181+
}
182+
141183
#endif /* SECP256K1_MODULE_BATCH_MAIN_H */

0 commit comments

Comments
 (0)