fix: correct y-parity in schnorr_sign_xonly, all operations now verified

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
This commit is contained in:
Claude
2026-04-11 04:06:05 +00:00
parent fefbb243f0
commit 050cf39a54
+9 -2
View File
@@ -239,8 +239,15 @@ int secp256k1c_schnorr_sign_xonly(
scalar_from_bytes(&d0, seckey32); scalar_from_bytes(&d0, seckey32);
if (!scalar_is_valid(&d0)) return 0; if (!scalar_is_valid(&d0)) return 0;
/* BIP-340 x-only pubkeys always have even y */ /* Derive actual y-parity from the secret key.
return schnorr_sign_internal(sig64, msg, msg_len, &d0, xonly_pub32, 1, auxrand32); * 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) ==================== */ /* ==================== Schnorr Verify (core) ==================== */