From c67c32ce0d0e21c1cafada0120bf1863f137c51f Mon Sep 17 00:00:00 2001 From: siv2r Date: Fri, 19 Aug 2022 16:38:48 +0530 Subject: [PATCH] batch: Add `create` and `destroy` APIs 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. --- include/secp256k1_batch.h | 45 ++++++++++++ src/modules/batch/main_impl.h | 135 ++++++++++++++++++++++++++++++++++ 2 files changed, 180 insertions(+) diff --git a/include/secp256k1_batch.h b/include/secp256k1_batch.h index ea8fa63995..bc01a55edc 100644 --- a/include/secp256k1_batch.h +++ b/include/secp256k1_batch.h @@ -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 diff --git a/src/modules/batch/main_impl.h b/src/modules/batch/main_impl.h index c07033a8bd..99007ef8b4 100644 --- a/src/modules/batch/main_impl.h +++ b/src/modules/batch/main_impl.h @@ -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 */