Merge bitcoin-core/secp256k1#1033: Add _fe_half and use in _gej_add_ge and _gej_double

e848c3799c Update sage files for new formulae (Peter Dettman)
d64bb5d4f3 Add fe_half tests for worst-case inputs (Peter Dettman)
4eb8b932ff Further improve doubling formula using fe_half (Peter Dettman)
557b31fac3 Doubling formula using fe_half (Pieter Wuille)
2cbb4b1a42 Run more iterations of run_field_misc (Pieter Wuille)
9cc5c257ed Add test for secp256k1_fe_half (Pieter Wuille)
925f78d55e Add _fe_half and use in _gej_add_ge (Peter Dettman)

Pull request description:

  - Trades 1 _half for 3 _mul_int and 2 _normalize_weak

  Gives around 2-3% faster signing and ECDH, depending on compiler/platform.

ACKs for top commit:
  sipa:
    utACK e848c3799c
  jonasnick:
    ACK e848c3799c
  real-or-random:
    ACK e848c3799c

Tree-SHA512: 81a6c93b3d983f1b48ec8e8b6f262ba914215045a95415147f41ee6e85296aa4d0cbbad9f370cdf475571447baad861d2cc8e0b04a71202d48959cb8a098f584
This commit is contained in:
Tim Ruffing 2022-02-21 10:59:28 +01:00
commit 1253a27756
No known key found for this signature in database
GPG Key ID: 8C461CCD293F6011
7 changed files with 318 additions and 75 deletions

View File

@ -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

View File

@ -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);

View File

@ -130,4 +130,13 @@ 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);
/** 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 */

View File

@ -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];
@ -1133,6 +1153,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));

View File

@ -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];
@ -477,6 +492,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));

View File

@ -271,37 +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) {
/* 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;
/* Operations: 3 mul, 4 sqr, 8 add/half/mul_int/negate */
secp256k1_fe l, s, t;
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*(X3 + T) + S^2)
* Z3 = Y1*Z1
*/
secp256k1_fe_mul(&r->z, &a->z, &a->y); /* Z3 = Y1*Z1 (1) */
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) {
@ -326,7 +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);
@ -492,7 +489,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 +510,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 +578,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 +587,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);

View File

@ -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;
@ -2478,9 +2527,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);
@ -2548,6 +2601,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));
}
}
@ -6912,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();