Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 33 additions & 3 deletions src/bench.c
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,9 @@ static void help(const char *executable_path, int default_iters) {
printf(" ecdsa : all ECDSA algorithms--sign, verify, recovery (if enabled)\n");
printf(" ecdsa_sign : ECDSA siging algorithm\n");
printf(" ecdsa_verify : ECDSA verification algorithm\n");
printf(" ec : all EC public key algorithms (keygen)\n");
printf(" ec : all EC public key algorithms (keygen, tweak)\n");
printf(" ec_keygen : EC public key generation\n");
printf(" ec_pk_tweak_add : EC public key additive tweaking\n");

#ifdef ENABLE_MODULE_RECOVERY
printf(" ecdsa_recover : ECDSA public key recovery algorithm\n");
Expand Down Expand Up @@ -79,6 +80,8 @@ typedef struct {
size_t siglen;
unsigned char pubkey[33];
size_t pubkeylen;
secp256k1_pubkey tweaked_pubkey;
unsigned char tweak[32];
} bench_data;

static void bench_verify(void* arg, int iters) {
Expand Down Expand Up @@ -153,6 +156,32 @@ static void bench_keygen_run(void *arg, int iters) {
}
}

static void bench_tweak_setup(void* arg) {
int i;
bench_data *data = (bench_data*)arg;
unsigned char seckey_one[32] = {0};

/* set starting pubkey to the generator point */
seckey_one[31] = 1;
CHECK(secp256k1_ec_pubkey_create(data->ctx, &data->tweaked_pubkey, seckey_one));
for (i = 0; i < 32; i++) {
data->tweak[i] = i + 129;
}
}

static void bench_pubkey_tweak_add(void *arg, int iters) {
int i;
bench_data *data = (bench_data*)arg;

for (i = 0; i < iters; i++) {
unsigned char pub33[33];
size_t len = 33;
CHECK(secp256k1_ec_pubkey_tweak_add(data->ctx, &data->tweaked_pubkey, data->tweak));
CHECK(secp256k1_ec_pubkey_serialize(data->ctx, pub33, &len, &data->tweaked_pubkey, SECP256K1_EC_COMPRESSED));
memcpy(data->tweak, pub33 + 1, 32);
}
}


#ifdef ENABLE_MODULE_ECDH
# include "modules/ecdh/bench_impl.h"
Expand Down Expand Up @@ -181,8 +210,8 @@ int main(int argc, char** argv) {
/* Check for invalid user arguments */
char* valid_args[] = {"ecdsa", "verify", "ecdsa_verify", "sign", "ecdsa_sign", "ecdh", "recover",
"ecdsa_recover", "schnorrsig", "schnorrsig_verify", "schnorrsig_sign", "ec",
"keygen", "ec_keygen", "ellswift", "encode", "ellswift_encode", "decode",
"ellswift_decode", "ellswift_keygen", "ellswift_ecdh"};
"keygen", "ec_keygen", "tweak", "ec_pk_tweak_add", "ellswift", "encode",
"ellswift_encode", "decode", "ellswift_decode", "ellswift_keygen", "ellswift_ecdh"};
int invalid_args = have_invalid_args(argc, argv, valid_args, ARRAY_SIZE(valid_args));

int default_iters = 20000;
Expand Down Expand Up @@ -261,6 +290,7 @@ int main(int argc, char** argv) {

if (d || have_flag(argc, argv, "ecdsa") || have_flag(argc, argv, "sign") || have_flag(argc, argv, "ecdsa_sign")) run_benchmark("ecdsa_sign", bench_sign_run, bench_sign_setup, NULL, &data, 10, iters);
if (d || have_flag(argc, argv, "ec") || have_flag(argc, argv, "keygen") || have_flag(argc, argv, "ec_keygen")) run_benchmark("ec_keygen", bench_keygen_run, bench_keygen_setup, NULL, &data, 10, iters);
if (d || have_flag(argc, argv, "ec") || have_flag(argc, argv, "tweak") || have_flag(argc, argv, "ec_pk_tweak_add")) run_benchmark("ec_pk_tweak_add", bench_pubkey_tweak_add, bench_tweak_setup, NULL, &data, 10, iters);

secp256k1_context_destroy(data.ctx);

Expand Down
11 changes: 11 additions & 0 deletions src/bench_ecmult.c
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,15 @@ static void bench_ecmult_gen(void* arg, int iters) {
}
}

static void bench_ecmult_gen_var(void* arg, int iters) {
bench_data* data = (bench_data*)arg;
int i;

for (i = 0; i < iters; ++i) {
secp256k1_ecmult_gen_var(&data->output[i], &data->scalars[(data->offset1+i) % POINTS]);
}
}

static void bench_ecmult_gen_teardown(void* arg, int iters) {
bench_data* data = (bench_data*)arg;
bench_ecmult_teardown_helper(data, NULL, NULL, &data->offset1, iters);
Expand Down Expand Up @@ -199,6 +208,8 @@ static void run_ecmult_bench(bench_data* data, int iters) {
char str[32];
sprintf(str, "ecmult_gen");
run_benchmark(str, bench_ecmult_gen, bench_ecmult_setup, bench_ecmult_gen_teardown, data, 10, iters);
sprintf(str, "ecmult_gen_var");
run_benchmark(str, bench_ecmult_gen_var, bench_ecmult_setup, bench_ecmult_gen_teardown, data, 10, iters);
sprintf(str, "ecmult_const");
run_benchmark(str, bench_ecmult_const, bench_ecmult_setup, bench_ecmult_const_teardown, data, 10, iters);
sprintf(str, "ecmult_const_xonly");
Expand Down
4 changes: 2 additions & 2 deletions src/eckey_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -61,8 +61,8 @@ static int secp256k1_eckey_privkey_tweak_add(secp256k1_scalar *key, const secp25

static int secp256k1_eckey_pubkey_tweak_add(secp256k1_ge *key, const secp256k1_scalar *tweak) {
secp256k1_gej pt;
secp256k1_gej_set_ge(&pt, key);
secp256k1_ecmult(&pt, &pt, &secp256k1_scalar_one, tweak);
secp256k1_ecmult_gen_var(&pt, tweak);
secp256k1_gej_add_ge_var(&pt, &pt, key, NULL);

if (secp256k1_gej_is_infinity(&pt)) {
return 0;
Expand Down
1 change: 1 addition & 0 deletions src/ecmult_gen.h
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,7 @@ static void secp256k1_ecmult_gen_context_clear(secp256k1_ecmult_gen_context* ctx

/** Multiply with the generator: R = a*G */
static void secp256k1_ecmult_gen(const secp256k1_ecmult_gen_context* ctx, secp256k1_gej *r, const secp256k1_scalar *a);
static void secp256k1_ecmult_gen_var(secp256k1_gej *r, const secp256k1_scalar *a);

static void secp256k1_ecmult_gen_blind(secp256k1_ecmult_gen_context *ctx, const secp256k1_hash_ctx *hash_ctx, const unsigned char *seed32);

Expand Down
1 change: 1 addition & 0 deletions src/ecmult_gen_compute_table.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,6 @@
#include "ecmult_gen.h"

static void secp256k1_ecmult_gen_compute_table(secp256k1_ge_storage* table, const secp256k1_ge* gen, int blocks, int teeth, int spacing);
static void secp256k1_ecmult_gen_compute_scalar_diff(secp256k1_scalar* diff, int blocks, int teeth, int spacing);

#endif /* SECP256K1_ECMULT_GEN_COMPUTE_TABLE_H */
22 changes: 22 additions & 0 deletions src/ecmult_gen_compute_table_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -105,4 +105,26 @@ static void secp256k1_ecmult_gen_compute_table(secp256k1_ge_storage* table, cons
free(prec);
}

/* Compute the scalar (2^COMB_BITS - 1) / 2, the difference between the gn argument to
* secp256k1_ecmult_gen, and the scalar whose encoding the table lookup bits are drawn
* from (before applying blinding). */
static void secp256k1_ecmult_gen_compute_scalar_diff(secp256k1_scalar* diff, int blocks, int teeth, int spacing) {
int i;
int comb_bits = blocks * teeth * spacing;

/* Compute scalar -1/2. */
secp256k1_scalar neghalf;
secp256k1_scalar_half(&neghalf, &secp256k1_scalar_one);
secp256k1_scalar_negate(&neghalf, &neghalf);

/* Compute offset = 2^(COMB_BITS - 1). */
*diff = secp256k1_scalar_one;
for (i = 0; i < comb_bits - 1; ++i) {
secp256k1_scalar_add(diff, diff, diff);
}

/* The result is the sum 2^(COMB_BITS - 1) + (-1/2). */
secp256k1_scalar_add(diff, diff, &neghalf);
}

#endif /* SECP256K1_ECMULT_GEN_COMPUTE_TABLE_IMPL_H */
88 changes: 63 additions & 25 deletions src/ecmult_gen_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,27 +30,6 @@ static void secp256k1_ecmult_gen_context_clear(secp256k1_ecmult_gen_context *ctx
secp256k1_fe_clear(&ctx->proj_blind);
}

/* Compute the scalar (2^COMB_BITS - 1) / 2, the difference between the gn argument to
* secp256k1_ecmult_gen, and the scalar whose encoding the table lookup bits are drawn
* from (before applying blinding). */
static void secp256k1_ecmult_gen_scalar_diff(secp256k1_scalar* diff) {
int i;

/* Compute scalar -1/2. */
secp256k1_scalar neghalf;
secp256k1_scalar_half(&neghalf, &secp256k1_scalar_one);
secp256k1_scalar_negate(&neghalf, &neghalf);

/* Compute offset = 2^(COMB_BITS - 1). */
*diff = secp256k1_scalar_one;
for (i = 0; i < COMB_BITS - 1; ++i) {
secp256k1_scalar_add(diff, diff, diff);
}

/* The result is the sum 2^(COMB_BITS - 1) + (-1/2). */
secp256k1_scalar_add(diff, diff, &neghalf);
}

static void secp256k1_ecmult_gen(const secp256k1_ecmult_gen_context *ctx, secp256k1_gej *r, const secp256k1_scalar *gn) {
uint32_t comb_off;
secp256k1_ge add;
Expand Down Expand Up @@ -281,19 +260,78 @@ static void secp256k1_ecmult_gen(const secp256k1_ecmult_gen_context *ctx, secp25
secp256k1_memclear_explicit(&recoded, sizeof(recoded));
}

/* Variable-time variant for generator point multiplication.
*
* This is essentially a copy of `secp256k1_ecmult_gen`, but with all side-channel
* mitigations (i.e., constant-time code, random scalar blinding, and memory clearing)
* removed. The EC point operation calls (addition, doubling) are replaced with their
* faster variable-time equivalents. This function is stateless and does not require
* a context parameter, enabling its use in internal functions (e.g., `eckey_pubkey_tweak_add`).
*
* Note that the branch for the first table lookup assignment is also removed: since
* the result is initialized to the point at infinity, adding to it with `_gej_add_ge_var`
* is equivalent to a simple copy. */
static void secp256k1_ecmult_gen_var(secp256k1_gej *r, const secp256k1_scalar *gn) {
uint32_t comb_off;
secp256k1_ge add;
secp256k1_scalar d;
uint32_t recoded[(COMB_BITS + 31) >> 5] = {0};
int i;

/* Adjust input scalar for difference and convert to recoded array. */
secp256k1_scalar_add(&d, &secp256k1_ecmult_gen_scalar_diff, gn);
for (i = 0; i < 8 && i < ((COMB_BITS + 31) >> 5); ++i) {
recoded[i] = secp256k1_scalar_get_bits_limb32(&d, 32 * i, 32);
}

/* Outer loop: iterate over comb_off from COMB_SPACING - 1 down to 0. */
secp256k1_gej_set_infinity(r);
comb_off = COMB_SPACING - 1;
while (1) {
uint32_t block;
uint32_t bit_pos = comb_off;
/* Inner loop: for each block, add table entries to the result. */
for (block = 0; block < COMB_BLOCKS; ++block) {
/* Gather the mask(block)-selected bits of d into bits. They're packed:
* bits[tooth] = d[(block*COMB_TEETH + tooth)*COMB_SPACING + comb_off]. */
uint32_t bits = 0, sign, abs, tooth;
for (tooth = 0; tooth < COMB_TEETH; ++tooth) {
uint32_t bit = (recoded[bit_pos >> 5] >> (bit_pos & 0x1f)) & 1;
bits |= bit << tooth;
bit_pos += COMB_SPACING;
}

/* If the top bit of bits is 1, flip them all (corresponding to looking up
* the negated table value), and remember to negate the result in sign. */
sign = (bits >> (COMB_TEETH - 1)) & 1;
abs = (bits ^ -sign) & (COMB_POINTS - 1);
VERIFY_CHECK(sign == 0 || sign == 1);
VERIFY_CHECK(abs < COMB_POINTS);

/* Perform lookup, negate if necessary and add to r. */
secp256k1_ge_from_storage(&add, &secp256k1_ecmult_gen_prec_table[block][abs]);
if (sign) {
secp256k1_fe_negate(&add.y, &add.y, 1);
}
secp256k1_gej_add_ge_var(r, r, &add, NULL);
}

/* Double the result, except in the last iteration. */
if (comb_off-- == 0) break;
secp256k1_gej_double_var(r, r, NULL);
}
}

/* Setup blinding values for secp256k1_ecmult_gen. */
static void secp256k1_ecmult_gen_blind(secp256k1_ecmult_gen_context *ctx, const secp256k1_hash_ctx *hash_ctx, const unsigned char *seed32) {
secp256k1_scalar b;
secp256k1_scalar diff;
const secp256k1_scalar diff = secp256k1_ecmult_gen_scalar_diff;
secp256k1_gej gb;
secp256k1_fe f;
unsigned char nonce32[32];
secp256k1_rfc6979_hmac_sha256 rng;
unsigned char keydata[64];

/* Compute the (2^COMB_BITS - 1)/2 term once. */
secp256k1_ecmult_gen_scalar_diff(&diff);

if (seed32 == NULL) {
/* When seed is NULL, reset the final point and blinding value. */
secp256k1_ge_neg(&ctx->ge_offset, &secp256k1_ge_const_g);
Expand Down
31 changes: 29 additions & 2 deletions src/precompute_ecmult_gen.c
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,21 @@ static void print_table(FILE* fp, int blocks, int teeth) {
free(table);
}

static void print_scalar_diff(FILE* fp, int blocks, int teeth) {
int spacing = CEIL_DIV(256, blocks * teeth);
secp256k1_scalar diff;
int limb;

secp256k1_ecmult_gen_compute_scalar_diff(&diff, blocks, teeth, spacing);
fprintf(fp, "#elif (COMB_BLOCKS == %d) && (COMB_TEETH == %d) && (COMB_SPACING == %d)\n", blocks, teeth, spacing);
fprintf(fp, " SECP256K1_SCALAR_CONST(");
for (limb = 7; limb >= 0; limb--) {
fprintf(fp, "0x%08x", secp256k1_scalar_get_bits_var(&diff, limb*32, 32));
if (limb != 0) fprintf(fp, ",");
}
fprintf(fp, ")\n");
}

int main(int argc, char **argv) {
const char outfile[] = "src/precomputed_ecmult_gen.c";
FILE* fp;
Expand Down Expand Up @@ -92,9 +107,21 @@ int main(int argc, char **argv) {
fprintf(fp, "#else\n");
fprintf(fp, "# error Configuration mismatch, invalid COMB_* parameters. Try deleting precomputed_ecmult_gen.c before the build.\n");
fprintf(fp, "#endif\n");

fprintf(fp, "};\n");
fprintf(fp, "#undef S\n");
fprintf(fp, "#undef S\n\n");

fprintf(fp, "const secp256k1_scalar secp256k1_ecmult_gen_scalar_diff =\n");
fprintf(fp, "#if 0\n");
for (config = 0; config < ARRAY_SIZE(CONFIGS); ++config) {
print_scalar_diff(fp, CONFIGS[config][0], CONFIGS[config][1]);
}
if (!did_current_config) {
print_scalar_diff(fp, COMB_BLOCKS, COMB_TEETH);
}
fprintf(fp, "#else\n");
fprintf(fp, "# error Configuration mismatch, invalid COMB_* parameters. Try deleting precomputed_ecmult_gen.c before the build.\n");
fprintf(fp, "#endif\n");
fprintf(fp, ";\n");
fclose(fp);

return EXIT_SUCCESS;
Expand Down
13 changes: 13 additions & 0 deletions src/precomputed_ecmult_gen.c

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions src/precomputed_ecmult_gen.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,10 @@ extern "C" {

#ifdef EXHAUSTIVE_TEST_ORDER
static secp256k1_ge_storage secp256k1_ecmult_gen_prec_table[COMB_BLOCKS][COMB_POINTS];
static secp256k1_scalar secp256k1_ecmult_gen_scalar_diff;
#else
SECP256K1_LOCAL_VAR const secp256k1_ge_storage secp256k1_ecmult_gen_prec_table[COMB_BLOCKS][COMB_POINTS];
SECP256K1_LOCAL_VAR const secp256k1_scalar secp256k1_ecmult_gen_scalar_diff;
#endif /* defined(EXHAUSTIVE_TEST_ORDER) */

#ifdef __cplusplus
Expand Down
10 changes: 8 additions & 2 deletions src/tests.c
Original file line number Diff line number Diff line change
Expand Up @@ -4600,10 +4600,14 @@ static void test_ecmult_target(const secp256k1_scalar* target, int mode) {
secp256k1_ecmult(&p1j, &pj, &n1, &secp256k1_scalar_zero);
secp256k1_ecmult(&p2j, &pj, &n2, &secp256k1_scalar_zero);
secp256k1_ecmult(&ptj, &pj, target, &secp256k1_scalar_zero);
} else {
} else if (mode == 2) {
secp256k1_ecmult_const(&p1j, &p, &n1);
secp256k1_ecmult_const(&p2j, &p, &n2);
secp256k1_ecmult_const(&ptj, &p, target);
} else {
secp256k1_ecmult_gen_var(&p1j, &n1);
secp256k1_ecmult_gen_var(&p2j, &n2);
secp256k1_ecmult_gen_var(&ptj, target);
}

/* Add them all up: n1*P + n2*P + target*P = (n1+n2+target)*P = (n1+n1-n1-n2)*P = 0. */
Expand All @@ -4620,6 +4624,7 @@ static void run_ecmult_near_split_bound(void) {
test_ecmult_target(&scalars_near_split_bounds[j], 0);
test_ecmult_target(&scalars_near_split_bounds[j], 1);
test_ecmult_target(&scalars_near_split_bounds[j], 2);
test_ecmult_target(&scalars_near_split_bounds[j], 3);
}
}
}
Expand Down Expand Up @@ -5637,7 +5642,7 @@ static void test_ecmult_accumulate(secp256k1_sha256* acc, const secp256k1_scalar
/* Compute x*G in many different ways, serialize it uncompressed, and feed it into acc. */
secp256k1_gej gj, infj;
secp256k1_ge r;
secp256k1_gej rj[7];
secp256k1_gej rj[8];
unsigned char bytes[65];
size_t i;
secp256k1_gej_set_ge(&gj, &secp256k1_ge_const_g);
Expand All @@ -5649,6 +5654,7 @@ static void test_ecmult_accumulate(secp256k1_sha256* acc, const secp256k1_scalar
CHECK(secp256k1_ecmult_multi_var(&CTX->error_callback, scratch, &rj[4], x, NULL, NULL, 0));
CHECK(secp256k1_ecmult_multi_var(&CTX->error_callback, scratch, &rj[5], &secp256k1_scalar_zero, test_ecmult_accumulate_cb, (void*)x, 1));
secp256k1_ecmult_const(&rj[6], &secp256k1_ge_const_g, x);
secp256k1_ecmult_gen_var(&rj[7], x);
secp256k1_ge_set_gej_var(&r, &rj[0]);
for (i = 0; i < ARRAY_SIZE(rj); i++) {
CHECK(secp256k1_gej_eq_ge_var(&rj[i], &r));
Expand Down
Loading
Loading