From 45f37b650635e46865104f37baed26ef8d2cfb97 Mon Sep 17 00:00:00 2001 From: Paul Miller Date: Mon, 17 Jan 2022 04:07:16 +0200 Subject: [PATCH 01/62] Modulo-reduce msg32 inside RFC6979 nonce fn to match spec. Fixes #1063. --- src/secp256k1.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/secp256k1.c b/src/secp256k1.c index 36fde24c3..8f34c3528 100644 --- a/src/secp256k1.c +++ b/src/secp256k1.c @@ -423,8 +423,12 @@ static int nonce_function_rfc6979(unsigned char *nonce32, const unsigned char *m unsigned int offset = 0; secp256k1_rfc6979_hmac_sha256 rng; unsigned int i; + secp256k1_scalar msg; + unsigned char msgmod32[32]; + secp256k1_scalar_set_b32(&msg, msg32, NULL); + secp256k1_scalar_get_b32(msgmod32, &msg); /* We feed a byte array to the PRNG as input, consisting of: - * - the private key (32 bytes) and message (32 bytes), see RFC 6979 3.2d. + * - the private key (32 bytes) and reduced message (32 bytes), see RFC 6979 3.2d. * - optionally 32 extra bytes of data, see RFC 6979 3.6 Additional Data. * - optionally 16 extra bytes with the algorithm name. * Because the arguments have distinct fixed lengths it is not possible for @@ -432,7 +436,7 @@ static int nonce_function_rfc6979(unsigned char *nonce32, const unsigned char *m * nonces. */ buffer_append(keydata, &offset, key32, 32); - buffer_append(keydata, &offset, msg32, 32); + buffer_append(keydata, &offset, msgmod32, 32); if (data != NULL) { buffer_append(keydata, &offset, data, 32); } From c9da1baad125e830901f0ed6ad65eb4f9ccb81f4 Mon Sep 17 00:00:00 2001 From: Russell O'Connor Date: Wed, 19 Jan 2022 09:53:02 -0500 Subject: [PATCH 02/62] Move secp256k1_fe_one to field.h This makes secp256k1_fe_one part of field.h's interface, and allows other modules to appropriately access the constant. --- src/field.h | 2 ++ src/field_impl.h | 2 -- src/group_impl.h | 3 +-- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/field.h b/src/field.h index 55679a2fc..80a0e7a6a 100644 --- a/src/field.h +++ b/src/field.h @@ -32,6 +32,8 @@ #error "Please select wide multiplication implementation" #endif +static const secp256k1_fe secp256k1_fe_one = SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 1); + /** Normalize a field element. This brings the field element to a canonical representation, reduces * its magnitude to 1, and reduces it modulo field size `p`. */ diff --git a/src/field_impl.h b/src/field_impl.h index 374284a1f..0a4a04d9a 100644 --- a/src/field_impl.h +++ b/src/field_impl.h @@ -135,6 +135,4 @@ static int secp256k1_fe_sqrt(secp256k1_fe *r, const secp256k1_fe *a) { return secp256k1_fe_equal(&t1, a); } -static const secp256k1_fe secp256k1_fe_one = SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 1); - #endif /* SECP256K1_FIELD_IMPL_H */ diff --git a/src/group_impl.h b/src/group_impl.h index f02891de7..1bb859efb 100644 --- a/src/group_impl.h +++ b/src/group_impl.h @@ -494,7 +494,6 @@ static void secp256k1_gej_add_zinv_var(secp256k1_gej *r, const secp256k1_gej *a, static void secp256k1_gej_add_ge(secp256k1_gej *r, const secp256k1_gej *a, const secp256k1_ge *b) { /* Operations: 7 mul, 5 sqr, 4 normalize, 21 mul_int/add/negate/cmov */ - static const secp256k1_fe fe_1 = SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 1); secp256k1_fe zz, u1, u2, s1, s2, t, tt, m, n, q, rr; secp256k1_fe m_alt, rr_alt; int infinity, degenerate; @@ -610,7 +609,7 @@ static void secp256k1_gej_add_ge(secp256k1_gej *r, const secp256k1_gej *a, const /** In case a->infinity == 1, replace r with (b->x, b->y, 1). */ secp256k1_fe_cmov(&r->x, &b->x, a->infinity); secp256k1_fe_cmov(&r->y, &b->y, a->infinity); - secp256k1_fe_cmov(&r->z, &fe_1, a->infinity); + secp256k1_fe_cmov(&r->z, &secp256k1_fe_one, a->infinity); r->infinity = infinity; } From e5c18892db69b5db44d282225ab4fea788af8035 Mon Sep 17 00:00:00 2001 From: Russell O'Connor Date: Fri, 26 Feb 2021 15:18:50 -0500 Subject: [PATCH 03/62] Eliminate the prej array from ecmult_strauss_wnaf. --- src/ecmult_const_impl.h | 7 ++-- src/ecmult_impl.h | 74 +++++++++++++++++++++++++---------------- src/group.h | 33 +++++++++++++----- src/group_impl.h | 15 ++++----- 4 files changed, 80 insertions(+), 49 deletions(-) diff --git a/src/ecmult_const_impl.h b/src/ecmult_const_impl.h index 3d198dced..12dbcc6c5 100644 --- a/src/ecmult_const_impl.h +++ b/src/ecmult_const_impl.h @@ -19,13 +19,10 @@ * It only operates on tables sized for WINDOW_A wnaf multiples. */ static void secp256k1_ecmult_odd_multiples_table_globalz_windowa(secp256k1_ge *pre, secp256k1_fe *globalz, const secp256k1_gej *a) { - secp256k1_gej prej[ECMULT_TABLE_SIZE(WINDOW_A)]; secp256k1_fe zr[ECMULT_TABLE_SIZE(WINDOW_A)]; - /* Compute the odd multiples in Jacobian form. */ - secp256k1_ecmult_odd_multiples_table(ECMULT_TABLE_SIZE(WINDOW_A), prej, zr, a); - /* Bring them to the same Z denominator. */ - secp256k1_ge_globalz_set_table_gej(ECMULT_TABLE_SIZE(WINDOW_A), pre, globalz, prej, zr); + secp256k1_ecmult_odd_multiples_table(ECMULT_TABLE_SIZE(WINDOW_A), pre, zr, globalz, a); + secp256k1_ge_table_set_globalz(ECMULT_TABLE_SIZE(WINDOW_A), pre, zr); } /* This is like `ECMULT_TABLE_GET_GE` but is constant time */ diff --git a/src/ecmult_impl.h b/src/ecmult_impl.h index 52af941fb..f3b581395 100644 --- a/src/ecmult_impl.h +++ b/src/ecmult_impl.h @@ -56,14 +56,23 @@ #define ECMULT_MAX_POINTS_PER_BATCH 5000000 -/** Fill a table 'prej' with precomputed odd multiples of a. Prej will contain - * the values [1*a,3*a,...,(2*n-1)*a], so it space for n values. zr[0] will - * contain prej[0].z / a.z. The other zr[i] values = prej[i].z / prej[i-1].z. - * Prej's Z values are undefined, except for the last value. +/** Fill a table 'pre_a' with precomputed odd multiples of a. + * pre_a will contain [1*a,3*a,...,(2*n-1)*a], so it needs space for n group elements. + * zr needs space for n field elements. + * + * Although pre_a is an array of _ge rather than _gej, it actually represents elements + * in Jacobian coordinates with their z coordinates omitted. The omitted z-coordinates + * can be recovered using z and zr. Using the notation z(b) to represent the omitted + * z coordinate of b: + * - z(pre_a[n-1]) = 'z' + * - z(pre_a[i-1]) = z(pre_a[i]) / zr[i] for n > i > 0 + * + * Lastly the zr[0] value, which isn't used above, is set so that: + * - a.z = z(pre_a[0]) / zr[0] */ -static void secp256k1_ecmult_odd_multiples_table(int n, secp256k1_gej *prej, secp256k1_fe *zr, const secp256k1_gej *a) { - secp256k1_gej d; - secp256k1_ge a_ge, d_ge; +static void secp256k1_ecmult_odd_multiples_table(int n, secp256k1_ge *pre_a, secp256k1_fe *zr, secp256k1_fe *z, const secp256k1_gej *a) { + secp256k1_gej d, ai; + secp256k1_ge d_ge; int i; VERIFY_CHECK(!a->infinity); @@ -71,29 +80,38 @@ static void secp256k1_ecmult_odd_multiples_table(int n, secp256k1_gej *prej, sec secp256k1_gej_double_var(&d, a, NULL); /* - * Perform the additions on an isomorphism where 'd' is affine: drop the z coordinate - * of 'd', and scale the 1P starting value's x/y coordinates without changing its z. + * Perform the additions using an isomorphic curve Y^2 = X^3 + 7*C^6 where C := d.z. + * The isomorphism, phi, maps a secp256k1 point (x, y) to the point (x*C^2, y*C^3) on the other curve. + * In Jacobian coordinates phi maps (x, y, z) to (x*C^2, y*C^3, z) or, equivalently to (x, y, z/C). + * + * phi(x, y, z) = (x*C^2, y*C^3, z) = (x, y, z/C) + * d_ge := phi(d) = (d.x, d.y, 1) + * ai := phi(a) = (a.x*C^2, a.y*C^3, a.z) + * + * The group addition functions work correctly on these isomorphic curves. + * In particular phi(d) is easy to represent in affine coordinates under this isomorphism. + * This lets us use the faster secp256k1_gej_add_ge_var group addition function that we wouldn't be able to use otherwise. */ - d_ge.x = d.x; - d_ge.y = d.y; - d_ge.infinity = 0; - - secp256k1_ge_set_gej_zinv(&a_ge, a, &d.z); - prej[0].x = a_ge.x; - prej[0].y = a_ge.y; - prej[0].z = a->z; - prej[0].infinity = 0; + secp256k1_ge_set_xy(&d_ge, &d.x, &d.y); + secp256k1_ge_set_gej_zinv(&pre_a[0], a, &d.z); + secp256k1_gej_set_ge(&ai, &pre_a[0]); + ai.z = a->z; + /* pre_a[0] is the point (a.x*C^2, a.y*C^3, a.z*C) which is equvalent to a. + * Set zr[0] to C, which is the ratio between the omitted z(pre_a[0]) value and a.z. + */ zr[0] = d.z; + for (i = 1; i < n; i++) { - secp256k1_gej_add_ge_var(&prej[i], &prej[i-1], &d_ge, &zr[i]); + secp256k1_gej_add_ge_var(&ai, &ai, &d_ge, &zr[i]); + secp256k1_ge_set_xy(&pre_a[i], &ai.x, &ai.y); } - /* - * Each point in 'prej' has a z coordinate too small by a factor of 'd.z'. Only - * the final point's z coordinate is actually used though, so just update that. + /* Multiply the last z-coordinate by C to undo the isomorphism. + * Since the z-coordinates of the pre_a values are implied by the zr array of z-coordinate ratios, + * undoing the isomorphism here undoes the isomorphism for all pre_a values. */ - secp256k1_fe_mul(&prej[n-1].z, &prej[n-1].z, &d.z); + secp256k1_fe_mul(z, &ai.z, &d.z); } /** The following two macro retrieves a particular odd multiple from a table @@ -246,18 +264,18 @@ static void secp256k1_ecmult_strauss_wnaf(const struct secp256k1_strauss_state * */ if (no > 0) { /* Compute the odd multiples in Jacobian form. */ - secp256k1_ecmult_odd_multiples_table(ECMULT_TABLE_SIZE(WINDOW_A), state->prej, state->zr, &a[state->ps[0].input_pos]); + secp256k1_ecmult_odd_multiples_table(ECMULT_TABLE_SIZE(WINDOW_A), state->pre_a, state->zr, &Z, &a[state->ps[0].input_pos]); for (np = 1; np < no; ++np) { secp256k1_gej tmp = a[state->ps[np].input_pos]; #ifdef VERIFY - secp256k1_fe_normalize_var(&(state->prej[(np - 1) * ECMULT_TABLE_SIZE(WINDOW_A) + ECMULT_TABLE_SIZE(WINDOW_A) - 1].z)); + secp256k1_fe_normalize_var(&Z); #endif - secp256k1_gej_rescale(&tmp, &(state->prej[(np - 1) * ECMULT_TABLE_SIZE(WINDOW_A) + ECMULT_TABLE_SIZE(WINDOW_A) - 1].z)); - secp256k1_ecmult_odd_multiples_table(ECMULT_TABLE_SIZE(WINDOW_A), state->prej + np * ECMULT_TABLE_SIZE(WINDOW_A), state->zr + np * ECMULT_TABLE_SIZE(WINDOW_A), &tmp); + secp256k1_gej_rescale(&tmp, &Z); + secp256k1_ecmult_odd_multiples_table(ECMULT_TABLE_SIZE(WINDOW_A), state->pre_a + np * ECMULT_TABLE_SIZE(WINDOW_A), state->zr + np * ECMULT_TABLE_SIZE(WINDOW_A), &Z, &tmp); secp256k1_fe_mul(state->zr + np * ECMULT_TABLE_SIZE(WINDOW_A), state->zr + np * ECMULT_TABLE_SIZE(WINDOW_A), &(a[state->ps[np].input_pos].z)); } /* Bring them to the same Z denominator. */ - secp256k1_ge_globalz_set_table_gej(ECMULT_TABLE_SIZE(WINDOW_A) * no, state->pre_a, &Z, state->prej, state->zr); + secp256k1_ge_table_set_globalz(ECMULT_TABLE_SIZE(WINDOW_A) * no, state->pre_a, state->zr); } else { secp256k1_fe_set_int(&Z, 1); } diff --git a/src/group.h b/src/group.h index 0b55ad73c..bb7dae1cf 100644 --- a/src/group.h +++ b/src/group.h @@ -9,7 +9,10 @@ #include "field.h" -/** A group element of the secp256k1 curve, in affine coordinates. */ +/** A group element in affine coordinates on the secp256k1 curve, + * or occasionally on an isomorphic curve of the form y^2 = x^3 + 7*t^6. + * Note: For exhaustive test mode, secp256k1 is replaced by a small subgroup of a different curve. + */ typedef struct { secp256k1_fe x; secp256k1_fe y; @@ -19,7 +22,9 @@ typedef struct { #define SECP256K1_GE_CONST(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p) {SECP256K1_FE_CONST((a),(b),(c),(d),(e),(f),(g),(h)), SECP256K1_FE_CONST((i),(j),(k),(l),(m),(n),(o),(p)), 0} #define SECP256K1_GE_CONST_INFINITY {SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), 1} -/** A group element of the secp256k1 curve, in jacobian coordinates. */ +/** A group element of the secp256k1 curve, in jacobian coordinates. + * Note: For exhastive test mode, sepc256k1 is replaced by a small subgroup of a different curve. + */ typedef struct { secp256k1_fe x; /* actual X: x/z^2 */ secp256k1_fe y; /* actual Y: y/z^3 */ @@ -64,12 +69,24 @@ static void secp256k1_ge_set_gej_var(secp256k1_ge *r, secp256k1_gej *a); /** Set a batch of group elements equal to the inputs given in jacobian coordinates */ static void secp256k1_ge_set_all_gej_var(secp256k1_ge *r, const secp256k1_gej *a, size_t len); -/** Bring a batch inputs given in jacobian coordinates (with known z-ratios) to - * the same global z "denominator". zr must contain the known z-ratios such - * that mul(a[i].z, zr[i+1]) == a[i+1].z. zr[0] is ignored. The x and y - * coordinates of the result are stored in r, the common z coordinate is - * stored in globalz. */ -static void secp256k1_ge_globalz_set_table_gej(size_t len, secp256k1_ge *r, secp256k1_fe *globalz, const secp256k1_gej *a, const secp256k1_fe *zr); +/** Bring a batch of inputs to the same global z "denominator", based on ratios between + * (omitted) z coordinates of adjacent elements. + * + * Although the elements a[i] are _ge rather than _gej, they actually represent elements + * in Jacobian coordinates with their z coordinates omitted. + * + * Using the notation z(b) to represent the omitted z coordinate of b, the array zr of + * z coordinate ratios must satisfy zr[i] == z(a[i]) / z(a[i-1]) for 0 < 'i' < len. + * The zr[0] value is unused. + * + * This function adjusts the coordinates of 'a' in place so that for all 'i', z(a[i]) == z(a[len-1]). + * In other words, the initial value of z(a[len-1]) becomes the global z "denominator". Only the + * a[i].x and a[i].y coordinates are explicitly modified; the adjustment of the omitted z coordinate is + * implicit. + * + * The coordinates of the final element a[len-1] are not changed. + */ +static void secp256k1_ge_table_set_globalz(size_t len, secp256k1_ge *a, const secp256k1_fe *zr); /** Set a group element (affine) equal to the point at infinity. */ static void secp256k1_ge_set_infinity(secp256k1_ge *r); diff --git a/src/group_impl.h b/src/group_impl.h index 1bb859efb..1d5cfa280 100644 --- a/src/group_impl.h +++ b/src/group_impl.h @@ -161,27 +161,26 @@ static void secp256k1_ge_set_all_gej_var(secp256k1_ge *r, const secp256k1_gej *a } } -static void secp256k1_ge_globalz_set_table_gej(size_t len, secp256k1_ge *r, secp256k1_fe *globalz, const secp256k1_gej *a, const secp256k1_fe *zr) { +static void secp256k1_ge_table_set_globalz(size_t len, secp256k1_ge *a, const secp256k1_fe *zr) { size_t i = len - 1; secp256k1_fe zs; if (len > 0) { - /* The z of the final point gives us the "global Z" for the table. */ - r[i].x = a[i].x; - r[i].y = a[i].y; /* Ensure all y values are in weak normal form for fast negation of points */ - secp256k1_fe_normalize_weak(&r[i].y); - *globalz = a[i].z; - r[i].infinity = 0; + secp256k1_fe_normalize_weak(&a[i].y); zs = zr[i]; /* Work our way backwards, using the z-ratios to scale the x/y values. */ while (i > 0) { + secp256k1_gej tmpa; if (i != len - 1) { secp256k1_fe_mul(&zs, &zs, &zr[i]); } i--; - secp256k1_ge_set_gej_zinv(&r[i], &a[i], &zs); + tmpa.x = a[i].x; + tmpa.y = a[i].y; + tmpa.infinity = 0; + secp256k1_ge_set_gej_zinv(&a[i], &tmpa, &zs); } } } From ae7ba0f922b4c1439888b8488b307cd0f0e8ec59 Mon Sep 17 00:00:00 2001 From: Russell O'Connor Date: Fri, 26 Feb 2021 16:10:59 -0500 Subject: [PATCH 04/62] Remove the unused prej allocations. --- src/ecmult_impl.h | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/src/ecmult_impl.h b/src/ecmult_impl.h index f3b581395..2a6734526 100644 --- a/src/ecmult_impl.h +++ b/src/ecmult_impl.h @@ -47,7 +47,7 @@ /* The number of objects allocated on the scratch space for ecmult_multi algorithms */ #define PIPPENGER_SCRATCH_OBJECTS 6 -#define STRAUSS_SCRATCH_OBJECTS 7 +#define STRAUSS_SCRATCH_OBJECTS 6 #define PIPPENGER_MAX_BUCKET_WINDOW 12 @@ -209,7 +209,6 @@ struct secp256k1_strauss_point_state { }; struct secp256k1_strauss_state { - secp256k1_gej* prej; secp256k1_fe* zr; secp256k1_ge* pre_a; secp256k1_ge* pre_a_lam; @@ -332,14 +331,12 @@ static void secp256k1_ecmult_strauss_wnaf(const struct secp256k1_strauss_state * } static void secp256k1_ecmult(secp256k1_gej *r, const secp256k1_gej *a, const secp256k1_scalar *na, const secp256k1_scalar *ng) { - secp256k1_gej prej[ECMULT_TABLE_SIZE(WINDOW_A)]; secp256k1_fe zr[ECMULT_TABLE_SIZE(WINDOW_A)]; secp256k1_ge pre_a[ECMULT_TABLE_SIZE(WINDOW_A)]; struct secp256k1_strauss_point_state ps[1]; secp256k1_ge pre_a_lam[ECMULT_TABLE_SIZE(WINDOW_A)]; struct secp256k1_strauss_state state; - state.prej = prej; state.zr = zr; state.pre_a = pre_a; state.pre_a_lam = pre_a_lam; @@ -348,7 +345,7 @@ static void secp256k1_ecmult(secp256k1_gej *r, const secp256k1_gej *a, const sec } static size_t secp256k1_strauss_scratch_size(size_t n_points) { - static const size_t point_size = (2 * sizeof(secp256k1_ge) + sizeof(secp256k1_gej) + sizeof(secp256k1_fe)) * ECMULT_TABLE_SIZE(WINDOW_A) + sizeof(struct secp256k1_strauss_point_state) + sizeof(secp256k1_gej) + sizeof(secp256k1_scalar); + static const size_t point_size = (2 * sizeof(secp256k1_ge) + sizeof(secp256k1_fe)) * ECMULT_TABLE_SIZE(WINDOW_A) + sizeof(struct secp256k1_strauss_point_state) + sizeof(secp256k1_gej) + sizeof(secp256k1_scalar); return n_points*point_size; } @@ -369,13 +366,12 @@ static int secp256k1_ecmult_strauss_batch(const secp256k1_callback* error_callba * constant and strauss_scratch_size accordingly. */ points = (secp256k1_gej*)secp256k1_scratch_alloc(error_callback, scratch, n_points * sizeof(secp256k1_gej)); scalars = (secp256k1_scalar*)secp256k1_scratch_alloc(error_callback, scratch, n_points * sizeof(secp256k1_scalar)); - state.prej = (secp256k1_gej*)secp256k1_scratch_alloc(error_callback, scratch, n_points * ECMULT_TABLE_SIZE(WINDOW_A) * sizeof(secp256k1_gej)); state.zr = (secp256k1_fe*)secp256k1_scratch_alloc(error_callback, scratch, n_points * ECMULT_TABLE_SIZE(WINDOW_A) * sizeof(secp256k1_fe)); state.pre_a = (secp256k1_ge*)secp256k1_scratch_alloc(error_callback, scratch, n_points * ECMULT_TABLE_SIZE(WINDOW_A) * sizeof(secp256k1_ge)); state.pre_a_lam = (secp256k1_ge*)secp256k1_scratch_alloc(error_callback, scratch, n_points * ECMULT_TABLE_SIZE(WINDOW_A) * sizeof(secp256k1_ge)); state.ps = (struct secp256k1_strauss_point_state*)secp256k1_scratch_alloc(error_callback, scratch, n_points * sizeof(struct secp256k1_strauss_point_state)); - if (points == NULL || scalars == NULL || state.prej == NULL || state.zr == NULL || state.pre_a == NULL || state.pre_a_lam == NULL || state.ps == NULL) { + if (points == NULL || scalars == NULL || state.zr == NULL || state.pre_a == NULL || state.pre_a_lam == NULL || state.ps == NULL) { secp256k1_scratch_apply_checkpoint(error_callback, scratch, scratch_checkpoint); return 0; } From b3b57ad6eedac86bda40f062daee7d5f4241d25c Mon Sep 17 00:00:00 2001 From: Russell O'Connor Date: Sat, 27 Feb 2021 07:57:16 -0500 Subject: [PATCH 05/62] Eliminate the pre_a_lam array from ecmult_strauss_wnaf. --- src/ecmult_impl.h | 61 ++++++++++++++++++++++++++++------------------- src/field.h | 4 ++++ src/group_impl.h | 6 +---- 3 files changed, 41 insertions(+), 30 deletions(-) diff --git a/src/ecmult_impl.h b/src/ecmult_impl.h index 2a6734526..8fcdf9a47 100644 --- a/src/ecmult_impl.h +++ b/src/ecmult_impl.h @@ -114,19 +114,29 @@ static void secp256k1_ecmult_odd_multiples_table(int n, secp256k1_ge *pre_a, sec secp256k1_fe_mul(z, &ai.z, &d.z); } -/** The following two macro retrieves a particular odd multiple from a table - * of precomputed multiples. */ -#define ECMULT_TABLE_GET_GE(r,pre,n,w) do { \ - VERIFY_CHECK(((n) & 1) == 1); \ - VERIFY_CHECK((n) >= -((1 << ((w)-1)) - 1)); \ - VERIFY_CHECK((n) <= ((1 << ((w)-1)) - 1)); \ - if ((n) > 0) { \ - *(r) = (pre)[((n)-1)/2]; \ - } else { \ - *(r) = (pre)[(-(n)-1)/2]; \ - secp256k1_fe_negate(&((r)->y), &((r)->y), 1); \ - } \ -} while(0) +SECP256K1_INLINE static void secp256k1_ecmult_table_get_ge(secp256k1_ge *r, const secp256k1_ge *pre, int n, int w) { + VERIFY_CHECK((n & 1) == 1); + VERIFY_CHECK(n >= -((1 << (w-1)) - 1)); + VERIFY_CHECK(n <= ((1 << (w-1)) - 1)); + if (n > 0) { + *r = pre[(n-1)/2]; + } else { + *r = pre[(-n-1)/2]; + secp256k1_fe_negate(&(r->y), &(r->y), 1); + } +} + +SECP256K1_INLINE static void secp256k1_ecmult_table_get_ge_lambda(secp256k1_ge *r, const secp256k1_ge *pre, const secp256k1_fe *x, int n, int w) { + VERIFY_CHECK((n & 1) == 1); + VERIFY_CHECK(n >= -((1 << (w-1)) - 1)); + VERIFY_CHECK(n <= ((1 << (w-1)) - 1)); + if (n > 0) { + secp256k1_ge_set_xy(r, &x[(n-1)/2], &pre[(n-1)/2].y); + } else { + secp256k1_ge_set_xy(r, &x[(-n-1)/2], &pre[(-n-1)/2].y); + secp256k1_fe_negate(&(r->y), &(r->y), 1); + } +} #define ECMULT_TABLE_GET_GE_STORAGE(r,pre,n,w) do { \ VERIFY_CHECK(((n) & 1) == 1); \ @@ -209,7 +219,8 @@ struct secp256k1_strauss_point_state { }; struct secp256k1_strauss_state { - secp256k1_fe* zr; + /* aux is used to hold z-ratios, and then used to hold pre_a[i].x * BETA values. */ + secp256k1_fe* aux; secp256k1_ge* pre_a; secp256k1_ge* pre_a_lam; struct secp256k1_strauss_point_state* ps; @@ -263,25 +274,25 @@ static void secp256k1_ecmult_strauss_wnaf(const struct secp256k1_strauss_state * */ if (no > 0) { /* Compute the odd multiples in Jacobian form. */ - secp256k1_ecmult_odd_multiples_table(ECMULT_TABLE_SIZE(WINDOW_A), state->pre_a, state->zr, &Z, &a[state->ps[0].input_pos]); + secp256k1_ecmult_odd_multiples_table(ECMULT_TABLE_SIZE(WINDOW_A), state->pre_a, state->aux, &Z, &a[state->ps[0].input_pos]); for (np = 1; np < no; ++np) { secp256k1_gej tmp = a[state->ps[np].input_pos]; #ifdef VERIFY secp256k1_fe_normalize_var(&Z); #endif secp256k1_gej_rescale(&tmp, &Z); - secp256k1_ecmult_odd_multiples_table(ECMULT_TABLE_SIZE(WINDOW_A), state->pre_a + np * ECMULT_TABLE_SIZE(WINDOW_A), state->zr + np * ECMULT_TABLE_SIZE(WINDOW_A), &Z, &tmp); - secp256k1_fe_mul(state->zr + np * ECMULT_TABLE_SIZE(WINDOW_A), state->zr + np * ECMULT_TABLE_SIZE(WINDOW_A), &(a[state->ps[np].input_pos].z)); + secp256k1_ecmult_odd_multiples_table(ECMULT_TABLE_SIZE(WINDOW_A), state->pre_a + np * ECMULT_TABLE_SIZE(WINDOW_A), state->aux + np * ECMULT_TABLE_SIZE(WINDOW_A), &Z, &tmp); + secp256k1_fe_mul(state->aux + np * ECMULT_TABLE_SIZE(WINDOW_A), state->aux + np * ECMULT_TABLE_SIZE(WINDOW_A), &(a[state->ps[np].input_pos].z)); } /* Bring them to the same Z denominator. */ - secp256k1_ge_table_set_globalz(ECMULT_TABLE_SIZE(WINDOW_A) * no, state->pre_a, state->zr); + secp256k1_ge_table_set_globalz(ECMULT_TABLE_SIZE(WINDOW_A) * no, state->pre_a, state->aux); } else { secp256k1_fe_set_int(&Z, 1); } for (np = 0; np < no; ++np) { for (i = 0; i < ECMULT_TABLE_SIZE(WINDOW_A); i++) { - secp256k1_ge_mul_lambda(&state->pre_a_lam[np * ECMULT_TABLE_SIZE(WINDOW_A) + i], &state->pre_a[np * ECMULT_TABLE_SIZE(WINDOW_A) + i]); + secp256k1_fe_mul(&state->aux[np * ECMULT_TABLE_SIZE(WINDOW_A) + i], &state->pre_a[np * ECMULT_TABLE_SIZE(WINDOW_A) + i].x, &secp256k1_const_beta); } } @@ -307,11 +318,11 @@ static void secp256k1_ecmult_strauss_wnaf(const struct secp256k1_strauss_state * secp256k1_gej_double_var(r, r, NULL); for (np = 0; np < no; ++np) { if (i < state->ps[np].bits_na_1 && (n = state->ps[np].wnaf_na_1[i])) { - ECMULT_TABLE_GET_GE(&tmpa, state->pre_a + np * ECMULT_TABLE_SIZE(WINDOW_A), n, WINDOW_A); + secp256k1_ecmult_table_get_ge(&tmpa, state->pre_a + np * ECMULT_TABLE_SIZE(WINDOW_A), n, WINDOW_A); secp256k1_gej_add_ge_var(r, r, &tmpa, NULL); } if (i < state->ps[np].bits_na_lam && (n = state->ps[np].wnaf_na_lam[i])) { - ECMULT_TABLE_GET_GE(&tmpa, state->pre_a_lam + np * ECMULT_TABLE_SIZE(WINDOW_A), n, WINDOW_A); + secp256k1_ecmult_table_get_ge_lambda(&tmpa, state->pre_a + np * ECMULT_TABLE_SIZE(WINDOW_A), state->aux + np * ECMULT_TABLE_SIZE(WINDOW_A), n, WINDOW_A); secp256k1_gej_add_ge_var(r, r, &tmpa, NULL); } } @@ -331,13 +342,13 @@ static void secp256k1_ecmult_strauss_wnaf(const struct secp256k1_strauss_state * } static void secp256k1_ecmult(secp256k1_gej *r, const secp256k1_gej *a, const secp256k1_scalar *na, const secp256k1_scalar *ng) { - secp256k1_fe zr[ECMULT_TABLE_SIZE(WINDOW_A)]; + secp256k1_fe aux[ECMULT_TABLE_SIZE(WINDOW_A)]; secp256k1_ge pre_a[ECMULT_TABLE_SIZE(WINDOW_A)]; struct secp256k1_strauss_point_state ps[1]; secp256k1_ge pre_a_lam[ECMULT_TABLE_SIZE(WINDOW_A)]; struct secp256k1_strauss_state state; - state.zr = zr; + state.aux = aux; state.pre_a = pre_a; state.pre_a_lam = pre_a_lam; state.ps = ps; @@ -366,12 +377,12 @@ static int secp256k1_ecmult_strauss_batch(const secp256k1_callback* error_callba * constant and strauss_scratch_size accordingly. */ points = (secp256k1_gej*)secp256k1_scratch_alloc(error_callback, scratch, n_points * sizeof(secp256k1_gej)); scalars = (secp256k1_scalar*)secp256k1_scratch_alloc(error_callback, scratch, n_points * sizeof(secp256k1_scalar)); - state.zr = (secp256k1_fe*)secp256k1_scratch_alloc(error_callback, scratch, n_points * ECMULT_TABLE_SIZE(WINDOW_A) * sizeof(secp256k1_fe)); + state.aux = (secp256k1_fe*)secp256k1_scratch_alloc(error_callback, scratch, n_points * ECMULT_TABLE_SIZE(WINDOW_A) * sizeof(secp256k1_fe)); state.pre_a = (secp256k1_ge*)secp256k1_scratch_alloc(error_callback, scratch, n_points * ECMULT_TABLE_SIZE(WINDOW_A) * sizeof(secp256k1_ge)); state.pre_a_lam = (secp256k1_ge*)secp256k1_scratch_alloc(error_callback, scratch, n_points * ECMULT_TABLE_SIZE(WINDOW_A) * sizeof(secp256k1_ge)); state.ps = (struct secp256k1_strauss_point_state*)secp256k1_scratch_alloc(error_callback, scratch, n_points * sizeof(struct secp256k1_strauss_point_state)); - if (points == NULL || scalars == NULL || state.zr == NULL || state.pre_a == NULL || state.pre_a_lam == NULL || state.ps == NULL) { + if (points == NULL || scalars == NULL || state.aux == NULL || state.pre_a == NULL || state.pre_a_lam == NULL || state.ps == NULL) { secp256k1_scratch_apply_checkpoint(error_callback, scratch, scratch_checkpoint); return 0; } diff --git a/src/field.h b/src/field.h index 80a0e7a6a..23d3e3ce9 100644 --- a/src/field.h +++ b/src/field.h @@ -33,6 +33,10 @@ #endif static const secp256k1_fe secp256k1_fe_one = SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 1); +static const secp256k1_fe secp256k1_const_beta = SECP256K1_FE_CONST( + 0x7ae96a2bul, 0x657c0710ul, 0x6e64479eul, 0xac3434e9ul, + 0x9cf04975ul, 0x12f58995ul, 0xc1396c28ul, 0x719501eeul +); /** Normalize a field element. This brings the field element to a canonical representation, reduces * its magnitude to 1, and reduces it modulo field size `p`. diff --git a/src/group_impl.h b/src/group_impl.h index 1d5cfa280..7acc2cbbe 100644 --- a/src/group_impl.h +++ b/src/group_impl.h @@ -654,12 +654,8 @@ static SECP256K1_INLINE void secp256k1_ge_storage_cmov(secp256k1_ge_storage *r, } static void secp256k1_ge_mul_lambda(secp256k1_ge *r, const secp256k1_ge *a) { - static const secp256k1_fe beta = SECP256K1_FE_CONST( - 0x7ae96a2bul, 0x657c0710ul, 0x6e64479eul, 0xac3434e9ul, - 0x9cf04975ul, 0x12f58995ul, 0xc1396c28ul, 0x719501eeul - ); *r = *a; - secp256k1_fe_mul(&r->x, &r->x, &beta); + secp256k1_fe_mul(&r->x, &r->x, &secp256k1_const_beta); } static int secp256k1_ge_is_in_correct_subgroup(const secp256k1_ge* ge) { From 7ba3ffcca0ae054cf0a1d6407c2dcf7445a46935 Mon Sep 17 00:00:00 2001 From: Russell O'Connor Date: Sat, 27 Feb 2021 08:00:55 -0500 Subject: [PATCH 06/62] Remove the unused pre_a_lam allocations. --- src/ecmult_impl.h | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/src/ecmult_impl.h b/src/ecmult_impl.h index 8fcdf9a47..0f6b91679 100644 --- a/src/ecmult_impl.h +++ b/src/ecmult_impl.h @@ -47,7 +47,7 @@ /* The number of objects allocated on the scratch space for ecmult_multi algorithms */ #define PIPPENGER_SCRATCH_OBJECTS 6 -#define STRAUSS_SCRATCH_OBJECTS 6 +#define STRAUSS_SCRATCH_OBJECTS 5 #define PIPPENGER_MAX_BUCKET_WINDOW 12 @@ -222,7 +222,6 @@ struct secp256k1_strauss_state { /* aux is used to hold z-ratios, and then used to hold pre_a[i].x * BETA values. */ secp256k1_fe* aux; secp256k1_ge* pre_a; - secp256k1_ge* pre_a_lam; struct secp256k1_strauss_point_state* ps; }; @@ -345,18 +344,16 @@ static void secp256k1_ecmult(secp256k1_gej *r, const secp256k1_gej *a, const sec secp256k1_fe aux[ECMULT_TABLE_SIZE(WINDOW_A)]; secp256k1_ge pre_a[ECMULT_TABLE_SIZE(WINDOW_A)]; struct secp256k1_strauss_point_state ps[1]; - secp256k1_ge pre_a_lam[ECMULT_TABLE_SIZE(WINDOW_A)]; struct secp256k1_strauss_state state; state.aux = aux; state.pre_a = pre_a; - state.pre_a_lam = pre_a_lam; state.ps = ps; secp256k1_ecmult_strauss_wnaf(&state, r, 1, a, na, ng); } static size_t secp256k1_strauss_scratch_size(size_t n_points) { - static const size_t point_size = (2 * sizeof(secp256k1_ge) + sizeof(secp256k1_fe)) * ECMULT_TABLE_SIZE(WINDOW_A) + sizeof(struct secp256k1_strauss_point_state) + sizeof(secp256k1_gej) + sizeof(secp256k1_scalar); + 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); return n_points*point_size; } @@ -379,10 +376,9 @@ static int secp256k1_ecmult_strauss_batch(const secp256k1_callback* error_callba scalars = (secp256k1_scalar*)secp256k1_scratch_alloc(error_callback, scratch, n_points * sizeof(secp256k1_scalar)); state.aux = (secp256k1_fe*)secp256k1_scratch_alloc(error_callback, scratch, n_points * ECMULT_TABLE_SIZE(WINDOW_A) * sizeof(secp256k1_fe)); state.pre_a = (secp256k1_ge*)secp256k1_scratch_alloc(error_callback, scratch, n_points * ECMULT_TABLE_SIZE(WINDOW_A) * sizeof(secp256k1_ge)); - state.pre_a_lam = (secp256k1_ge*)secp256k1_scratch_alloc(error_callback, scratch, n_points * ECMULT_TABLE_SIZE(WINDOW_A) * sizeof(secp256k1_ge)); state.ps = (struct secp256k1_strauss_point_state*)secp256k1_scratch_alloc(error_callback, scratch, n_points * sizeof(struct secp256k1_strauss_point_state)); - if (points == NULL || scalars == NULL || state.aux == NULL || state.pre_a == NULL || state.pre_a_lam == NULL || state.ps == NULL) { + if (points == NULL || scalars == NULL || state.aux == NULL || state.pre_a == NULL || state.ps == NULL) { secp256k1_scratch_apply_checkpoint(error_callback, scratch, scratch_checkpoint); return 0; } From 0397d00ba0401bf5be7c4312d84d17fc789a6566 Mon Sep 17 00:00:00 2001 From: Russell O'Connor Date: Sat, 27 Feb 2021 08:36:22 -0500 Subject: [PATCH 07/62] Eliminate na_1 and na_lam state fields from ecmult_strauss_wnaf. --- src/ecmult_impl.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/ecmult_impl.h b/src/ecmult_impl.h index 0f6b91679..587c88f6a 100644 --- a/src/ecmult_impl.h +++ b/src/ecmult_impl.h @@ -210,7 +210,6 @@ static int secp256k1_ecmult_wnaf(int *wnaf, int len, const secp256k1_scalar *a, } struct secp256k1_strauss_point_state { - secp256k1_scalar na_1, na_lam; int wnaf_na_1[129]; int wnaf_na_lam[129]; int bits_na_1; @@ -240,16 +239,17 @@ static void secp256k1_ecmult_strauss_wnaf(const struct secp256k1_strauss_state * size_t no = 0; for (np = 0; np < num; ++np) { + secp256k1_scalar na_1, na_lam; if (secp256k1_scalar_is_zero(&na[np]) || secp256k1_gej_is_infinity(&a[np])) { continue; } state->ps[no].input_pos = np; /* split na into na_1 and na_lam (where na = na_1 + na_lam*lambda, and na_1 and na_lam are ~128 bit) */ - secp256k1_scalar_split_lambda(&state->ps[no].na_1, &state->ps[no].na_lam, &na[np]); + secp256k1_scalar_split_lambda(&na_1, &na_lam, &na[np]); /* build wnaf representation for na_1 and na_lam. */ - state->ps[no].bits_na_1 = secp256k1_ecmult_wnaf(state->ps[no].wnaf_na_1, 129, &state->ps[no].na_1, WINDOW_A); - state->ps[no].bits_na_lam = secp256k1_ecmult_wnaf(state->ps[no].wnaf_na_lam, 129, &state->ps[no].na_lam, WINDOW_A); + state->ps[no].bits_na_1 = secp256k1_ecmult_wnaf(state->ps[no].wnaf_na_1, 129, &na_1, WINDOW_A); + state->ps[no].bits_na_lam = secp256k1_ecmult_wnaf(state->ps[no].wnaf_na_lam, 129, &na_lam, WINDOW_A); VERIFY_CHECK(state->ps[no].bits_na_1 <= 129); VERIFY_CHECK(state->ps[no].bits_na_lam <= 129); if (state->ps[no].bits_na_1 > bits) { From fe34d9f3419d090e94b0c0897895c5b2b9fdc244 Mon Sep 17 00:00:00 2001 From: Russell O'Connor Date: Sat, 27 Feb 2021 14:07:45 -0500 Subject: [PATCH 08/62] Eliminate input_pos state field from ecmult_strauss_wnaf. --- src/ecmult_impl.h | 46 +++++++++++++++++++++------------------------- 1 file changed, 21 insertions(+), 25 deletions(-) diff --git a/src/ecmult_impl.h b/src/ecmult_impl.h index 587c88f6a..73674db93 100644 --- a/src/ecmult_impl.h +++ b/src/ecmult_impl.h @@ -214,7 +214,6 @@ struct secp256k1_strauss_point_state { int wnaf_na_lam[129]; int bits_na_1; int bits_na_lam; - size_t input_pos; }; struct secp256k1_strauss_state { @@ -238,12 +237,13 @@ static void secp256k1_ecmult_strauss_wnaf(const struct secp256k1_strauss_state * size_t np; size_t no = 0; + secp256k1_fe_set_int(&Z, 1); for (np = 0; np < num; ++np) { + secp256k1_gej tmp; secp256k1_scalar na_1, na_lam; if (secp256k1_scalar_is_zero(&na[np]) || secp256k1_gej_is_infinity(&a[np])) { continue; } - state->ps[no].input_pos = np; /* split na into na_1 and na_lam (where na = na_1 + na_lam*lambda, and na_1 and na_lam are ~128 bit) */ secp256k1_scalar_split_lambda(&na_1, &na_lam, &na[np]); @@ -258,37 +258,33 @@ static void secp256k1_ecmult_strauss_wnaf(const struct secp256k1_strauss_state * if (state->ps[no].bits_na_lam > bits) { bits = state->ps[no].bits_na_lam; } - ++no; - } - /* Calculate odd multiples of a. - * All multiples are brought to the same Z 'denominator', which is stored - * in Z. Due to secp256k1' isomorphism we can do all operations pretending - * that the Z coordinate was 1, use affine addition formulae, and correct - * the Z coordinate of the result once at the end. - * The exception is the precomputed G table points, which are actually - * affine. Compared to the base used for other points, they have a Z ratio - * of 1/Z, so we can use secp256k1_gej_add_zinv_var, which uses the same - * isomorphism to efficiently add with a known Z inverse. - */ - if (no > 0) { - /* Compute the odd multiples in Jacobian form. */ - secp256k1_ecmult_odd_multiples_table(ECMULT_TABLE_SIZE(WINDOW_A), state->pre_a, state->aux, &Z, &a[state->ps[0].input_pos]); - for (np = 1; np < no; ++np) { - secp256k1_gej tmp = a[state->ps[np].input_pos]; + /* Calculate odd multiples of a. + * All multiples are brought to the same Z 'denominator', which is stored + * in Z. Due to secp256k1' isomorphism we can do all operations pretending + * that the Z coordinate was 1, use affine addition formulae, and correct + * the Z coordinate of the result once at the end. + * The exception is the precomputed G table points, which are actually + * affine. Compared to the base used for other points, they have a Z ratio + * of 1/Z, so we can use secp256k1_gej_add_zinv_var, which uses the same + * isomorphism to efficiently add with a known Z inverse. + */ + tmp = a[np]; + if (no) { #ifdef VERIFY secp256k1_fe_normalize_var(&Z); #endif secp256k1_gej_rescale(&tmp, &Z); - secp256k1_ecmult_odd_multiples_table(ECMULT_TABLE_SIZE(WINDOW_A), state->pre_a + np * ECMULT_TABLE_SIZE(WINDOW_A), state->aux + np * ECMULT_TABLE_SIZE(WINDOW_A), &Z, &tmp); - secp256k1_fe_mul(state->aux + np * ECMULT_TABLE_SIZE(WINDOW_A), state->aux + np * ECMULT_TABLE_SIZE(WINDOW_A), &(a[state->ps[np].input_pos].z)); } - /* Bring them to the same Z denominator. */ - secp256k1_ge_table_set_globalz(ECMULT_TABLE_SIZE(WINDOW_A) * no, state->pre_a, state->aux); - } else { - secp256k1_fe_set_int(&Z, 1); + secp256k1_ecmult_odd_multiples_table(ECMULT_TABLE_SIZE(WINDOW_A), state->pre_a + no * ECMULT_TABLE_SIZE(WINDOW_A), state->aux + no * ECMULT_TABLE_SIZE(WINDOW_A), &Z, &tmp); + if (no) secp256k1_fe_mul(state->aux + no * ECMULT_TABLE_SIZE(WINDOW_A), state->aux + no * ECMULT_TABLE_SIZE(WINDOW_A), &(a[np].z)); + + ++no; } + /* Bring them to the same Z denominator. */ + secp256k1_ge_table_set_globalz(ECMULT_TABLE_SIZE(WINDOW_A) * no, state->pre_a, state->aux); + for (np = 0; np < no; ++np) { for (i = 0; i < ECMULT_TABLE_SIZE(WINDOW_A); i++) { secp256k1_fe_mul(&state->aux[np * ECMULT_TABLE_SIZE(WINDOW_A) + i], &state->pre_a[np * ECMULT_TABLE_SIZE(WINDOW_A) + i].x, &secp256k1_const_beta); From a731200cc30fcf019af08a41f7b6f329a08eaa0c Mon Sep 17 00:00:00 2001 From: Russell O'Connor Date: Tue, 18 Jan 2022 17:27:12 -0500 Subject: [PATCH 09/62] Replace ECMULT_TABLE_GET_GE_STORAGE macro with a function. --- src/ecmult_impl.h | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/src/ecmult_impl.h b/src/ecmult_impl.h index 73674db93..9a5da8c9f 100644 --- a/src/ecmult_impl.h +++ b/src/ecmult_impl.h @@ -138,17 +138,17 @@ SECP256K1_INLINE static void secp256k1_ecmult_table_get_ge_lambda(secp256k1_ge * } } -#define ECMULT_TABLE_GET_GE_STORAGE(r,pre,n,w) do { \ - VERIFY_CHECK(((n) & 1) == 1); \ - VERIFY_CHECK((n) >= -((1 << ((w)-1)) - 1)); \ - VERIFY_CHECK((n) <= ((1 << ((w)-1)) - 1)); \ - if ((n) > 0) { \ - secp256k1_ge_from_storage((r), &(pre)[((n)-1)/2]); \ - } else { \ - secp256k1_ge_from_storage((r), &(pre)[(-(n)-1)/2]); \ - secp256k1_fe_negate(&((r)->y), &((r)->y), 1); \ - } \ -} while(0) +SECP256K1_INLINE static void secp256k1_ecmult_table_get_ge_storage(secp256k1_ge *r, const secp256k1_ge_storage *pre, int n, int w) { + VERIFY_CHECK((n & 1) == 1); + VERIFY_CHECK(n >= -((1 << (w-1)) - 1)); + VERIFY_CHECK(n <= ((1 << (w-1)) - 1)); + if (n > 0) { + secp256k1_ge_from_storage(r, &pre[(n-1)/2]); + } else { + secp256k1_ge_from_storage(r, &pre[(-n-1)/2]); + secp256k1_fe_negate(&(r->y), &(r->y), 1); + } +} /** Convert a number to WNAF notation. The number becomes represented by sum(2^i * wnaf[i], i=0..bits), * with the following guarantees: @@ -322,11 +322,11 @@ static void secp256k1_ecmult_strauss_wnaf(const struct secp256k1_strauss_state * } } if (i < bits_ng_1 && (n = wnaf_ng_1[i])) { - ECMULT_TABLE_GET_GE_STORAGE(&tmpa, secp256k1_pre_g, n, WINDOW_G); + secp256k1_ecmult_table_get_ge_storage(&tmpa, secp256k1_pre_g, n, WINDOW_G); secp256k1_gej_add_zinv_var(r, r, &tmpa, &Z); } if (i < bits_ng_128 && (n = wnaf_ng_128[i])) { - ECMULT_TABLE_GET_GE_STORAGE(&tmpa, secp256k1_pre_g_128, n, WINDOW_G); + secp256k1_ecmult_table_get_ge_storage(&tmpa, secp256k1_pre_g_128, n, WINDOW_G); secp256k1_gej_add_zinv_var(r, r, &tmpa, &Z); } } From b797a500ec194948eecbea8bd80f6b7d455f7ca2 Mon Sep 17 00:00:00 2001 From: Russell O'Connor Date: Tue, 18 Jan 2022 22:41:00 -0500 Subject: [PATCH 10/62] Create a SECP256K1_ECMULT_TABLE_VERIFY macro. --- src/ecmult_impl.h | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/src/ecmult_impl.h b/src/ecmult_impl.h index 9a5da8c9f..bbc820c77 100644 --- a/src/ecmult_impl.h +++ b/src/ecmult_impl.h @@ -114,10 +114,13 @@ static void secp256k1_ecmult_odd_multiples_table(int n, secp256k1_ge *pre_a, sec secp256k1_fe_mul(z, &ai.z, &d.z); } +#define SECP256K1_ECMULT_TABLE_VERIFY(n,w) \ + VERIFY_CHECK(((n) & 1) == 1); \ + VERIFY_CHECK((n) >= -((1 << ((w)-1)) - 1)); \ + VERIFY_CHECK((n) <= ((1 << ((w)-1)) - 1)); + SECP256K1_INLINE static void secp256k1_ecmult_table_get_ge(secp256k1_ge *r, const secp256k1_ge *pre, int n, int w) { - VERIFY_CHECK((n & 1) == 1); - VERIFY_CHECK(n >= -((1 << (w-1)) - 1)); - VERIFY_CHECK(n <= ((1 << (w-1)) - 1)); + SECP256K1_ECMULT_TABLE_VERIFY(n,w) if (n > 0) { *r = pre[(n-1)/2]; } else { @@ -127,9 +130,7 @@ SECP256K1_INLINE static void secp256k1_ecmult_table_get_ge(secp256k1_ge *r, cons } SECP256K1_INLINE static void secp256k1_ecmult_table_get_ge_lambda(secp256k1_ge *r, const secp256k1_ge *pre, const secp256k1_fe *x, int n, int w) { - VERIFY_CHECK((n & 1) == 1); - VERIFY_CHECK(n >= -((1 << (w-1)) - 1)); - VERIFY_CHECK(n <= ((1 << (w-1)) - 1)); + SECP256K1_ECMULT_TABLE_VERIFY(n,w) if (n > 0) { secp256k1_ge_set_xy(r, &x[(n-1)/2], &pre[(n-1)/2].y); } else { @@ -139,9 +140,7 @@ SECP256K1_INLINE static void secp256k1_ecmult_table_get_ge_lambda(secp256k1_ge * } SECP256K1_INLINE static void secp256k1_ecmult_table_get_ge_storage(secp256k1_ge *r, const secp256k1_ge_storage *pre, int n, int w) { - VERIFY_CHECK((n & 1) == 1); - VERIFY_CHECK(n >= -((1 << (w-1)) - 1)); - VERIFY_CHECK(n <= ((1 << (w-1)) - 1)); + SECP256K1_ECMULT_TABLE_VERIFY(n,w) if (n > 0) { secp256k1_ge_from_storage(r, &pre[(n-1)/2]); } else { From 070e772211b3fcd297577b90b56bbf7a5cfbd0a3 Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Wed, 22 Dec 2021 13:39:45 -0500 Subject: [PATCH 11/62] Faster fixed-input ecmult tests --- src/tests.c | 81 +++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 78 insertions(+), 3 deletions(-) diff --git a/src/tests.c b/src/tests.c index 0ed01b7cf..e71433b4d 100644 --- a/src/tests.c +++ b/src/tests.c @@ -28,6 +28,8 @@ #include "modinv64_impl.h" #endif +#define CONDITIONAL_TEST(cnt, nam) if (count < (cnt)) { printf("Skipping %s (iteration count too low)\n", nam); } else + static int count = 64; static secp256k1_context *ctx = NULL; @@ -4712,8 +4714,8 @@ void test_ecmult_accumulate(secp256k1_sha256* acc, const secp256k1_scalar* x, se } } -void test_ecmult_constants(void) { - /* Test ecmult_gen for: +void test_ecmult_constants_2bit(void) { + /* Using test_ecmult_accumulate, test ecmult for: * - For i in 0..36: * - Key i * - Key -i @@ -4756,8 +4758,81 @@ void test_ecmult_constants(void) { secp256k1_scratch_space_destroy(ctx, scratch); } +void test_ecmult_constants_sha(uint32_t prefix, size_t iter, const unsigned char* expected32) { + /* Using test_ecmult_accumulate, test ecmult for: + * - Key 0 + * - Key 1 + * - Key -1 + * - For i in range(iter): + * - Key SHA256(LE32(prefix) || LE16(i)) + */ + secp256k1_scalar x; + secp256k1_sha256 acc; + unsigned char b32[32]; + unsigned char inp[6]; + size_t i; + secp256k1_scratch_space *scratch = secp256k1_scratch_space_create(ctx, 65536); + + inp[0] = prefix & 0xFF; + inp[1] = (prefix >> 8) & 0xFF; + inp[2] = (prefix >> 16) & 0xFF; + inp[3] = (prefix >> 24) & 0xFF; + secp256k1_sha256_initialize(&acc); + secp256k1_scalar_set_int(&x, 0); + test_ecmult_accumulate(&acc, &x, scratch); + secp256k1_scalar_set_int(&x, 1); + test_ecmult_accumulate(&acc, &x, scratch); + secp256k1_scalar_negate(&x, &x); + test_ecmult_accumulate(&acc, &x, scratch); + + for (i = 0; i < iter; ++i) { + secp256k1_sha256 gen; + inp[4] = i & 0xff; + inp[5] = (i >> 8) & 0xff; + secp256k1_sha256_initialize(&gen); + secp256k1_sha256_write(&gen, inp, sizeof(inp)); + secp256k1_sha256_finalize(&gen, b32); + secp256k1_scalar_set_b32(&x, b32, NULL); + test_ecmult_accumulate(&acc, &x, scratch); + } + secp256k1_sha256_finalize(&acc, b32); + CHECK(secp256k1_memcmp_var(b32, expected32, 32) == 0); + + secp256k1_scratch_space_destroy(ctx, scratch); +} + void run_ecmult_constants(void) { - test_ecmult_constants(); + /* Expected hashes of all points in the tests below. Computed using an + * independent implementation. */ + static const unsigned char expected32_6bit20[32] = { + 0x68, 0xb6, 0xed, 0x6f, 0x28, 0xca, 0xc9, 0x7f, + 0x8e, 0x8b, 0xd6, 0xc0, 0x61, 0x79, 0x34, 0x6e, + 0x5a, 0x8f, 0x2b, 0xbc, 0x3e, 0x1f, 0xc5, 0x2e, + 0x2a, 0xd0, 0x45, 0x67, 0x7f, 0x95, 0x95, 0x8e + }; + static const unsigned char expected32_8bit8[32] = { + 0x8b, 0x65, 0x8e, 0xea, 0x86, 0xae, 0x3c, 0x95, + 0x90, 0xb6, 0x77, 0xa4, 0x8c, 0x76, 0xd9, 0xec, + 0xf5, 0xab, 0x8a, 0x2f, 0xfd, 0xdb, 0x19, 0x12, + 0x1a, 0xee, 0xe6, 0xb7, 0x6e, 0x05, 0x3f, 0xc6 + }; + /* For every combination of 6 bit positions out of 256, restricted to + * 20-bit windows (i.e., the first and last bit position are no more than + * 19 bits apart), all 64 bit patterns occur in the input scalars used in + * this test. */ + CONDITIONAL_TEST(1, "test_ecmult_constants_sha 1024") { + test_ecmult_constants_sha(4808378u, 1024, expected32_6bit20); + } + + /* For every combination of 8 consecutive bit positions, all 256 bit + * patterns occur in the input scalars used in this test. */ + CONDITIONAL_TEST(3, "test_ecmult_constants_sha 2048") { + test_ecmult_constants_sha(1607366309u, 2048, expected32_8bit8); + } + + CONDITIONAL_TEST(35, "test_ecmult_constants_2bit") { + test_ecmult_constants_2bit(); + } } void test_ecmult_gen_blind(void) { From e108d0039c36483dffe4be00815c1b6d65ef5751 Mon Sep 17 00:00:00 2001 From: Tim Ruffing Date: Mon, 31 Jan 2022 12:13:37 +0100 Subject: [PATCH 12/62] sage: Fix incompatibility with sage 9.4 `allexprs` is already the product all numerators. Don't take it's numerator again. Fixes #1067. --- sage/group_prover.sage | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sage/group_prover.sage b/sage/group_prover.sage index b200bfeae..999da52a8 100644 --- a/sage/group_prover.sage +++ b/sage/group_prover.sage @@ -227,7 +227,7 @@ def prove_nonzero(R, exprs, assume): if ok: return (True, None) ok = True - for (f, n) in zero.reduce(numerator(allexprs)).factor(): + for (f, n) in zero.reduce(allexprs).factor(): if f not in nonzero: ok = False if ok: From 925f78d55e112cd00f1e2867886bdc751a5d6606 Mon Sep 17 00:00:00 2001 From: Peter Dettman Date: Mon, 6 Dec 2021 01:24:30 +0700 Subject: [PATCH 13/62] Add _fe_half and use in _gej_add_ge - Trades 1 _half for 3 _mul_int and 2 _normalize_weak - Updated formula and comments in _gej_add_ge - Added internal benchmark for _fe_half --- src/bench_internal.c | 10 ++++++ src/field.h | 5 +++ src/field_10x26_impl.h | 76 ++++++++++++++++++++++++++++++++++++++++++ src/field_5x52_impl.h | 65 ++++++++++++++++++++++++++++++++++++ src/group_impl.h | 36 +++++++++----------- 5 files changed, 172 insertions(+), 20 deletions(-) diff --git a/src/bench_internal.c b/src/bench_internal.c index aed821612..3c145f306 100644 --- a/src/bench_internal.c +++ b/src/bench_internal.c @@ -140,6 +140,15 @@ void bench_scalar_inverse_var(void* arg, int iters) { CHECK(j <= iters); } +void bench_field_half(void* arg, int iters) { + int i; + bench_inv *data = (bench_inv*)arg; + + for (i = 0; i < iters; i++) { + secp256k1_fe_half(&data->fe[0]); + } +} + void bench_field_normalize(void* arg, int iters) { int i; bench_inv *data = (bench_inv*)arg; @@ -354,6 +363,7 @@ int main(int argc, char **argv) { if (d || have_flag(argc, argv, "scalar") || have_flag(argc, argv, "inverse")) run_benchmark("scalar_inverse", bench_scalar_inverse, bench_setup, NULL, &data, 10, iters); if (d || have_flag(argc, argv, "scalar") || have_flag(argc, argv, "inverse")) run_benchmark("scalar_inverse_var", bench_scalar_inverse_var, bench_setup, NULL, &data, 10, iters); + if (d || have_flag(argc, argv, "field") || have_flag(argc, argv, "half")) run_benchmark("field_half", bench_field_half, bench_setup, NULL, &data, 10, iters*100); if (d || have_flag(argc, argv, "field") || have_flag(argc, argv, "normalize")) run_benchmark("field_normalize", bench_field_normalize, bench_setup, NULL, &data, 10, iters*100); if (d || have_flag(argc, argv, "field") || have_flag(argc, argv, "normalize")) run_benchmark("field_normalize_weak", bench_field_normalize_weak, bench_setup, NULL, &data, 10, iters*100); if (d || have_flag(argc, argv, "field") || have_flag(argc, argv, "sqr")) run_benchmark("field_sqr", bench_field_sqr, bench_setup, NULL, &data, 10, iters*10); diff --git a/src/field.h b/src/field.h index 23d3e3ce9..a52e056dd 100644 --- a/src/field.h +++ b/src/field.h @@ -130,4 +130,9 @@ static void secp256k1_fe_storage_cmov(secp256k1_fe_storage *r, const secp256k1_f /** If flag is true, set *r equal to *a; otherwise leave it. Constant-time. Both *r and *a must be initialized.*/ static void secp256k1_fe_cmov(secp256k1_fe *r, const secp256k1_fe *a, int flag); +/** Halves the value of a field element modulo the field prime. Constant-time. + * For an input magnitude 'm', the output magnitude is set to 'floor(m/2) + 1'. + * The output is not guaranteed to be normalized, regardless of the input. */ +static void secp256k1_fe_half(secp256k1_fe *r); + #endif /* SECP256K1_FIELD_H */ diff --git a/src/field_10x26_impl.h b/src/field_10x26_impl.h index aecb6117e..627cd5b57 100644 --- a/src/field_10x26_impl.h +++ b/src/field_10x26_impl.h @@ -1133,6 +1133,82 @@ static SECP256K1_INLINE void secp256k1_fe_cmov(secp256k1_fe *r, const secp256k1_ #endif } +static SECP256K1_INLINE void secp256k1_fe_half(secp256k1_fe *r) { + uint32_t t0 = r->n[0], t1 = r->n[1], t2 = r->n[2], t3 = r->n[3], t4 = r->n[4], + t5 = r->n[5], t6 = r->n[6], t7 = r->n[7], t8 = r->n[8], t9 = r->n[9]; + uint32_t one = (uint32_t)1; + uint32_t mask = -(t0 & one) >> 6; + +#ifdef VERIFY + secp256k1_fe_verify(r); + VERIFY_CHECK(r->magnitude < 32); +#endif + + /* Bounds analysis (over the rationals). + * + * Let m = r->magnitude + * C = 0x3FFFFFFUL * 2 + * D = 0x03FFFFFUL * 2 + * + * Initial bounds: t0..t8 <= C * m + * t9 <= D * m + */ + + t0 += 0x3FFFC2FUL & mask; + t1 += 0x3FFFFBFUL & mask; + t2 += mask; + t3 += mask; + t4 += mask; + t5 += mask; + t6 += mask; + t7 += mask; + t8 += mask; + t9 += mask >> 4; + + VERIFY_CHECK((t0 & one) == 0); + + /* t0..t8: added <= C/2 + * t9: added <= D/2 + * + * Current bounds: t0..t8 <= C * (m + 1/2) + * t9 <= D * (m + 1/2) + */ + + r->n[0] = (t0 >> 1) + ((t1 & one) << 25); + r->n[1] = (t1 >> 1) + ((t2 & one) << 25); + r->n[2] = (t2 >> 1) + ((t3 & one) << 25); + r->n[3] = (t3 >> 1) + ((t4 & one) << 25); + r->n[4] = (t4 >> 1) + ((t5 & one) << 25); + r->n[5] = (t5 >> 1) + ((t6 & one) << 25); + r->n[6] = (t6 >> 1) + ((t7 & one) << 25); + r->n[7] = (t7 >> 1) + ((t8 & one) << 25); + r->n[8] = (t8 >> 1) + ((t9 & one) << 25); + r->n[9] = (t9 >> 1); + + /* t0..t8: shifted right and added <= C/4 + 1/2 + * t9: shifted right + * + * Current bounds: t0..t8 <= C * (m/2 + 1/2) + * t9 <= D * (m/2 + 1/4) + */ + +#ifdef VERIFY + /* Therefore the output magnitude (M) has to be set such that: + * t0..t8: C * M >= C * (m/2 + 1/2) + * t9: D * M >= D * (m/2 + 1/4) + * + * It suffices for all limbs that, for any input magnitude m: + * M >= m/2 + 1/2 + * + * and since we want the smallest such integer value for M: + * M == floor(m/2) + 1 + */ + r->magnitude = (r->magnitude >> 1) + 1; + r->normalized = 0; + secp256k1_fe_verify(r); +#endif +} + static SECP256K1_INLINE void secp256k1_fe_storage_cmov(secp256k1_fe_storage *r, const secp256k1_fe_storage *a, int flag) { uint32_t mask0, mask1; VG_CHECK_VERIFY(r->n, sizeof(r->n)); diff --git a/src/field_5x52_impl.h b/src/field_5x52_impl.h index 9b824b920..6e6bb3c64 100644 --- a/src/field_5x52_impl.h +++ b/src/field_5x52_impl.h @@ -477,6 +477,71 @@ static SECP256K1_INLINE void secp256k1_fe_cmov(secp256k1_fe *r, const secp256k1_ #endif } +static SECP256K1_INLINE void secp256k1_fe_half(secp256k1_fe *r) { + uint64_t t0 = r->n[0], t1 = r->n[1], t2 = r->n[2], t3 = r->n[3], t4 = r->n[4]; + uint64_t one = (uint64_t)1; + uint64_t mask = -(t0 & one) >> 12; + +#ifdef VERIFY + secp256k1_fe_verify(r); + VERIFY_CHECK(r->magnitude < 32); +#endif + + /* Bounds analysis (over the rationals). + * + * Let m = r->magnitude + * C = 0xFFFFFFFFFFFFFULL * 2 + * D = 0x0FFFFFFFFFFFFULL * 2 + * + * Initial bounds: t0..t3 <= C * m + * t4 <= D * m + */ + + t0 += 0xFFFFEFFFFFC2FULL & mask; + t1 += mask; + t2 += mask; + t3 += mask; + t4 += mask >> 4; + + VERIFY_CHECK((t0 & one) == 0); + + /* t0..t3: added <= C/2 + * t4: added <= D/2 + * + * Current bounds: t0..t3 <= C * (m + 1/2) + * t4 <= D * (m + 1/2) + */ + + r->n[0] = (t0 >> 1) + ((t1 & one) << 51); + r->n[1] = (t1 >> 1) + ((t2 & one) << 51); + r->n[2] = (t2 >> 1) + ((t3 & one) << 51); + r->n[3] = (t3 >> 1) + ((t4 & one) << 51); + r->n[4] = (t4 >> 1); + + /* t0..t3: shifted right and added <= C/4 + 1/2 + * t4: shifted right + * + * Current bounds: t0..t3 <= C * (m/2 + 1/2) + * t4 <= D * (m/2 + 1/4) + */ + +#ifdef VERIFY + /* Therefore the output magnitude (M) has to be set such that: + * t0..t3: C * M >= C * (m/2 + 1/2) + * t4: D * M >= D * (m/2 + 1/4) + * + * It suffices for all limbs that, for any input magnitude m: + * M >= m/2 + 1/2 + * + * and since we want the smallest such integer value for M: + * M == floor(m/2) + 1 + */ + r->magnitude = (r->magnitude >> 1) + 1; + r->normalized = 0; + secp256k1_fe_verify(r); +#endif +} + static SECP256K1_INLINE void secp256k1_fe_storage_cmov(secp256k1_fe_storage *r, const secp256k1_fe_storage *a, int flag) { uint64_t mask0, mask1; VG_CHECK_VERIFY(r->n, sizeof(r->n)); diff --git a/src/group_impl.h b/src/group_impl.h index 7acc2cbbe..d59beaeb2 100644 --- a/src/group_impl.h +++ b/src/group_impl.h @@ -492,7 +492,7 @@ static void secp256k1_gej_add_zinv_var(secp256k1_gej *r, const secp256k1_gej *a, static void secp256k1_gej_add_ge(secp256k1_gej *r, const secp256k1_gej *a, const secp256k1_ge *b) { - /* Operations: 7 mul, 5 sqr, 4 normalize, 21 mul_int/add/negate/cmov */ + /* Operations: 7 mul, 5 sqr, 24 add/cmov/half/mul_int/negate/normalize_weak/normalizes_to_zero */ secp256k1_fe zz, u1, u2, s1, s2, t, tt, m, n, q, rr; secp256k1_fe m_alt, rr_alt; int infinity, degenerate; @@ -513,11 +513,11 @@ static void secp256k1_gej_add_ge(secp256k1_gej *r, const secp256k1_gej *a, const * Z = Z1*Z2 * T = U1+U2 * M = S1+S2 - * Q = T*M^2 + * Q = -T*M^2 * R = T^2-U1*U2 - * X3 = 4*(R^2-Q) - * Y3 = 4*(R*(3*Q-2*R^2)-M^4) - * Z3 = 2*M*Z + * X3 = R^2+Q + * Y3 = -(R*(2*X3+Q)+M^4)/2 + * Z3 = M*Z * (Note that the paper uses xi = Xi / Zi and yi = Yi / Zi instead.) * * This formula has the benefit of being the same for both addition @@ -581,7 +581,8 @@ static void secp256k1_gej_add_ge(secp256k1_gej *r, const secp256k1_gej *a, const * and denominator of lambda; R and M represent the explicit * expressions x1^2 + x2^2 + x1x2 and y1 + y2. */ secp256k1_fe_sqr(&n, &m_alt); /* n = Malt^2 (1) */ - secp256k1_fe_mul(&q, &n, &t); /* q = Q = T*Malt^2 (1) */ + secp256k1_fe_negate(&q, &t, 2); /* q = -T (3) */ + secp256k1_fe_mul(&q, &q, &n); /* q = Q = -T*Malt^2 (1) */ /* These two lines use the observation that either M == Malt or M == 0, * so M^3 * Malt is either Malt^4 (which is computed by squaring), or * zero (which is "computed" by cmov). So the cost is one squaring @@ -589,21 +590,16 @@ static void secp256k1_gej_add_ge(secp256k1_gej *r, const secp256k1_gej *a, const secp256k1_fe_sqr(&n, &n); secp256k1_fe_cmov(&n, &m, degenerate); /* n = M^3 * Malt (2) */ secp256k1_fe_sqr(&t, &rr_alt); /* t = Ralt^2 (1) */ - secp256k1_fe_mul(&r->z, &a->z, &m_alt); /* r->z = Malt*Z (1) */ + secp256k1_fe_mul(&r->z, &a->z, &m_alt); /* r->z = Z3 = Malt*Z (1) */ infinity = secp256k1_fe_normalizes_to_zero(&r->z) & ~a->infinity; - secp256k1_fe_mul_int(&r->z, 2); /* r->z = Z3 = 2*Malt*Z (2) */ - secp256k1_fe_negate(&q, &q, 1); /* q = -Q (2) */ - secp256k1_fe_add(&t, &q); /* t = Ralt^2-Q (3) */ - secp256k1_fe_normalize_weak(&t); - r->x = t; /* r->x = Ralt^2-Q (1) */ - secp256k1_fe_mul_int(&t, 2); /* t = 2*x3 (2) */ - secp256k1_fe_add(&t, &q); /* t = 2*x3 - Q: (4) */ - secp256k1_fe_mul(&t, &t, &rr_alt); /* t = Ralt*(2*x3 - Q) (1) */ - secp256k1_fe_add(&t, &n); /* t = Ralt*(2*x3 - Q) + M^3*Malt (3) */ - secp256k1_fe_negate(&r->y, &t, 3); /* r->y = Ralt*(Q - 2x3) - M^3*Malt (4) */ - secp256k1_fe_normalize_weak(&r->y); - secp256k1_fe_mul_int(&r->x, 4); /* r->x = X3 = 4*(Ralt^2-Q) */ - secp256k1_fe_mul_int(&r->y, 4); /* r->y = Y3 = 4*Ralt*(Q - 2x3) - 4*M^3*Malt (4) */ + secp256k1_fe_add(&t, &q); /* t = Ralt^2 + Q (2) */ + r->x = t; /* r->x = X3 = Ralt^2 + Q (2) */ + secp256k1_fe_mul_int(&t, 2); /* t = 2*X3 (4) */ + secp256k1_fe_add(&t, &q); /* t = 2*X3 + Q (5) */ + secp256k1_fe_mul(&t, &t, &rr_alt); /* t = Ralt*(2*X3 + Q) (1) */ + secp256k1_fe_add(&t, &n); /* t = Ralt*(2*X3 + Q) + M^3*Malt (3) */ + secp256k1_fe_negate(&r->y, &t, 3); /* r->y = -(Ralt*(2*X3 + Q) + M^3*Malt) (4) */ + secp256k1_fe_half(&r->y); /* r->y = Y3 = -(Ralt*(2*X3 + Q) + M^3*Malt)/2 (3) */ /** In case a->infinity == 1, replace r with (b->x, b->y, 1). */ secp256k1_fe_cmov(&r->x, &b->x, a->infinity); From 9cc5c257eddc2d7614985be60bee29cf2bec65fb Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Tue, 21 Dec 2021 20:39:55 -0500 Subject: [PATCH 14/62] Add test for secp256k1_fe_half --- src/tests.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/tests.c b/src/tests.c index 8b3ac8140..155c1c7b3 100644 --- a/src/tests.c +++ b/src/tests.c @@ -2548,6 +2548,14 @@ void run_field_misc(void) { secp256k1_fe_add(&q, &x); CHECK(check_fe_equal(&y, &z)); CHECK(check_fe_equal(&q, &y)); + /* Check secp256k1_fe_half. */ + z = x; + secp256k1_fe_half(&z); + secp256k1_fe_add(&z, &z); + CHECK(check_fe_equal(&x, &z)); + secp256k1_fe_add(&z, &z); + secp256k1_fe_half(&z); + CHECK(check_fe_equal(&x, &z)); } } From 2cbb4b1a424d9dee12a4e11f0479410b7e4cc930 Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Tue, 21 Dec 2021 20:47:06 -0500 Subject: [PATCH 15/62] Run more iterations of run_field_misc At count=64, this makes the test take around 1% of the total time. --- src/tests.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/tests.c b/src/tests.c index 155c1c7b3..9da95ca60 100644 --- a/src/tests.c +++ b/src/tests.c @@ -2478,9 +2478,13 @@ void run_field_misc(void) { secp256k1_fe q; secp256k1_fe fe5 = SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 5); int i, j; - for (i = 0; i < 5*count; i++) { + for (i = 0; i < 1000 * count; i++) { secp256k1_fe_storage xs, ys, zs; - random_fe(&x); + if (i & 1) { + random_fe(&x); + } else { + random_fe_test(&x); + } random_fe_non_zero(&y); /* Test the fe equality and comparison operations. */ CHECK(secp256k1_fe_cmp_var(&x, &x) == 0); From 557b31fac36529948709d4bfcc00ad3acb7e83b9 Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Wed, 22 Dec 2021 19:39:22 -0500 Subject: [PATCH 16/62] Doubling formula using fe_half --- src/group_impl.h | 57 +++++++++++++++++++++++------------------------- 1 file changed, 27 insertions(+), 30 deletions(-) diff --git a/src/group_impl.h b/src/group_impl.h index d59beaeb2..ca9fc3837 100644 --- a/src/group_impl.h +++ b/src/group_impl.h @@ -271,37 +271,36 @@ static int secp256k1_ge_is_valid_var(const secp256k1_ge *a) { } static SECP256K1_INLINE 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 - * https://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-0.html#doubling-dbl-2009-l - * which trades a multiply for a square, but in practice this is actually slower, - * mainly because it requires more normalizations. - */ - secp256k1_fe t1,t2,t3,t4; + secp256k1_fe l, s, t, q; r->infinity = a->infinity; - 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); - secp256k1_fe_mul_int(&t1, 3); /* T1 = 3*X^2 (3) */ - secp256k1_fe_sqr(&t2, &t1); /* T2 = 9*X^4 (1) */ - secp256k1_fe_sqr(&t3, &a->y); - secp256k1_fe_mul_int(&t3, 2); /* T3 = 2*Y^2 (2) */ - secp256k1_fe_sqr(&t4, &t3); - secp256k1_fe_mul_int(&t4, 2); /* T4 = 8*Y^4 (2) */ - secp256k1_fe_mul(&t3, &t3, &a->x); /* T3 = 2*X*Y^2 (1) */ - r->x = t3; - secp256k1_fe_mul_int(&r->x, 4); /* X' = 8*X*Y^2 (4) */ - secp256k1_fe_negate(&r->x, &r->x, 4); /* X' = -8*X*Y^2 (5) */ - secp256k1_fe_add(&r->x, &t2); /* X' = 9*X^4 - 8*X*Y^2 (6) */ - secp256k1_fe_negate(&t2, &t2, 1); /* T2 = -9*X^4 (2) */ - secp256k1_fe_mul_int(&t3, 6); /* T3 = 12*X*Y^2 (6) */ - secp256k1_fe_add(&t3, &t2); /* T3 = 12*X*Y^2 - 9*X^4 (8) */ - secp256k1_fe_mul(&r->y, &t1, &t3); /* Y' = 36*X^3*Y^2 - 27*X^6 (1) */ - secp256k1_fe_negate(&t2, &t4, 2); /* T2 = -8*Y^4 (3) */ - secp256k1_fe_add(&r->y, &t2); /* Y' = 36*X^3*Y^2 - 27*X^6 - 8*Y^4 (4) */ + /* Formula used: + * L = (3/2) * X1^2 + * S = Y1^2 + * T = X1*S + * X3 = L^2 - 2*T + * Y3 = L*(T - X3) - S^2 + * Z3 = Y1*Z1 + */ + + secp256k1_fe_mul(&r->z, &a->z, &a->y); /* Z3 = Y1*Z1 (1) */ + secp256k1_fe_sqr(&l, &a->x); /* L = X1^2 (1) */ + secp256k1_fe_mul_int(&l, 3); /* L = 3*X1^2 (3) */ + secp256k1_fe_half(&l); /* L = 3/2*X1^2 (2) */ + secp256k1_fe_sqr(&s, &a->y); /* S = Y1^2 (1) */ + secp256k1_fe_mul(&t, &a->x, &s); /* T = X1*S (1) */ + q = t; + secp256k1_fe_add(&q, &t); /* Q = 2*T (2) */ + secp256k1_fe_negate(&r->x, &q, 2); /* X3 = -2*T (3) */ + secp256k1_fe_sqr(&q, &l); /* Q = L^2 (1) */ + secp256k1_fe_add(&r->x, &q); /* X3 = L^2 - 2*T (4) */ + secp256k1_fe_negate(&q, &r->x, 4); /* Q = -X3 (5) */ + secp256k1_fe_add(&q, &t); /* Q = T-X3 (6) */ + secp256k1_fe_mul(&q, &q, &l); /* Q = L*(T-X3) (1) */ + secp256k1_fe_sqr(&s, &s); + secp256k1_fe_negate(&r->y, &s, 1); /* Y3 = -S^2 (2) */ + secp256k1_fe_add(&r->y, &q); /* Y3 = L*(T-X3) - S^2 (3) */ } static void secp256k1_gej_double_var(secp256k1_gej *r, const secp256k1_gej *a, secp256k1_fe *rzr) { @@ -325,8 +324,6 @@ static void secp256k1_gej_double_var(secp256k1_gej *r, const secp256k1_gej *a, s if (rzr != NULL) { *rzr = a->y; - secp256k1_fe_normalize_weak(rzr); - secp256k1_fe_mul_int(rzr, 2); } secp256k1_gej_double(r, a); From 4eb8b932ff8e64f8de3ae8ecfebeab1e84ca420e Mon Sep 17 00:00:00 2001 From: Peter Dettman Date: Thu, 23 Dec 2021 14:18:46 +0700 Subject: [PATCH 17/62] Further improve doubling formula using fe_half --- src/group_impl.h | 40 ++++++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/src/group_impl.h b/src/group_impl.h index ca9fc3837..b19b02a01 100644 --- a/src/group_impl.h +++ b/src/group_impl.h @@ -271,36 +271,35 @@ static int secp256k1_ge_is_valid_var(const secp256k1_ge *a) { } static SECP256K1_INLINE void secp256k1_gej_double(secp256k1_gej *r, const secp256k1_gej *a) { - secp256k1_fe l, s, t, q; + /* Operations: 3 mul, 4 sqr, 8 add/half/mul_int/negate */ + secp256k1_fe l, s, t; r->infinity = a->infinity; /* Formula used: * L = (3/2) * X1^2 * S = Y1^2 - * T = X1*S - * X3 = L^2 - 2*T - * Y3 = L*(T - X3) - S^2 + * T = -X1*S + * X3 = L^2 + 2*T + * Y3 = -(L*(X3 + T) + S^2) * Z3 = Y1*Z1 */ secp256k1_fe_mul(&r->z, &a->z, &a->y); /* Z3 = Y1*Z1 (1) */ - secp256k1_fe_sqr(&l, &a->x); /* L = X1^2 (1) */ - secp256k1_fe_mul_int(&l, 3); /* L = 3*X1^2 (3) */ - secp256k1_fe_half(&l); /* L = 3/2*X1^2 (2) */ - secp256k1_fe_sqr(&s, &a->y); /* S = Y1^2 (1) */ - secp256k1_fe_mul(&t, &a->x, &s); /* T = X1*S (1) */ - q = t; - secp256k1_fe_add(&q, &t); /* Q = 2*T (2) */ - secp256k1_fe_negate(&r->x, &q, 2); /* X3 = -2*T (3) */ - secp256k1_fe_sqr(&q, &l); /* Q = L^2 (1) */ - secp256k1_fe_add(&r->x, &q); /* X3 = L^2 - 2*T (4) */ - secp256k1_fe_negate(&q, &r->x, 4); /* Q = -X3 (5) */ - secp256k1_fe_add(&q, &t); /* Q = T-X3 (6) */ - secp256k1_fe_mul(&q, &q, &l); /* Q = L*(T-X3) (1) */ - secp256k1_fe_sqr(&s, &s); - secp256k1_fe_negate(&r->y, &s, 1); /* Y3 = -S^2 (2) */ - secp256k1_fe_add(&r->y, &q); /* Y3 = L*(T-X3) - S^2 (3) */ + secp256k1_fe_sqr(&s, &a->y); /* S = Y1^2 (1) */ + secp256k1_fe_sqr(&l, &a->x); /* L = X1^2 (1) */ + secp256k1_fe_mul_int(&l, 3); /* L = 3*X1^2 (3) */ + secp256k1_fe_half(&l); /* L = 3/2*X1^2 (2) */ + secp256k1_fe_negate(&t, &s, 1); /* T = -S (2) */ + secp256k1_fe_mul(&t, &t, &a->x); /* T = -X1*S (1) */ + secp256k1_fe_sqr(&r->x, &l); /* X3 = L^2 (1) */ + secp256k1_fe_add(&r->x, &t); /* X3 = L^2 + T (2) */ + secp256k1_fe_add(&r->x, &t); /* X3 = L^2 + 2*T (3) */ + secp256k1_fe_sqr(&s, &s); /* S' = S^2 (1) */ + secp256k1_fe_add(&t, &r->x); /* T' = X3 + T (4) */ + secp256k1_fe_mul(&r->y, &t, &l); /* Y3 = L*(X3 + T) (1) */ + secp256k1_fe_add(&r->y, &s); /* Y3 = L*(X3 + T) + S^2 (2) */ + secp256k1_fe_negate(&r->y, &r->y, 2); /* Y3 = -(L*(X3 + T) + S^2) (3) */ } static void secp256k1_gej_double_var(secp256k1_gej *r, const secp256k1_gej *a, secp256k1_fe *rzr) { @@ -324,6 +323,7 @@ static void secp256k1_gej_double_var(secp256k1_gej *r, const secp256k1_gej *a, s if (rzr != NULL) { *rzr = a->y; + secp256k1_fe_normalize_weak(rzr); } secp256k1_gej_double(r, a); From b54d843eac905993eba2314a89a2d7f155445eaa Mon Sep 17 00:00:00 2001 From: Tim Ruffing Date: Mon, 31 Jan 2022 15:17:46 +0100 Subject: [PATCH 18/62] sage: Fix printing of errors Python 3 often returns iterable map objects where Python 2 returned list. We can just them down to lists explicitly. Overlooked in 13c88efed0005eb6745a222963ee74564054eafb. --- sage/group_prover.sage | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sage/group_prover.sage b/sage/group_prover.sage index 999da52a8..a35d17b07 100644 --- a/sage/group_prover.sage +++ b/sage/group_prover.sage @@ -254,7 +254,7 @@ def prove_zero(R, exprs, assume): """Check whether all of the passed expressions are provably zero, given assumptions""" r, e = prove_nonzero(R, dict(map(lambda x: (fastfrac(R, x.bot, 1), exprs[x]), exprs)), assume) if not r: - return (False, map(lambda x: "Possibly zero denominator: %s" % x, e)) + return (False, list(map(lambda x: "Possibly zero denominator: %s" % x, e))) zero = R.ideal(list(map(numerator, assume.zero))) nonzero = prod(x for x in assume.nonzero) expl = [] From d64bb5d4f3fbd48b570d847c9389b9cf8f3d9abc Mon Sep 17 00:00:00 2001 From: Peter Dettman Date: Thu, 23 Dec 2021 16:41:10 +0700 Subject: [PATCH 19/62] Add fe_half tests for worst-case inputs - Add field method _fe_get_bounds --- src/field.h | 4 ++++ src/field_10x26_impl.h | 20 +++++++++++++++++ src/field_5x52_impl.h | 15 +++++++++++++ src/tests.c | 50 ++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 89 insertions(+) diff --git a/src/field.h b/src/field.h index a52e056dd..2584a494e 100644 --- a/src/field.h +++ b/src/field.h @@ -135,4 +135,8 @@ static void secp256k1_fe_cmov(secp256k1_fe *r, const secp256k1_fe *a, int flag); * The output is not guaranteed to be normalized, regardless of the input. */ static void secp256k1_fe_half(secp256k1_fe *r); +/** Sets each limb of 'r' to its upper bound at magnitude 'm'. The output will also have its + * magnitude set to 'm' and is normalized if (and only if) 'm' is zero. */ +static void secp256k1_fe_get_bounds(secp256k1_fe *r, int m); + #endif /* SECP256K1_FIELD_H */ diff --git a/src/field_10x26_impl.h b/src/field_10x26_impl.h index 627cd5b57..21742bf6e 100644 --- a/src/field_10x26_impl.h +++ b/src/field_10x26_impl.h @@ -49,6 +49,26 @@ static void secp256k1_fe_verify(const secp256k1_fe *a) { } #endif +static void secp256k1_fe_get_bounds(secp256k1_fe *r, int m) { + VERIFY_CHECK(m >= 0); + VERIFY_CHECK(m <= 2048); + r->n[0] = 0x3FFFFFFUL * 2 * m; + r->n[1] = 0x3FFFFFFUL * 2 * m; + r->n[2] = 0x3FFFFFFUL * 2 * m; + r->n[3] = 0x3FFFFFFUL * 2 * m; + r->n[4] = 0x3FFFFFFUL * 2 * m; + r->n[5] = 0x3FFFFFFUL * 2 * m; + r->n[6] = 0x3FFFFFFUL * 2 * m; + r->n[7] = 0x3FFFFFFUL * 2 * m; + r->n[8] = 0x3FFFFFFUL * 2 * m; + r->n[9] = 0x03FFFFFUL * 2 * m; +#ifdef VERIFY + r->magnitude = m; + r->normalized = (m == 0); + secp256k1_fe_verify(r); +#endif +} + static void secp256k1_fe_normalize(secp256k1_fe *r) { uint32_t t0 = r->n[0], t1 = r->n[1], t2 = r->n[2], t3 = r->n[3], t4 = r->n[4], t5 = r->n[5], t6 = r->n[6], t7 = r->n[7], t8 = r->n[8], t9 = r->n[9]; diff --git a/src/field_5x52_impl.h b/src/field_5x52_impl.h index 6e6bb3c64..6bd202f58 100644 --- a/src/field_5x52_impl.h +++ b/src/field_5x52_impl.h @@ -58,6 +58,21 @@ static void secp256k1_fe_verify(const secp256k1_fe *a) { } #endif +static void secp256k1_fe_get_bounds(secp256k1_fe *r, int m) { + VERIFY_CHECK(m >= 0); + VERIFY_CHECK(m <= 2048); + r->n[0] = 0xFFFFFFFFFFFFFULL * 2 * m; + r->n[1] = 0xFFFFFFFFFFFFFULL * 2 * m; + r->n[2] = 0xFFFFFFFFFFFFFULL * 2 * m; + r->n[3] = 0xFFFFFFFFFFFFFULL * 2 * m; + r->n[4] = 0x0FFFFFFFFFFFFULL * 2 * m; +#ifdef VERIFY + r->magnitude = m; + r->normalized = (m == 0); + secp256k1_fe_verify(r); +#endif +} + static void secp256k1_fe_normalize(secp256k1_fe *r) { uint64_t t0 = r->n[0], t1 = r->n[1], t2 = r->n[2], t3 = r->n[3], t4 = r->n[4]; diff --git a/src/tests.c b/src/tests.c index 9da95ca60..26063a815 100644 --- a/src/tests.c +++ b/src/tests.c @@ -2471,6 +2471,55 @@ int fe_identical(const secp256k1_fe *a, const secp256k1_fe *b) { return ret; } +void run_field_half(void) { + secp256k1_fe t, u; + int m; + + /* Check magnitude 0 input */ + secp256k1_fe_get_bounds(&t, 0); + secp256k1_fe_half(&t); +#ifdef VERIFY + CHECK(t.magnitude == 1); + CHECK(t.normalized == 0); +#endif + CHECK(secp256k1_fe_normalizes_to_zero(&t)); + + /* Check non-zero magnitudes in the supported range */ + for (m = 1; m < 32; m++) { + /* Check max-value input */ + secp256k1_fe_get_bounds(&t, m); + + u = t; + secp256k1_fe_half(&u); +#ifdef VERIFY + CHECK(u.magnitude == (m >> 1) + 1); + CHECK(u.normalized == 0); +#endif + secp256k1_fe_normalize_weak(&u); + secp256k1_fe_add(&u, &u); + CHECK(check_fe_equal(&t, &u)); + + /* Check worst-case input: ensure the LSB is 1 so that P will be added, + * which will also cause all carries to be 1, since all limbs that can + * generate a carry are initially even and all limbs of P are odd in + * every existing field implementation. */ + secp256k1_fe_get_bounds(&t, m); + CHECK(t.n[0] > 0); + CHECK((t.n[0] & 1) == 0); + --t.n[0]; + + u = t; + secp256k1_fe_half(&u); +#ifdef VERIFY + CHECK(u.magnitude == (m >> 1) + 1); + CHECK(u.normalized == 0); +#endif + secp256k1_fe_normalize_weak(&u); + secp256k1_fe_add(&u, &u); + CHECK(check_fe_equal(&t, &u)); + } +} + void run_field_misc(void) { secp256k1_fe x; secp256k1_fe y; @@ -6924,6 +6973,7 @@ int main(int argc, char **argv) { run_scalar_tests(); /* field tests */ + run_field_half(); run_field_misc(); run_field_convert(); run_fe_mul(); From e848c3799c4f31367c3ed98d17e3b7de504d4c6e Mon Sep 17 00:00:00 2001 From: Peter Dettman Date: Mon, 27 Dec 2021 14:02:13 +0700 Subject: [PATCH 20/62] Update sage files for new formulae - formula_secp256k1_gej_double_var - formula_secp256k1_gej_add_ge --- sage/prove_group_implementations.sage | 41 +++++++++++---------------- 1 file changed, 17 insertions(+), 24 deletions(-) diff --git a/sage/prove_group_implementations.sage b/sage/prove_group_implementations.sage index a97e732f7..eb5fe7cfe 100644 --- a/sage/prove_group_implementations.sage +++ b/sage/prove_group_implementations.sage @@ -8,25 +8,20 @@ load("weierstrass_prover.sage") def formula_secp256k1_gej_double_var(a): """libsecp256k1's secp256k1_gej_double_var, used by various addition functions""" rz = a.Z * a.Y - rz = rz * 2 - t1 = a.X^2 - t1 = t1 * 3 - t2 = t1^2 - t3 = a.Y^2 - t3 = t3 * 2 - t4 = t3^2 - t4 = t4 * 2 - t3 = t3 * a.X - rx = t3 - rx = rx * 4 - rx = -rx - rx = rx + t2 - t2 = -t2 - t3 = t3 * 6 - t3 = t3 + t2 - ry = t1 * t3 - t2 = -t4 - ry = ry + t2 + s = a.Y^2 + l = a.X^2 + l = l * 3 + l = l / 2 + t = -s + t = t * a.X + rx = l^2 + rx = rx + t + rx = rx + t + s = s^2 + t = t + rx + ry = t * l + ry = ry + s + ry = -ry return jacobianpoint(rx, ry, rz) def formula_secp256k1_gej_add_var(branch, a, b): @@ -197,7 +192,8 @@ def formula_secp256k1_gej_add_ge(branch, a, b): rr_alt = rr m_alt = m n = m_alt^2 - q = n * t + q = -t + q = q * n n = n^2 if degenerate: n = m @@ -210,8 +206,6 @@ def formula_secp256k1_gej_add_ge(branch, a, b): zeroes.update({rz : 'r.z=0'}) else: nonzeroes.update({rz : 'r.z!=0'}) - rz = rz * 2 - q = -q t = t + q rx = t t = t * 2 @@ -219,8 +213,7 @@ def formula_secp256k1_gej_add_ge(branch, a, b): t = t * rr_alt t = t + n ry = -t - rx = rx * 4 - ry = ry * 4 + ry = ry / 2 if a_infinity: rx = b.X ry = b.Y From e0db3f8a25c20a247a0b49ca00dd6c973b6a16e0 Mon Sep 17 00:00:00 2001 From: laanwj <126646+laanwj@users.noreply.github.com> Date: Wed, 2 Feb 2022 16:46:13 +0100 Subject: [PATCH 21/62] build: Replace use of deprecated autoconf macro AC_PROG_CC_C89 According to [autoconf 2.70](https://www.gnu.org/software/autoconf/manual/autoconf-2.70/html_node/Obsolete-Macros.html) documentation, the `AC_PROG_CC_C89' is replaced by `AC_PROG_CC`, which defines the same variable `ac_cv_prog_cc_c89`. Avoids the following message: ``` configure.ac:23: warning: The macro `AC_PROG_CC_C89' is obsolete. ``` Also, remove deprecated `AM_PROG_CC_C_O`. --- configure.ac | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/configure.ac b/configure.ac index 92e497df8..6f1cd623c 100644 --- a/configure.ac +++ b/configure.ac @@ -38,8 +38,7 @@ AC_PATH_TOOL(AR, ar) AC_PATH_TOOL(RANLIB, ranlib) AC_PATH_TOOL(STRIP, strip) -AM_PROG_CC_C_O -AC_PROG_CC_C89 +AC_PROG_CC if test x"$ac_cv_prog_cc_c89" = x"no"; then AC_MSG_ERROR([c89 compiler support required]) fi From d9396a56da13df97827e193f636a7593fb4d7864 Mon Sep 17 00:00:00 2001 From: Tim Ruffing Date: Fri, 4 Feb 2022 10:44:50 +0100 Subject: [PATCH 22/62] ci: Attempt to make macOS builds more reliable The macOS CI tasks often error fail when doing `brew update` with git fetch errors: ``` remote: fatal: packfile /data/repositories/b/nw/b6/07/5c/123272362/network.git/objects/pack/pack-2139bd07361b62a358e380a0e7d58ec35593d191.pack cannot be accessed fatal: protocol error: bad pack header Error: Fetching /usr/local/Homebrew/Library/Taps/homebrew/homebrew-core failed! ``` Superficially this seems to be a problem on the GitHub server because the message shows a "remote" error. But it seems we're the only one in the world running into this specific issue when doing `brew update`, so it's more likely that the something else is the culprit, and this error message is just a symptom. This commit replaces `brew update` with a complete reinstallation of brew. This is essentially a shot in the dark but it's worth a try, and I doubt it's significantly more expensive. If that won't work, we may consider simply retrying `brew update` a few times. --- .cirrus.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.cirrus.yml b/.cirrus.yml index ffbd82071..699811954 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -135,7 +135,9 @@ task: ## - rm /tmp/.com.apple.dt.CommandLineTools.installondemand.in-progress ## brew_valgrind_pre_script: - - brew update + # Reinstall brew. We could do `brew update` instead but that often fails. + - /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/uninstall.sh)" + - /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" - brew config - brew tap LouisBrunner/valgrind # Fetch valgrind source but don't build it yet. From eae75869cfbbbcb69d881bc5d313bd94c6155655 Mon Sep 17 00:00:00 2001 From: Tim Ruffing Date: Mon, 31 Jan 2022 17:53:04 +0100 Subject: [PATCH 23/62] sage: Exit with non-zero status in case of failures --- sage/group_prover.sage | 13 ++++++------- sage/prove_group_implementations.sage | 23 +++++++++++++---------- sage/weierstrass_prover.sage | 11 ++++++++++- 3 files changed, 29 insertions(+), 18 deletions(-) diff --git a/sage/group_prover.sage b/sage/group_prover.sage index a35d17b07..4f8fce966 100644 --- a/sage/group_prover.sage +++ b/sage/group_prover.sage @@ -299,22 +299,21 @@ def check_symbolic(R, assumeLaw, assumeAssert, assumeBranch, require): if conflicts(R, assume): # This formula does not apply - return None + return (True, None) describe = describe_extra(R, assumeLaw + assumeBranch, assumeAssert) + if describe != "": + describe = " (assuming " + describe + ")" ok, msg = prove_zero(R, require.zero, assume) if not ok: - return "FAIL, %s fails (assuming %s)" % (str(msg), describe) + return (False, "FAIL, %s fails%s" % (str(msg), describe)) res, expl = prove_nonzero(R, require.nonzero, assume) if not res: - return "FAIL, %s fails (assuming %s)" % (str(expl), describe) + return (False, "FAIL, %s fails%s" % (str(expl), describe)) - if describe != "": - return "OK (assuming %s)" % describe - else: - return "OK" + return (True, "OK%s" % describe) def concrete_verify(c): diff --git a/sage/prove_group_implementations.sage b/sage/prove_group_implementations.sage index a97e732f7..49af9b1ce 100644 --- a/sage/prove_group_implementations.sage +++ b/sage/prove_group_implementations.sage @@ -292,15 +292,18 @@ def formula_secp256k1_gej_add_ge_old(branch, a, b): return (constraints(zero={b.Z - 1 : 'b.z=1', b.Infinity : 'b_finite'}), constraints(zero=zero, nonzero=nonzero), jacobianpoint(rx, ry, rz)) if __name__ == "__main__": - check_symbolic_jacobian_weierstrass("secp256k1_gej_add_var", 0, 7, 5, formula_secp256k1_gej_add_var) - check_symbolic_jacobian_weierstrass("secp256k1_gej_add_ge_var", 0, 7, 5, formula_secp256k1_gej_add_ge_var) - check_symbolic_jacobian_weierstrass("secp256k1_gej_add_zinv_var", 0, 7, 5, formula_secp256k1_gej_add_zinv_var) - check_symbolic_jacobian_weierstrass("secp256k1_gej_add_ge", 0, 7, 16, formula_secp256k1_gej_add_ge) - check_symbolic_jacobian_weierstrass("secp256k1_gej_add_ge_old [should fail]", 0, 7, 4, formula_secp256k1_gej_add_ge_old) + success = True + success = success & check_symbolic_jacobian_weierstrass("secp256k1_gej_add_var", 0, 7, 5, formula_secp256k1_gej_add_var) + success = success & check_symbolic_jacobian_weierstrass("secp256k1_gej_add_ge_var", 0, 7, 5, formula_secp256k1_gej_add_ge_var) + success = success & check_symbolic_jacobian_weierstrass("secp256k1_gej_add_zinv_var", 0, 7, 5, formula_secp256k1_gej_add_zinv_var) + success = success & check_symbolic_jacobian_weierstrass("secp256k1_gej_add_ge", 0, 7, 16, formula_secp256k1_gej_add_ge) + success = success & (not check_symbolic_jacobian_weierstrass("secp256k1_gej_add_ge_old [should fail]", 0, 7, 4, formula_secp256k1_gej_add_ge_old)) if len(sys.argv) >= 2 and sys.argv[1] == "--exhaustive": - check_exhaustive_jacobian_weierstrass("secp256k1_gej_add_var", 0, 7, 5, formula_secp256k1_gej_add_var, 43) - check_exhaustive_jacobian_weierstrass("secp256k1_gej_add_ge_var", 0, 7, 5, formula_secp256k1_gej_add_ge_var, 43) - check_exhaustive_jacobian_weierstrass("secp256k1_gej_add_zinv_var", 0, 7, 5, formula_secp256k1_gej_add_zinv_var, 43) - check_exhaustive_jacobian_weierstrass("secp256k1_gej_add_ge", 0, 7, 16, formula_secp256k1_gej_add_ge, 43) - check_exhaustive_jacobian_weierstrass("secp256k1_gej_add_ge_old [should fail]", 0, 7, 4, formula_secp256k1_gej_add_ge_old, 43) + success = success & check_exhaustive_jacobian_weierstrass("secp256k1_gej_add_var", 0, 7, 5, formula_secp256k1_gej_add_var, 43) + success = success & check_exhaustive_jacobian_weierstrass("secp256k1_gej_add_ge_var", 0, 7, 5, formula_secp256k1_gej_add_ge_var, 43) + success = success & check_exhaustive_jacobian_weierstrass("secp256k1_gej_add_zinv_var", 0, 7, 5, formula_secp256k1_gej_add_zinv_var, 43) + success = success & check_exhaustive_jacobian_weierstrass("secp256k1_gej_add_ge", 0, 7, 16, formula_secp256k1_gej_add_ge, 43) + success = success & (not check_exhaustive_jacobian_weierstrass("secp256k1_gej_add_ge_old [should fail]", 0, 7, 4, formula_secp256k1_gej_add_ge_old, 43)) + + sys.exit(int(not success)) diff --git a/sage/weierstrass_prover.sage b/sage/weierstrass_prover.sage index b770c6daf..21e69b3ac 100644 --- a/sage/weierstrass_prover.sage +++ b/sage/weierstrass_prover.sage @@ -184,6 +184,7 @@ def check_exhaustive_jacobian_weierstrass(name, A, B, branches, formula, p): if r: points.append(point) + ret = True for za in range(1, p): for zb in range(1, p): for pa in points: @@ -211,8 +212,11 @@ def check_exhaustive_jacobian_weierstrass(name, A, B, branches, formula, p): match = True r, e = concrete_verify(require) if not r: + ret = False print(" failure in branch %i for (%s,%s,%s,%s) + (%s,%s,%s,%s) = (%s,%s,%s,%s): %s" % (branch, pA.X, pA.Y, pA.Z, pA.Infinity, pB.X, pB.Y, pB.Z, pB.Infinity, pC.X, pC.Y, pC.Z, pC.Infinity, e)) + print() + return ret def check_symbolic_function(R, assumeAssert, assumeBranch, f, A, B, pa, pb, pA, pB, pC): @@ -244,6 +248,7 @@ def check_symbolic_jacobian_weierstrass(name, A, B, branches, formula): print("Formula " + name + ":") count = 0 + ret = True for branch in range(branches): assumeFormula, assumeBranch, pC = formula(branch, pA, pB) pC.X = lift(pC.X) @@ -252,7 +257,10 @@ def check_symbolic_jacobian_weierstrass(name, A, B, branches, formula): pC.Infinity = lift(pC.Infinity) for key in laws_jacobian_weierstrass: - res[key].append((check_symbolic_function(R, assumeFormula, assumeBranch, laws_jacobian_weierstrass[key], A, B, pa, pb, pA, pB, pC), branch)) + success, msg = check_symbolic_function(R, assumeFormula, assumeBranch, laws_jacobian_weierstrass[key], A, B, pa, pb, pA, pB, pC) + if not success: + ret = False + res[key].append((msg, branch)) for key in res: print(" %s:" % key) @@ -262,3 +270,4 @@ def check_symbolic_jacobian_weierstrass(name, A, B, branches, formula): print(" branch %i: %s" % (x[1], x[0])) print() + return ret From 77cfa98dbc40f9494048447b8a302867235300da Mon Sep 17 00:00:00 2001 From: Tim Ruffing Date: Thu, 3 Feb 2022 12:46:42 +0100 Subject: [PATCH 24/62] sage: Normalize sign of polynomial factors in prover The prover, when run on recent sage versions, failed to prove some of its goals due to a change in sage. This commit adapts our code accordingly. The prover passes again after this commit. --- sage/group_prover.sage | 44 ++++++++++++++++++++++++++++++++---------- 1 file changed, 34 insertions(+), 10 deletions(-) diff --git a/sage/group_prover.sage b/sage/group_prover.sage index 4f8fce966..706b859b2 100644 --- a/sage/group_prover.sage +++ b/sage/group_prover.sage @@ -177,6 +177,30 @@ class constraints: def __repr__(self): return "%s" % self +def normalize_factor(p): + """Normalizes the sign of primitive polynomials (as returned by factor()) + + This function ensures that the polynomial has a positive leading coefficient. + + This is necessary because recent sage versions (starting with v9.3 or v9.4, + we don't know) are inconsistent about the placement of the minus sign in + polynomial factorizations: + ``` + sage: R. = PolynomialRing(QQ,8,order='invlex') + sage: R((-2 * (bx - ax)) ^ 1).factor() + (-2) * (bx - ax) + sage: R((-2 * (bx - ax)) ^ 2).factor() + (4) * (-bx + ax)^2 + sage: R((-2 * (bx - ax)) ^ 3).factor() + (8) * (-bx + ax)^3 + ``` + """ + # Assert p is not 0 and that its non-zero coeffients are coprime. + # (We could just work with the primitive part p/p.content() but we want to be + # aware if factor() does not return a primitive part in future sage versions.) + assert p.content() == 1 + # Ensure that the first non-zero coefficient is positive. + return p if p.lc() > 0 else -p def conflicts(R, con): """Check whether any of the passed non-zero assumptions is implied by the zero assumptions""" @@ -204,10 +228,10 @@ def get_nonzero_set(R, assume): nonzero = set() for nz in map(numerator, assume.nonzero): for (f,n) in nz.factor(): - nonzero.add(f) + nonzero.add(normalize_factor(f)) rnz = zero.reduce(nz) for (f,n) in rnz.factor(): - nonzero.add(f) + nonzero.add(normalize_factor(f)) return nonzero @@ -222,27 +246,27 @@ def prove_nonzero(R, exprs, assume): return (False, [exprs[expr]]) allexprs = reduce(lambda a,b: numerator(a)*numerator(b), exprs, 1) for (f, n) in allexprs.factor(): - if f not in nonzero: + if normalize_factor(f) not in nonzero: ok = False if ok: return (True, None) ok = True for (f, n) in zero.reduce(allexprs).factor(): - if f not in nonzero: + if normalize_factor(f) not in nonzero: ok = False if ok: return (True, None) ok = True for expr in exprs: for (f,n) in numerator(expr).factor(): - if f not in nonzero: + if normalize_factor(f) not in nonzero: ok = False if ok: return (True, None) ok = True for expr in exprs: for (f,n) in zero.reduce(numerator(expr)).factor(): - if f not in nonzero: + if normalize_factor(f) not in nonzero: expl.add(exprs[expr]) if expl: return (False, list(expl)) @@ -279,8 +303,8 @@ def describe_extra(R, assume, assumeExtra): if base not in zero: add = [] for (f, n) in numerator(base).factor(): - if f not in nonzero: - add += ["%s" % f] + if normalize_factor(f) not in nonzero: + add += ["%s" % normalize_factor(f)] if add: ret.add((" * ".join(add)) + " = 0 [%s]" % assumeExtra.zero[base]) # Iterate over the extra nonzero expressions @@ -288,8 +312,8 @@ def describe_extra(R, assume, assumeExtra): nzr = zeroextra.reduce(numerator(nz)) if nzr not in zeroextra: for (f,n) in nzr.factor(): - if zeroextra.reduce(f) not in nonzero: - ret.add("%s != 0" % zeroextra.reduce(f)) + if normalize_factor(zeroextra.reduce(f)) not in nonzero: + ret.add("%s != 0" % normalize_factor(zeroextra.reduce(f))) return ", ".join(x for x in ret) From d8d54859ed138a8ed9a8486d847155211c9f4a7d Mon Sep 17 00:00:00 2001 From: Tim Ruffing Date: Thu, 3 Feb 2022 12:58:55 +0100 Subject: [PATCH 25/62] ci: Run sage prover on CI --- .cirrus.yml | 7 +++++++ ci/linux-debian.Dockerfile | 3 ++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/.cirrus.yml b/.cirrus.yml index ffbd82071..727a73f37 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -322,3 +322,10 @@ task: test_script: - ./ci/cirrus.sh << : *CAT_LOGS + +task: + name: "sage prover" + << : *LINUX_CONTAINER + test_script: + - cd sage + - sage prove_group_implementations.sage diff --git a/ci/linux-debian.Dockerfile b/ci/linux-debian.Dockerfile index fdba12aa0..5cccbb556 100644 --- a/ci/linux-debian.Dockerfile +++ b/ci/linux-debian.Dockerfile @@ -19,7 +19,8 @@ RUN apt-get install --no-install-recommends --no-upgrade -y \ gcc-arm-linux-gnueabihf libc6-dev-armhf-cross libc6-dbg:armhf \ gcc-aarch64-linux-gnu libc6-dev-arm64-cross libc6-dbg:arm64 \ gcc-powerpc64le-linux-gnu libc6-dev-ppc64el-cross libc6-dbg:ppc64el \ - wine gcc-mingw-w64-x86-64 + wine gcc-mingw-w64-x86-64 \ + sagemath # Run a dummy command in wine to make it set up configuration RUN wine64-stable xcopy || true From ebb1beea7832207e5c8c5112d250fd216259ef41 Mon Sep 17 00:00:00 2001 From: Tim Ruffing Date: Fri, 4 Feb 2022 15:06:58 +0100 Subject: [PATCH 26/62] sage: Ensure that constraints are always fastfracs Even if they are constants created in the formula functions. We now lift integer constants to fastfracs. --- sage/group_prover.sage | 3 +++ sage/weierstrass_prover.sage | 2 ++ 2 files changed, 5 insertions(+) diff --git a/sage/group_prover.sage b/sage/group_prover.sage index 706b859b2..9305c215d 100644 --- a/sage/group_prover.sage +++ b/sage/group_prover.sage @@ -164,6 +164,9 @@ class constraints: def negate(self): return constraints(zero=self.nonzero, nonzero=self.zero) + def map(self, fun): + return constraints(zero={fun(k): v for k, v in self.zero.items()}, nonzero={fun(k): v for k, v in self.nonzero.items()}) + def __add__(self, other): zero = self.zero.copy() zero.update(other.zero) diff --git a/sage/weierstrass_prover.sage b/sage/weierstrass_prover.sage index 21e69b3ac..be9cfd4c7 100644 --- a/sage/weierstrass_prover.sage +++ b/sage/weierstrass_prover.sage @@ -251,6 +251,8 @@ def check_symbolic_jacobian_weierstrass(name, A, B, branches, formula): ret = True for branch in range(branches): assumeFormula, assumeBranch, pC = formula(branch, pA, pB) + assumeBranch = assumeBranch.map(lift) + assumeFormula = assumeFormula.map(lift) pC.X = lift(pC.X) pC.Y = lift(pC.Y) pC.Z = lift(pC.Z) From b1cb969e8ab9ad86fec3ce30cb732f4c42e015e0 Mon Sep 17 00:00:00 2001 From: Tim Ruffing Date: Tue, 8 Feb 2022 13:53:05 +0100 Subject: [PATCH 27/62] ci: Revert "Attempt to make macOS builds more reliable" This reverts commit d9396a56da13df97827e193f636a7593fb4d7864, which didn't have the desired effect. --- .cirrus.yml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/.cirrus.yml b/.cirrus.yml index b1ccb4d3d..727a73f37 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -135,9 +135,7 @@ task: ## - rm /tmp/.com.apple.dt.CommandLineTools.installondemand.in-progress ## brew_valgrind_pre_script: - # Reinstall brew. We could do `brew update` instead but that often fails. - - /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/uninstall.sh)" - - /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" + - brew update - brew config - brew tap LouisBrunner/valgrind # Fetch valgrind source but don't build it yet. From e51ad3b737a2f12605d346cf9c0e7eaabf6a39ec Mon Sep 17 00:00:00 2001 From: Tim Ruffing Date: Tue, 8 Feb 2022 14:09:58 +0100 Subject: [PATCH 28/62] ci: Retry `brew update` a few times to avoid random failures --- .cirrus.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.cirrus.yml b/.cirrus.yml index 727a73f37..a352406e2 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -135,7 +135,8 @@ task: ## - rm /tmp/.com.apple.dt.CommandLineTools.installondemand.in-progress ## brew_valgrind_pre_script: - - brew update + # Retry a few times because this tends to fail randomly. + - for i in {1..5}; do brew update && break || sleep 15; done - brew config - brew tap LouisBrunner/valgrind # Fetch valgrind source but don't build it yet. From c881dd49bdb865d9e455d504b90aebf77e807e85 Mon Sep 17 00:00:00 2001 From: Tim Ruffing Date: Fri, 3 Dec 2021 12:58:49 +0100 Subject: [PATCH 29/62] ecdh: Add test computing shared_secret=basepoint with random inputs --- src/modules/ecdh/tests_impl.h | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/src/modules/ecdh/tests_impl.h b/src/modules/ecdh/tests_impl.h index be07447a4..5053194d7 100644 --- a/src/modules/ecdh/tests_impl.h +++ b/src/modules/ecdh/tests_impl.h @@ -123,10 +123,43 @@ void test_bad_scalar(void) { CHECK(secp256k1_ecdh(ctx, output, &point, s_overflow, ecdh_hash_function_test_fail, NULL) == 0); } +/** Test that ECDH(sG, 1/s) == ECDH((1/s)G, s) == ECDH(G, 1) for a few random s. */ +void test_result_basepoint(void) { + secp256k1_pubkey point; + secp256k1_scalar rand; + unsigned char s[32]; + unsigned char s_inv[32]; + unsigned char out[32]; + unsigned char out_inv[32]; + unsigned char out_base[32]; + int i; + + unsigned char s_one[32] = { 0 }; + s_one[31] = 1; + CHECK(secp256k1_ec_pubkey_create(ctx, &point, s_one) == 1); + CHECK(secp256k1_ecdh(ctx, out_base, &point, s_one, NULL, NULL) == 1); + + for (i = 0; i < 2 * count; i++) { + random_scalar_order(&rand); + secp256k1_scalar_get_b32(s, &rand); + secp256k1_scalar_inverse(&rand, &rand); + secp256k1_scalar_get_b32(s_inv, &rand); + + CHECK(secp256k1_ec_pubkey_create(ctx, &point, s) == 1); + CHECK(secp256k1_ecdh(ctx, out, &point, s_inv, NULL, NULL) == 1); + CHECK(secp256k1_memcmp_var(out, out_base, 32) == 0); + + CHECK(secp256k1_ec_pubkey_create(ctx, &point, s_inv) == 1); + CHECK(secp256k1_ecdh(ctx, out_inv, &point, s, NULL, NULL) == 1); + CHECK(secp256k1_memcmp_var(out_inv, out_base, 32) == 0); + } +} + void run_ecdh_tests(void) { test_ecdh_api(); test_ecdh_generator_basepoint(); test_bad_scalar(); + test_result_basepoint(); } #endif /* SECP256K1_MODULE_ECDH_TESTS_H */ From 3531a43b5bc739838f5634afcfd02bdbef71b1ef Mon Sep 17 00:00:00 2001 From: Tim Ruffing Date: Wed, 9 Feb 2022 11:35:45 +0100 Subject: [PATCH 30/62] ecdh: Make generator_basepoint test depend on global iteration count Co-authored-by: Elliott Jin --- src/modules/ecdh/tests_impl.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/ecdh/tests_impl.h b/src/modules/ecdh/tests_impl.h index 5053194d7..10b7075c3 100644 --- a/src/modules/ecdh/tests_impl.h +++ b/src/modules/ecdh/tests_impl.h @@ -60,7 +60,7 @@ void test_ecdh_generator_basepoint(void) { s_one[31] = 1; /* Check against pubkey creation when the basepoint is the generator */ - for (i = 0; i < 100; ++i) { + for (i = 0; i < 2 * count; ++i) { secp256k1_sha256 sha; unsigned char s_b32[32]; unsigned char output_ecdh[65]; From fee7d4bf9e4ea316ea4ff3151bbe52bec1f0745c Mon Sep 17 00:00:00 2001 From: Elichai Turkel Date: Thu, 30 Apr 2020 13:37:52 +0300 Subject: [PATCH 31/62] Add an ECDSA signing and verifying example Co-authored-by: Jonas Nick --- examples/ecdsa.c | 137 ++++++++++++++++++++++++++++++++++++++++++++++ examples/random.h | 73 ++++++++++++++++++++++++ 2 files changed, 210 insertions(+) create mode 100644 examples/ecdsa.c create mode 100644 examples/random.h diff --git a/examples/ecdsa.c b/examples/ecdsa.c new file mode 100644 index 000000000..434c856ba --- /dev/null +++ b/examples/ecdsa.c @@ -0,0 +1,137 @@ +/************************************************************************* + * Written in 2020-2022 by Elichai Turkel * + * To the extent possible under law, the author(s) have dedicated all * + * copyright and related and neighboring rights to the software in this * + * file to the public domain worldwide. This software is distributed * + * without any warranty. For the CC0 Public Domain Dedication, see * + * EXAMPLES_COPYING or https://creativecommons.org/publicdomain/zero/1.0 * + *************************************************************************/ + +#include +#include +#include + +#include + +#include "random.h" + + + +int main(void) { + /* Instead of signing the message directly, we must sign a 32-byte hash. + * Here the message is "Hello, world!" and the hash function was SHA-256. + * An actual implementation should just call SHA-256, but this example + * hardcodes the output to avoid depending on an additional library. + * See https://bitcoin.stackexchange.com/questions/81115/if-someone-wanted-to-pretend-to-be-satoshi-by-posting-a-fake-signature-to-defrau/81116#81116 */ + unsigned char msg_hash[32] = { + 0x31, 0x5F, 0x5B, 0xDB, 0x76, 0xD0, 0x78, 0xC4, + 0x3B, 0x8A, 0xC0, 0x06, 0x4E, 0x4A, 0x01, 0x64, + 0x61, 0x2B, 0x1F, 0xCE, 0x77, 0xC8, 0x69, 0x34, + 0x5B, 0xFC, 0x94, 0xC7, 0x58, 0x94, 0xED, 0xD3, + }; + unsigned char seckey[32]; + unsigned char randomize[32]; + unsigned char compressed_pubkey[33]; + unsigned char serialized_signature[64]; + size_t len; + int is_signature_valid; + int return_val; + secp256k1_pubkey pubkey; + secp256k1_ecdsa_signature sig; + /* The specification in secp256k1.h states that `secp256k1_ec_pubkey_create` needs + * a context object initialized for signing and `secp256k1_ecdsa_verify` needs + * a context initialized for verification, which is why we create a context + * for both signing and verification with the SECP256K1_CONTEXT_SIGN and + * SECP256K1_CONTEXT_VERIFY flags. */ + secp256k1_context* ctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY); + if (!fill_random(randomize, sizeof(randomize))) { + printf("Failed to generate randomness\n"); + return 1; + } + /* Randomizing the context is recommended to protect against side-channel + * leakage See `secp256k1_context_randomize` in secp256k1.h for more + * information about it. This should never fail. */ + return_val = secp256k1_context_randomize(ctx, randomize); + assert(return_val); + + /*** Key Generation ***/ + + /* If the secret key is zero or out of range (bigger than secp256k1's + * order), we try to sample a new key. Note that the probability of this + * happening is negligible. */ + while (1) { + if (!fill_random(seckey, sizeof(seckey))) { + printf("Failed to generate randomness\n"); + return 1; + } + if (secp256k1_ec_seckey_verify(ctx, seckey)) { + break; + } + } + + /* Public key creation using a valid context with a verified secret key should never fail */ + return_val = secp256k1_ec_pubkey_create(ctx, &pubkey, seckey); + assert(return_val); + + /* Serialize the pubkey in a compressed form(33 bytes). Should always return 1. */ + len = sizeof(compressed_pubkey); + return_val = secp256k1_ec_pubkey_serialize(ctx, compressed_pubkey, &len, &pubkey, SECP256K1_EC_COMPRESSED); + assert(return_val); + /* Should be the same size as the size of the output, because we passed a 33 byte array. */ + assert(len == sizeof(compressed_pubkey)); + + /*** Signing ***/ + + /* Generate an ECDSA signature `noncefp` and `ndata` allows you to pass a + * custom nonce function, passing `NULL` will use the RFC-6979 safe default. + * Signing with a valid context, verified secret key + * and the default nonce function should never fail. */ + return_val = secp256k1_ecdsa_sign(ctx, &sig, msg_hash, seckey, NULL, NULL); + assert(return_val); + + /* Serialize the signature in a compact form. Should always return 1 + * according to the documentation in secp256k1.h. */ + return_val = secp256k1_ecdsa_signature_serialize_compact(ctx, serialized_signature, &sig); + assert(return_val); + + + /*** Verification ***/ + + /* Deserialize the signature. This will return 0 if the signature can't be parsed correctly. */ + if (!secp256k1_ecdsa_signature_parse_compact(ctx, &sig, serialized_signature)) { + printf("Failed parsing the signature\n"); + return 1; + } + + /* Deserialize the public key. This will return 0 if the public key can't be parsed correctly. */ + if (!secp256k1_ec_pubkey_parse(ctx, &pubkey, compressed_pubkey, sizeof(compressed_pubkey))) { + printf("Failed parsing the public key\n"); + return 1; + } + + /* Verify a signature. This will return 1 if it's valid and 0 if it's not. */ + is_signature_valid = secp256k1_ecdsa_verify(ctx, &sig, msg_hash, &pubkey); + + printf("Is the signature valid? %s\n", is_signature_valid ? "true" : "false"); + printf("Secret Key: "); + print_hex(seckey, sizeof(seckey)); + printf("Public Key: "); + print_hex(compressed_pubkey, sizeof(compressed_pubkey)); + printf("Signature: "); + print_hex(serialized_signature, sizeof(serialized_signature)); + + + /* This will clear everything from the context and free the memory */ + secp256k1_context_destroy(ctx); + + /* It's best practice to try to clear secrets from memory after using them. + * This is done because some bugs can allow an attacker to leak memory, for + * example through "out of bounds" array access (see Heartbleed), Or the OS + * swapping them to disk. Hence, we overwrite the secret key buffer with zeros. + * + * TODO: Prevent these writes from being optimized out, as any good compiler + * will remove any writes that aren't used. */ + memset(seckey, 0, sizeof(seckey)); + + return 0; +} diff --git a/examples/random.h b/examples/random.h new file mode 100644 index 000000000..439226f09 --- /dev/null +++ b/examples/random.h @@ -0,0 +1,73 @@ +/************************************************************************* + * Copyright (c) 2020-2021 Elichai Turkel * + * Distributed under the CC0 software license, see the accompanying file * + * EXAMPLES_COPYING or https://creativecommons.org/publicdomain/zero/1.0 * + *************************************************************************/ + +/* + * This file is an attempt at collecting best practice methods for obtaining randomness with different operating systems. + * It may be out-of-date. Consult the documentation of the operating system before considering to use the methods below. + * + * Platform randomness sources: + * Linux -> `getrandom(2)`(`sys/random.h`), if not available `/dev/urandom` should be used. http://man7.org/linux/man-pages/man2/getrandom.2.html, https://linux.die.net/man/4/urandom + * macOS -> `getentropy(2)`(`sys/random.h`), if not available `/dev/urandom` should be used. https://www.unix.com/man-page/mojave/2/getentropy, https://opensource.apple.com/source/xnu/xnu-517.12.7/bsd/man/man4/random.4.auto.html + * FreeBSD -> `getrandom(2)`(`sys/random.h`), if not available `kern.arandom` should be used. https://www.freebsd.org/cgi/man.cgi?query=getrandom, https://www.freebsd.org/cgi/man.cgi?query=random&sektion=4 + * OpenBSD -> `getentropy(2)`(`unistd.h`), if not available `/dev/urandom` should be used. https://man.openbsd.org/getentropy, https://man.openbsd.org/urandom + * Windows -> `BCryptGenRandom`(`bcrypt.h`). https://docs.microsoft.com/en-us/windows/win32/api/bcrypt/nf-bcrypt-bcryptgenrandom + */ + +#if defined(_WIN32) +#include +#include +#include +#elif defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__) +#include +#elif defined(__OpenBSD__) +#include +#else +#error "Couldn't identify the OS" +#endif + +#include +#include +#include + + +/* Returns 1 on success, and 0 on failure. */ +static int fill_random(unsigned char* data, size_t size) { +#if defined(_WIN32) + NTSTATUS res = BCryptGenRandom(NULL, data, size, BCRYPT_USE_SYSTEM_PREFERRED_RNG); + if (res != STATUS_SUCCESS || size > ULONG_MAX) { + return 0; + } else { + return 1; + } +#elif defined(__linux__) || defined(__FreeBSD__) + /* If `getrandom(2)` is not available you should fallback to /dev/urandom */ + ssize_t res = getrandom(data, size, 0); + if (res < 0 || (size_t)res != size ) { + return 0; + } else { + return 1; + } +#elif defined(__APPLE__) || defined(__OpenBSD__) + /* If `getentropy(2)` is not available you should fallback to either + * `SecRandomCopyBytes` or /dev/urandom */ + int res = getentropy(data, size); + if (res == 0) { + return 1; + } else { + return 0; + } +#endif + return 0; +} + +static void print_hex(unsigned char* data, size_t size) { + size_t i; + printf("0x"); + for (i = 0; i < size; i++) { + printf("%02x", data[i]); + } + printf("\n"); +} From b0cfbcc14347ff6b04ff62a0d935638840a37971 Mon Sep 17 00:00:00 2001 From: Elichai Turkel Date: Mon, 9 Nov 2020 18:11:43 +0200 Subject: [PATCH 32/62] Add a Schnorr signing and verifying example Co-authored-by: Jonas Nick --- examples/schnorr.c | 136 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 136 insertions(+) create mode 100644 examples/schnorr.c diff --git a/examples/schnorr.c b/examples/schnorr.c new file mode 100644 index 000000000..a3533b640 --- /dev/null +++ b/examples/schnorr.c @@ -0,0 +1,136 @@ +/************************************************************************* + * Written in 2020-2022 by Elichai Turkel * + * To the extent possible under law, the author(s) have dedicated all * + * copyright and related and neighboring rights to the software in this * + * file to the public domain worldwide. This software is distributed * + * without any warranty. For the CC0 Public Domain Dedication, see * + * EXAMPLES_COPYING or https://creativecommons.org/publicdomain/zero/1.0 * + *************************************************************************/ + +#include +#include +#include + +#include +#include +#include + +#include "random.h" + +int main(void) { + /* Instead of signing the message directly, we must sign a 32-byte hash. + * Here the message is "Hello, world!" and the hash function was SHA-256. + * An actual implementation should just call SHA-256, but this example + * hardcodes the output to avoid depending on an additional library. */ + unsigned char msg_hash[32] = { + 0x31, 0x5F, 0x5B, 0xDB, 0x76, 0xD0, 0x78, 0xC4, + 0x3B, 0x8A, 0xC0, 0x06, 0x4E, 0x4A, 0x01, 0x64, + 0x61, 0x2B, 0x1F, 0xCE, 0x77, 0xC8, 0x69, 0x34, + 0x5B, 0xFC, 0x94, 0xC7, 0x58, 0x94, 0xED, 0xD3, + }; + unsigned char seckey[32]; + unsigned char randomize[32]; + unsigned char auxiliary_rand[32]; + unsigned char serialized_pubkey[32]; + unsigned char signature[64]; + int is_signature_valid; + int return_val; + secp256k1_xonly_pubkey pubkey; + secp256k1_keypair keypair; + /* The specification in secp256k1_extrakeys.h states that `secp256k1_keypair_create` + * needs a context object initialized for signing. And in secp256k1_schnorrsig.h + * they state that `secp256k1_schnorrsig_verify` needs a context initialized for + * verification, which is why we create a context for both signing and verification + * with the SECP256K1_CONTEXT_SIGN and SECP256K1_CONTEXT_VERIFY flags. */ + secp256k1_context* ctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY); + if (!fill_random(randomize, sizeof(randomize))) { + printf("Failed to generate randomness\n"); + return 1; + } + /* Randomizing the context is recommended to protect against side-channel + * leakage See `secp256k1_context_randomize` in secp256k1.h for more + * information about it. This should never fail. */ + return_val = secp256k1_context_randomize(ctx, randomize); + assert(return_val); + + /*** Key Generation ***/ + + /* If the secret key is zero or out of range (bigger than secp256k1's + * order), we try to sample a new key. Note that the probability of this + * happening is negligible. */ + while (1) { + if (!fill_random(seckey, sizeof(seckey))) { + printf("Failed to generate randomness\n"); + return 1; + } + /* Try to create a keypair with a valid context, it should only fail if + * the secret key is zero or out of range. */ + if (secp256k1_keypair_create(ctx, &keypair, seckey)) { + break; + } + } + + /* Extract the X-only public key from the keypair. We pass NULL for + * `pk_parity` as the parity isn't needed for signing or verification. + * `secp256k1_keypair_xonly_pub` supports returning the parity for + * other use cases such as tests or verifying Taproot tweaks. + * This should never fail with a valid context and public key. */ + return_val = secp256k1_keypair_xonly_pub(ctx, &pubkey, NULL, &keypair); + assert(return_val); + + /* Serialize the public key. Should always return 1 for a valid public key. */ + return_val = secp256k1_xonly_pubkey_serialize(ctx, serialized_pubkey, &pubkey); + assert(return_val); + + /*** Signing ***/ + + /* Generate 32 bytes of randomness to use with BIP-340 schnorr signing. */ + if (!fill_random(auxiliary_rand, sizeof(auxiliary_rand))) { + printf("Failed to generate randomness\n"); + return 1; + } + + /* Generate a Schnorr signature `noncefp` and `ndata` allows you to pass a + * custom nonce function, passing `NULL` will use the BIP-340 safe default. + * BIP-340 recommends passing 32 bytes of randomness to the nonce function to + * improve security against side-channel attacks. Signing with a valid + * context, verified keypair and the default nonce function should never + * fail. */ + return_val = secp256k1_schnorrsig_sign(ctx, signature, msg_hash, &keypair, auxiliary_rand); + assert(return_val); + + /*** Verification ***/ + + /* Deserialize the public key. This will return 0 if the public key can't + * be parsed correctly */ + if (!secp256k1_xonly_pubkey_parse(ctx, &pubkey, serialized_pubkey)) { + printf("Failed parsing the public key\n"); + return 1; + } + + /* Verify a signature. This will return 1 if it's valid and 0 if it's not. */ + is_signature_valid = secp256k1_schnorrsig_verify(ctx, signature, msg_hash, 32, &pubkey); + + + printf("Is the signature valid? %s\n", is_signature_valid ? "true" : "false"); + printf("Secret Key: "); + print_hex(seckey, sizeof(seckey)); + printf("Public Key: "); + print_hex(serialized_pubkey, sizeof(serialized_pubkey)); + printf("Signature: "); + print_hex(signature, sizeof(signature)); + + /* This will clear everything from the context and free the memory */ + secp256k1_context_destroy(ctx); + + /* It's best practice to try to clear secrets from memory after using them. + * This is done because some bugs can allow an attacker to leak memory, for + * example through "out of bounds" array access (see Heartbleed), Or the OS + * swapping them to disk. Hence, we overwrite the secret key buffer with zeros. + * + * TODO: Prevent these writes from being optimized out, as any good compiler + * will remove any writes that aren't used. */ + memset(seckey, 0, sizeof(seckey)); + + return 0; +} From 422a7cc86ae86496794c5014028ee249bbe0e072 Mon Sep 17 00:00:00 2001 From: Elichai Turkel Date: Thu, 30 Apr 2020 14:33:22 +0300 Subject: [PATCH 33/62] Add a ecdh shared secret example Co-authored-by: Jonas Nick --- examples/ecdh.c | 127 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 127 insertions(+) create mode 100644 examples/ecdh.c diff --git a/examples/ecdh.c b/examples/ecdh.c new file mode 100644 index 000000000..d7e8add36 --- /dev/null +++ b/examples/ecdh.c @@ -0,0 +1,127 @@ +/************************************************************************* + * Written in 2020-2022 by Elichai Turkel * + * To the extent possible under law, the author(s) have dedicated all * + * copyright and related and neighboring rights to the software in this * + * file to the public domain worldwide. This software is distributed * + * without any warranty. For the CC0 Public Domain Dedication, see * + * EXAMPLES_COPYING or https://creativecommons.org/publicdomain/zero/1.0 * + *************************************************************************/ + +#include +#include +#include + +#include +#include + +#include "random.h" + + +int main(void) { + unsigned char seckey1[32]; + unsigned char seckey2[32]; + unsigned char compressed_pubkey1[33]; + unsigned char compressed_pubkey2[33]; + unsigned char shared_secret1[32]; + unsigned char shared_secret2[32]; + unsigned char randomize[32]; + int return_val; + size_t len; + secp256k1_pubkey pubkey1; + secp256k1_pubkey pubkey2; + + /* The specification in secp256k1.h states that `secp256k1_ec_pubkey_create` + * needs a context object initialized for signing, which is why we create + * a context with the SECP256K1_CONTEXT_SIGN flag. + * (The docs for `secp256k1_ecdh` don't require any special context, just + * some initialized context) */ + secp256k1_context* ctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN); + if (!fill_random(randomize, sizeof(randomize))) { + printf("Failed to generate randomness\n"); + return 1; + } + /* Randomizing the context is recommended to protect against side-channel + * leakage See `secp256k1_context_randomize` in secp256k1.h for more + * information about it. This should never fail. */ + return_val = secp256k1_context_randomize(ctx, randomize); + assert(return_val); + + /*** Key Generation ***/ + + /* If the secret key is zero or out of range (bigger than secp256k1's + * order), we try to sample a new key. Note that the probability of this + * happening is negligible. */ + while (1) { + if (!fill_random(seckey1, sizeof(seckey1)) || !fill_random(seckey2, sizeof(seckey2))) { + printf("Failed to generate randomness\n"); + return 1; + } + if (secp256k1_ec_seckey_verify(ctx, seckey1) && secp256k1_ec_seckey_verify(ctx, seckey2)) { + break; + } + } + + /* Public key creation using a valid context with a verified secret key should never fail */ + return_val = secp256k1_ec_pubkey_create(ctx, &pubkey1, seckey1); + assert(return_val); + return_val = secp256k1_ec_pubkey_create(ctx, &pubkey2, seckey2); + assert(return_val); + + /* Serialize pubkey1 in a compressed form (33 bytes), should always return 1 */ + len = sizeof(compressed_pubkey1); + return_val = secp256k1_ec_pubkey_serialize(ctx, compressed_pubkey1, &len, &pubkey1, SECP256K1_EC_COMPRESSED); + assert(return_val); + /* Should be the same size as the size of the output, because we passed a 33 byte array. */ + assert(len == sizeof(compressed_pubkey1)); + + /* Serialize pubkey2 in a compressed form (33 bytes) */ + len = sizeof(compressed_pubkey2); + return_val = secp256k1_ec_pubkey_serialize(ctx, compressed_pubkey2, &len, &pubkey2, SECP256K1_EC_COMPRESSED); + assert(return_val); + /* Should be the same size as the size of the output, because we passed a 33 byte array. */ + assert(len == sizeof(compressed_pubkey2)); + + /*** Creating the shared secret ***/ + + /* Perform ECDH with seckey1 and pubkey2. Should never fail with a verified + * seckey and valid pubkey */ + return_val = secp256k1_ecdh(ctx, shared_secret1, &pubkey2, seckey1, NULL, NULL); + assert(return_val); + + /* Perform ECDH with seckey2 and pubkey1. Should never fail with a verified + * seckey and valid pubkey */ + return_val = secp256k1_ecdh(ctx, shared_secret2, &pubkey1, seckey2, NULL, NULL); + assert(return_val); + + /* Both parties should end up with the same shared secret */ + return_val = memcmp(shared_secret1, shared_secret2, sizeof(shared_secret1)); + assert(return_val == 0); + + printf("Secret Key1: "); + print_hex(seckey1, sizeof(seckey1)); + printf("Compressed Pubkey1: "); + print_hex(compressed_pubkey1, sizeof(compressed_pubkey1)); + printf("\nSecret Key2: "); + print_hex(seckey2, sizeof(seckey2)); + printf("Compressed Pubkey2: "); + print_hex(compressed_pubkey2, sizeof(compressed_pubkey2)); + printf("\nShared Secret: "); + print_hex(shared_secret1, sizeof(shared_secret1)); + + /* This will clear everything from the context and free the memory */ + secp256k1_context_destroy(ctx); + + /* It's best practice to try to clear secrets from memory after using them. + * This is done because some bugs can allow an attacker to leak memory, for + * example through "out of bounds" array access (see Heartbleed), Or the OS + * swapping them to disk. Hence, we overwrite the secret key buffer with zeros. + * + * TODO: Prevent these writes from being optimized out, as any good compiler + * will remove any writes that aren't used. */ + memset(seckey1, 0, sizeof(seckey1)); + memset(seckey2, 0, sizeof(seckey2)); + memset(shared_secret1, 0, sizeof(shared_secret1)); + memset(shared_secret2, 0, sizeof(shared_secret2)); + + return 0; +} From 517644eab14ef397e1f0bc2b45f2dff8b1a473ec Mon Sep 17 00:00:00 2001 From: Elichai Turkel Date: Thu, 30 Apr 2020 14:34:24 +0300 Subject: [PATCH 34/62] Optionally compile the examples in autotools, compile+run in travis --- .cirrus.yml | 2 ++ .gitignore | 5 +++++ Makefile.am | 35 +++++++++++++++++++++++++++++++++++ ci/cirrus.sh | 1 + configure.ac | 13 +++++++++++++ 5 files changed, 56 insertions(+) diff --git a/.cirrus.yml b/.cirrus.yml index ffbd82071..8e18c6ea5 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -23,6 +23,8 @@ env: BENCH: yes SECP256K1_BENCH_ITERS: 2 CTIMETEST: yes + # Compile and run the tests + EXAMPLES: yes cat_logs_snippet: &CAT_LOGS always: diff --git a/.gitignore b/.gitignore index cb52998ad..53941f23a 100644 --- a/.gitignore +++ b/.gitignore @@ -6,11 +6,16 @@ exhaustive_tests precompute_ecmult_gen precompute_ecmult valgrind_ctime_test +ecdh_example +ecdsa_example +schnorr_example *.exe *.so *.a *.csv !.gitignore +*.log +*.trs Makefile configure diff --git a/Makefile.am b/Makefile.am index f87852974..51c596030 100644 --- a/Makefile.am +++ b/Makefile.am @@ -63,6 +63,7 @@ noinst_HEADERS += contrib/lax_der_parsing.h noinst_HEADERS += contrib/lax_der_parsing.c noinst_HEADERS += contrib/lax_der_privatekey_parsing.h noinst_HEADERS += contrib/lax_der_privatekey_parsing.c +noinst_HEADERS += examples/random.h PRECOMPUTED_LIB = libsecp256k1_precomputed.la noinst_LTLIBRARIES = $(PRECOMPUTED_LIB) @@ -139,6 +140,40 @@ exhaustive_tests_LDFLAGS = -static TESTS += exhaustive_tests endif +if USE_EXAMPLES +noinst_PROGRAMS += ecdsa_example +ecdsa_example_SOURCES = examples/ecdsa.c +ecdsa_example_CPPFLAGS = -I$(top_srcdir)/include +ecdsa_example_LDADD = libsecp256k1.la +ecdsa_example_LDFLAGS = -static +if BUILD_WINDOWS +ecdsa_example_LDFLAGS += -lbcrypt +endif +TESTS += ecdsa_example +if ENABLE_MODULE_ECDH +noinst_PROGRAMS += ecdh_example +ecdh_example_SOURCES = examples/ecdh.c +ecdh_example_CPPFLAGS = -I$(top_srcdir)/include +ecdh_example_LDADD = libsecp256k1.la +ecdh_example_LDFLAGS = -static +if BUILD_WINDOWS +ecdh_example_LDFLAGS += -lbcrypt +endif +TESTS += ecdh_example +endif +if ENABLE_MODULE_SCHNORRSIG +noinst_PROGRAMS += schnorr_example +schnorr_example_SOURCES = examples/schnorr.c +schnorr_example_CPPFLAGS = -I$(top_srcdir)/include +schnorr_example_LDADD = libsecp256k1.la +schnorr_example_LDFLAGS = -static +if BUILD_WINDOWS +schnorr_example_LDFLAGS += -lbcrypt +endif +TESTS += schnorr_example +endif +endif + ### Precomputed tables EXTRA_PROGRAMS = precompute_ecmult precompute_ecmult_gen CLEANFILES = $(EXTRA_PROGRAMS) diff --git a/ci/cirrus.sh b/ci/cirrus.sh index 4379e2504..b85f012d3 100755 --- a/ci/cirrus.sh +++ b/ci/cirrus.sh @@ -19,6 +19,7 @@ valgrind --version || true --with-ecmult-gen-precision="$ECMULTGENPRECISION" \ --enable-module-ecdh="$ECDH" --enable-module-recovery="$RECOVERY" \ --enable-module-schnorrsig="$SCHNORRSIG" \ + --enable-examples="$EXAMPLES" \ --with-valgrind="$WITH_VALGRIND" \ --host="$HOST" $EXTRAFLAGS diff --git a/configure.ac b/configure.ac index 92e497df8..4a78b88b6 100644 --- a/configure.ac +++ b/configure.ac @@ -45,6 +45,8 @@ if test x"$ac_cv_prog_cc_c89" = x"no"; then fi AM_PROG_AS +build_windows=no + case $host_os in *darwin*) if test x$cross_compiling != xyes; then @@ -68,6 +70,9 @@ case $host_os in fi fi ;; + cygwin*|mingw*) + build_windows=yes + ;; esac # Try if some desirable compiler flags are supported and append them to SECP_CFLAGS. @@ -135,6 +140,11 @@ AC_ARG_ENABLE(exhaustive_tests, [use_exhaustive_tests=$enableval], [use_exhaustive_tests=yes]) +AC_ARG_ENABLE(examples, + AS_HELP_STRING([--enable-examples],[compile the examples [default=no]]), + [use_examples=$enableval], + [use_examples=no]) + AC_ARG_ENABLE(module_ecdh, AS_HELP_STRING([--enable-module-ecdh],[enable ECDH shared secret computation]), [enable_module_ecdh=$enableval], @@ -393,6 +403,7 @@ AC_SUBST(SECP_CFLAGS) AM_CONDITIONAL([ENABLE_COVERAGE], [test x"$enable_coverage" = x"yes"]) AM_CONDITIONAL([USE_TESTS], [test x"$use_tests" != x"no"]) AM_CONDITIONAL([USE_EXHAUSTIVE_TESTS], [test x"$use_exhaustive_tests" != x"no"]) +AM_CONDITIONAL([USE_EXAMPLES], [test x"$use_examples" != x"no"]) AM_CONDITIONAL([USE_BENCHMARK], [test x"$use_benchmark" = x"yes"]) AM_CONDITIONAL([ENABLE_MODULE_ECDH], [test x"$enable_module_ecdh" = x"yes"]) AM_CONDITIONAL([ENABLE_MODULE_RECOVERY], [test x"$enable_module_recovery" = x"yes"]) @@ -400,6 +411,7 @@ AM_CONDITIONAL([ENABLE_MODULE_EXTRAKEYS], [test x"$enable_module_extrakeys" = x" AM_CONDITIONAL([ENABLE_MODULE_SCHNORRSIG], [test x"$enable_module_schnorrsig" = x"yes"]) AM_CONDITIONAL([USE_EXTERNAL_ASM], [test x"$use_external_asm" = x"yes"]) AM_CONDITIONAL([USE_ASM_ARM], [test x"$set_asm" = x"arm"]) +AM_CONDITIONAL([BUILD_WINDOWS], [test "$build_windows" = "yes"]) AC_SUBST(LIB_VERSION_CURRENT, _LIB_VERSION_CURRENT) AC_SUBST(LIB_VERSION_REVISION, _LIB_VERSION_REVISION) AC_SUBST(LIB_VERSION_AGE, _LIB_VERSION_AGE) @@ -417,6 +429,7 @@ echo " with external callbacks = $use_external_default_callbacks" echo " with benchmarks = $use_benchmark" echo " with tests = $use_tests" echo " with coverage = $enable_coverage" +echo " with examples = $use_examples" echo " module ecdh = $enable_module_ecdh" echo " module recovery = $enable_module_recovery" echo " module extrakeys = $enable_module_extrakeys" From 42e03432e6be7f0bf18c7f86130d3930bdf4038d Mon Sep 17 00:00:00 2001 From: Elichai Turkel Date: Thu, 30 Apr 2020 15:08:50 +0300 Subject: [PATCH 35/62] Add usage examples to the readme --- README.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/README.md b/README.md index 5fc07dd4f..f1ae215c8 100644 --- a/README.md +++ b/README.md @@ -69,6 +69,14 @@ libsecp256k1 is built using autotools: $ make check # run the test suite $ sudo make install # optional +Usage examples +----------- + Usage examples can be found in the [examples](examples) directory. To compile them you need to configure with `--enable-examples`. + For experimental modules, you will also need `--enable-experimental` as well as a flag for each individual module, e.g. `--enable-module-ecdh`. + * [ECDSA example](examples/ecdsa.c) + * [Schnorr Signatures example](examples/schnorr.c) + * [Deriving a shared secret(ECDH) example](examples/ecdh.c) + Test coverage ----------- From 7c9502cece9c9e8d811333f7ab5bb22f4eb01c04 Mon Sep 17 00:00:00 2001 From: Elichai Turkel Date: Sun, 4 Jul 2021 17:42:30 +0300 Subject: [PATCH 36/62] Add a copy of the CC0 license to the examples --- examples/EXAMPLES_COPYING | 121 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 121 insertions(+) create mode 100644 examples/EXAMPLES_COPYING diff --git a/examples/EXAMPLES_COPYING b/examples/EXAMPLES_COPYING new file mode 100644 index 000000000..0e259d42c --- /dev/null +++ b/examples/EXAMPLES_COPYING @@ -0,0 +1,121 @@ +Creative Commons Legal Code + +CC0 1.0 Universal + + CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE + LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN + ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS + INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES + REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS + PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM + THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED + HEREUNDER. + +Statement of Purpose + +The laws of most jurisdictions throughout the world automatically confer +exclusive Copyright and Related Rights (defined below) upon the creator +and subsequent owner(s) (each and all, an "owner") of an original work of +authorship and/or a database (each, a "Work"). + +Certain owners wish to permanently relinquish those rights to a Work for +the purpose of contributing to a commons of creative, cultural and +scientific works ("Commons") that the public can reliably and without fear +of later claims of infringement build upon, modify, incorporate in other +works, reuse and redistribute as freely as possible in any form whatsoever +and for any purposes, including without limitation commercial purposes. +These owners may contribute to the Commons to promote the ideal of a free +culture and the further production of creative, cultural and scientific +works, or to gain reputation or greater distribution for their Work in +part through the use and efforts of others. + +For these and/or other purposes and motivations, and without any +expectation of additional consideration or compensation, the person +associating CC0 with a Work (the "Affirmer"), to the extent that he or she +is an owner of Copyright and Related Rights in the Work, voluntarily +elects to apply CC0 to the Work and publicly distribute the Work under its +terms, with knowledge of his or her Copyright and Related Rights in the +Work and the meaning and intended legal effect of CC0 on those rights. + +1. Copyright and Related Rights. A Work made available under CC0 may be +protected by copyright and related or neighboring rights ("Copyright and +Related Rights"). Copyright and Related Rights include, but are not +limited to, the following: + + i. the right to reproduce, adapt, distribute, perform, display, + communicate, and translate a Work; + ii. moral rights retained by the original author(s) and/or performer(s); +iii. publicity and privacy rights pertaining to a person's image or + likeness depicted in a Work; + iv. rights protecting against unfair competition in regards to a Work, + subject to the limitations in paragraph 4(a), below; + v. rights protecting the extraction, dissemination, use and reuse of data + in a Work; + vi. database rights (such as those arising under Directive 96/9/EC of the + European Parliament and of the Council of 11 March 1996 on the legal + protection of databases, and under any national implementation + thereof, including any amended or successor version of such + directive); and +vii. other similar, equivalent or corresponding rights throughout the + world based on applicable law or treaty, and any national + implementations thereof. + +2. Waiver. To the greatest extent permitted by, but not in contravention +of, applicable law, Affirmer hereby overtly, fully, permanently, +irrevocably and unconditionally waives, abandons, and surrenders all of +Affirmer's Copyright and Related Rights and associated claims and causes +of action, whether now known or unknown (including existing as well as +future claims and causes of action), in the Work (i) in all territories +worldwide, (ii) for the maximum duration provided by applicable law or +treaty (including future time extensions), (iii) in any current or future +medium and for any number of copies, and (iv) for any purpose whatsoever, +including without limitation commercial, advertising or promotional +purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each +member of the public at large and to the detriment of Affirmer's heirs and +successors, fully intending that such Waiver shall not be subject to +revocation, rescission, cancellation, termination, or any other legal or +equitable action to disrupt the quiet enjoyment of the Work by the public +as contemplated by Affirmer's express Statement of Purpose. + +3. Public License Fallback. Should any part of the Waiver for any reason +be judged legally invalid or ineffective under applicable law, then the +Waiver shall be preserved to the maximum extent permitted taking into +account Affirmer's express Statement of Purpose. In addition, to the +extent the Waiver is so judged Affirmer hereby grants to each affected +person a royalty-free, non transferable, non sublicensable, non exclusive, +irrevocable and unconditional license to exercise Affirmer's Copyright and +Related Rights in the Work (i) in all territories worldwide, (ii) for the +maximum duration provided by applicable law or treaty (including future +time extensions), (iii) in any current or future medium and for any number +of copies, and (iv) for any purpose whatsoever, including without +limitation commercial, advertising or promotional purposes (the +"License"). The License shall be deemed effective as of the date CC0 was +applied by Affirmer to the Work. Should any part of the License for any +reason be judged legally invalid or ineffective under applicable law, such +partial invalidity or ineffectiveness shall not invalidate the remainder +of the License, and in such case Affirmer hereby affirms that he or she +will not (i) exercise any of his or her remaining Copyright and Related +Rights in the Work or (ii) assert any associated claims and causes of +action with respect to the Work, in either case contrary to Affirmer's +express Statement of Purpose. + +4. Limitations and Disclaimers. + + a. No trademark or patent rights held by Affirmer are waived, abandoned, + surrendered, licensed or otherwise affected by this document. + b. Affirmer offers the Work as-is and makes no representations or + warranties of any kind concerning the Work, express, implied, + statutory or otherwise, including without limitation warranties of + title, merchantability, fitness for a particular purpose, non + infringement, or the absence of latent or other defects, accuracy, or + the present or absence of errors, whether or not discoverable, all to + the greatest extent permissible under applicable law. + c. Affirmer disclaims responsibility for clearing rights of other persons + that may apply to the Work or any use thereof, including without + limitation any person's Copyright and Related Rights in the Work. + Further, Affirmer disclaims responsibility for obtaining any necessary + consents, permissions or other rights required for any use of the + Work. + d. Affirmer understands and acknowledges that Creative Commons is not a + party to this document and has no duty or obligation with respect to + this CC0 or use of the Work. From 0d4226c051415d12384586ae938499975a248cab Mon Sep 17 00:00:00 2001 From: Tim Ruffing Date: Wed, 23 Feb 2022 20:40:18 +0100 Subject: [PATCH 37/62] configure: Use canonical variable prefix _enable consistently --- configure.ac | 50 +++++++++++++++++++++++++------------------------- 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/configure.ac b/configure.ac index d2a4690be..9930fa514 100644 --- a/configure.ac +++ b/configure.ac @@ -116,8 +116,8 @@ SECP_TRY_APPEND_DEFAULT_CFLAGS(SECP_CFLAGS) AC_ARG_ENABLE(benchmark, AS_HELP_STRING([--enable-benchmark],[compile benchmark [default=yes]]), - [use_benchmark=$enableval], - [use_benchmark=yes]) + [enable_benchmark=$enableval], + [enable_benchmark=yes]) AC_ARG_ENABLE(coverage, AS_HELP_STRING([--enable-coverage],[enable compiler flags to support kcov coverage analysis [default=no]]), @@ -126,23 +126,23 @@ AC_ARG_ENABLE(coverage, AC_ARG_ENABLE(tests, AS_HELP_STRING([--enable-tests],[compile tests [default=yes]]), - [use_tests=$enableval], - [use_tests=yes]) + [enable_tests=$enableval], + [enable_tests=yes]) AC_ARG_ENABLE(experimental, AS_HELP_STRING([--enable-experimental],[allow experimental configure options [default=no]]), - [use_experimental=$enableval], - [use_experimental=no]) + [enable_experimental=$enableval], + [enable_experimental=no]) AC_ARG_ENABLE(exhaustive_tests, AS_HELP_STRING([--enable-exhaustive-tests],[compile exhaustive tests [default=yes]]), - [use_exhaustive_tests=$enableval], - [use_exhaustive_tests=yes]) + [enable_exhaustive_tests=$enableval], + [enable_exhaustive_tests=yes]) AC_ARG_ENABLE(examples, AS_HELP_STRING([--enable-examples],[compile the examples [default=no]]), - [use_examples=$enableval], - [use_examples=no]) + [enable_examples=$enableval], + [enable_examples=no]) AC_ARG_ENABLE(module_ecdh, AS_HELP_STRING([--enable-module-ecdh],[enable ECDH shared secret computation]), @@ -166,8 +166,8 @@ AC_ARG_ENABLE(module_schnorrsig, AC_ARG_ENABLE(external_default_callbacks, AS_HELP_STRING([--enable-external-default-callbacks],[enable external default callback functions [default=no]]), - [use_external_default_callbacks=$enableval], - [use_external_default_callbacks=no]) + [enable_external_default_callbacks=$enableval], + [enable_external_default_callbacks=no]) # Test-only override of the (autodetected by the C code) "widemul" setting. # Legal values are int64 (for [u]int64_t), int128 (for [unsigned] __int128), and auto (the default). @@ -257,14 +257,14 @@ else fi # Select assembly optimization -use_external_asm=no +enable_external_asm=no case $set_asm in x86_64) AC_DEFINE(USE_ASM_X86_64, 1, [Define this symbol to enable x86_64 assembly optimizations]) ;; arm) - use_external_asm=yes + enable_external_asm=yes ;; no) ;; @@ -273,7 +273,7 @@ no) ;; esac -if test x"$use_external_asm" = x"yes"; then +if test x"$enable_external_asm" = x"yes"; then AC_DEFINE(USE_EXTERNAL_ASM, 1, [Define this symbol if an external (non-inline) assembly implementation is used]) fi @@ -361,7 +361,7 @@ if test x"$enable_module_extrakeys" = x"yes"; then AC_DEFINE(ENABLE_MODULE_EXTRAKEYS, 1, [Define this symbol to enable the extrakeys module]) fi -if test x"$use_external_default_callbacks" = x"yes"; then +if test x"$enable_external_default_callbacks" = x"yes"; then AC_DEFINE(USE_EXTERNAL_DEFAULT_CALLBACKS, 1, [Define this symbol if an external implementation of the default callbacks is used]) fi @@ -400,15 +400,15 @@ AC_SUBST(SECP_TEST_LIBS) AC_SUBST(SECP_TEST_INCLUDES) AC_SUBST(SECP_CFLAGS) AM_CONDITIONAL([ENABLE_COVERAGE], [test x"$enable_coverage" = x"yes"]) -AM_CONDITIONAL([USE_TESTS], [test x"$use_tests" != x"no"]) -AM_CONDITIONAL([USE_EXHAUSTIVE_TESTS], [test x"$use_exhaustive_tests" != x"no"]) -AM_CONDITIONAL([USE_EXAMPLES], [test x"$use_examples" != x"no"]) -AM_CONDITIONAL([USE_BENCHMARK], [test x"$use_benchmark" = x"yes"]) +AM_CONDITIONAL([USE_TESTS], [test x"$enable_tests" != x"no"]) +AM_CONDITIONAL([USE_EXHAUSTIVE_TESTS], [test x"$enable_exhaustive_tests" != x"no"]) +AM_CONDITIONAL([USE_EXAMPLES], [test x"$enable_examples" != x"no"]) +AM_CONDITIONAL([USE_BENCHMARK], [test x"$enable_benchmark" = x"yes"]) AM_CONDITIONAL([ENABLE_MODULE_ECDH], [test x"$enable_module_ecdh" = x"yes"]) AM_CONDITIONAL([ENABLE_MODULE_RECOVERY], [test x"$enable_module_recovery" = x"yes"]) AM_CONDITIONAL([ENABLE_MODULE_EXTRAKEYS], [test x"$enable_module_extrakeys" = x"yes"]) AM_CONDITIONAL([ENABLE_MODULE_SCHNORRSIG], [test x"$enable_module_schnorrsig" = x"yes"]) -AM_CONDITIONAL([USE_EXTERNAL_ASM], [test x"$use_external_asm" = x"yes"]) +AM_CONDITIONAL([USE_EXTERNAL_ASM], [test x"$enable_external_asm" = x"yes"]) AM_CONDITIONAL([USE_ASM_ARM], [test x"$set_asm" = x"arm"]) AM_CONDITIONAL([BUILD_WINDOWS], [test "$build_windows" = "yes"]) AC_SUBST(LIB_VERSION_CURRENT, _LIB_VERSION_CURRENT) @@ -424,11 +424,11 @@ AC_OUTPUT echo echo "Build Options:" -echo " with external callbacks = $use_external_default_callbacks" -echo " with benchmarks = $use_benchmark" -echo " with tests = $use_tests" +echo " with external callbacks = $enable_external_default_callbacks" +echo " with benchmarks = $enable_benchmark" +echo " with tests = $enable_tests" echo " with coverage = $enable_coverage" -echo " with examples = $use_examples" +echo " with examples = $enable_examples" echo " module ecdh = $enable_module_ecdh" echo " module recovery = $enable_module_recovery" echo " module extrakeys = $enable_module_extrakeys" From fabd579dfa85711e96e4012d20352b8ee8f2a9d1 Mon Sep 17 00:00:00 2001 From: Tim Ruffing Date: Wed, 23 Feb 2022 20:44:36 +0100 Subject: [PATCH 38/62] configure: Remove redundant code that sets _enable variables These are set automatically by autoconf [1], and this has been the case in at least since 2.60, which is our minimum supported version. [1] https://www.gnu.org/software/autoconf/manual/autoconf-2.70/html_node/Package-Options.html [2] https://www.gnu.org/software/autoconf/manual/autoconf-2.60/html_node/Package-Options.html --- configure.ac | 33 +++++++++++---------------------- 1 file changed, 11 insertions(+), 22 deletions(-) diff --git a/configure.ac b/configure.ac index 9930fa514..829cc67c3 100644 --- a/configure.ac +++ b/configure.ac @@ -115,58 +115,47 @@ SECP_TRY_APPEND_DEFAULT_CFLAGS(SECP_CFLAGS) ### AC_ARG_ENABLE(benchmark, - AS_HELP_STRING([--enable-benchmark],[compile benchmark [default=yes]]), - [enable_benchmark=$enableval], + AS_HELP_STRING([--enable-benchmark],[compile benchmark [default=yes]]), [], [enable_benchmark=yes]) AC_ARG_ENABLE(coverage, - AS_HELP_STRING([--enable-coverage],[enable compiler flags to support kcov coverage analysis [default=no]]), - [enable_coverage=$enableval], + AS_HELP_STRING([--enable-coverage],[enable compiler flags to support kcov coverage analysis [default=no]]), [], [enable_coverage=no]) AC_ARG_ENABLE(tests, - AS_HELP_STRING([--enable-tests],[compile tests [default=yes]]), - [enable_tests=$enableval], + AS_HELP_STRING([--enable-tests],[compile tests [default=yes]]), [], [enable_tests=yes]) AC_ARG_ENABLE(experimental, - AS_HELP_STRING([--enable-experimental],[allow experimental configure options [default=no]]), - [enable_experimental=$enableval], + AS_HELP_STRING([--enable-experimental],[allow experimental configure options [default=no]]), [], [enable_experimental=no]) AC_ARG_ENABLE(exhaustive_tests, - AS_HELP_STRING([--enable-exhaustive-tests],[compile exhaustive tests [default=yes]]), - [enable_exhaustive_tests=$enableval], + AS_HELP_STRING([--enable-exhaustive-tests],[compile exhaustive tests [default=yes]]), [], [enable_exhaustive_tests=yes]) AC_ARG_ENABLE(examples, - AS_HELP_STRING([--enable-examples],[compile the examples [default=no]]), - [enable_examples=$enableval], + AS_HELP_STRING([--enable-examples],[compile the examples [default=no]]), [], [enable_examples=no]) AC_ARG_ENABLE(module_ecdh, - AS_HELP_STRING([--enable-module-ecdh],[enable ECDH shared secret computation]), - [enable_module_ecdh=$enableval], + AS_HELP_STRING([--enable-module-ecdh],[enable ECDH shared secret computation]), [], [enable_module_ecdh=no]) AC_ARG_ENABLE(module_recovery, - AS_HELP_STRING([--enable-module-recovery],[enable ECDSA pubkey recovery module [default=no]]), - [enable_module_recovery=$enableval], + AS_HELP_STRING([--enable-module-recovery],[enable ECDSA pubkey recovery module [default=no]]), [], [enable_module_recovery=no]) AC_ARG_ENABLE(module_extrakeys, - AS_HELP_STRING([--enable-module-extrakeys],[enable extrakeys module (experimental)]), - [enable_module_extrakeys=$enableval], + AS_HELP_STRING([--enable-module-extrakeys],[enable extrakeys module (experimental)]), [], [enable_module_extrakeys=no]) AC_ARG_ENABLE(module_schnorrsig, - AS_HELP_STRING([--enable-module-schnorrsig],[enable schnorrsig module (experimental)]), - [enable_module_schnorrsig=$enableval], + AS_HELP_STRING([--enable-module-schnorrsig],[enable schnorrsig module (experimental)]), [], [enable_module_schnorrsig=no]) AC_ARG_ENABLE(external_default_callbacks, - AS_HELP_STRING([--enable-external-default-callbacks],[enable external default callback functions [default=no]]), - [enable_external_default_callbacks=$enableval], + AS_HELP_STRING([--enable-external-default-callbacks],[enable external default callback functions [default=no]]), [], [enable_external_default_callbacks=no]) # Test-only override of the (autodetected by the C code) "widemul" setting. From e0838d663deba5a9663a0ad02bd7e4c563325c4d Mon Sep 17 00:00:00 2001 From: Tim Ruffing Date: Wed, 23 Feb 2022 21:03:15 +0100 Subject: [PATCH 39/62] configure: Add hidden --enable-dev-mode to enable all the stuff Co-authored-by: Elichai Turkel --- build-aux/m4/bitcoin_secp.m4 | 13 +++++++++++++ configure.ac | 27 ++++++++++++++++----------- 2 files changed, 29 insertions(+), 11 deletions(-) diff --git a/build-aux/m4/bitcoin_secp.m4 b/build-aux/m4/bitcoin_secp.m4 index c14d09fa1..dda770e00 100644 --- a/build-aux/m4/bitcoin_secp.m4 +++ b/build-aux/m4/bitcoin_secp.m4 @@ -38,3 +38,16 @@ AC_DEFUN([SECP_TRY_APPEND_CFLAGS], [ unset flag_works AC_SUBST($2) ]) + +dnl SECP_SET_DEFAULT(VAR, default, default-dev-mode) +dnl Set VAR to default or default-dev-mode, depending on whether dev mode is enabled +AC_DEFUN([SECP_SET_DEFAULT], [ + if test "${enable_dev_mode+set}" != set; then + AC_MSG_ERROR([[Set enable_dev_mode before calling SECP_SET_DEFAULT]]) + fi + if test x"$enable_dev_mode" = x"yes"; then + $1="$3" + else + $1="$2" + fi +]) diff --git a/configure.ac b/configure.ac index 829cc67c3..cc766b20a 100644 --- a/configure.ac +++ b/configure.ac @@ -114,49 +114,54 @@ SECP_TRY_APPEND_DEFAULT_CFLAGS(SECP_CFLAGS) ### Define config arguments ### +# In dev mode, we enable all binaries and modules by default but individual options can still be overridden explicitly. +# Check for dev mode first because SECP_SET_DEFAULT needs enable_dev_mode set. +AC_ARG_ENABLE(dev_mode, [], [], + [enable_dev_mode=no]) + AC_ARG_ENABLE(benchmark, AS_HELP_STRING([--enable-benchmark],[compile benchmark [default=yes]]), [], - [enable_benchmark=yes]) + [SECP_SET_DEFAULT([enable_benchmark], [yes], [yes])]) AC_ARG_ENABLE(coverage, AS_HELP_STRING([--enable-coverage],[enable compiler flags to support kcov coverage analysis [default=no]]), [], - [enable_coverage=no]) + [SECP_SET_DEFAULT([enable_coverage], [no], [no])]) AC_ARG_ENABLE(tests, AS_HELP_STRING([--enable-tests],[compile tests [default=yes]]), [], - [enable_tests=yes]) + [SECP_SET_DEFAULT([enable_tests], [yes], [yes])]) AC_ARG_ENABLE(experimental, AS_HELP_STRING([--enable-experimental],[allow experimental configure options [default=no]]), [], - [enable_experimental=no]) + [SECP_SET_DEFAULT([enable_experimental], [no], [yes])]) AC_ARG_ENABLE(exhaustive_tests, AS_HELP_STRING([--enable-exhaustive-tests],[compile exhaustive tests [default=yes]]), [], - [enable_exhaustive_tests=yes]) + [SECP_SET_DEFAULT([enable_exhaustive_tests], [yes], [yes])]) AC_ARG_ENABLE(examples, AS_HELP_STRING([--enable-examples],[compile the examples [default=no]]), [], - [enable_examples=no]) + [SECP_SET_DEFAULT([enable_examples], [no], [yes])]) AC_ARG_ENABLE(module_ecdh, AS_HELP_STRING([--enable-module-ecdh],[enable ECDH shared secret computation]), [], - [enable_module_ecdh=no]) + [SECP_SET_DEFAULT([enable_module_ecdh], [no], [yes])]) AC_ARG_ENABLE(module_recovery, AS_HELP_STRING([--enable-module-recovery],[enable ECDSA pubkey recovery module [default=no]]), [], - [enable_module_recovery=no]) + [SECP_SET_DEFAULT([enable_module_recovery], [no], [yes])]) AC_ARG_ENABLE(module_extrakeys, AS_HELP_STRING([--enable-module-extrakeys],[enable extrakeys module (experimental)]), [], - [enable_module_extrakeys=no]) + [SECP_SET_DEFAULT([enable_module_extrakeys], [no], [yes])]) AC_ARG_ENABLE(module_schnorrsig, AS_HELP_STRING([--enable-module-schnorrsig],[enable schnorrsig module (experimental)]), [], - [enable_module_schnorrsig=no]) + [SECP_SET_DEFAULT([enable_module_schnorrsig], [no], [yes])]) AC_ARG_ENABLE(external_default_callbacks, AS_HELP_STRING([--enable-external-default-callbacks],[enable external default callback functions [default=no]]), [], - [enable_external_default_callbacks=no]) + [SECP_SET_DEFAULT([enable_external_default_callbacks], [no], [no])]) # Test-only override of the (autodetected by the C code) "widemul" setting. # Legal values are int64 (for [u]int64_t), int128 (for [unsigned] __int128), and auto (the default). From eb28464a8bf8652a2b49d2ed765801d7c0aa195d Mon Sep 17 00:00:00 2001 From: Tim Ruffing Date: Tue, 31 Mar 2020 13:40:37 +0200 Subject: [PATCH 40/62] Change SHA256 byte counter from size_t to uint64_t This avoids that the SHA256 implementation would produce wrong paddings and thus wrong digests for messages of length >= 2^32 bytes on 32-bit platforms. This is not exploitable in any way since the SHA256 API is an internal API and we never call it with that long messages. --- src/hash.h | 2 +- src/hash_impl.h | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/hash.h b/src/hash.h index 0947a0969..7da677013 100644 --- a/src/hash.h +++ b/src/hash.h @@ -13,7 +13,7 @@ typedef struct { uint32_t s[8]; uint32_t buf[16]; /* In big endian */ - size_t bytes; + uint64_t bytes; } secp256k1_sha256; static void secp256k1_sha256_initialize(secp256k1_sha256 *hash); diff --git a/src/hash_impl.h b/src/hash_impl.h index f8cd3a163..18342bb29 100644 --- a/src/hash_impl.h +++ b/src/hash_impl.h @@ -153,6 +153,8 @@ static void secp256k1_sha256_finalize(secp256k1_sha256 *hash, unsigned char *out uint32_t sizedesc[2]; uint32_t out[8]; int i = 0; + /* The maximum message size of SHA256 is 2^64-1 bits. */ + VERIFY_CHECK(hash->bytes < ((uint64_t)1 << 61)); sizedesc[0] = BE32(hash->bytes >> 29); sizedesc[1] = BE32(hash->bytes << 3); secp256k1_sha256_write(hash, pad, 1 + ((119 - (hash->bytes % 64)) % 64)); From 8e3dde113741615fcb1aedcacfc54bd3b3d204f1 Mon Sep 17 00:00:00 2001 From: Tim Ruffing Date: Tue, 31 Mar 2020 13:55:30 +0200 Subject: [PATCH 41/62] Simplify struct initializer for SHA256 padding Since missing elements are initialized with zeros, this change is purely syntactical. --- src/hash_impl.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/hash_impl.h b/src/hash_impl.h index 18342bb29..abb22c88a 100644 --- a/src/hash_impl.h +++ b/src/hash_impl.h @@ -149,7 +149,7 @@ static void secp256k1_sha256_write(secp256k1_sha256 *hash, const unsigned char * } static void secp256k1_sha256_finalize(secp256k1_sha256 *hash, unsigned char *out32) { - static const unsigned char pad[64] = {0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + static const unsigned char pad[64] = {0x80}; uint32_t sizedesc[2]; uint32_t out[8]; int i = 0; From 9b514ce1d25f1944c549ead30cc84367d616e0e6 Mon Sep 17 00:00:00 2001 From: Tim Ruffing Date: Thu, 16 Apr 2020 14:08:36 +0200 Subject: [PATCH 42/62] Add test vector for very long SHA256 messages The vector has been taken from https://www.di-mgt.com.au/sha_testvectors.html. It can be independently verified using the following Python code. ``` h = hashlib.sha256() for i in range(1_000_000): h.update(b'a') print(h.hexdigest()) ``` --- src/tests.c | 43 ++++++++++++++++++++++++++++++++----------- 1 file changed, 32 insertions(+), 11 deletions(-) diff --git a/src/tests.c b/src/tests.c index 26063a815..7dbe4644a 100644 --- a/src/tests.c +++ b/src/tests.c @@ -451,14 +451,18 @@ void run_ctz_tests(void) { /***** HASH TESTS *****/ -void run_sha256_tests(void) { - static const char *inputs[8] = { +void run_sha256_known_output_tests(void) { + static const char *inputs[] = { "", "abc", "message digest", "secure hash algorithm", "SHA256 is considered to be safe", "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq", "For this sample, this 63-byte string will be used as input data", - "This is exactly 64 bytes long, not counting the terminating byte" + "This is exactly 64 bytes long, not counting the terminating byte", + "aaaaa", }; - static const unsigned char outputs[8][32] = { + static const unsigned int repeat[] = { + 1, 1, 1, 1, 1, 1, 1, 1, 1000000/5 + }; + static const unsigned char outputs[][32] = { {0xe3, 0xb0, 0xc4, 0x42, 0x98, 0xfc, 0x1c, 0x14, 0x9a, 0xfb, 0xf4, 0xc8, 0x99, 0x6f, 0xb9, 0x24, 0x27, 0xae, 0x41, 0xe4, 0x64, 0x9b, 0x93, 0x4c, 0xa4, 0x95, 0x99, 0x1b, 0x78, 0x52, 0xb8, 0x55}, {0xba, 0x78, 0x16, 0xbf, 0x8f, 0x01, 0xcf, 0xea, 0x41, 0x41, 0x40, 0xde, 0x5d, 0xae, 0x22, 0x23, 0xb0, 0x03, 0x61, 0xa3, 0x96, 0x17, 0x7a, 0x9c, 0xb4, 0x10, 0xff, 0x61, 0xf2, 0x00, 0x15, 0xad}, {0xf7, 0x84, 0x6f, 0x55, 0xcf, 0x23, 0xe1, 0x4e, 0xeb, 0xea, 0xb5, 0xb4, 0xe1, 0x55, 0x0c, 0xad, 0x5b, 0x50, 0x9e, 0x33, 0x48, 0xfb, 0xc4, 0xef, 0xa3, 0xa1, 0x41, 0x3d, 0x39, 0x3c, 0xb6, 0x50}, @@ -466,21 +470,38 @@ void run_sha256_tests(void) { {0x68, 0x19, 0xd9, 0x15, 0xc7, 0x3f, 0x4d, 0x1e, 0x77, 0xe4, 0xe1, 0xb5, 0x2d, 0x1f, 0xa0, 0xf9, 0xcf, 0x9b, 0xea, 0xea, 0xd3, 0x93, 0x9f, 0x15, 0x87, 0x4b, 0xd9, 0x88, 0xe2, 0xa2, 0x36, 0x30}, {0x24, 0x8d, 0x6a, 0x61, 0xd2, 0x06, 0x38, 0xb8, 0xe5, 0xc0, 0x26, 0x93, 0x0c, 0x3e, 0x60, 0x39, 0xa3, 0x3c, 0xe4, 0x59, 0x64, 0xff, 0x21, 0x67, 0xf6, 0xec, 0xed, 0xd4, 0x19, 0xdb, 0x06, 0xc1}, {0xf0, 0x8a, 0x78, 0xcb, 0xba, 0xee, 0x08, 0x2b, 0x05, 0x2a, 0xe0, 0x70, 0x8f, 0x32, 0xfa, 0x1e, 0x50, 0xc5, 0xc4, 0x21, 0xaa, 0x77, 0x2b, 0xa5, 0xdb, 0xb4, 0x06, 0xa2, 0xea, 0x6b, 0xe3, 0x42}, - {0xab, 0x64, 0xef, 0xf7, 0xe8, 0x8e, 0x2e, 0x46, 0x16, 0x5e, 0x29, 0xf2, 0xbc, 0xe4, 0x18, 0x26, 0xbd, 0x4c, 0x7b, 0x35, 0x52, 0xf6, 0xb3, 0x82, 0xa9, 0xe7, 0xd3, 0xaf, 0x47, 0xc2, 0x45, 0xf8} + {0xab, 0x64, 0xef, 0xf7, 0xe8, 0x8e, 0x2e, 0x46, 0x16, 0x5e, 0x29, 0xf2, 0xbc, 0xe4, 0x18, 0x26, 0xbd, 0x4c, 0x7b, 0x35, 0x52, 0xf6, 0xb3, 0x82, 0xa9, 0xe7, 0xd3, 0xaf, 0x47, 0xc2, 0x45, 0xf8}, + {0xcd, 0xc7, 0x6e, 0x5c, 0x99, 0x14, 0xfb, 0x92, 0x81, 0xa1, 0xc7, 0xe2, 0x84, 0xd7, 0x3e, 0x67, 0xf1, 0x80, 0x9a, 0x48, 0xa4, 0x97, 0x20, 0x0e, 0x04, 0x6d, 0x39, 0xcc, 0xc7, 0x11, 0x2c, 0xd0}, }; - int i; - for (i = 0; i < 8; i++) { + unsigned int i, ninputs; + + /* Skip last input vector for low iteration counts */ + ninputs = sizeof(inputs)/sizeof(inputs[0]) - 1; + CONDITIONAL_TEST(16, "run_sha256_known_output_tests 1000000") ninputs++; + + for (i = 0; i < ninputs; i++) { unsigned char out[32]; secp256k1_sha256 hasher; + unsigned int j; + /* 1. Run: simply write the input bytestrings */ + j = repeat[i]; secp256k1_sha256_initialize(&hasher); - secp256k1_sha256_write(&hasher, (const unsigned char*)(inputs[i]), strlen(inputs[i])); + while (j > 0) { + secp256k1_sha256_write(&hasher, (const unsigned char*)(inputs[i]), strlen(inputs[i])); + j--; + } secp256k1_sha256_finalize(&hasher, out); CHECK(secp256k1_memcmp_var(out, outputs[i], 32) == 0); + /* 2. Run: split the input bytestrings randomly before writing */ if (strlen(inputs[i]) > 0) { int split = secp256k1_testrand_int(strlen(inputs[i])); secp256k1_sha256_initialize(&hasher); - secp256k1_sha256_write(&hasher, (const unsigned char*)(inputs[i]), split); - secp256k1_sha256_write(&hasher, (const unsigned char*)(inputs[i] + split), strlen(inputs[i]) - split); + j = repeat[i]; + while (j > 0) { + secp256k1_sha256_write(&hasher, (const unsigned char*)(inputs[i]), split); + secp256k1_sha256_write(&hasher, (const unsigned char*)(inputs[i] + split), strlen(inputs[i]) - split); + j--; + } secp256k1_sha256_finalize(&hasher, out); CHECK(secp256k1_memcmp_var(out, outputs[i], 32) == 0); } @@ -6964,7 +6985,7 @@ int main(int argc, char **argv) { run_modinv_tests(); run_inverse_tests(); - run_sha256_tests(); + run_sha256_known_output_tests(); run_hmac_sha256_tests(); run_rfc6979_hmac_sha256_tests(); run_tagged_sha256_tests(); From 0d253d52e804a5affb0f1c851ec250071e7345d9 Mon Sep 17 00:00:00 2001 From: Tim Ruffing Date: Sun, 13 Mar 2022 10:39:55 +0100 Subject: [PATCH 43/62] configure: Use modern way to set AR This uses AM_PROG_AR to discover ar, which is the recommended way to do so. Among other advantages, it honors the AR environment variable (as set from the outside). The macro has been around since automake 1.11.2 (Dec 2011). This commit also removes code that discovers ranlib and strip. ranlib has been obsolete for decades (ar does its task now automatically), and anyway LT_INIT takes care of discovering it. The code we used to set STRIP was last mentioned in the automake 1.5 manual. Since automake 1.6 (Mar 2002), strip is discovered automatically when necessary (look for the *private* macro AM_PROG_INSTALL_STRIP in the automake manual). --- .gitignore | 1 + configure.ac | 11 +++++------ 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.gitignore b/.gitignore index 53941f23a..d88627d72 100644 --- a/.gitignore +++ b/.gitignore @@ -46,6 +46,7 @@ coverage.*.html src/libsecp256k1-config.h src/libsecp256k1-config.h.in +build-aux/ar-lib build-aux/config.guess build-aux/config.sub build-aux/depcomp diff --git a/configure.ac b/configure.ac index cc766b20a..16a492071 100644 --- a/configure.ac +++ b/configure.ac @@ -25,24 +25,23 @@ AC_CANONICAL_HOST AH_TOP([#ifndef LIBSECP256K1_CONFIG_H]) AH_TOP([#define LIBSECP256K1_CONFIG_H]) AH_BOTTOM([#endif /*LIBSECP256K1_CONFIG_H*/]) -AM_INIT_AUTOMAKE([foreign subdir-objects]) -LT_INIT([win32-dll]) +# Require Automake 1.11.2 for AM_PROG_AR +AM_INIT_AUTOMAKE([1.11.2 foreign subdir-objects]) # Make the compilation flags quiet unless V=1 is used. m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])]) PKG_PROG_PKG_CONFIG -AC_PATH_TOOL(AR, ar) -AC_PATH_TOOL(RANLIB, ranlib) -AC_PATH_TOOL(STRIP, strip) - AC_PROG_CC if test x"$ac_cv_prog_cc_c89" = x"no"; then AC_MSG_ERROR([c89 compiler support required]) fi AM_PROG_AS +AM_PROG_AR + +LT_INIT([win32-dll]) build_windows=no From 21b2ebaf74222017f85123deb6f30a33c7678513 Mon Sep 17 00:00:00 2001 From: Tim Ruffing Date: Wed, 16 Mar 2022 16:38:17 +0100 Subject: [PATCH 44/62] configure: Remove redundant pkg-config code This removes code that detects the pkg-config tool. We used this back in the days when we had dependencies. ;) It can always be brought back if we'll need it in the future. Note that we still deliver a .pc file for this library, and there is code in Makefile.am to install it. But this does not require the pkg-config tool; only consumers of the .pc file will need it. This can be verified by running `make install` (maybe after `mkdir /tmp/pre` and `./configure --prefix=/tmp/pre` and checking that the .pc file is installed correctly. --- configure.ac | 7 ------- 1 file changed, 7 deletions(-) diff --git a/configure.ac b/configure.ac index 16a492071..ffa17942d 100644 --- a/configure.ac +++ b/configure.ac @@ -32,8 +32,6 @@ AM_INIT_AUTOMAKE([1.11.2 foreign subdir-objects]) # Make the compilation flags quiet unless V=1 is used. m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])]) -PKG_PROG_PKG_CONFIG - AC_PROG_CC if test x"$ac_cv_prog_cc_c89" = x"no"; then AC_MSG_ERROR([c89 compiler support required]) @@ -408,11 +406,6 @@ AC_SUBST(LIB_VERSION_CURRENT, _LIB_VERSION_CURRENT) AC_SUBST(LIB_VERSION_REVISION, _LIB_VERSION_REVISION) AC_SUBST(LIB_VERSION_AGE, _LIB_VERSION_AGE) -# Make sure nothing new is exported so that we don't break the cache. -PKGCONFIG_PATH_TEMP="$PKG_CONFIG_PATH" -unset PKG_CONFIG_PATH -PKG_CONFIG_PATH="$PKGCONFIG_PATH_TEMP" - AC_OUTPUT echo From 80cf4eea5fa0162350614c08f2252a07f9d7804b Mon Sep 17 00:00:00 2001 From: Jonas Nick Date: Wed, 20 Oct 2021 11:45:31 +0000 Subject: [PATCH 45/62] build: stop treating schnorrsig, extrakeys modules as experimental --- .cirrus.yml | 18 +++++------------- README.md | 5 +---- configure.ac | 12 ++---------- 3 files changed, 8 insertions(+), 27 deletions(-) diff --git a/.cirrus.yml b/.cirrus.yml index 271cbe5f5..a2e7f36d1 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -67,12 +67,12 @@ task: << : *LINUX_CONTAINER matrix: &ENV_MATRIX - env: {WIDEMUL: int64, RECOVERY: yes} - - env: {WIDEMUL: int64, ECDH: yes, EXPERIMENTAL: yes, SCHNORRSIG: yes} + - env: {WIDEMUL: int64, ECDH: yes, SCHNORRSIG: yes} - env: {WIDEMUL: int128} - - env: {WIDEMUL: int128, RECOVERY: yes, EXPERIMENTAL: yes, SCHNORRSIG: yes} - - env: {WIDEMUL: int128, ECDH: yes, EXPERIMENTAL: yes, SCHNORRSIG: yes} + - env: {WIDEMUL: int128, RECOVERY: yes, SCHNORRSIG: yes} + - env: {WIDEMUL: int128, ECDH: yes, SCHNORRSIG: yes} - env: {WIDEMUL: int128, ASM: x86_64} - - env: { RECOVERY: yes, EXPERIMENTAL: yes, SCHNORRSIG: yes} + - env: { RECOVERY: yes, SCHNORRSIG: yes} - env: {BUILD: distcheck, WITH_VALGRIND: no, CTIMETEST: no, BENCH: no} - env: {CPPFLAGS: -DDETERMINISTIC} - env: {CFLAGS: -O0, CTIMETEST: no} @@ -95,7 +95,6 @@ task: HOST: i686-linux-gnu ECDH: yes RECOVERY: yes - EXPERIMENTAL: yes SCHNORRSIG: yes matrix: - env: @@ -178,7 +177,6 @@ task: WITH_VALGRIND: no ECDH: yes RECOVERY: yes - EXPERIMENTAL: yes SCHNORRSIG: yes CTIMETEST: no << : *MERGE_BASE @@ -198,12 +196,11 @@ task: WITH_VALGRIND: no ECDH: yes RECOVERY: yes - EXPERIMENTAL: yes SCHNORRSIG: yes CTIMETEST: no matrix: - env: {} - - env: {ASM: arm} + - env: {EXPERIMENTAL: yes, ASM: arm} << : *MERGE_BASE test_script: - ./ci/cirrus.sh @@ -219,7 +216,6 @@ task: WITH_VALGRIND: no ECDH: yes RECOVERY: yes - EXPERIMENTAL: yes SCHNORRSIG: yes CTIMETEST: no << : *MERGE_BASE @@ -237,7 +233,6 @@ task: WITH_VALGRIND: no ECDH: yes RECOVERY: yes - EXPERIMENTAL: yes SCHNORRSIG: yes CTIMETEST: no << : *MERGE_BASE @@ -255,7 +250,6 @@ task: WITH_VALGRIND: no ECDH: yes RECOVERY: yes - EXPERIMENTAL: yes SCHNORRSIG: yes CTIMETEST: no << : *MERGE_BASE @@ -269,7 +263,6 @@ task: env: ECDH: yes RECOVERY: yes - EXPERIMENTAL: yes SCHNORRSIG: yes CTIMETEST: no matrix: @@ -317,7 +310,6 @@ task: CC: gcc MAKEFLAGS: -j4 CC=g++ CFLAGS=-fpermissive\ -g WERROR_CFLAGS: - EXPERIMENTAL: yes ECDH: yes RECOVERY: yes SCHNORRSIG: yes diff --git a/README.md b/README.md index f1ae215c8..adaa7d9aa 100644 --- a/README.md +++ b/README.md @@ -17,9 +17,7 @@ Features: * Suitable for embedded systems. * Optional module for public key recovery. * Optional module for ECDH key exchange. -* Optional module for Schnorr signatures according to [BIP-340](https://github.com/bitcoin/bips/blob/master/bip-0340.mediawiki) (experimental). - -Experimental features have not received enough scrutiny to satisfy the standard of quality of this library but are made available for testing and review by the community. The APIs of these features should not be considered stable. +* Optional module for Schnorr signatures according to [BIP-340](https://github.com/bitcoin/bips/blob/master/bip-0340.mediawiki). Implementation details ---------------------- @@ -72,7 +70,6 @@ libsecp256k1 is built using autotools: Usage examples ----------- Usage examples can be found in the [examples](examples) directory. To compile them you need to configure with `--enable-examples`. - For experimental modules, you will also need `--enable-experimental` as well as a flag for each individual module, e.g. `--enable-module-ecdh`. * [ECDSA example](examples/ecdsa.c) * [Schnorr Signatures example](examples/schnorr.c) * [Deriving a shared secret(ECDH) example](examples/ecdh.c) diff --git a/configure.ac b/configure.ac index cc766b20a..dbf97959b 100644 --- a/configure.ac +++ b/configure.ac @@ -152,11 +152,11 @@ AC_ARG_ENABLE(module_recovery, [SECP_SET_DEFAULT([enable_module_recovery], [no], [yes])]) AC_ARG_ENABLE(module_extrakeys, - AS_HELP_STRING([--enable-module-extrakeys],[enable extrakeys module (experimental)]), [], + AS_HELP_STRING([--enable-module-extrakeys],[enable extrakeys module]), [], [SECP_SET_DEFAULT([enable_module_extrakeys], [no], [yes])]) AC_ARG_ENABLE(module_schnorrsig, - AS_HELP_STRING([--enable-module-schnorrsig],[enable schnorrsig module (experimental)]), [], + AS_HELP_STRING([--enable-module-schnorrsig],[enable schnorrsig module]), [], [SECP_SET_DEFAULT([enable_module_schnorrsig], [no], [yes])]) AC_ARG_ENABLE(external_default_callbacks, @@ -367,16 +367,8 @@ if test x"$enable_experimental" = x"yes"; then AC_MSG_NOTICE([******]) AC_MSG_NOTICE([WARNING: experimental build]) AC_MSG_NOTICE([Experimental features do not have stable APIs or properties, and may not be safe for production use.]) - AC_MSG_NOTICE([Building extrakeys module: $enable_module_extrakeys]) - AC_MSG_NOTICE([Building schnorrsig module: $enable_module_schnorrsig]) AC_MSG_NOTICE([******]) else - if test x"$enable_module_extrakeys" = x"yes"; then - AC_MSG_ERROR([extrakeys module is experimental. Use --enable-experimental to allow.]) - fi - if test x"$enable_module_schnorrsig" = x"yes"; then - AC_MSG_ERROR([schnorrsig module is experimental. Use --enable-experimental to allow.]) - fi if test x"$set_asm" = x"arm"; then AC_MSG_ERROR([ARM assembly optimization is experimental. Use --enable-experimental to allow.]) fi From 3db0560606acb285cc7ef11662ce166ed67e9015 Mon Sep 17 00:00:00 2001 From: Tim Ruffing Date: Wed, 16 Mar 2022 11:12:54 +0100 Subject: [PATCH 46/62] Add SECP256K1_DEPRECATED attribute for marking API parts as deprecated --- include/secp256k1.h | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/include/secp256k1.h b/include/secp256k1.h index 57114b8f2..c1186731c 100644 --- a/include/secp256k1.h +++ b/include/secp256k1.h @@ -169,6 +169,17 @@ typedef int (*secp256k1_nonce_function)( # define SECP256K1_ARG_NONNULL(_x) # endif +/** Attribute for marking functions, types, and variables as deprecated */ +#if !defined(SECP256K1_BUILD) && defined(__has_attribute) +# if __has_attribute(__deprecated__) +# define SECP256K1_DEPRECATED(_msg) __attribute__ ((__deprecated__(_msg))) +# else +# define SECP256K1_DEPRECATED(_msg) +# endif +#else +# define SECP256K1_DEPRECATED(_msg) +#endif + /** All flags' lower 8 bits indicate what they're for. Do not use directly. */ #define SECP256K1_FLAGS_TYPE_MASK ((1 << 8) - 1) #define SECP256K1_FLAGS_TYPE_CONTEXT (1 << 0) From fc94a2da4457325c4be539838ceed21b31c60fbd Mon Sep 17 00:00:00 2001 From: Tim Ruffing Date: Wed, 16 Mar 2022 11:21:22 +0100 Subject: [PATCH 47/62] Use SECP256K1_DEPRECATED for existing deprecated API functions --- include/secp256k1.h | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/include/secp256k1.h b/include/secp256k1.h index c1186731c..20b30d97b 100644 --- a/include/secp256k1.h +++ b/include/secp256k1.h @@ -652,7 +652,8 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_seckey_negate( SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_privkey_negate( const secp256k1_context* ctx, unsigned char *seckey -) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2); +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) + SECP256K1_DEPRECATED("Use secp256k1_ec_seckey_negate instead"); /** Negates a public key in place. * @@ -692,7 +693,8 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_privkey_tweak_add( const secp256k1_context* ctx, unsigned char *seckey, const unsigned char *tweak32 -) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) + SECP256K1_DEPRECATED("Use secp256k1_ec_seckey_tweak_add instead"); /** Tweak a public key by adding tweak times the generator to it. * @@ -738,7 +740,8 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_privkey_tweak_mul( const secp256k1_context* ctx, unsigned char *seckey, const unsigned char *tweak32 -) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) + SECP256K1_DEPRECATED("Use secp256k1_ec_seckey_tweak_mul instead"); /** Tweak a public key by multiplying it by a tweak value. * From 99e6568fc6ea2768f5355eb4617283086f756931 Mon Sep 17 00:00:00 2001 From: Tim Ruffing Date: Wed, 16 Mar 2022 11:43:13 +0100 Subject: [PATCH 48/62] schnorrsig: Rename schnorrsig_sign to schnorsig_sign32 and deprecate --- examples/schnorr.c | 2 +- include/secp256k1_schnorrsig.h | 13 ++++++++++- src/modules/schnorrsig/main_impl.h | 6 ++++- src/modules/schnorrsig/tests_impl.h | 35 ++++++++++++++++------------- src/valgrind_ctime_test.c | 2 +- 5 files changed, 38 insertions(+), 20 deletions(-) diff --git a/examples/schnorr.c b/examples/schnorr.c index a3533b640..fa6c9ba9d 100644 --- a/examples/schnorr.c +++ b/examples/schnorr.c @@ -96,7 +96,7 @@ int main(void) { * improve security against side-channel attacks. Signing with a valid * context, verified keypair and the default nonce function should never * fail. */ - return_val = secp256k1_schnorrsig_sign(ctx, signature, msg_hash, &keypair, auxiliary_rand); + return_val = secp256k1_schnorrsig_sign32(ctx, signature, msg_hash, &keypair, auxiliary_rand); assert(return_val); /*** Verification ***/ diff --git a/include/secp256k1_schnorrsig.h b/include/secp256k1_schnorrsig.h index e971ddc2a..5fedcb07b 100644 --- a/include/secp256k1_schnorrsig.h +++ b/include/secp256k1_schnorrsig.h @@ -116,7 +116,7 @@ typedef struct { * BIP-340 "Default Signing" for a full explanation of this * argument and for guidance if randomness is expensive. */ -SECP256K1_API int secp256k1_schnorrsig_sign( +SECP256K1_API int secp256k1_schnorrsig_sign32( const secp256k1_context* ctx, unsigned char *sig64, const unsigned char *msg32, @@ -124,6 +124,17 @@ SECP256K1_API int secp256k1_schnorrsig_sign( const unsigned char *aux_rand32 ) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4); +/** Same as secp256k1_schnorrsig_sign32, but DEPRECATED. Will be removed in + * future versions. */ +SECP256K1_API int secp256k1_schnorrsig_sign( + const secp256k1_context* ctx, + unsigned char *sig64, + const unsigned char *msg32, + const secp256k1_keypair *keypair, + const unsigned char *aux_rand32 +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4) + SECP256K1_DEPRECATED("Use secp256k1_schnorrsig_sign32 instead"); + /** Create a Schnorr signature with a more flexible API. * * Same arguments as secp256k1_schnorrsig_sign except that it allows signing diff --git a/src/modules/schnorrsig/main_impl.h b/src/modules/schnorrsig/main_impl.h index 94e3ee414..cd651591c 100644 --- a/src/modules/schnorrsig/main_impl.h +++ b/src/modules/schnorrsig/main_impl.h @@ -192,11 +192,15 @@ static int secp256k1_schnorrsig_sign_internal(const secp256k1_context* ctx, unsi return ret; } -int secp256k1_schnorrsig_sign(const secp256k1_context* ctx, unsigned char *sig64, const unsigned char *msg32, const secp256k1_keypair *keypair, const unsigned char *aux_rand32) { +int secp256k1_schnorrsig_sign32(const secp256k1_context* ctx, unsigned char *sig64, const unsigned char *msg32, const secp256k1_keypair *keypair, const unsigned char *aux_rand32) { /* We cast away const from the passed aux_rand32 argument since we know the default nonce function does not modify it. */ return secp256k1_schnorrsig_sign_internal(ctx, sig64, msg32, 32, keypair, secp256k1_nonce_function_bip340, (unsigned char*)aux_rand32); } +int secp256k1_schnorrsig_sign(const secp256k1_context* ctx, unsigned char *sig64, const unsigned char *msg32, const secp256k1_keypair *keypair, const unsigned char *aux_rand32) { + return secp256k1_schnorrsig_sign32(ctx, sig64, msg32, keypair, aux_rand32); +} + int secp256k1_schnorrsig_sign_custom(const secp256k1_context* ctx, unsigned char *sig64, const unsigned char *msg, size_t msglen, const secp256k1_keypair *keypair, secp256k1_schnorrsig_extraparams *extraparams) { secp256k1_nonce_function_hardened noncefp = NULL; void *ndata = NULL; diff --git a/src/modules/schnorrsig/tests_impl.h b/src/modules/schnorrsig/tests_impl.h index 7c4321f97..25840b8fa 100644 --- a/src/modules/schnorrsig/tests_impl.h +++ b/src/modules/schnorrsig/tests_impl.h @@ -160,21 +160,21 @@ void test_schnorrsig_api(void) { /** main test body **/ ecount = 0; - CHECK(secp256k1_schnorrsig_sign(none, sig, msg, &keypairs[0], NULL) == 1); + CHECK(secp256k1_schnorrsig_sign32(none, sig, msg, &keypairs[0], NULL) == 1); CHECK(ecount == 0); - CHECK(secp256k1_schnorrsig_sign(vrfy, sig, msg, &keypairs[0], NULL) == 1); + CHECK(secp256k1_schnorrsig_sign32(vrfy, sig, msg, &keypairs[0], NULL) == 1); CHECK(ecount == 0); - CHECK(secp256k1_schnorrsig_sign(sign, sig, msg, &keypairs[0], NULL) == 1); + CHECK(secp256k1_schnorrsig_sign32(sign, sig, msg, &keypairs[0], NULL) == 1); CHECK(ecount == 0); - CHECK(secp256k1_schnorrsig_sign(sign, NULL, msg, &keypairs[0], NULL) == 0); + CHECK(secp256k1_schnorrsig_sign32(sign, NULL, msg, &keypairs[0], NULL) == 0); CHECK(ecount == 1); - CHECK(secp256k1_schnorrsig_sign(sign, sig, NULL, &keypairs[0], NULL) == 0); + CHECK(secp256k1_schnorrsig_sign32(sign, sig, NULL, &keypairs[0], NULL) == 0); CHECK(ecount == 2); - CHECK(secp256k1_schnorrsig_sign(sign, sig, msg, NULL, NULL) == 0); + CHECK(secp256k1_schnorrsig_sign32(sign, sig, msg, NULL, NULL) == 0); CHECK(ecount == 3); - CHECK(secp256k1_schnorrsig_sign(sign, sig, msg, &invalid_keypair, NULL) == 0); + CHECK(secp256k1_schnorrsig_sign32(sign, sig, msg, &invalid_keypair, NULL) == 0); CHECK(ecount == 4); - CHECK(secp256k1_schnorrsig_sign(sttc, sig, msg, &keypairs[0], NULL) == 0); + CHECK(secp256k1_schnorrsig_sign32(sttc, sig, msg, &keypairs[0], NULL) == 0); CHECK(ecount == 5); ecount = 0; @@ -202,7 +202,7 @@ void test_schnorrsig_api(void) { CHECK(ecount == 6); ecount = 0; - CHECK(secp256k1_schnorrsig_sign(sign, sig, msg, &keypairs[0], NULL) == 1); + CHECK(secp256k1_schnorrsig_sign32(sign, sig, msg, &keypairs[0], NULL) == 1); CHECK(secp256k1_schnorrsig_verify(none, sig, msg, sizeof(msg), &pk[0]) == 1); CHECK(ecount == 0); CHECK(secp256k1_schnorrsig_verify(sign, sig, msg, sizeof(msg), &pk[0]) == 1); @@ -247,7 +247,7 @@ void test_schnorrsig_bip_vectors_check_signing(const unsigned char *sk, const un secp256k1_xonly_pubkey pk, pk_expected; CHECK(secp256k1_keypair_create(ctx, &keypair, sk)); - CHECK(secp256k1_schnorrsig_sign(ctx, sig, msg32, &keypair, aux_rand)); + CHECK(secp256k1_schnorrsig_sign32(ctx, sig, msg32, &keypair, aux_rand)); CHECK(secp256k1_memcmp_var(sig, expected_sig, 64) == 0); CHECK(secp256k1_xonly_pubkey_parse(ctx, &pk_expected, pk_serialized)); @@ -740,8 +740,11 @@ void test_schnorrsig_sign(void) { secp256k1_testrand256(aux_rand); CHECK(secp256k1_keypair_create(ctx, &keypair, sk)); CHECK(secp256k1_keypair_xonly_pub(ctx, &pk, NULL, &keypair)); - CHECK(secp256k1_schnorrsig_sign(ctx, sig, msg, &keypair, NULL) == 1); + CHECK(secp256k1_schnorrsig_sign32(ctx, sig, msg, &keypair, NULL) == 1); CHECK(secp256k1_schnorrsig_verify(ctx, sig, msg, sizeof(msg), &pk)); + /* Check that deprecated alias gives the same result */ + CHECK(secp256k1_schnorrsig_sign(ctx, sig2, msg, &keypair, NULL) == 1); + CHECK(secp256k1_memcmp_var(sig, sig2, sizeof(sig)) == 0); /* Test different nonce functions */ CHECK(secp256k1_schnorrsig_sign_custom(ctx, sig, msg, sizeof(msg), &keypair, &extraparams) == 1); @@ -764,7 +767,7 @@ void test_schnorrsig_sign(void) { extraparams.noncefp = NULL; extraparams.ndata = aux_rand; CHECK(secp256k1_schnorrsig_sign_custom(ctx, sig, msg, sizeof(msg), &keypair, &extraparams) == 1); - CHECK(secp256k1_schnorrsig_sign(ctx, sig2, msg, &keypair, extraparams.ndata) == 1); + CHECK(secp256k1_schnorrsig_sign32(ctx, sig2, msg, &keypair, extraparams.ndata) == 1); CHECK(secp256k1_memcmp_var(sig, sig2, sizeof(sig)) == 0); } @@ -787,7 +790,7 @@ void test_schnorrsig_sign_verify(void) { for (i = 0; i < N_SIGS; i++) { secp256k1_testrand256(msg[i]); - CHECK(secp256k1_schnorrsig_sign(ctx, sig[i], msg[i], &keypair, NULL)); + CHECK(secp256k1_schnorrsig_sign32(ctx, sig[i], msg[i], &keypair, NULL)); CHECK(secp256k1_schnorrsig_verify(ctx, sig[i], msg[i], sizeof(msg[i]), &pk)); } @@ -816,13 +819,13 @@ void test_schnorrsig_sign_verify(void) { } /* Test overflowing s */ - CHECK(secp256k1_schnorrsig_sign(ctx, sig[0], msg[0], &keypair, NULL)); + CHECK(secp256k1_schnorrsig_sign32(ctx, sig[0], msg[0], &keypair, NULL)); CHECK(secp256k1_schnorrsig_verify(ctx, sig[0], msg[0], sizeof(msg[0]), &pk)); memset(&sig[0][32], 0xFF, 32); CHECK(!secp256k1_schnorrsig_verify(ctx, sig[0], msg[0], sizeof(msg[0]), &pk)); /* Test negative s */ - CHECK(secp256k1_schnorrsig_sign(ctx, sig[0], msg[0], &keypair, NULL)); + CHECK(secp256k1_schnorrsig_sign32(ctx, sig[0], msg[0], &keypair, NULL)); CHECK(secp256k1_schnorrsig_verify(ctx, sig[0], msg[0], sizeof(msg[0]), &pk)); secp256k1_scalar_set_b32(&s, &sig[0][32], NULL); secp256k1_scalar_negate(&s, &s); @@ -873,7 +876,7 @@ void test_schnorrsig_taproot(void) { /* Key spend */ secp256k1_testrand256(msg); - CHECK(secp256k1_schnorrsig_sign(ctx, sig, msg, &keypair, NULL) == 1); + CHECK(secp256k1_schnorrsig_sign32(ctx, sig, msg, &keypair, NULL) == 1); /* Verify key spend */ CHECK(secp256k1_xonly_pubkey_parse(ctx, &output_pk, output_pk_bytes) == 1); CHECK(secp256k1_schnorrsig_verify(ctx, sig, msg, sizeof(msg), &output_pk) == 1); diff --git a/src/valgrind_ctime_test.c b/src/valgrind_ctime_test.c index ea6d4b3de..6ff0085d3 100644 --- a/src/valgrind_ctime_test.c +++ b/src/valgrind_ctime_test.c @@ -166,7 +166,7 @@ void run_tests(secp256k1_context *ctx, unsigned char *key) { ret = secp256k1_keypair_create(ctx, &keypair, key); VALGRIND_MAKE_MEM_DEFINED(&ret, sizeof(ret)); CHECK(ret == 1); - ret = secp256k1_schnorrsig_sign(ctx, sig, msg, &keypair, NULL); + ret = secp256k1_schnorrsig_sign32(ctx, sig, msg, &keypair, NULL); VALGRIND_MAKE_MEM_DEFINED(&ret, sizeof(ret)); CHECK(ret == 1); #endif From f813bb0df3153dc055e0e76101ed9e4607155870 Mon Sep 17 00:00:00 2001 From: Tim Ruffing Date: Wed, 16 Mar 2022 12:40:08 +0100 Subject: [PATCH 49/62] schnorrsig: Adapt example to new API --- examples/schnorr.c | 48 ++++++++++++++++++++++++++++++---------------- 1 file changed, 32 insertions(+), 16 deletions(-) diff --git a/examples/schnorr.c b/examples/schnorr.c index fa6c9ba9d..82eb07d5d 100644 --- a/examples/schnorr.c +++ b/examples/schnorr.c @@ -18,16 +18,9 @@ #include "random.h" int main(void) { - /* Instead of signing the message directly, we must sign a 32-byte hash. - * Here the message is "Hello, world!" and the hash function was SHA-256. - * An actual implementation should just call SHA-256, but this example - * hardcodes the output to avoid depending on an additional library. */ - unsigned char msg_hash[32] = { - 0x31, 0x5F, 0x5B, 0xDB, 0x76, 0xD0, 0x78, 0xC4, - 0x3B, 0x8A, 0xC0, 0x06, 0x4E, 0x4A, 0x01, 0x64, - 0x61, 0x2B, 0x1F, 0xCE, 0x77, 0xC8, 0x69, 0x34, - 0x5B, 0xFC, 0x94, 0xC7, 0x58, 0x94, 0xED, 0xD3, - }; + unsigned char msg[12] = "Hello World!"; + unsigned char msg_hash[32]; + unsigned char tag[17] = "my_fancy_protocol"; unsigned char seckey[32]; unsigned char randomize[32]; unsigned char auxiliary_rand[32]; @@ -84,18 +77,37 @@ int main(void) { /*** Signing ***/ + /* Instead of signing (possibly very long) messages directly, we sign a + * 32-byte hash of the message in this example. + * + * We use secp256k1_tagged_sha256 to create this hash. This function expects + * a context-specific "tag", which restricts the context in which the signed + * messages should be considered valid. For example, if protocol A mandates + * to use the tag "my_fancy_protocol" and protocol B mandates to use the tag + * "my_boring_protocol", then signed messages from protocol A will never be + * valid in protocol B (and vice versa), even if keys are reused across + * protocols. This implements "domain separation", which is considered good + * practice. It avoids attacks in which users are tricked into signing a + * message that has intended consequences in the intended context (e.g., + * protocol A) but would have unintended consequences if it were valid in + * some other context (e.g., protocol B). */ + return_val = secp256k1_tagged_sha256(ctx, msg_hash, tag, sizeof(tag), msg, sizeof(msg)); + assert(return_val); + /* Generate 32 bytes of randomness to use with BIP-340 schnorr signing. */ if (!fill_random(auxiliary_rand, sizeof(auxiliary_rand))) { printf("Failed to generate randomness\n"); return 1; } - /* Generate a Schnorr signature `noncefp` and `ndata` allows you to pass a - * custom nonce function, passing `NULL` will use the BIP-340 safe default. - * BIP-340 recommends passing 32 bytes of randomness to the nonce function to - * improve security against side-channel attacks. Signing with a valid - * context, verified keypair and the default nonce function should never - * fail. */ + /* Generate a Schnorr signature. + * + * We use the secp256k1_schnorrsig_sign32 function that provides a simple + * interface for signing 32-byte messages (which in our case is a hash of + * the actual message). BIP-340 recommends passing 32 bytes of randomness + * to the signing function to improve security against side-channel attacks. + * Signing with a valid context, a 32-byte message, a verified keypair, and + * any 32 bytes of auxiliary random data should never fail. */ return_val = secp256k1_schnorrsig_sign32(ctx, signature, msg_hash, &keypair, auxiliary_rand); assert(return_val); @@ -108,6 +120,10 @@ int main(void) { return 1; } + /* Compute the tagged hash on the received messages using the same tag as the signer. */ + return_val = secp256k1_tagged_sha256(ctx, msg_hash, tag, sizeof(tag), msg, sizeof(msg)); + assert(return_val); + /* Verify a signature. This will return 1 if it's valid and 0 if it's not. */ is_signature_valid = secp256k1_schnorrsig_verify(ctx, signature, msg_hash, 32, &pubkey); From b8f8b99f0fb3a5cd4c6fb1c9c8dfed881839e19e Mon Sep 17 00:00:00 2001 From: Tim Ruffing Date: Wed, 16 Mar 2022 12:43:18 +0100 Subject: [PATCH 50/62] docs: Fix return value for functions that don't have invalid inputs _tagged_sha256 simply cannot have invalid inputs. The other functions could in some sense have invalid inputs but only in violation of the type system. For example, a pubkey could be invalid but invalid objects of type secp256k1_pubkey either can't be obtained via the API or will be caught by an ARG_CHECK when calling pubkey_load. This is consistent with similar functions in the public API, e.g., _ec_pubkey_negate or _ec_pubkey_serialize. --- include/secp256k1.h | 2 +- include/secp256k1_extrakeys.h | 9 ++++----- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/include/secp256k1.h b/include/secp256k1.h index 20b30d97b..86ab7e556 100644 --- a/include/secp256k1.h +++ b/include/secp256k1.h @@ -814,7 +814,7 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_pubkey_combine( * implementations optimized for a specific tag can precompute the SHA256 state * after hashing the tag hashes. * - * Returns 0 if the arguments are invalid and 1 otherwise. + * Returns: 1 always. * Args: ctx: pointer to a context object * Out: hash32: pointer to a 32-byte array to store the resulting hash * In: tag: pointer to an array containing the tag diff --git a/include/secp256k1_extrakeys.h b/include/secp256k1_extrakeys.h index a64d561b6..09cbeaaa8 100644 --- a/include/secp256k1_extrakeys.h +++ b/include/secp256k1_extrakeys.h @@ -81,8 +81,7 @@ SECP256K1_API int secp256k1_xonly_pubkey_cmp( /** Converts a secp256k1_pubkey into a secp256k1_xonly_pubkey. * - * Returns: 1 if the public key was successfully converted - * 0 otherwise + * Returns: 1 always. * * Args: ctx: pointer to a context object. * Out: xonly_pubkey: pointer to an x-only public key object for placing the converted public key. @@ -172,7 +171,7 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_keypair_create( /** Get the secret key from a keypair. * - * Returns: 0 if the arguments are invalid. 1 otherwise. + * Returns: 1 always. * Args: ctx: pointer to a context object. * Out: seckey: pointer to a 32-byte buffer for the secret key. * In: keypair: pointer to a keypair. @@ -185,7 +184,7 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_keypair_sec( /** Get the public key from a keypair. * - * Returns: 0 if the arguments are invalid. 1 otherwise. + * Returns: 1 always. * Args: ctx: pointer to a context object. * Out: pubkey: pointer to a pubkey object. If 1 is returned, it is set to * the keypair public key. If not, it's set to an invalid value. @@ -202,7 +201,7 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_keypair_pub( * This is the same as calling secp256k1_keypair_pub and then * secp256k1_xonly_pubkey_from_pubkey. * - * Returns: 0 if the arguments are invalid. 1 otherwise. + * Returns: 1 always. * Args: ctx: pointer to a context object. * Out: pubkey: pointer to an xonly_pubkey object. If 1 is returned, it is set * to the keypair public key after converting it to an From 7f09d0f311117289719b690f91f6a907c2c6f3e2 Mon Sep 17 00:00:00 2001 From: Jonas Nick Date: Wed, 20 Oct 2021 13:35:14 +0000 Subject: [PATCH 51/62] README: mention that ARM assembly is experimental --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index adaa7d9aa..e347889c8 100644 --- a/README.md +++ b/README.md @@ -33,6 +33,7 @@ Implementation details * Optimized implementation of arithmetic modulo the curve's field size (2^256 - 0x1000003D1). * Using 5 52-bit limbs (including hand-optimized assembly for x86_64, by Diederik Huys). * Using 10 26-bit limbs (including hand-optimized assembly for 32-bit ARM, by Wladimir J. van der Laan). + * This is an experimental feature that has not received enough scrutiny to satisfy the standard of quality of this library but is made available for testing and review by the community. * Scalar operations * Optimized implementation without data-dependent branches of arithmetic modulo the curve's order. * Using 4 64-bit limbs (relying on __int128 support in the compiler). From f8d9174357391ab4bf65a2f4d9d9cfb8039dc592 Mon Sep 17 00:00:00 2001 From: Tim Ruffing Date: Wed, 22 Apr 2020 11:21:08 +0200 Subject: [PATCH 52/62] Add SHA256 bit counter tests --- src/tests.c | 103 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 103 insertions(+) diff --git a/src/tests.c b/src/tests.c index 7dbe4644a..059ebaa00 100644 --- a/src/tests.c +++ b/src/tests.c @@ -508,6 +508,108 @@ void run_sha256_known_output_tests(void) { } } +/** SHA256 counter tests + +The tests verify that the SHA256 counter doesn't wrap around at message length +2^i bytes for i = 20, ..., 33. This wide range aims at being independent of the +implementation of the counter and it catches multiple natural 32-bit overflows +(e.g., counting bits, counting bytes, counting blocks, ...). + +The test vectors have been generated using following Python script which relies +on https://github.com/cloudtools/sha256/ (v0.3 on Python v3.10.2). + +``` +from sha256 import sha256 +from copy import copy + +def midstate_c_definition(hasher): + ret = ' {{0x' + hasher.state[0].hex('_', 4).replace('_', ', 0x') + '},\n' + ret += ' {0x00}, ' + str(hex(hasher.state[1])) + '}' + return ret + +def output_c_literal(hasher): + return '{0x' + hasher.digest().hex('_').replace('_', ', 0x') + '}' + +MESSAGE = b'abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmno' +assert(len(MESSAGE) == 64) +BYTE_BOUNDARIES = [(2**b)//len(MESSAGE) - 1 for b in range(20, 34)] + +midstates = [] +digests = [] +hasher = sha256() +for i in range(BYTE_BOUNDARIES[-1] + 1): + if i in BYTE_BOUNDARIES: + midstates.append(midstate_c_definition(hasher)) + hasher_copy = copy(hasher) + hasher_copy.update(MESSAGE) + digests.append(output_c_literal(hasher_copy)) + hasher.update(MESSAGE) + +for x in midstates: + print(x + ',') + +for x in digests: + print(x + ',') +``` +*/ +void run_sha256_counter_tests(void) { + static const char *input = "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmno"; + static const secp256k1_sha256 midstates[] = { + {{0xa2b5c8bb, 0x26c88bb3, 0x2abdc3d2, 0x9def99a3, 0xdfd21a6e, 0x41fe585b, 0x7ef2c440, 0x2b79adda}, + {0x00}, 0xfffc0}, + {{0xa0d29445, 0x9287de66, 0x76aabd71, 0x41acd765, 0x0c7528b4, 0x84e14906, 0x942faec6, 0xcc5a7b26}, + {0x00}, 0x1fffc0}, + {{0x50449526, 0xb9f1d657, 0xa0fc13e9, 0x50860f10, 0xa550c431, 0x3fbc97c1, 0x7bbb2d89, 0xdb67bac1}, + {0x00}, 0x3fffc0}, + {{0x54a6efdc, 0x46762e7b, 0x88bfe73f, 0xbbd149c7, 0x41620c43, 0x1168da7b, 0x2c5960f9, 0xeccffda6}, + {0x00}, 0x7fffc0}, + {{0x2515a8f5, 0x5faa2977, 0x3a850486, 0xac858cad, 0x7b7276ee, 0x235c0385, 0xc53a157c, 0x7cb3e69c}, + {0x00}, 0xffffc0}, + {{0x34f39828, 0x409fedb7, 0x4bbdd0fb, 0x3b643634, 0x7806bf2e, 0xe0d1b713, 0xca3f2e1e, 0xe38722c2}, + {0x00}, 0x1ffffc0}, + {{0x389ef5c5, 0x38c54167, 0x8f5d56ab, 0x582a75cc, 0x8217caef, 0xf10947dd, 0x6a1998a8, 0x048f0b8c}, + {0x00}, 0x3ffffc0}, + {{0xd6c3f394, 0x0bee43b9, 0x6783f497, 0x29fa9e21, 0x6ce491c1, 0xa81fe45e, 0x2fc3859a, 0x269012d0}, + {0x00}, 0x7ffffc0}, + {{0x6dd3c526, 0x44d88aa0, 0x806a1bae, 0xfbcc0d32, 0x9d6144f3, 0x9d2bd757, 0x9851a957, 0xb50430ad}, + {0x00}, 0xfffffc0}, + {{0x2add4021, 0xdfe8a9e6, 0xa56317c6, 0x7a15f5bb, 0x4a48aacd, 0x5d368414, 0x4f00e6f0, 0xd9355023}, + {0x00}, 0x1fffffc0}, + {{0xb66666b4, 0xdbeac32b, 0x0ea351ae, 0xcba9da46, 0x6278b874, 0x8c508e23, 0xe16ca776, 0x8465bac1}, + {0x00}, 0x3fffffc0}, + {{0xb6744789, 0x9cce87aa, 0xc4c478b7, 0xf38404d8, 0x2e38ba62, 0xa3f7019b, 0x50458fe7, 0x3047dbec}, + {0x00}, 0x7fffffc0}, + {{0x8b1297ba, 0xba261a80, 0x2ba1b0dd, 0xfbc67d6d, 0x61072c4e, 0x4b5a2a0f, 0x52872760, 0x2dfeb162}, + {0x00}, 0xffffffc0}, + {{0x24f33cf7, 0x41ad6583, 0x41c8ff5d, 0xca7ef35f, 0x50395756, 0x021b743e, 0xd7126cd7, 0xd037473a}, + {0x00}, 0x1ffffffc0}, + }; + static const unsigned char outputs[][32] = { + {0x0e, 0x83, 0xe2, 0xc9, 0x4f, 0xb2, 0xb8, 0x2b, 0x89, 0x06, 0x92, 0x78, 0x04, 0x03, 0x48, 0x5c, 0x48, 0x44, 0x67, 0x61, 0x77, 0xa4, 0xc7, 0x90, 0x9e, 0x92, 0x55, 0x10, 0x05, 0xfe, 0x39, 0x15}, + {0x1d, 0x1e, 0xd7, 0xb8, 0xa3, 0xa7, 0x8a, 0x79, 0xfd, 0xa0, 0x05, 0x08, 0x9c, 0xeb, 0xf0, 0xec, 0x67, 0x07, 0x9f, 0x8e, 0x3c, 0x0d, 0x8e, 0xf9, 0x75, 0x55, 0x13, 0xc1, 0xe8, 0x77, 0xf8, 0xbb}, + {0x66, 0x95, 0x6c, 0xc9, 0xe0, 0x39, 0x65, 0xb6, 0xb0, 0x05, 0xd1, 0xaf, 0xaf, 0xf3, 0x1d, 0xb9, 0xa4, 0xda, 0x6f, 0x20, 0xcd, 0x3a, 0xae, 0x64, 0xc2, 0xdb, 0xee, 0xf5, 0xb8, 0x8d, 0x57, 0x0e}, + {0x3c, 0xbb, 0x1c, 0x12, 0x5e, 0x17, 0xfd, 0x54, 0x90, 0x45, 0xa7, 0x7b, 0x61, 0x6c, 0x1d, 0xfe, 0xe6, 0xcc, 0x7f, 0xee, 0xcf, 0xef, 0x33, 0x35, 0x50, 0x62, 0x16, 0x70, 0x2f, 0x87, 0xc3, 0xc9}, + {0x53, 0x4d, 0xa8, 0xe7, 0x1e, 0x98, 0x73, 0x8d, 0xd9, 0xa3, 0x54, 0xa5, 0x0e, 0x59, 0x2c, 0x25, 0x43, 0x6f, 0xaa, 0xa2, 0xf5, 0x21, 0x06, 0x3e, 0xc9, 0x82, 0x06, 0x94, 0x98, 0x72, 0x9d, 0xa7}, + {0xef, 0x7e, 0xe9, 0x6b, 0xd3, 0xe5, 0xb7, 0x41, 0x4c, 0xc8, 0xd3, 0x07, 0x52, 0x9a, 0x5a, 0x8b, 0x4e, 0x1e, 0x75, 0xa4, 0x17, 0x78, 0xc8, 0x36, 0xcd, 0xf8, 0x2e, 0xd9, 0x57, 0xe3, 0xd7, 0x07}, + {0x87, 0x16, 0xfb, 0xf9, 0xa5, 0xf8, 0xc4, 0x56, 0x2b, 0x48, 0x52, 0x8e, 0x2d, 0x30, 0x85, 0xb6, 0x4c, 0x56, 0xb5, 0xd1, 0x16, 0x9c, 0xcf, 0x32, 0x95, 0xad, 0x03, 0xe8, 0x05, 0x58, 0x06, 0x76}, + {0x75, 0x03, 0x80, 0x28, 0xf2, 0xa7, 0x63, 0x22, 0x1a, 0x26, 0x9c, 0x68, 0xe0, 0x58, 0xfc, 0x73, 0xeb, 0x42, 0xf6, 0x86, 0x16, 0x24, 0x4b, 0xbc, 0x24, 0xf7, 0x02, 0xc8, 0x3d, 0x90, 0xe2, 0xb0}, + {0xdf, 0x49, 0x0f, 0x15, 0x7b, 0x7d, 0xbf, 0xe0, 0xd4, 0xcf, 0x47, 0xc0, 0x80, 0x93, 0x4a, 0x61, 0xaa, 0x03, 0x07, 0x66, 0xb3, 0x38, 0x5d, 0xc8, 0xc9, 0x07, 0x61, 0xfb, 0x97, 0x10, 0x2f, 0xd8}, + {0x77, 0x19, 0x40, 0x56, 0x41, 0xad, 0xbc, 0x59, 0xda, 0x1e, 0xc5, 0x37, 0x14, 0x63, 0x7b, 0xfb, 0x79, 0xe2, 0x7a, 0xb1, 0x55, 0x42, 0x99, 0x42, 0x56, 0xfe, 0x26, 0x9d, 0x0f, 0x7e, 0x80, 0xc6}, + {0x50, 0xe7, 0x2a, 0x0e, 0x26, 0x44, 0x2f, 0xe2, 0x55, 0x2d, 0xc3, 0x93, 0x8a, 0xc5, 0x86, 0x58, 0x22, 0x8c, 0x0c, 0xbf, 0xb1, 0xd2, 0xca, 0x87, 0x2a, 0xe4, 0x35, 0x26, 0x6f, 0xcd, 0x05, 0x5e}, + {0xe4, 0x80, 0x6f, 0xdb, 0x3d, 0x7d, 0xba, 0xde, 0x50, 0x3f, 0xea, 0x00, 0x3d, 0x46, 0x59, 0x64, 0xfd, 0x58, 0x1c, 0xa1, 0xb8, 0x7d, 0x5f, 0xac, 0x94, 0x37, 0x9e, 0xa0, 0xc0, 0x9c, 0x93, 0x8b}, + {0x2c, 0xf3, 0xa9, 0xf6, 0x15, 0x25, 0x80, 0x70, 0x76, 0x99, 0x7d, 0xf1, 0xc3, 0x2f, 0xa3, 0x31, 0xff, 0x92, 0x35, 0x2e, 0x8d, 0x04, 0x13, 0x33, 0xd8, 0x0d, 0xdb, 0x4a, 0xf6, 0x8c, 0x03, 0x34}, + {0xec, 0x12, 0x24, 0x9f, 0x35, 0xa4, 0x29, 0x8b, 0x9e, 0x4a, 0x95, 0xf8, 0x61, 0xaf, 0x61, 0xc5, 0x66, 0x55, 0x3e, 0x3f, 0x2a, 0x98, 0xea, 0x71, 0x16, 0x6b, 0x1c, 0xd9, 0xe4, 0x09, 0xd2, 0x8e}, + }; + unsigned int i; + for (i = 0; i < sizeof(midstates)/sizeof(midstates[0]); i++) { + unsigned char out[32]; + secp256k1_sha256 hasher = midstates[i]; + secp256k1_sha256_write(&hasher, (const unsigned char*)input, strlen(input)); + secp256k1_sha256_finalize(&hasher, out); + CHECK(secp256k1_memcmp_var(out, outputs[i], 32) == 0); + } +} + void run_hmac_sha256_tests(void) { static const char *keys[6] = { "\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b", @@ -6986,6 +7088,7 @@ int main(int argc, char **argv) { run_inverse_tests(); run_sha256_known_output_tests(); + run_sha256_counter_tests(); run_hmac_sha256_tests(); run_rfc6979_hmac_sha256_tests(); run_tagged_sha256_tests(); From 8d89b9e6e562000cdb91a70a85fae5e4817cec8a Mon Sep 17 00:00:00 2001 From: Tim Ruffing Date: Fri, 25 Mar 2022 10:30:24 +0100 Subject: [PATCH 53/62] hash: Make code agnostic of endianness Recent compilers compile the two new functions to very efficient code on various platforms. In particular, already GCC >= 5 and clang >= 5 understand do this for the read function, which is the one critical for performance (called 16 times per SHA256 transform). Fixes #1080. --- src/hash.h | 2 +- src/hash_impl.h | 57 +++++++++++++++++++++---------------------------- src/util.h | 16 ++++++++++++++ 3 files changed, 41 insertions(+), 34 deletions(-) diff --git a/src/hash.h b/src/hash.h index 7da677013..4e0384cfb 100644 --- a/src/hash.h +++ b/src/hash.h @@ -12,7 +12,7 @@ typedef struct { uint32_t s[8]; - uint32_t buf[16]; /* In big endian */ + unsigned char buf[64]; uint64_t bytes; } secp256k1_sha256; diff --git a/src/hash_impl.h b/src/hash_impl.h index abb22c88a..0991fe783 100644 --- a/src/hash_impl.h +++ b/src/hash_impl.h @@ -28,12 +28,6 @@ (h) = t1 + t2; \ } while(0) -#if defined(SECP256K1_BIG_ENDIAN) -#define BE32(x) (x) -#elif defined(SECP256K1_LITTLE_ENDIAN) -#define BE32(p) ((((p) & 0xFF) << 24) | (((p) & 0xFF00) << 8) | (((p) & 0xFF0000) >> 8) | (((p) & 0xFF000000) >> 24)) -#endif - static void secp256k1_sha256_initialize(secp256k1_sha256 *hash) { hash->s[0] = 0x6a09e667ul; hash->s[1] = 0xbb67ae85ul; @@ -47,26 +41,26 @@ static void secp256k1_sha256_initialize(secp256k1_sha256 *hash) { } /** Perform one SHA-256 transformation, processing 16 big endian 32-bit words. */ -static void secp256k1_sha256_transform(uint32_t* s, const uint32_t* chunk) { +static void secp256k1_sha256_transform(uint32_t* s, const unsigned char* buf) { uint32_t a = s[0], b = s[1], c = s[2], d = s[3], e = s[4], f = s[5], g = s[6], h = s[7]; uint32_t w0, w1, w2, w3, w4, w5, w6, w7, w8, w9, w10, w11, w12, w13, w14, w15; - Round(a, b, c, d, e, f, g, h, 0x428a2f98, w0 = BE32(chunk[0])); - Round(h, a, b, c, d, e, f, g, 0x71374491, w1 = BE32(chunk[1])); - Round(g, h, a, b, c, d, e, f, 0xb5c0fbcf, w2 = BE32(chunk[2])); - Round(f, g, h, a, b, c, d, e, 0xe9b5dba5, w3 = BE32(chunk[3])); - Round(e, f, g, h, a, b, c, d, 0x3956c25b, w4 = BE32(chunk[4])); - Round(d, e, f, g, h, a, b, c, 0x59f111f1, w5 = BE32(chunk[5])); - Round(c, d, e, f, g, h, a, b, 0x923f82a4, w6 = BE32(chunk[6])); - Round(b, c, d, e, f, g, h, a, 0xab1c5ed5, w7 = BE32(chunk[7])); - Round(a, b, c, d, e, f, g, h, 0xd807aa98, w8 = BE32(chunk[8])); - Round(h, a, b, c, d, e, f, g, 0x12835b01, w9 = BE32(chunk[9])); - Round(g, h, a, b, c, d, e, f, 0x243185be, w10 = BE32(chunk[10])); - Round(f, g, h, a, b, c, d, e, 0x550c7dc3, w11 = BE32(chunk[11])); - Round(e, f, g, h, a, b, c, d, 0x72be5d74, w12 = BE32(chunk[12])); - Round(d, e, f, g, h, a, b, c, 0x80deb1fe, w13 = BE32(chunk[13])); - Round(c, d, e, f, g, h, a, b, 0x9bdc06a7, w14 = BE32(chunk[14])); - Round(b, c, d, e, f, g, h, a, 0xc19bf174, w15 = BE32(chunk[15])); + Round(a, b, c, d, e, f, g, h, 0x428a2f98, w0 = secp256k1_read_be32(&buf[0])); + Round(h, a, b, c, d, e, f, g, 0x71374491, w1 = secp256k1_read_be32(&buf[4])); + Round(g, h, a, b, c, d, e, f, 0xb5c0fbcf, w2 = secp256k1_read_be32(&buf[8])); + Round(f, g, h, a, b, c, d, e, 0xe9b5dba5, w3 = secp256k1_read_be32(&buf[12])); + Round(e, f, g, h, a, b, c, d, 0x3956c25b, w4 = secp256k1_read_be32(&buf[16])); + Round(d, e, f, g, h, a, b, c, 0x59f111f1, w5 = secp256k1_read_be32(&buf[20])); + Round(c, d, e, f, g, h, a, b, 0x923f82a4, w6 = secp256k1_read_be32(&buf[24])); + Round(b, c, d, e, f, g, h, a, 0xab1c5ed5, w7 = secp256k1_read_be32(&buf[28])); + Round(a, b, c, d, e, f, g, h, 0xd807aa98, w8 = secp256k1_read_be32(&buf[32])); + Round(h, a, b, c, d, e, f, g, 0x12835b01, w9 = secp256k1_read_be32(&buf[36])); + Round(g, h, a, b, c, d, e, f, 0x243185be, w10 = secp256k1_read_be32(&buf[40])); + Round(f, g, h, a, b, c, d, e, 0x550c7dc3, w11 = secp256k1_read_be32(&buf[44])); + Round(e, f, g, h, a, b, c, d, 0x72be5d74, w12 = secp256k1_read_be32(&buf[48])); + Round(d, e, f, g, h, a, b, c, 0x80deb1fe, w13 = secp256k1_read_be32(&buf[52])); + Round(c, d, e, f, g, h, a, b, 0x9bdc06a7, w14 = secp256k1_read_be32(&buf[56])); + Round(b, c, d, e, f, g, h, a, 0xc19bf174, w15 = secp256k1_read_be32(&buf[60])); Round(a, b, c, d, e, f, g, h, 0xe49b69c1, w0 += sigma1(w14) + w9 + sigma0(w1)); Round(h, a, b, c, d, e, f, g, 0xefbe4786, w1 += sigma1(w15) + w10 + sigma0(w2)); @@ -136,7 +130,7 @@ static void secp256k1_sha256_write(secp256k1_sha256 *hash, const unsigned char * while (len >= 64 - bufsize) { /* Fill the buffer, and process it. */ size_t chunk_len = 64 - bufsize; - memcpy(((unsigned char*)hash->buf) + bufsize, data, chunk_len); + memcpy(hash->buf + bufsize, data, chunk_len); data += chunk_len; len -= chunk_len; secp256k1_sha256_transform(hash->s, hash->buf); @@ -150,20 +144,18 @@ static void secp256k1_sha256_write(secp256k1_sha256 *hash, const unsigned char * static void secp256k1_sha256_finalize(secp256k1_sha256 *hash, unsigned char *out32) { static const unsigned char pad[64] = {0x80}; - uint32_t sizedesc[2]; - uint32_t out[8]; - int i = 0; + unsigned char sizedesc[8]; + int i; /* The maximum message size of SHA256 is 2^64-1 bits. */ VERIFY_CHECK(hash->bytes < ((uint64_t)1 << 61)); - sizedesc[0] = BE32(hash->bytes >> 29); - sizedesc[1] = BE32(hash->bytes << 3); + secp256k1_write_be32(&sizedesc[0], hash->bytes >> 29); + secp256k1_write_be32(&sizedesc[4], hash->bytes << 3); secp256k1_sha256_write(hash, pad, 1 + ((119 - (hash->bytes % 64)) % 64)); - secp256k1_sha256_write(hash, (const unsigned char*)sizedesc, 8); + secp256k1_sha256_write(hash, sizedesc, 8); for (i = 0; i < 8; i++) { - out[i] = BE32(hash->s[i]); + secp256k1_write_be32(&out32[4*i], hash->s[i]); hash->s[i] = 0; } - memcpy(out32, (const unsigned char*)out, 32); } /* Initializes a sha256 struct and writes the 64 byte string @@ -287,7 +279,6 @@ static void secp256k1_rfc6979_hmac_sha256_finalize(secp256k1_rfc6979_hmac_sha256 rng->retry = 0; } -#undef BE32 #undef Round #undef sigma1 #undef sigma0 diff --git a/src/util.h b/src/util.h index 04227a7c9..fd6cc643e 100644 --- a/src/util.h +++ b/src/util.h @@ -338,4 +338,20 @@ static SECP256K1_INLINE int secp256k1_ctz64_var(uint64_t x) { #endif } +/* Read a uint32_t in big endian */ +SECP256K1_INLINE static uint32_t secp256k1_read_be32(const unsigned char* p) { + return (uint32_t)p[0] << 24 | + (uint32_t)p[1] << 16 | + (uint32_t)p[2] << 8 | + (uint32_t)p[3]; +} + +/* Write a uint32_t in big endian */ +SECP256K1_INLINE static void secp256k1_write_be32(unsigned char* p, uint32_t x) { + p[3] = x; + p[2] = x >> 8; + p[1] = x >> 16; + p[0] = x >> 24; +} + #endif /* SECP256K1_UTIL_H */ From 616b43dd3b6949923f0752cd60382153cfd5dda9 Mon Sep 17 00:00:00 2001 From: Tim Ruffing Date: Fri, 25 Mar 2022 10:36:50 +0100 Subject: [PATCH 54/62] util: Remove endianness detection --- src/util.h | 25 ------------------------- 1 file changed, 25 deletions(-) diff --git a/src/util.h b/src/util.h index fd6cc643e..dac86bd77 100644 --- a/src/util.h +++ b/src/util.h @@ -173,31 +173,6 @@ static SECP256K1_INLINE void *checked_realloc(const secp256k1_callback* cb, void # define SECP256K1_GNUC_EXT #endif -/* If SECP256K1_{LITTLE,BIG}_ENDIAN is not explicitly provided, infer from various other system macros. */ -#if !defined(SECP256K1_LITTLE_ENDIAN) && !defined(SECP256K1_BIG_ENDIAN) -/* Inspired by https://github.com/rofl0r/endianness.h/blob/9853923246b065a3b52d2c43835f3819a62c7199/endianness.h#L52L73 */ -# if (defined(__BYTE_ORDER__) && defined(__ORDER_LITTLE_ENDIAN__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) || \ - defined(_X86_) || defined(__x86_64__) || defined(__i386__) || \ - defined(__i486__) || defined(__i586__) || defined(__i686__) || \ - defined(__MIPSEL) || defined(_MIPSEL) || defined(MIPSEL) || \ - defined(__ARMEL__) || defined(__AARCH64EL__) || \ - (defined(__LITTLE_ENDIAN__) && __LITTLE_ENDIAN__ == 1) || \ - (defined(_LITTLE_ENDIAN) && _LITTLE_ENDIAN == 1) || \ - defined(_M_IX86) || defined(_M_AMD64) || defined(_M_ARM) /* MSVC */ -# define SECP256K1_LITTLE_ENDIAN -# endif -# if (defined(__BYTE_ORDER__) && defined(__ORDER_BIG_ENDIAN__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__) || \ - defined(__MIPSEB) || defined(_MIPSEB) || defined(MIPSEB) || \ - defined(__MICROBLAZEEB__) || defined(__ARMEB__) || defined(__AARCH64EB__) || \ - (defined(__BIG_ENDIAN__) && __BIG_ENDIAN__ == 1) || \ - (defined(_BIG_ENDIAN) && _BIG_ENDIAN == 1) -# define SECP256K1_BIG_ENDIAN -# endif -#endif -#if defined(SECP256K1_LITTLE_ENDIAN) == defined(SECP256K1_BIG_ENDIAN) -# error Please make sure that either SECP256K1_LITTLE_ENDIAN or SECP256K1_BIG_ENDIAN is set, see src/util.h. -#endif - /* Zero memory if flag == 1. Flag must be 0 or 1. Constant time. */ static SECP256K1_INLINE void secp256k1_memczero(void *s, size_t len, int flag) { unsigned char *p = (unsigned char *)s; From d9d94a9969785abbb3e04b2b9f75f1049c7b8936 Mon Sep 17 00:00:00 2001 From: Elliott Jin Date: Fri, 25 Mar 2022 07:09:36 -0700 Subject: [PATCH 55/62] doc: mention optional modules in README --- README.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index e347889c8..f5db915e8 100644 --- a/README.md +++ b/README.md @@ -68,12 +68,15 @@ libsecp256k1 is built using autotools: $ make check # run the test suite $ sudo make install # optional +To compile optional modules (such as Schnorr signatures), you need to run `./configure` with additional flags (such as `--enable-module-schnorrsig`). Run `./configure --help` to see the full list of available flags. + Usage examples ----------- Usage examples can be found in the [examples](examples) directory. To compile them you need to configure with `--enable-examples`. * [ECDSA example](examples/ecdsa.c) - * [Schnorr Signatures example](examples/schnorr.c) - * [Deriving a shared secret(ECDH) example](examples/ecdh.c) + * [Schnorr signatures example](examples/schnorr.c) + * [Deriving a shared secret (ECDH) example](examples/ecdh.c) + To compile the Schnorr signature and ECDH examples, you also need to configure with `--enable-module-schnorrsig` and `--enable-module-ecdh`. Test coverage ----------- From 55512d30b7921bc46247a78be1da98108f243c1c Mon Sep 17 00:00:00 2001 From: Elliott Jin Date: Fri, 25 Mar 2022 07:13:08 -0700 Subject: [PATCH 56/62] doc: clean up module help text in configure.ac --- configure.ac | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/configure.ac b/configure.ac index a7460bca9..2db59a8ff 100644 --- a/configure.ac +++ b/configure.ac @@ -141,7 +141,7 @@ AC_ARG_ENABLE(examples, [SECP_SET_DEFAULT([enable_examples], [no], [yes])]) AC_ARG_ENABLE(module_ecdh, - AS_HELP_STRING([--enable-module-ecdh],[enable ECDH shared secret computation]), [], + AS_HELP_STRING([--enable-module-ecdh],[enable ECDH module [default=no]]), [], [SECP_SET_DEFAULT([enable_module_ecdh], [no], [yes])]) AC_ARG_ENABLE(module_recovery, @@ -149,11 +149,11 @@ AC_ARG_ENABLE(module_recovery, [SECP_SET_DEFAULT([enable_module_recovery], [no], [yes])]) AC_ARG_ENABLE(module_extrakeys, - AS_HELP_STRING([--enable-module-extrakeys],[enable extrakeys module]), [], + AS_HELP_STRING([--enable-module-extrakeys],[enable extrakeys module [default=no]]), [], [SECP_SET_DEFAULT([enable_module_extrakeys], [no], [yes])]) AC_ARG_ENABLE(module_schnorrsig, - AS_HELP_STRING([--enable-module-schnorrsig],[enable schnorrsig module]), [], + AS_HELP_STRING([--enable-module-schnorrsig],[enable schnorrsig module [default=no]]), [], [SECP_SET_DEFAULT([enable_module_schnorrsig], [no], [yes])]) AC_ARG_ENABLE(external_default_callbacks, From 37d36927dff793e61df6d6f6a6c50e8fa2519e33 Mon Sep 17 00:00:00 2001 From: Tim Ruffing Date: Sat, 26 Mar 2022 10:26:53 +0100 Subject: [PATCH 57/62] tests: Add tests for _read_be32 and _write_be32 --- src/tests.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/tests.c b/src/tests.c index 059ebaa00..dd5317393 100644 --- a/src/tests.c +++ b/src/tests.c @@ -6887,6 +6887,19 @@ void run_secp256k1_memczero_test(void) { CHECK(secp256k1_memcmp_var(buf1, buf2, sizeof(buf1)) == 0); } +void run_secp256k1_byteorder_tests(void) { + const uint32_t x = 0xFF03AB45; + const unsigned char x_be[4] = {0xFF, 0x03, 0xAB, 0x45}; + unsigned char buf[4]; + uint32_t x_; + + secp256k1_write_be32(buf, x); + CHECK(secp256k1_memcmp_var(buf, x_be, sizeof(buf)) == 0); + + x_ = secp256k1_read_be32(buf); + CHECK(x == x_); +} + void int_cmov_test(void) { int r = INT_MAX; int a = 0; @@ -7161,6 +7174,7 @@ int main(int argc, char **argv) { /* util tests */ run_secp256k1_memczero_test(); + run_secp256k1_byteorder_tests(); run_cmov_tests(); From 2adb741c45ecc4ef7180009c42f961e4aee4bf5c Mon Sep 17 00:00:00 2001 From: Jonas Nick Date: Wed, 30 Mar 2022 15:06:46 +0000 Subject: [PATCH 58/62] examples: rename example_musig to musig_example for consistency --- .gitignore | 2 +- Makefile.am | 14 +++++++------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/.gitignore b/.gitignore index c32a5ae76..1ec887ded 100644 --- a/.gitignore +++ b/.gitignore @@ -66,4 +66,4 @@ src/stamp-h1 libsecp256k1.pc contrib/gh-pr-create.sh -example_musig \ No newline at end of file +musig_example \ No newline at end of file diff --git a/Makefile.am b/Makefile.am index 61e272e51..0b50f7a8b 100644 --- a/Makefile.am +++ b/Makefile.am @@ -175,15 +175,15 @@ endif TESTS += schnorr_example endif if ENABLE_MODULE_MUSIG -noinst_PROGRAMS += example_musig -example_musig_SOURCES = examples/musig.c -example_musig_CPPFLAGS = -I$(top_srcdir)/include -example_musig_LDADD = libsecp256k1.la -example_musig_LDFLAGS = -static +noinst_PROGRAMS += musig_example +musig_example_SOURCES = examples/musig.c +musig_example_CPPFLAGS = -I$(top_srcdir)/include +musig_example_LDADD = libsecp256k1.la +musig_example_LDFLAGS = -static if BUILD_WINDOWS -example_musig_LDFLAGS += -lbcrypt +musig_example_LDFLAGS += -lbcrypt endif -TESTS += example_musig +TESTS += musig_example endif endif From 03bea1e173d84b33ca81bba4c06b876e160dbe65 Mon Sep 17 00:00:00 2001 From: Jonas Nick Date: Wed, 30 Mar 2022 15:18:07 +0000 Subject: [PATCH 59/62] configure: add -zkp modules to dev-mode and remove redundant code --- configure.ac | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/configure.ac b/configure.ac index f23dbd953..66053fc9f 100644 --- a/configure.ac +++ b/configure.ac @@ -146,8 +146,8 @@ AC_ARG_ENABLE(module_ecdh, AC_ARG_ENABLE(module_musig, AS_HELP_STRING([--enable-module-musig],[enable MuSig module (experimental)]), - [enable_module_musig=$enableval], - [enable_module_musig=no]) + [], + [SECP_SET_DEFAULT([enable_module_musig], [no], [yes])]) AC_ARG_ENABLE(module_recovery, AS_HELP_STRING([--enable-module-recovery],[enable ECDSA pubkey recovery module [default=no]]), [], @@ -155,18 +155,18 @@ AC_ARG_ENABLE(module_recovery, AC_ARG_ENABLE(module_generator, AS_HELP_STRING([--enable-module-generator],[enable NUMS generator module [default=no]]), - [enable_module_generator=$enableval], - [enable_module_generator=no]) + [], + [SECP_SET_DEFAULT([enable_module_generator], [no], [yes])]) AC_ARG_ENABLE(module_rangeproof, AS_HELP_STRING([--enable-module-rangeproof],[enable Pedersen / zero-knowledge range proofs module [default=no]]), - [enable_module_rangeproof=$enableval], - [enable_module_rangeproof=no]) + [], + [SECP_SET_DEFAULT([enable_module_rangeproof], [no], [yes])]) AC_ARG_ENABLE(module_whitelist, AS_HELP_STRING([--enable-module-whitelist],[enable key whitelisting module [default=no]]), - [enable_module_whitelist=$enableval], - [enable_module_whitelist=no]) + [], + [SECP_SET_DEFAULT([enable_module_whitelist], [no], [yes])]) AC_ARG_ENABLE(module_extrakeys, AS_HELP_STRING([--enable-module-extrakeys],[enable extrakeys module [default=no]]), [], @@ -178,13 +178,13 @@ AC_ARG_ENABLE(module_schnorrsig, AC_ARG_ENABLE(module_ecdsa_s2c, AS_HELP_STRING([--enable-module-ecdsa-s2c],[enable ECDSA sign-to-contract module [default=no]]), - [enable_module_ecdsa_s2c=$enableval], - [enable_module_ecdsa_s2c=no]) + [], + [SECP_SET_DEFAULT([enable_module_ecdsa_s2c], [no], [yes])]) AC_ARG_ENABLE(module_ecdsa-adaptor, AS_HELP_STRING([--enable-module-ecdsa-adaptor],[enable ECDSA adaptor module [default=no]]), - [enable_module_ecdsa_adaptor=$enableval], - [enable_module_ecdsa_adaptor=no]) + [], + [SECP_SET_DEFAULT([enable_module_ecdsa_adaptor], [no], [yes])]) AC_ARG_ENABLE(external_default_callbacks, AS_HELP_STRING([--enable-external-default-callbacks],[enable external default callback functions [default=no]]), [], @@ -192,13 +192,13 @@ AC_ARG_ENABLE(external_default_callbacks, AC_ARG_ENABLE(module_surjectionproof, AS_HELP_STRING([--enable-module-surjectionproof],[enable surjection proof module [default=no]]), - [enable_module_surjectionproof=$enableval], - [enable_module_surjectionproof=no]) + [], + [SECP_SET_DEFAULT([enable_module_surjectionproof], [no], [yes])]) AC_ARG_ENABLE(reduced_surjection_proof_size, AS_HELP_STRING([--enable-reduced-surjection-proof-size],[use reduced surjection proof size (disabling parsing and verification) [default=no]]), - [use_reduced_surjection_proof_size=$enableval], - [use_reduced_surjection_proof_size=no]) + [], + [SECP_SET_DEFAULT([use_reduced_surjection_proof_size], [no], [no])]) # Test-only override of the (autodetected by the C code) "widemul" setting. # Legal values are int64 (for [u]int64_t), int128 (for [unsigned] __int128), and auto (the default). From 7c5af740fab0d36a54c9e36872a87de2727c49f1 Mon Sep 17 00:00:00 2001 From: Jonas Nick Date: Wed, 30 Mar 2022 18:45:59 +0000 Subject: [PATCH 60/62] ci: fix missing EXPERIMENTAL flags This was introduced when merging upstream PRs. --- .cirrus.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.cirrus.yml b/.cirrus.yml index 7df661eef..60928eb01 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -101,6 +101,7 @@ task: ECDH: yes RECOVERY: yes SCHNORRSIG: yes + EXPERIMENTAL: yes ECDSA_S2C: yes RANGEPROOF: yes WHITELIST: yes @@ -191,6 +192,7 @@ task: ECDH: yes RECOVERY: yes SCHNORRSIG: yes + EXPERIMENTAL: yes ECDSA_S2C: yes RANGEPROOF: yes WHITELIST: yes @@ -284,6 +286,7 @@ task: ECDH: yes RECOVERY: yes SCHNORRSIG: yes + EXPERIMENTAL: yes ECDSA_S2C: yes RANGEPROOF: yes WHITELIST: yes From eccba5b4e5eb70710f4d34ae89e62abfab542f7c Mon Sep 17 00:00:00 2001 From: Jonas Nick Date: Thu, 31 Mar 2022 13:33:30 +0000 Subject: [PATCH 61/62] examples: relicense musig example to CC0 public domain --- examples/musig.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/examples/musig.c b/examples/musig.c index fddb16945..1fbd2207c 100644 --- a/examples/musig.c +++ b/examples/musig.c @@ -1,8 +1,11 @@ -/*********************************************************************** - * Copyright (c) 2018 Jonas Nick * - * Distributed under the MIT software license, see the accompanying * - * file COPYING or https://www.opensource.org/licenses/mit-license.php.* - **********************************************************************/ +/************************************************************************* + * Written in 2018 by Jonas Nick * + * To the extent possible under law, the author(s) have dedicated all * + * copyright and related and neighboring rights to the software in this * + * file to the public domain worldwide. This software is distributed * + * without any warranty. For the CC0 Public Domain Dedication, see * + * EXAMPLES_COPYING or https://creativecommons.org/publicdomain/zero/1.0 * + *************************************************************************/ /** This file demonstrates how to use the MuSig module to create a * 3-of-3 multisignature. Additionally, see the documentation in From 645d9c53c4d9095342cf770c98e3d367f670ae46 Mon Sep 17 00:00:00 2001 From: Jonas Nick Date: Thu, 31 Mar 2022 13:38:30 +0000 Subject: [PATCH 62/62] examples: let musig use random.h instead of /dev/urandom --- examples/musig.c | 33 +++++++++++---------------------- 1 file changed, 11 insertions(+), 22 deletions(-) diff --git a/examples/musig.c b/examples/musig.c index 1fbd2207c..3a6574108 100644 --- a/examples/musig.c +++ b/examples/musig.c @@ -18,6 +18,8 @@ #include #include +#include "random.h" + struct signer_secrets { secp256k1_keypair keypair; secp256k1_musig_secnonce secnonce; @@ -34,20 +36,14 @@ struct signer { /* Create a key pair, store it in signer_secrets->keypair and signer->pubkey */ int create_keypair(const secp256k1_context* ctx, struct signer_secrets *signer_secrets, struct signer *signer) { unsigned char seckey[32]; - FILE *frand = fopen("/dev/urandom", "r"); - if (frand == NULL) { - return 0; - } - do { - if(!fread(seckey, sizeof(seckey), 1, frand)) { - fclose(frand); - return 0; - } - /* The probability that this not a valid secret key is approximately 2^-128 */ - } while (!secp256k1_ec_seckey_verify(ctx, seckey)); - fclose(frand); - if (!secp256k1_keypair_create(ctx, &signer_secrets->keypair, seckey)) { - return 0; + while (1) { + if (!fill_random(seckey, sizeof(seckey))) { + printf("Failed to generate randomness\n"); + return 1; + } + if (secp256k1_keypair_create(ctx, &signer_secrets->keypair, seckey)) { + break; + } } if (!secp256k1_keypair_xonly_pub(ctx, &signer->pubkey, NULL, &signer_secrets->keypair)) { return 0; @@ -103,21 +99,14 @@ int sign(const secp256k1_context* ctx, struct signer_secrets *signer_secrets, st secp256k1_musig_session session; for (i = 0; i < N_SIGNERS; i++) { - FILE *frand; unsigned char seckey[32]; unsigned char session_id[32]; /* Create random session ID. It is absolutely necessary that the session ID * is unique for every call of secp256k1_musig_nonce_gen. Otherwise * it's trivial for an attacker to extract the secret key! */ - frand = fopen("/dev/urandom", "r"); - if(frand == NULL) { - return 0; - } - if (!fread(session_id, 32, 1, frand)) { - fclose(frand); + if (!fill_random(session_id, sizeof(session_id))) { return 0; } - fclose(frand); if (!secp256k1_keypair_sec(ctx, seckey, &signer_secrets[i].keypair)) { return 0; }