libsecp256k1 does one curve and does it carefully: constant-time signing, exhaustive tests and formal verification of parts of the field arithmetic. Bitcoin Core, many wallets and bindings such as coincurve and go-ethereum's cgo backend use it. The facts here were checked on the Alpine 3.24.2 package, version 0.5.0 with the recovery, extrakeys and schnorrsig modules; upstream had reached 0.8.0 at the time.
Signatures live in an opaque struct; the encoding is chosen when serializing — serialize_compact gives 64 bytes of r‖s, serialize_der gives DER — and parsing is equally explicit. parse_der is strict: it accepted minimal DER and refused the same signature with a redundant leading zero in r, following Bitcoin's BIP-66 rules. Lax historical signatures need the lax parser kept in the repository's contrib directory.
Signing takes a 32-byte hash — the library never hashes your message — uses RFC 6979 (the check matched an independent implementation) and always produces low-S. Verification is the strict part: the mirrored high-S form of a valid signature failed, and secp256k1_ecdsa_signature_normalize exists precisely to fix signatures from general-purpose signers. When a signature from OpenSSL, Java or a cloud KMS verifies in roughly half the cases, high-S is the cause; the sample normalizes before verifying.
Public keys are SEC1 points, 65 bytes uncompressed or 33 compressed; a 64-byte x‖y is refused, and there is no SPKI, PEM or JWK support. The recovery module recovers the public key from a signature — the basis of Ethereum addresses — and the schnorrsig module implements BIP-340. Check Bitcoin message signatures with the Bitcoin message verifier and Nostr events with the Nostr event verifier.