Skip to content

Optimized secp384r1 (NIST P-384) ECDSA: ~3.6x faster verify than RustCrypto p384 #972

Description

@tamashi095

Context

Sui recently added a Move-callable P-384 ECDSA verify native (sui#26934, 0x2::ecdsa_p384::secp384r1_verify) for verifying Apple App Attest / Android Key Attestation certificate chains on-chain. It currently verifies via the RustCrypto p384 crate, which benchmarks ~12.8x slower than fastcrypto's secp256r1 verify (~532µs vs ~41.6µs on Apple Silicon), so the native shipped with a correspondingly high gas base (54,000).

A faster P-384 in fastcrypto would let Sui cut that gas cost ~3.6x and consolidate P-384 into its canonical crypto library.

Proposal

I have a working implementation that mirrors the existing secp256r1 module's architecture exactly:

  • ark-secp384r1 0.4.0 (same arkworks 0.4 family already used for ark-secp256r1/ark-secp256k1) for field/curve arithmetic.
  • The existing generic WindowedScalarMultiplier (256-point precomputed generator table, width-5 sliding window Straus double-mul) — same constants as secp256r1; width 6 and a 512-point table both benchmarked worse/marginal.
  • The p384 crate only for key/signature encoding and RFC6979 nonce generation, exactly as secp256r1 uses p256.

Measured (Apple Silicon, criterion, same inputs for both):

op fastcrypto RustCrypto p384 speedup
verify 143.5 µs 518.6 µs 3.61x
sign 119.1 µs 284.1 µs 2.39x

Correctness: wycheproof EcdsaSecp384r1Sha384 + EcdsaSecp384r1Sha512 sets (each vector also cross-checked to accept/reject identically to the p384 crate), RFC 6979 A.2.6 KATs, byte-for-byte sign equality with p384, proptest round-trips. ~34 scheme tests mirroring secp256r1_tests.rs.

Questions for maintainers before I finalize the PR

  1. Low-s policy: unlike secp256r1, this module does not enforce low-s on verify (and does not normalize s on sign), because the target use case is verifying externally-produced X.509/attestation signatures and the goal is exact accept/reject equivalence with the p384 crate the Sui native uses today (which accepts high-s). Is this divergence acceptable if clearly documented, or would you prefer a separate strict/lenient API?
  2. Feature gating: I've gated the module behind experimental ("features not yet audited"). If Sui is to consume this for a production native, what's your preferred path — audit then un-gate, or un-gated from the start like secp256r1?
  3. Scope: v1 is the full trait family (KeyPair/Signer/VerifyingKey/serde/zeroize/etc.) but without public-key recovery (recoverable.rs), since the use case doesn't need it. OK?

Draft PR with the full implementation incoming — happy to adjust any of the above. The eventual win is letting sui#26934 lower its P-384 gas once this lands.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions