Skip to content

Commit

Permalink
batch: Add create and destroy APIs
Browse files Browse the repository at this point in the history
This commit adds the _batch_create and _batch_destroy APIs.

Relevant Links:
1. _batch_scratch_size allocation formula is taken from bench ecmult:
https://github.com/bitcoin-core/secp256k1/blob/694ce8fb2d1fd8a3d641d7c33705691d41a2a860/src/bench_ecmult.c#L312.
2. aux_rand16 param in _batch_create enables synthetic randomness for
randomizer generation: sipa/bips#204.
  • Loading branch information
siv2r authored and fjahr committed Oct 9, 2023
1 parent b11c241 commit c67c32c
Show file tree
Hide file tree
Showing 2 changed files with 180 additions and 0 deletions.
45 changes: 45 additions & 0 deletions include/secp256k1_batch.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,51 @@ extern "C" {
* (https://github.com/bitcoin/bips/blob/master/bip-0341.mediawiki).
*/

/** Opaque data structure that holds information required for the batch verification.
*
* The purpose of this structure is to store elliptic curve points, their scalar
* coefficients, and scalar coefficient of generator point participating in Multi-Scalar
* Point Multiplication computation, which is done by `secp256k1_ecmult_strauss_batch_internal`
*/
typedef struct secp256k1_batch_struct secp256k1_batch;

/** Create a secp256k1 batch object object (in dynamically allocated memory).
*
* This function uses malloc to allocate memory. It is guaranteed that malloc is
* called at most twice for every call of this function.
*
* Returns: a newly created batch object.
* Args: ctx: an existing `secp256k1_context` object. Not to be confused
* with the batch object object that this function creates.
* In: max_terms: Max number of (scalar, curve point) pairs that the batch
* object can store.
* 1. `batch_add_schnorrsig` - adds two scalar-point pairs to the batch
* 2. `batch_add_xonpub_tweak_check` - adds one scalar-point pair to the batch
* Hence, for adding n schnorrsigs and m tweak checks, `max_terms`
* should be set to 2*n + m.
* aux_rand16: 16 bytes of fresh randomness. While recommended to provide
* this, it is only supplemental to security and can be NULL. A
* NULL argument is treated the same as an all-zero one.
*/
SECP256K1_API secp256k1_batch* secp256k1_batch_create(
const secp256k1_context* ctx,
size_t max_terms,
const unsigned char *aux_rand16
) SECP256K1_ARG_NONNULL(1) SECP256K1_WARN_UNUSED_RESULT;

/** Destroy a secp256k1 batch object (created in dynamically allocated memory).
*
* The batch object's pointer may not be used afterwards.
*
* Args: ctx: a secp256k1 context object.
* batch: an existing batch object to destroy, constructed
* using `secp256k1_batch_create`
*/
SECP256K1_API void secp256k1_batch_destroy(
const secp256k1_context* ctx,
secp256k1_batch* batch
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2);

#ifdef __cplusplus
}
#endif
Expand Down
135 changes: 135 additions & 0 deletions src/modules/batch/main_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,139 @@

#include "include/secp256k1_batch.h"

/** Opaque data structure that holds information required for the batch verification.
*
* Members:
* data: scratch space object that contains points (_gej) and their
* respective scalars. To be used in Multi-Scalar Multiplication
* algorithms such as Strauss and Pippenger.
* scalars: pointer to scalars allocated on the scratch space.
* points: pointer to points allocated on the scratch space.
* sc_g: scalar corresponding to the generator point (G) in Multi-Scalar
* Multiplication equation.
* sha256: contains hash of all the inputs (schnorrsig/tweaks) present in
* the batch object, expect the first input. Used for generating a random secp256k1_scalar
* for each term added by secp256k1_batch_add_*.
* sha256: contains hash of all inputs (except the first one) present in the batch.
* `secp256k1_batch_add_` APIs use these for randomizing the scalar (i.e., multiplying
* it with a newly generated scalar) before adding it to the batch.
* len: number of scalar-point pairs present in the batch.
* capacity: max number of scalar-point pairs that the batch can hold.
* result: tells whether the given set of inputs (schnorrsigs or tweak checks) is valid
* or invalid. 1 = valid and 0 = invalid. By default, this is set to 1
* during batch object creation (i.e., `secp256k1_batch_create`).
*
* The following struct name is typdef as secp256k1_batch (in include/secp256k1_batch.h).
*/
struct secp256k1_batch_struct{
secp256k1_scratch *data;
secp256k1_scalar *scalars;
secp256k1_gej *points;
secp256k1_scalar sc_g;
secp256k1_sha256 sha256;
size_t len;
size_t capacity;
int result;
};

static size_t secp256k1_batch_scratch_size(int max_terms) {
size_t ret = secp256k1_strauss_scratch_size(max_terms) + STRAUSS_SCRATCH_OBJECTS*16;
VERIFY_CHECK(ret != 0);

return ret;
}

/** Allocates space for `batch->capacity` number of scalars and points on batch
* object's scratch space */
static int secp256k1_batch_scratch_alloc(const secp256k1_callback* error_callback, secp256k1_batch* batch) {
size_t checkpoint = secp256k1_scratch_checkpoint(error_callback, batch->data);
size_t count = batch->capacity;

VERIFY_CHECK(count > 0);

batch->scalars = (secp256k1_scalar*)secp256k1_scratch_alloc(error_callback, batch->data, count*sizeof(secp256k1_scalar));
batch->points = (secp256k1_gej*)secp256k1_scratch_alloc(error_callback, batch->data, count*sizeof(secp256k1_gej));

/* If scalar or point allocation fails, restore scratch space to previous state */
if (batch->scalars == NULL || batch->points == NULL) {
secp256k1_scratch_apply_checkpoint(error_callback, batch->data, checkpoint);
return 0;
}

return 1;
}

/* Initializes SHA256 with fixed midstate. This midstate was computed by applying
* SHA256 to SHA256("BIP0340/batch")||SHA256("BIP0340/batch"). */
static void secp256k1_batch_sha256_tagged(secp256k1_sha256 *sha) {
secp256k1_sha256_initialize(sha);
sha->s[0] = 0x79e3e0d2ul;
sha->s[1] = 0x12284f32ul;
sha->s[2] = 0xd7d89e1cul;
sha->s[3] = 0x6491ea9aul;
sha->s[4] = 0xad823b2ful;
sha->s[5] = 0xfacfe0b6ul;
sha->s[6] = 0x342b78baul;
sha->s[7] = 0x12ece87cul;

sha->bytes = 64;
}

secp256k1_batch* secp256k1_batch_create(const secp256k1_context* ctx, size_t max_terms, const unsigned char *aux_rand16) {
size_t batch_size;
secp256k1_batch* batch;
size_t batch_scratch_size;
unsigned char zeros[16] = {0};

VERIFY_CHECK(ctx != NULL);
ARG_CHECK(max_terms != 0);

batch_size = sizeof(secp256k1_batch);
batch = (secp256k1_batch *)checked_malloc(&ctx->error_callback, batch_size);
batch_scratch_size = secp256k1_batch_scratch_size(max_terms);
if (batch != NULL) {
/* create scratch space inside batch object, if that fails return NULL*/
batch->data = secp256k1_scratch_create(&ctx->error_callback, batch_scratch_size);
if (batch->data == NULL) {
return NULL;
}
/* allocate memeory for `max_terms` number of scalars and points on scratch space */
batch->capacity = max_terms;
if (!secp256k1_batch_scratch_alloc(&ctx->error_callback, batch)) {
/* if scratch memory allocation fails, free all the previous the allocated memory
and return NULL */
secp256k1_scratch_destroy(&ctx->error_callback, batch->data);
free(batch);
return NULL;
}

/* set remaining data members */
secp256k1_scalar_clear(&batch->sc_g);
secp256k1_batch_sha256_tagged(&batch->sha256);
if (aux_rand16 != NULL) {
secp256k1_sha256_write(&batch->sha256, aux_rand16, 16);
} else {
/* use 16 bytes of 0x0000...000, if no fresh randomness provided */
secp256k1_sha256_write(&batch->sha256, zeros, 16);
}
batch->len = 0;
batch->result = 1;
}

return batch;
}

void secp256k1_batch_destroy(const secp256k1_context *ctx, secp256k1_batch *batch) {
VERIFY_CHECK(ctx != NULL);

if (batch != NULL) {
if(batch->data != NULL) {
/* can't destroy a scratch space with non-zero size */
secp256k1_scratch_apply_checkpoint(&ctx->error_callback, batch->data, 0);
secp256k1_scratch_destroy(&ctx->error_callback, batch->data);
}
free(batch);
}
}

#endif /* SECP256K1_MODULE_BATCH_MAIN_H */

0 comments on commit c67c32c

Please sign in to comment.