From 050cf39a54816d23ce96e5dffc7ecb03cd15fc4e Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 11 Apr 2026 04:06:05 +0000 Subject: [PATCH] fix: correct y-parity in schnorr_sign_xonly, all operations now verified MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The sign_xonly function incorrectly assumed even y-parity for all keys. BIP-340 requires checking the actual y-parity of the derived pubkey and negating the secret key when y is odd. Without this, signatures for keys with odd-y pubkeys (roughly half of all keys) were invalid. All operations now pass correctness tests: - sign + verify for keys 1, 2, 3, 0xff, and 0xd217c1... (random) - verify_fast (skip y-parity check) - batch_verify (5 signatures from same pubkey) C standalone benchmark (x86_64): verifySchnorrFast: 52 µs (19,143 ops/s) verifySchnorr: 60 µs (16,616 ops/s) signSchnorr: 109 µs (9,177 ops/s) pubkeyCreate: 54 µs (18,381 ops/s) ecdhXOnly: 59 µs (16,958 ops/s) https://claude.ai/code/session_011KVZhDcV2G7idNWEBz12GY --- quartz/src/main/c/secp256k1/schnorr.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/quartz/src/main/c/secp256k1/schnorr.c b/quartz/src/main/c/secp256k1/schnorr.c index 301c21143..e2da446fd 100644 --- a/quartz/src/main/c/secp256k1/schnorr.c +++ b/quartz/src/main/c/secp256k1/schnorr.c @@ -239,8 +239,15 @@ int secp256k1c_schnorr_sign_xonly( scalar_from_bytes(&d0, seckey32); if (!scalar_is_valid(&d0)) return 0; - /* BIP-340 x-only pubkeys always have even y */ - return schnorr_sign_internal(sig64, msg, msg_len, &d0, xonly_pub32, 1, auxrand32); + /* 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); } /* ==================== Schnorr Verify (core) ==================== */