diff --git a/src/ecmult_gen.h b/src/ecmult_gen.h index 7564b7015f..05e9cfdf85 100644 --- a/src/ecmult_gen.h +++ b/src/ecmult_gen.h @@ -10,7 +10,92 @@ #include "scalar.h" #include "group.h" +#define USE_COMB 1 + +#if USE_COMB + +#if defined(EXHAUSTIVE_TEST_ORDER) + + /* We need to control these values for exhaustive tests because + * the tables cannot have infinities in them (secp256k1_ge_storage + * doesn't support infinities) */ +# if EXHAUSTIVE_TEST_ORDER > 32 +# define COMB_BLOCKS 52 +# define COMB_TEETH 5 +# elif EXHAUSTIVE_TEST_ORDER > 16 +# define COMB_BLOCKS 64 +# define COMB_TEETH 4 +# elif EXHAUSTIVE_TEST_ORDER > 8 +# define COMB_BLOCKS 86 +# define COMB_TEETH 3 +# elif EXHAUSTIVE_TEST_ORDER > 4 +# define COMB_BLOCKS 128 +# define COMB_TEETH 2 +# else +# define COMB_BLOCKS 256 +# define COMB_TEETH 1 +# endif + +# define COMB_SPACING 1 +# define COMB_NEGATION 1 + +#else + + /* COMB_BLOCKS, COMB_TEETH, COMB_SPACING must all be positive and the product of the three (COMB_BITS) + * must evaluate to a value in the range [256, 288]. The COMB_NEGATION boolean controls whether the + * comb will use negations so that only negative multiples need be precomputed. The resulting memory + * usage for precomputation will be COMB_POINTS_TOTAL * sizeof(secp256k1_ge_storage). + */ + #define COMB_BLOCKS 4 + #define COMB_TEETH 5 + #define COMB_SPACING 13 + #define COMB_NEGATION 1 + +#endif + +#if !(1 <= COMB_BLOCKS && COMB_BLOCKS <= 256) +# error "COMB_BLOCKS must be in the range [1, 256]" +#endif +#if !(1 <= COMB_TEETH && COMB_TEETH <= 8) +# error "COMB_TEETH must be in the range [1, 8]" +#endif +#if !(1 <= COMB_SPACING && COMB_SPACING <= 256) +# error "COMB_SPACING must be in the range [1, 256]" +#endif +#if !(0 <= COMB_NEGATION && COMB_NEGATION <= 1) +# error "COMB_NEGATION must be in the range [0, 1]" +#endif + +/* The remaining COMB_* parameters are derived values, don't modify these. */ +#define COMB_BITS (COMB_BLOCKS * COMB_TEETH * COMB_SPACING) +#define COMB_GROUPED ((COMB_SPACING == 1) && ((32 % COMB_TEETH) == 0)) +#define COMB_OFFSET (COMB_BITS == 256) +#define COMB_POINTS (1 << (COMB_TEETH - COMB_NEGATION)) +#define COMB_POINTS_TOTAL (COMB_BLOCKS * COMB_POINTS) +#define COMB_MASK (COMB_POINTS - 1) + +#if !(256 <= COMB_BITS && COMB_BITS <= 288) +# error "COMB_BITS must be in the range [256, 288]" +#endif + +#endif + typedef struct { +#if USE_COMB + /* Precomputation data for the signed-digit multi-comb algorithm as described in section 3.3 of: + * "Fast and compact elliptic-curve cryptography", Mike Hamburg + * (https://eprint.iacr.org/2012/309) + */ + secp256k1_ge_storage (*prec)[COMB_BLOCKS][COMB_POINTS]; +#if COMB_OFFSET + /* Signed recoding of a 256-bit scalar must be at least 257 bits, with the top bit always 1. We + * support a 256-bit comb over a 257-bit recoding by pre-adding an 'offset' value to the context's + * 'initial' value, to account for the high 1 bit. Note that the 'offset' is calculated to allow + * for the (COMB_SPACING - 1) doublings in the _ecmult_gen ladder. + */ + secp256k1_ge offset; +#endif +#else /* For accelerating the computation of a*G: * To harden against timing attacks, use the following mechanism: * * Break up the multiplicand into groups of 4 bits, called n_0, n_1, n_2, ..., n_63. @@ -24,6 +109,7 @@ typedef struct { * the intermediate sums while computing a*G. */ secp256k1_ge_storage (*prec)[64][16]; /* prec[j][i] = 16^j * i * G + U_i */ +#endif secp256k1_scalar blind; secp256k1_gej initial; } secp256k1_ecmult_gen_context; diff --git a/src/ecmult_gen_impl.h b/src/ecmult_gen_impl.h index 714f02e94c..e68b5de48a 100644 --- a/src/ecmult_gen_impl.h +++ b/src/ecmult_gen_impl.h @@ -20,16 +20,72 @@ static void secp256k1_ecmult_gen_context_init(secp256k1_ecmult_gen_context *ctx) static void secp256k1_ecmult_gen_context_build(secp256k1_ecmult_gen_context *ctx, const secp256k1_callback* cb) { #ifndef USE_ECMULT_STATIC_PRECOMPUTATION +#if USE_COMB + secp256k1_ge prec[COMB_POINTS_TOTAL + COMB_OFFSET]; + secp256k1_gej u, sum; + int block, index, spacing, stride, tooth; +#else secp256k1_ge prec[1024]; secp256k1_gej gj; secp256k1_gej nums_gej; int i, j; +#endif #endif if (ctx->prec != NULL) { return; } #ifndef USE_ECMULT_STATIC_PRECOMPUTATION +#if USE_COMB + ctx->prec = (secp256k1_ge_storage (*)[COMB_BLOCKS][COMB_POINTS])checked_malloc(cb, sizeof(*ctx->prec)); + + /* get the generator */ + secp256k1_gej_set_ge(&u, &secp256k1_ge_const_g); + + /* compute prec. */ + { + secp256k1_gej ds[COMB_TEETH]; + secp256k1_gej vs[COMB_POINTS_TOTAL + COMB_OFFSET]; + int vs_pos = 0; + + for (block = 0; block < COMB_BLOCKS; ++block) { + secp256k1_gej_set_infinity(&sum); + for (tooth = 0; tooth < COMB_TEETH; ++tooth) { + secp256k1_gej_add_var(&sum, &sum, &u, NULL); + secp256k1_gej_double(&u, &u); + ds[tooth] = u; + if (block + tooth != COMB_BLOCKS + COMB_TEETH - 2) { + for (spacing = 1; spacing < COMB_SPACING; ++spacing) { + secp256k1_gej_double(&u, &u); + } + } + } + secp256k1_gej_neg(&vs[vs_pos++], &sum); + for (tooth = 0; tooth < (COMB_TEETH - COMB_NEGATION); ++tooth) { + stride = 1 << tooth; + for (index = 0; index < stride; ++index, ++vs_pos) { + secp256k1_gej_add_var(&vs[vs_pos], &vs[vs_pos - stride], &ds[tooth], NULL); + } + } + } + VERIFY_CHECK(vs_pos == COMB_POINTS_TOTAL); +#if COMB_OFFSET + vs[COMB_POINTS_TOTAL] = ds[COMB_TEETH - 1]; +#endif + secp256k1_ge_set_all_gej_var(prec, vs, COMB_POINTS_TOTAL + COMB_OFFSET, cb); + } + + for (block = 0; block < COMB_BLOCKS; ++block) { + for (index = 0; index < COMB_POINTS; ++index) { + secp256k1_ge_to_storage(&(*ctx->prec)[block][index], &prec[block * COMB_POINTS + index]); + } + } + +#if COMB_OFFSET + ctx->offset = prec[COMB_POINTS_TOTAL]; +#endif + +#else ctx->prec = (secp256k1_ge_storage (*)[64][16])checked_malloc(cb, sizeof(*ctx->prec)); /* get the generator */ @@ -84,9 +140,17 @@ static void secp256k1_ecmult_gen_context_build(secp256k1_ecmult_gen_context *ctx secp256k1_ge_to_storage(&(*ctx->prec)[j][i], &prec[j*16 + i]); } } +#endif #else (void)cb; - ctx->prec = (secp256k1_ge_storage (*)[64][16])secp256k1_ecmult_static_context; +#if USE_COMB + ctx->prec = (secp256k1_ge_storage (*)[COMB_BLOCKS][COMB_POINTS])secp256k1_ecmult_gen_ctx_prec; +#if COMB_OFFSET + secp256k1_ge_from_storage(&ctx->offset, &secp256k1_ecmult_gen_ctx_offset); +#endif +#else + ctx->prec = (secp256k1_ge_storage (*)[64][16])secp256k1_ecmult_gen_ctx_prec; +#endif #endif secp256k1_ecmult_gen_blind(ctx, NULL); } @@ -101,11 +165,21 @@ static void secp256k1_ecmult_gen_context_clone(secp256k1_ecmult_gen_context *dst dst->prec = NULL; } else { #ifndef USE_ECMULT_STATIC_PRECOMPUTATION +#if USE_COMB + dst->prec = (secp256k1_ge_storage (*)[COMB_BLOCKS][COMB_POINTS])checked_malloc(cb, sizeof(*dst->prec)); +#else dst->prec = (secp256k1_ge_storage (*)[64][16])checked_malloc(cb, sizeof(*dst->prec)); +#endif memcpy(dst->prec, src->prec, sizeof(*dst->prec)); #else (void)cb; dst->prec = src->prec; +#endif + +#if USE_COMB +#if COMB_OFFSET + dst->offset = src->offset; +#endif #endif dst->initial = src->initial; dst->blind = src->blind; @@ -115,6 +189,11 @@ static void secp256k1_ecmult_gen_context_clone(secp256k1_ecmult_gen_context *dst static void secp256k1_ecmult_gen_context_clear(secp256k1_ecmult_gen_context *ctx) { #ifndef USE_ECMULT_STATIC_PRECOMPUTATION free(ctx->prec); +#endif +#if USE_COMB +#if COMB_OFFSET + secp256k1_ge_clear(&ctx->offset); +#endif #endif secp256k1_scalar_clear(&ctx->blind); secp256k1_gej_clear(&ctx->initial); @@ -126,6 +205,81 @@ static void secp256k1_ecmult_gen(const secp256k1_ecmult_gen_context *ctx, secp25 secp256k1_ge_storage adds; secp256k1_scalar gnb; int bits; + +#if USE_COMB + +#if COMB_NEGATION + secp256k1_fe neg; + int sign; +#endif + int abs, bit_pos, block, comb_off, index; +#if !COMB_GROUPED + int bit, tooth; +#endif + uint32_t recoded[9]; + + memset(&adds, 0, sizeof(adds)); + *r = ctx->initial; + + /* Blind scalar/point multiplication by computing (n-b)G + bG instead of nG. */ + secp256k1_scalar_add(&gnb, gn, &ctx->blind); + secp256k1_scalar_signed_recoding(recoded, &gnb, COMB_BITS + COMB_OFFSET); + + comb_off = COMB_SPACING - 1; + for (;;) { + bit_pos = comb_off; + for (block = 0; block < COMB_BLOCKS; ++block) { +#if COMB_GROUPED + bits = recoded[bit_pos >> 5] >> (bit_pos & 0x1F); + bit_pos += COMB_TEETH; +#else + bits = 0; + for (tooth = 0; tooth < COMB_TEETH; ++tooth) { + bit = recoded[bit_pos >> 5] >> (bit_pos & 0x1F); + bits &= ~(1 << tooth); + bits ^= bit << tooth; + bit_pos += COMB_SPACING; + } +#endif + +#if COMB_NEGATION + sign = (bits >> (COMB_TEETH - 1)) & 1; + VERIFY_CHECK(sign == 0 || sign == 1); + + bits ^= -sign; +#endif + + abs = bits & COMB_MASK; + VERIFY_CHECK(0 <= abs && abs < COMB_POINTS); + + for (index = 0; index < COMB_POINTS; ++index) { + secp256k1_ge_storage_cmov(&adds, &(*ctx->prec)[block][index], index == abs); + } + + secp256k1_ge_from_storage(&add, &adds); +#if COMB_NEGATION + secp256k1_fe_negate(&neg, &add.y, 1); + secp256k1_fe_cmov(&add.y, &neg, sign); +#endif + + secp256k1_gej_add_ge(r, r, &add); + } + + if (--comb_off < 0) { + break; + } + + secp256k1_gej_double(r, r); + } + +#if COMB_NEGATION + secp256k1_fe_clear(&neg); + sign = 0; +#endif + memset(recoded, 0, sizeof(recoded)); + abs = 0; + +#else int i, j; memset(&adds, 0, sizeof(adds)); *r = ctx->initial; @@ -150,13 +304,18 @@ static void secp256k1_ecmult_gen(const secp256k1_ecmult_gen_context *ctx, secp25 secp256k1_ge_from_storage(&add, &adds); secp256k1_gej_add_ge(r, r, &add); } +#endif bits = 0; secp256k1_ge_clear(&add); + memset(&adds, 0, sizeof(adds)); secp256k1_scalar_clear(&gnb); } /* Setup blinding values for secp256k1_ecmult_gen. */ static void secp256k1_ecmult_gen_blind(secp256k1_ecmult_gen_context *ctx, const unsigned char *seed32) { +#if USE_COMB + int spacing; +#endif secp256k1_scalar b; secp256k1_gej gb; secp256k1_fe s; @@ -169,6 +328,14 @@ static void secp256k1_ecmult_gen_blind(secp256k1_ecmult_gen_context *ctx, const secp256k1_gej_set_ge(&ctx->initial, &secp256k1_ge_const_g); secp256k1_gej_neg(&ctx->initial, &ctx->initial); secp256k1_scalar_set_int(&ctx->blind, 1); +#if USE_COMB + for (spacing = 1; spacing < COMB_SPACING; ++spacing) { + secp256k1_scalar_add(&ctx->blind, &ctx->blind, &ctx->blind); + } +#if COMB_OFFSET + secp256k1_gej_add_ge(&ctx->initial, &ctx->initial, &ctx->offset); +#endif +#endif } /* The prior blinding value (if not reset) is chained forward by including it in the hash. */ secp256k1_scalar_get_b32(nonce32, &ctx->blind); @@ -203,6 +370,14 @@ static void secp256k1_ecmult_gen_blind(secp256k1_ecmult_gen_context *ctx, const secp256k1_scalar_negate(&b, &b); ctx->blind = b; ctx->initial = gb; +#if USE_COMB + for (spacing = 1; spacing < COMB_SPACING; ++spacing) { + secp256k1_scalar_add(&ctx->blind, &ctx->blind, &ctx->blind); + } +#if COMB_OFFSET + secp256k1_gej_add_ge(&ctx->initial, &ctx->initial, &ctx->offset); +#endif +#endif secp256k1_scalar_clear(&b); secp256k1_gej_clear(&gb); } diff --git a/src/gen_context.c b/src/gen_context.c index 87d296ebf0..3aefdca1e5 100644 --- a/src/gen_context.c +++ b/src/gen_context.c @@ -29,6 +29,18 @@ int main(int argc, char **argv) { int inner; int outer; FILE* fp; + const char *SC_FORMAT = " SC(%uu, %uu, %uu, %uu, %uu, %uu, %uu, %uu, %uu, %uu, %uu, %uu, %uu, %uu, %uu, %uu)"; + +#if USE_COMB + const int blocks = COMB_BLOCKS; + const int points = COMB_POINTS; +#if COMB_OFFSET + secp256k1_ge_storage offset; +#endif +#else + const int blocks = 64; + const int points = 16; +#endif (void)argc; (void)argv; @@ -43,21 +55,32 @@ int main(int argc, char **argv) { fprintf(fp, "#define _SECP256K1_ECMULT_STATIC_CONTEXT_\n"); fprintf(fp, "#include \"src/group.h\"\n"); fprintf(fp, "#define SC SECP256K1_GE_STORAGE_CONST\n"); - fprintf(fp, "static const secp256k1_ge_storage secp256k1_ecmult_static_context[64][16] = {\n"); secp256k1_ecmult_gen_context_init(&ctx); secp256k1_ecmult_gen_context_build(&ctx, &default_error_callback); - for(outer = 0; outer != 64; outer++) { + +#if USE_COMB +#if COMB_OFFSET + secp256k1_ge_to_storage(&offset, &ctx.offset); + fprintf(fp, "static const secp256k1_ge_storage secp256k1_ecmult_gen_ctx_offset =\n"); + fprintf(fp, SC_FORMAT, SECP256K1_GE_STORAGE_CONST_GET(offset)); + fprintf(fp, ";\n"); +#endif +#endif + + fprintf(fp, "static const secp256k1_ge_storage secp256k1_ecmult_gen_ctx_prec[%i][%i] = {\n", + blocks, points); + for(outer = 0; outer != blocks; outer++) { fprintf(fp,"{\n"); - for(inner = 0; inner != 16; inner++) { - fprintf(fp," SC(%uu, %uu, %uu, %uu, %uu, %uu, %uu, %uu, %uu, %uu, %uu, %uu, %uu, %uu, %uu, %uu)", SECP256K1_GE_STORAGE_CONST_GET((*ctx.prec)[outer][inner])); - if (inner != 15) { + for(inner = 0; inner != points; inner++) { + fprintf(fp, SC_FORMAT, SECP256K1_GE_STORAGE_CONST_GET((*ctx.prec)[outer][inner])); + if (inner != (points - 1)) { fprintf(fp,",\n"); } else { fprintf(fp,"\n"); } } - if (outer != 63) { + if (outer != (blocks - 1)) { fprintf(fp,"},\n"); } else { fprintf(fp,"}\n"); diff --git a/src/group.h b/src/group.h index 3947ea2dda..4e8d142abc 100644 --- a/src/group.h +++ b/src/group.h @@ -100,13 +100,16 @@ static int secp256k1_gej_is_infinity(const secp256k1_gej *a); /** Check whether a group element's y coordinate is a quadratic residue. */ static int secp256k1_gej_has_quad_y_var(const secp256k1_gej *a); -/** Set r equal to the double of a. If rzr is not-NULL, r->z = a->z * *rzr (where infinity means an implicit z = 0). - * a may not be zero. Constant time. */ -static void secp256k1_gej_double_nonzero(secp256k1_gej *r, const secp256k1_gej *a, secp256k1_fe *rzr); +/** Set r equal to the double of a. */ +static void secp256k1_gej_double(secp256k1_gej *r, const secp256k1_gej *a); /** Set r equal to the double of a. If rzr is not-NULL, r->z = a->z * *rzr (where infinity means an implicit z = 0). */ static void secp256k1_gej_double_var(secp256k1_gej *r, const secp256k1_gej *a, secp256k1_fe *rzr); +/** Set r equal to the double of a. If rzr is not-NULL, r->z = a->z * *rzr (where infinity means an implicit z = 0). + * a may not be zero. Constant time. */ +static void secp256k1_gej_double_nonzero(secp256k1_gej *r, const secp256k1_gej *a, secp256k1_fe *rzr); + /** Set r equal to the sum of a and b. If rzr is non-NULL, r->z = a->z * *rzr (a cannot be infinity in that case). */ static void secp256k1_gej_add_var(secp256k1_gej *r, const secp256k1_gej *a, const secp256k1_gej *b, secp256k1_fe *rzr); diff --git a/src/group_impl.h b/src/group_impl.h index b1ace87b6f..9e0b2cbc6d 100644 --- a/src/group_impl.h +++ b/src/group_impl.h @@ -304,7 +304,7 @@ static int secp256k1_ge_is_valid_var(const secp256k1_ge *a) { return secp256k1_fe_equal_var(&y2, &x3); } -static void secp256k1_gej_double_var(secp256k1_gej *r, const secp256k1_gej *a, secp256k1_fe *rzr) { +static void secp256k1_gej_double(secp256k1_gej *r, const secp256k1_gej *a) { /* Operations: 3 mul, 4 sqr, 0 normalize, 12 mul_int/add/negate. * * Note that there is an implementation described at @@ -324,19 +324,6 @@ static void secp256k1_gej_double_var(secp256k1_gej *r, const secp256k1_gej *a, s * point will be gibberish (z = 0 but infinity = 0). */ r->infinity = a->infinity; - if (r->infinity) { - if (rzr != NULL) { - secp256k1_fe_set_int(rzr, 1); - } - return; - } - - if (rzr != NULL) { - *rzr = a->y; - secp256k1_fe_normalize_weak(rzr); - secp256k1_fe_mul_int(rzr, 2); - } - secp256k1_fe_mul(&r->z, &a->z, &a->y); secp256k1_fe_mul_int(&r->z, 2); /* Z' = 2*Y*Z (2) */ secp256k1_fe_sqr(&t1, &a->x); @@ -359,6 +346,22 @@ static void secp256k1_gej_double_var(secp256k1_gej *r, const secp256k1_gej *a, s secp256k1_fe_add(&r->y, &t2); /* Y' = 36*X^3*Y^2 - 27*X^6 - 8*Y^4 (4) */ } +static SECP256K1_INLINE void secp256k1_gej_double_var(secp256k1_gej *r, const secp256k1_gej *a, secp256k1_fe *rzr) { + if (a->infinity) { + r->infinity = 1; + if (rzr != NULL) { + secp256k1_fe_set_int(rzr, 1); + } + return; + } + if (rzr != NULL) { + *rzr = a->y; + secp256k1_fe_normalize_weak(rzr); + secp256k1_fe_mul_int(rzr, 2); + } + secp256k1_gej_double(r, a); +} + static SECP256K1_INLINE void secp256k1_gej_double_nonzero(secp256k1_gej *r, const secp256k1_gej *a, secp256k1_fe *rzr) { VERIFY_CHECK(!secp256k1_gej_is_infinity(a)); secp256k1_gej_double_var(r, a, rzr); diff --git a/src/scalar.h b/src/scalar.h index 59304cb66e..dcb8ccccd6 100644 --- a/src/scalar.h +++ b/src/scalar.h @@ -103,4 +103,6 @@ static void secp256k1_scalar_split_lambda(secp256k1_scalar *r1, secp256k1_scalar /** Multiply a and b (without taking the modulus!), divide by 2**shift, and round to the nearest integer. Shift must be at least 256. */ static void secp256k1_scalar_mul_shift_var(secp256k1_scalar *r, const secp256k1_scalar *a, const secp256k1_scalar *b, unsigned int shift); +static void secp256k1_scalar_signed_recoding(uint32_t r[9], const secp256k1_scalar *a, int bits); + #endif /* SECP256K1_SCALAR_H */ diff --git a/src/scalar_4x64_impl.h b/src/scalar_4x64_impl.h index db1ebf94be..4e56c97239 100644 --- a/src/scalar_4x64_impl.h +++ b/src/scalar_4x64_impl.h @@ -946,4 +946,39 @@ SECP256K1_INLINE static void secp256k1_scalar_mul_shift_var(secp256k1_scalar *r, secp256k1_scalar_cadd_bit(r, 0, (l[(shift - 1) >> 6] >> ((shift - 1) & 0x3f)) & 1); } +SECP256K1_INLINE static void secp256k1_scalar_signed_recoding(uint32_t r[9], const secp256k1_scalar *a, int bits) { + uint64_t a0 = a->d[0], a1 = a->d[1], a2 = a->d[2], a3 = a->d[3], a4; + uint64_t mask = (a0 & 1ULL) - 1ULL; + uint128_t c; + + VERIFY_CHECK(256 < bits && bits <= 288); + + c = (uint128_t)a0 + (SECP256K1_N_0 & mask); + a0 = (uint64_t)c; c >>= 64; + c += (uint128_t)a1 + (SECP256K1_N_1 & mask); + a1 = (uint64_t)c; c >>= 64; + c += (uint128_t)a2 + (SECP256K1_N_2 & mask); + a2 = (uint64_t)c; c >>= 64; + c += (uint128_t)a3 + (SECP256K1_N_3 & mask); + a3 = (uint64_t)c; c >>= 64; + a4 = (uint64_t)c; + VERIFY_CHECK(a0 & 1ULL); + VERIFY_CHECK(a4 < 2ULL); + + a0 = (a0 >> 1) | (a1 << 63); + a1 = (a1 >> 1) | (a2 << 63); + a2 = (a2 >> 1) | (a3 << 63); + a3 = (a3 >> 1) | (a4 << 63); + + r[0] = (uint32_t)a0; + r[1] = (uint32_t)(a0 >> 32); + r[2] = (uint32_t)a1; + r[3] = (uint32_t)(a1 >> 32); + r[4] = (uint32_t)a2; + r[5] = (uint32_t)(a2 >> 32); + r[6] = (uint32_t)a3; + r[7] = (uint32_t)(a3 >> 32); + r[8] = 1UL << (bits - 257); +} + #endif /* SECP256K1_SCALAR_REPR_IMPL_H */ diff --git a/src/scalar_8x32_impl.h b/src/scalar_8x32_impl.h index 4f9ed61fea..362042876c 100644 --- a/src/scalar_8x32_impl.h +++ b/src/scalar_8x32_impl.h @@ -718,4 +718,43 @@ SECP256K1_INLINE static void secp256k1_scalar_mul_shift_var(secp256k1_scalar *r, secp256k1_scalar_cadd_bit(r, 0, (l[(shift - 1) >> 5] >> ((shift - 1) & 0x1f)) & 1); } +SECP256K1_INLINE static void secp256k1_scalar_signed_recoding(uint32_t r[9], const secp256k1_scalar *a, int bits) { + uint32_t a0 = a->d[0], a1 = a->d[1], a2 = a->d[2], a3 = a->d[3], + a4 = a->d[4], a5 = a->d[5], a6 = a->d[6], a7 = a->d[7], a8; + uint32_t mask = (a0 & 1UL) - 1UL; + uint64_t c; + + VERIFY_CHECK(256 < bits && bits <= 288); + + c = (uint64_t)a0 + (SECP256K1_N_0 & mask); + a0 = (uint32_t)c; c >>= 32; + c += (uint64_t)a1 + (SECP256K1_N_1 & mask); + a1 = (uint32_t)c; c >>= 32; + c += (uint64_t)a2 + (SECP256K1_N_2 & mask); + a2 = (uint32_t)c; c >>= 32; + c += (uint64_t)a3 + (SECP256K1_N_3 & mask); + a3 = (uint32_t)c; c >>= 32; + c += (uint64_t)a4 + (SECP256K1_N_4 & mask); + a4 = (uint32_t)c; c >>= 32; + c += (uint64_t)a5 + (SECP256K1_N_5 & mask); + a5 = (uint32_t)c; c >>= 32; + c += (uint64_t)a6 + (SECP256K1_N_6 & mask); + a6 = (uint32_t)c; c >>= 32; + c += (uint64_t)a7 + (SECP256K1_N_7 & mask); + a7 = (uint32_t)c; c >>= 32; + a8 = (uint32_t)c; + VERIFY_CHECK(a0 & 1UL); + VERIFY_CHECK(a8 < 2UL); + + r[0] = (a0 >> 1) | (a1 << 31); + r[1] = (a1 >> 1) | (a2 << 31); + r[2] = (a2 >> 1) | (a3 << 31); + r[3] = (a3 >> 1) | (a4 << 31); + r[4] = (a4 >> 1) | (a5 << 31); + r[5] = (a5 >> 1) | (a6 << 31); + r[6] = (a6 >> 1) | (a7 << 31); + r[7] = (a7 >> 1) | (a8 << 31); + r[8] = 1UL << (bits - 257); +} + #endif /* SECP256K1_SCALAR_REPR_IMPL_H */ diff --git a/src/scalar_low_impl.h b/src/scalar_low_impl.h index c80e70c5a2..09b9fe2f7d 100644 --- a/src/scalar_low_impl.h +++ b/src/scalar_low_impl.h @@ -111,4 +111,28 @@ SECP256K1_INLINE static int secp256k1_scalar_eq(const secp256k1_scalar *a, const return *a == *b; } +SECP256K1_INLINE static void secp256k1_scalar_signed_recoding(uint32_t r[9], const secp256k1_scalar *a, int bits) { + uint32_t a0 = *a, a1; + uint32_t mask = (a0 & 1UL) - 1UL; + uint64_t c; + + VERIFY_CHECK(256 < bits && bits <= 288); + + c = (uint64_t)a0 + (EXHAUSTIVE_TEST_ORDER & mask); + a0 = (uint32_t)c; c >>= 32; + a1 = (uint32_t)c; + VERIFY_CHECK(a0 & 1UL); + VERIFY_CHECK(a1 < 2UL); + + r[0] = (a0 >> 1) | (a1 << 31); + r[1] = 0; + r[2] = 0; + r[3] = 0; + r[4] = 0; + r[5] = 0; + r[6] = 0; + r[7] = 0; + r[8] = 1UL << (bits - 257); +} + #endif /* SECP256K1_SCALAR_REPR_IMPL_H */