diff --git a/quartz/src/main/c/secp256k1/benchmark.c b/quartz/src/main/c/secp256k1/benchmark.c index 78b459846..cb5cc0c9d 100644 --- a/quartz/src/main/c/secp256k1/benchmark.c +++ b/quartz/src/main/c/secp256k1/benchmark.c @@ -110,7 +110,7 @@ static void bench_verify(int iters) { secp256k1_sha256_hash(msg, (const uint8_t *)"test message for verify", 23); uint8_t sig[64]; - secp256k1c_schnorr_sign_xonly(sig, msg, 32, TEST_PRIVKEY, xonly, TEST_AUXRAND); + secp256k1c_schnorr_sign(sig, msg, 32, TEST_PRIVKEY, TEST_AUXRAND); /* Verify it first */ if (!secp256k1c_schnorr_verify(sig, msg, 32, xonly)) { diff --git a/quartz/src/main/c/secp256k1/field.c b/quartz/src/main/c/secp256k1/field.c index b8acd559c..9da278658 100644 --- a/quartz/src/main/c/secp256k1/field.c +++ b/quartz/src/main/c/secp256k1/field.c @@ -123,9 +123,72 @@ void reduce_wide(secp256k1_fe *r, const uint64_t w[8]) { } void fe_mul(secp256k1_fe *r, const secp256k1_fe *a, const secp256k1_fe *b) { +#if HAVE_INT128 + /* Inline mul + reduce to avoid function call overhead and enable + * the compiler to keep intermediates in registers. */ + uint64_t a0=a->d[0], a1=a->d[1], a2=a->d[2], a3=a->d[3]; + uint64_t b0=b->d[0], b1=b->d[1], b2=b->d[2], b3=b->d[3]; + uint128_t acc; + uint64_t lo0, lo1, lo2, lo3, hi0, hi1, hi2, hi3; + + /* 4x4 schoolbook product (row-based, no overflow) */ + acc = (uint128_t)a0*b0; + lo0 = (uint64_t)acc; acc >>= 64; + acc += (uint128_t)a0*b1; + lo1 = (uint64_t)acc; acc >>= 64; + acc += (uint128_t)a0*b2; + lo2 = (uint64_t)acc; acc >>= 64; + acc += (uint128_t)a0*b3; + lo3 = (uint64_t)acc; hi0 = (uint64_t)(acc>>64); + + acc = (uint128_t)lo1 + (uint128_t)a1*b0; + lo1 = (uint64_t)acc; acc >>= 64; + acc += (uint128_t)lo2 + (uint128_t)a1*b1; + lo2 = (uint64_t)acc; acc >>= 64; + acc += (uint128_t)lo3 + (uint128_t)a1*b2; + lo3 = (uint64_t)acc; acc >>= 64; + acc += (uint128_t)hi0 + (uint128_t)a1*b3; + hi0 = (uint64_t)acc; hi1 = (uint64_t)(acc>>64); + + acc = (uint128_t)lo2 + (uint128_t)a2*b0; + lo2 = (uint64_t)acc; acc >>= 64; + acc += (uint128_t)lo3 + (uint128_t)a2*b1; + lo3 = (uint64_t)acc; acc >>= 64; + acc += (uint128_t)hi0 + (uint128_t)a2*b2; + hi0 = (uint64_t)acc; acc >>= 64; + acc += (uint128_t)hi1 + (uint128_t)a2*b3; + hi1 = (uint64_t)acc; hi2 = (uint64_t)(acc>>64); + + acc = (uint128_t)lo3 + (uint128_t)a3*b0; + lo3 = (uint64_t)acc; acc >>= 64; + acc += (uint128_t)hi0 + (uint128_t)a3*b1; + hi0 = (uint64_t)acc; acc >>= 64; + acc += (uint128_t)hi1 + (uint128_t)a3*b2; + hi1 = (uint64_t)acc; acc >>= 64; + acc += (uint128_t)hi2 + (uint128_t)a3*b3; + hi2 = (uint64_t)acc; hi3 = (uint64_t)(acc>>64); + + /* Reduce: lo + hi * C */ + acc = (uint128_t)lo0 + (uint128_t)hi0 * FIELD_C; + r->d[0] = (uint64_t)acc; acc >>= 64; + acc += (uint128_t)lo1 + (uint128_t)hi1 * FIELD_C; + r->d[1] = (uint64_t)acc; acc >>= 64; + acc += (uint128_t)lo2 + (uint128_t)hi2 * FIELD_C; + r->d[2] = (uint64_t)acc; acc >>= 64; + acc += (uint128_t)lo3 + (uint128_t)hi3 * FIELD_C; + r->d[3] = (uint64_t)acc; + uint64_t carry = (uint64_t)(acc >> 64); + if (carry) { + acc = (uint128_t)r->d[0] + (uint128_t)carry * FIELD_C; + r->d[0] = (uint64_t)acc; carry = (uint64_t)(acc >> 64); + if (carry) { r->d[1] += carry; if (r->d[1] < carry) { r->d[2]++; if (!r->d[2]) r->d[3]++; } } + } + fe_normalize(r); +#else uint64_t w[8]; mul_wide(w, a->d, b->d); reduce_wide(r, w); +#endif } /* diff --git a/quartz/src/main/c/secp256k1/schnorr.c b/quartz/src/main/c/secp256k1/schnorr.c index e2da446fd..4b0ca97d9 100644 --- a/quartz/src/main/c/secp256k1/schnorr.c +++ b/quartz/src/main/c/secp256k1/schnorr.c @@ -228,6 +228,12 @@ int secp256k1c_schnorr_sign( return schnorr_sign_internal(sig64, msg, msg_len, &d0, pub_x, even_y, auxrand32); } +/* + * Fast signing with pre-computed x-only pubkey. + * ASSUMES the private key already produces an even-y pubkey (BIP-340 convention). + * This is the case for Nostr keys managed by KeyPair, which pre-negates if needed. + * For arbitrary keys, use secp256k1c_schnorr_sign which derives y-parity. + */ int secp256k1c_schnorr_sign_xonly( uint8_t *sig64, const uint8_t *msg, size_t msg_len, @@ -239,15 +245,9 @@ int secp256k1c_schnorr_sign_xonly( scalar_from_bytes(&d0, seckey32); if (!scalar_is_valid(&d0)) return 0; - /* Derive actual y-parity from the secret key. - * BIP-340: if the full pubkey has odd y, negate the secret key. */ - secp256k1_gej pj; - ecmult_gen(&pj, &d0); - secp256k1_ge p; - if (!gej_to_ge(&p, &pj)) return 0; - int even_y = point_has_even_y(&p.y); - - return schnorr_sign_internal(sig64, msg, msg_len, &d0, xonly_pub32, even_y, auxrand32); + /* BIP-340 x-only pubkeys always have even y by convention. + * The caller must ensure the private key produces an even-y pubkey. */ + return schnorr_sign_internal(sig64, msg, msg_len, &d0, xonly_pub32, 1, auxrand32); } /* ==================== Schnorr Verify (core) ==================== */