Skip to content

secp256k1: Add alternative 64-bit field backend (FieldVal64) #3707

Description

@valery-osheter-cb

Summary

I'd like to add an alternative 4x64-bit representation of the secp256k1 base
field element (FieldVal64) alongside the current 10x26-bit FieldVal,
selectable at build time. It comes with assembly fast paths for amd64
(BMI2/MULX + ADX) and arm64 (UMULH/MUL), and a portable Go fallback for
all other arches and purego builds. The existing default is unchanged.
I'm opening this before submitting a PR to get maintainer input on the
overall direction.

Motivation

On 64-bit hardware a tightly packed 256-bit representation with a Crandall
reduction performs fewer limb multiplications than the base-2^26 schedule.
In local benchmarks (ECDSA sign/verify, ECDH, pubkey parse/serialize) the
64-bit backend is consistently faster, including under emulation. I'll post
a benchstat comparison in a follow-up comment.

Proposed design

  • FieldVal64: four little-endian uint64 limbs, fully reduced after each op,
    matching the existing FieldVal API surface.
  • Constant-time throughout: no secret-dependent branches or memory accesses.
    The final conditional subtraction uses CMOVcc (amd64) / CSEL (arm64) and
    arithmetic masking in the portable path.
  • Portable building blocks (512-bit multiply/square + Crandall reduce) used on
    all platforms.
  • field64Mul / field64Square dispatch:
    • arm64: dedicated asm
    • amd64: MULX/ADX asm, runtime-gated on CPU support, portable fallback
    • everything else / purego: portable Go
  • FieldVal type-aliased to FieldVal26 (default) or FieldVal64
    (-tags fieldval64), so curve/ECDSA/Schnorr all run against the chosen type.

Testing

  • Differential tests against a math/big oracle and against the portable path
    (asm-vs-Go cross-check), plus edge cases and randomized inputs.

Open questions for maintainers

  1. Is a build-tag-selectable alternative backend acceptable, or would you
    prefer a single implementation (e.g. eventually making 64-bit the default
    on 64-bit targets)?
  2. CPU feature detection currently uses golang.org/x/sys/cpu. This module
    only depends on blake256 today; would you rather I avoid the new dependency
    and detect BMI2/ADX inline?
  3. Any preference on how this is split for review?

Happy to adjust the approach based on your guidance before sending the PR.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions