@noble/curves implements elliptic-curve cryptography in plain TypeScript with no dependencies and has been audited several times. It is the engine under ethers v6, viem and many Bitcoin and Nostr tools, and the usual answer when WebCrypto lacks a curve: it covers P-256, P-384, P-521, secp256k1 and the Brainpool r1 curves, Ed25519 and Ed448 with their prehash and context variants, and BIP-340 Schnorr signatures.
Its defaults are the strict ones. A signature is 64 bytes of compact r‖s, the nonce follows RFC 6979 — the check reproduced the RFC's P-256 test vector — and s is always normalized into the lower half. Verification is just as strict: every high-S signature that node:crypto produced was rejected, every low-S one accepted. Signatures from OpenSSL, Java, Go or WebCrypto are high-S about half the time, so verifying them needs lowS: false, and DER input needs format: "der" — without it a 70-byte DER blob throws a length error.
Version 2 changed the meaning of the first argument. In 1.x, sign() and verify() took a hash you computed; in 2.x they hash the message themselves with the curve's hash, and the check confirmed that 1.9.7 reproduces the RFC vector only when given SHA-256 of the message. Code upgraded without changes therefore hashes twice and fails against every other library; pass prehash: false where you really hold a digest. Version 1 also left P-256 signatures unnormalized, so signatures that verified in 1.x can fail in 2.x.
Keys are raw bytes only: a 32-byte secret scalar, and public keys as SEC1 points — compressed (33 bytes) by default, uncompressed on request. SPKI, PKCS#8 and JWK are not read, so keys from OpenSSL or a JWKS have to be unpacked first; the JWK converter and the curve playground help with that. There is no post-quantum code in this package; the same author publishes it separately.
The sample shows the two options that make noble interoperate with an OpenSSL-style signer — format and lowS — and the deterministic output of its own signer. To compare a signature across encodings by hand, use the DER ⇄ raw converter; for wallet signatures, the Ethereum signature verifier runs on the same curve code.