group: enforce fixed coordinate contracts #1936

pull koko1123 wants to merge 1 commits into bitcoin-core:master from koko1123:koko/group-magnitude-contracts changing 18 files +504 −110
  1. koko1123 commented at 4:48 AM on September 11, 2026: none

    Refs #1001, building on #1923.

    Group operations currently inherit coordinate metadata that can be tighter than their advertised bounds, so successful VERIFY tests can depend on the caller that constructed a point. Require complete ge/gej inputs to have exactly the affine (4,3) or Jacobian (4,4,1) magnitude bounds, and publish those bounds at complete outputs after checking the original metadata and actual field limbs. This makes checking each operation against its declared input bounds sufficient, provided its reachable paths are exercised.

    The contract also fixes normalized = 0: preserving an accidentally normalized input/output would leave the same dependence between callers. That deliberately extends the magnitude-only proposal and requires explicit normalization before parity, serialization and zero checks in several modules. Multiplication tables normalize where a tighter field bound is needed, and complete points are published after direct coordinate construction and the final Z adjustments; partial scratch values and failed parse outputs remain outside the complete-point contract, and clearing still wipes all bytes. Boxed bare-field arithmetic is deferred.

    The group_magnitude, group_magnitude_tables and group_magnitude_reject test targets exercise physical representations and unchanged values/limbs, infinity, aliasing, conversions, parsing, serialization, clearing, signed table selection, and rejection of invalid input/output metadata. On POSIX/VERIFY builds, the 26 rejection cases include actual limbs inconsistent with their original claims, so relaxing metadata cannot hide invalid fields; initialized infinity coordinates are checked for exact zero limbs.

    Validation of the final source tree:

    • ARM64 Clang, default backend, all modules/examples: 242/242 tests at 64 iterations, with -Werror -pedantic-errors.
    • Native x86_64 Clang: upstream ci/ci.sh with int128, assembly, all modules/examples, 64 iterations, warnings as errors, symbol checking, benchmark smoke tests, Valgrind ctime checks, and byte-identical regenerated precomputation/test vectors.
    • Native x86_64 int64/10x26, ASan+UBSan, no assembly: 240/240 tests at 16 iterations.
    • Fresh LLVM instrumentation preserving VERIFY: 150/150 instrumented changed production lines; 152/152 group branch outcomes after merging matching locations across VERIFY/noverify/exhaustive binaries and filtering assertion/preprocessor/void lines. Raw group_impl.h coverage is 99.66% lines and 71.24% branches; the filtered count is not raw branch coverage.
    • Additional local parent/patch differential checks: 1,030 keygen/ECDSA/recovery/Schnorr cases and 256 expanded module scenarios plus tweak boundaries, byte-identical across parent Release, patch Release and patched int64 ASan/UBSan builds. The expanded corpus covers ECDH, ElligatorSwift, extrakeys, MuSig and Silent Payments. Mutation checks also exercise the sensitivity of the new assertions.

    The subprocess rejection tests require POSIX fork; they are not enabled on every platform.

    Native x86_64, AMD EPYC 9R14, Ubuntu 24.04, Clang 18 Release with x86_64 assembly: ten alternating parent/patch pairs, 10,000 iterations per internal sample. Medians of reported round averages, in microseconds:

    Operation Parent Patch Change
    ecdh 35.1 35.3 +0.57%
    ellswift_ecdh 38 38.3 +0.79%
    ellswift_encode 17.6 17.6 +0.00%
    ellswift_decode 7.78 7.77 -0.13%
    ellswift_keygen 34.4 34.4 +0.00%
    ec_keygen 16.8 16.8 +0.00%
    ecdsa_sign 22.7 22.7 +0.00%
    ecdsa_verify 36.6 36.7 +0.27%
    ecdsa_recover 37.8 37.9 +0.26%
    schnorrsig_sign 17.8 17.8 +0.00%
    schnorrsig_verify 37.1 37.2 +0.27%

    ECDH and ElligatorSwift ECDH show small measured increases; this is not a zero-overhead claim. The measurements are specific to this configuration, with finite sampling and rounded benchmark output.

    <details> <summary>Reproduction commands</summary>

    All modules are enabled (including optional recovery). The ordinary suite and the focused new tests can be run with:

    cmake -S . -B build -DCMAKE_BUILD_TYPE=RelWithDebInfo \
      -DCMAKE_C_FLAGS='-Werror -pedantic-errors' \
      -DSECP256K1_ENABLE_MODULE_RECOVERY=ON -DSECP256K1_BUILD_EXAMPLES=ON
    cmake --build build -j2
    SECP256K1_TEST_ITERS=64 ctest --test-dir build --output-on-failure
    build/bin/tests --target=group_magnitude --target=group_magnitude_tables \
      --target=group_magnitude_reject --iterations=16
    

    For the 10x26 sanitizer configuration, additionally select SECP256K1_TEST_OVERRIDE_WIDE_MULTIPLY=int64, SECP256K1_ASM=OFF, SECP256K1_BUILD_CTIME_TESTS=OFF, and append -fsanitize=address,undefined -fno-sanitize-recover=all -fno-omit-frame-pointer to CMAKE_C_FLAGS.

    The native benchmarks use identical Clang Release configurations on parent and patch, with SECP256K1_ASM=x86_64 and recovery enabled. Run ten pairs, alternating which revision runs first:

    SECP256K1_BENCH_ITERS=10000 build/bin/bench ecdh ellswift keygen sign verify recover
    

    Each invocation performs ten internal samples per operation. Results in the table are medians of the ten reported round averages; the machine ran no other validation work during measurement.

    </details>

    One behavior difference for corrupted opaque pubkeys outside the API contract: an X storage value equal to the field modulus now invokes the illegal-argument callback instead of passing the old zero check.

    Implementation and validation used AI assistance under human direction; the checks reported here were executed, and are finite evidence rather than a proof of correctness or constant-time behavior on every platform.

  2. group: enforce fixed coordinate contracts
    Require fixed magnitudes and unnormalized metadata for complete ge/gej inputs. Validate original field metadata and limb bounds before relaxing outputs to the affine (4,3) and Jacobian (4,4,1) contracts.
    
    Make normalization requirements explicit in multiplication tables and module callers. Publish complete points after direct coordinate construction or mutation, including the final z adjustments in scalar multiplication.
    
    Test physical representations, aliasing, infinity, table selection, parsing, serialization, clearing and invalid original limb metadata. Preserve exact-zero assertions for initialized infinity coordinates.
    
    This implements the group-boundary portion of #1001; boxed bare-field arithmetic remains separate.
    543db37b1b
  3. gmaxwell commented at 11:34 PM on September 11, 2026: contributor

    This PR description is very unclear to me-- from the text I can't tell what it's doing, why, and why it has a runtime cost.

  4. real-or-random commented at 11:05 AM on September 12, 2026: contributor

    This PR description is very unclear to me-- from the text I can't tell what it's doing, why, and why it has a runtime cost.

    Second this. (I can mostly follow but that's only because I wrote up the idea in #1001.)

    Also, putting everything in a single commit makes it even harder for a reviewer to follow. I have a local WIP branch that does roughly the same (maybe a bit more and/or more sophisticated), and I think it will end up having about 10 small commits.


github-metadata-mirror

This is a metadata mirror of the GitHub repository bitcoin-core/secp256k1. This site is not affiliated with GitHub. Content is generated from a GitHub metadata backup.
generated: 2026-09-13 01:15 UTC

This site is hosted by @0xB10C
More mirrored repositories can be found on mirror.b10c.me