"""Gate: signatures produced by the Rust engines (libsecp256k1 via rust-secp256k1, RustCrypto k256) must verify under the pure-Python exact-arithmetic references src/wp34/{ecdsa,schnorr}.py, and the Python references must re-derive the same deterministic signatures byte for byte. The triples are written by experiments/signature_benchmarks/sigbench (--crosscheck-out) through run_bench.py into results/signature_benchmarks/crosscheck_triples_.json. If no such file exists the test builds the crate and generates a small set itself. """ import json, pathlib, subprocess, sys import pytest ROOT = pathlib.Path(__file__).resolve().parents[1] sys.path.insert(0, str(ROOT / "src")) sys.path.insert(0, str(ROOT / "experiments" / "signature_benchmarks")) from python_crosscheck import crosscheck # noqa: E402 from wp34 import schnorr as B # noqa: E402 RESULTS = ROOT / "results" / "signature_benchmarks" CRATE = ROOT / "experiments" / "signature_benchmarks" / "sigbench" def _triples_file(tmp_path_factory) -> pathlib.Path: files = sorted(RESULTS.glob("crosscheck_triples_*.json")) if RESULTS.exists() else [] if files: return files[0] binary = CRATE / "target" / "release" / "sigbench" if not binary.exists(): subprocess.run(["cargo", "build", "--release"], cwd=CRATE, check=True) out = tmp_path_factory.mktemp("sig") / "triples.json" subprocess.run([str(binary), "--vectors", str(ROOT / "data/vectors/bip340_test_vectors.csv"), "--reps", "1", "--batch-ops", "50", "--batch-min-sigs", "50", "--batch-sizes", "1,4", "--crosscheck-n", "40", "--python-crosscheck-n", "40", "--bootstrap", "10", "--crosscheck-out", str(out)], check=True, capture_output=True) return out @pytest.fixture(scope="module") def triples(tmp_path_factory): return _triples_file(tmp_path_factory) def test_rust_signatures_verify_under_python_reference(triples): c = crosscheck(triples, resign=True) n = c["n"] assert n >= 40 assert c["ecdsa_k256_sig_verified_by_python"] == n assert c["ecdsa_libsecp_sig_verified_by_python"] == n assert c["schnorr_k256_sig_verified_by_python"] == n assert c["schnorr_libsecp_sig_verified_by_python"] == n # deterministic nonces: RFC 6979 (ECDSA) and BIP340 aux-rand nonce derivation -> identical bytes assert c["ecdsa_python_resign_identical"] == n assert c["schnorr_python_resign_identical"] == n # negative control: one flipped bit must be rejected by the reference assert c["tampered_sig_rejected_by_python"] == n assert c["all_pass"] def test_bip340_vectors_python_reference(): """The Python reference itself must agree with the official BIP340 vectors (independent of Rust).""" rows = (ROOT / "data/vectors/bip340_test_vectors.csv").read_text().splitlines()[1:] n_checked = 0 for row in rows: f = row.split(",") pk, msg, sig, expected = bytes.fromhex(f[2]), bytes.fromhex(f[4]), bytes.fromhex(f[5]), f[6] == "TRUE" assert B.verify(msg, pk, sig) == expected, f"vector {f[0]}: {f[7]}" if f[1]: assert B.sign(msg, bytes.fromhex(f[1]), bytes.fromhex(f[3])) == sig, f"vector {f[0]} sign" n_checked += 1 assert n_checked == 19