elliptic implements ECDSA over the NIST curves and secp256k1, and Ed25519, in plain JavaScript on top of bn.js. It spread through crypto-browserify, older Ethereum and Bitcoin libraries and the Node.js polyfills that Webpack 4 injected into browser bundles, which is why it still turns up in lockfiles. ethers v6 and viem build on @noble/curves instead, which covers the same curves with stricter defaults.
The API differs from most libraries in three ways that cause interop bugs. sign() takes the digest, not the message, and treats whatever bytes it receives as that digest — the check signed a raw message and got a different signature than for its SHA-256. It returns a Signature object with r, s and recoveryParam, so the wire format is whatever you serialize: toDER() for OpenSSL-style peers, or both values padded to 32 bytes for raw r‖s. And s is not normalized unless you pass canonical: true, while verification accepts both halves.
Nonces follow RFC 6979 — the P-256 test vector reproduced exactly — but advisory GHSA-848j-6mx2-7j84 (CVE-2025-14505), still open for 6.6.1, reports that an interim value is truncated when it has leading zero bytes. The check reproduced it: on P-521 with SHA-512, 9 of 2,048 fixed inputs produced a nonce different from RFC 6979, while secp256k1 with SHA-256 and P-256 with SHA-512 showed no difference in 4,096 inputs each. Those signatures still verify, but signing the same message for the same key with another RFC 6979 implementation yields related nonces, which the advisory says can expose the key. Avoid elliptic for P-521, and see the nonce reuse checker for why related nonces matter.
Earlier advisories were fixed in 6.5.7 through 6.6.1, among them BER-encoded signatures being accepted and a key-extraction bug when a string was signed instead of a hash; 6.6.1 rejected a DER signature with a redundant leading zero in the check. Keys are hex strings or byte arrays — SEC1 points, compressed or uncompressed, and raw scalars — with no PEM, SPKI or JWK support.
The sample hashes first, asks for a canonical low-S signature and shows both serializations. When a signature from elliptic fails elsewhere, compare its two encodings in the DER ⇄ raw converter.