npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

js-ecutils

v0.0.2-alpha.bcaac67

Published

JavaScript Library for Elliptic Curve Cryptography: key exchanges (Diffie-Hellman, Massey-Omura), ECDSA signatures, and Koblitz encoding. Suitable for crypto education and secure systems.

Readme

js-ecutils

JavaScript Library for Elliptic Curve Cryptography

Latest Version Downloads Downloads Downloads codecov

js-ecutils is a JavaScript library for Elliptic Curve Cryptography (ECC) on short Weierstrass curves (y² = x³ + ax + b over a prime field F_p). It provides ECDSA digital signatures, key exchange protocols (Diffie-Hellman, Massey-Omura), Koblitz message encoding, SEC 1 point compression, and both affine and Jacobian coordinate arithmetic. Suitable for cryptography education and secure systems.

Features

  • Point arithmetic — addition, doubling, scalar multiplication in affine and Jacobian coordinates
  • 8 standard curves — secp192k1, secp192r1, secp224k1, secp224r1, secp256k1, secp256r1, secp384r1, secp521r1
  • ECDSA signaturessign(), verify(), signMessage(), verifyMessage() with SHA-256
  • Koblitz encoding — encode/decode text messages as elliptic curve points
  • Diffie-Hellman key exchange — ECDH shared secret computation
  • Massey-Omura three-pass protocol — key-free message exchange via encrypt/decrypt
  • SEC 1 point compressioncompress(), decompress(), compressSec1(), toUncompressedSec1(), fromSec1()
  • Modular arithmetic utilities — modular square root (Tonelli-Shanks), quadratic residue testing
  • Secure nonce generation — uses crypto.getRandomValues() (CSPRNG) instead of Math.random()
  • Cross-platform — works in Node.js and browsers (Web Crypto API)

Table of Contents

Installation

Using npm:

npm install js-ecutils

Or, for web usage:

<script src="https://unpkg.com/js-ecutils@latest/dist/web/min.js"></script>

Quick Start

Node.js

const {
  Point, CurveParams, getCurve, getGenerator,
  DigitalSignature, Koblitz,
  DiffieHellman, MasseyOmura
} = require('js-ecutils')

// Point arithmetic on a toy curve  y² = x³ + x + 1  over F₂₃
const curve = new CurveParams({ p: 23n, a: 1n, b: 1n, n: 28n, h: 1n })
const P = new Point(0n, 1n, curve)
const Q = new Point(6n, 19n, curve)
const R = P.add(Q)
console.log(`P + Q = (${R.x}, ${R.y})`)  // (13, 16)

// Scalar multiplication on secp256k1
const G = getGenerator('secp256k1')
const pubKey = G.mul(42n)
console.log(`42·G = (${pubKey.x}, ${pubKey.y})`)

Browser

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>js-ecutils Example</title>
    <script src="https://unpkg.com/js-ecutils@latest/dist/web/min.js"></script>
    <script>
        window.onload = function() {
            const { Point, CurveParams } = window.ecutils

            const curve = new CurveParams({ p: 23n, a: 1n, b: 1n, n: 28n, h: 1n })
            const P = new Point(0n, 1n, curve)
            const Q = new Point(6n, 19n, curve)
            const R = P.add(Q)
            console.log(`P + Q = (${R.x}, ${R.y})`)
        }
    </script>
</head>
<body>
    <h1>js-ecutils Example</h1>
</body>
</html>

API Documentation

Core

Point

Represents a point on an elliptic curve. The identity element (point at infinity) is represented as Point(0n, 0n).

const { Point, CurveParams } = require('js-ecutils')

const curve = new CurveParams({ p: 23n, a: 1n, b: 1n, n: 28n, h: 1n })
const P = new Point(0n, 1n, curve)

Constructor: new Point(x, y, curve, _trusted)

  • x — x-coordinate (BigInt)
  • y — y-coordinate (BigInt)
  • curveCurveParams instance (optional, can be borrowed from another point during operations)
  • _trusted — skip on-curve validation (internal use only)

Properties:

  • isIdentitytrue if this is the point at infinity

Methods:

  • isOnCurve() — verifies y² ≡ x³ + ax + b (mod p)
  • neg() — additive inverse: returns (x, p - y)
  • add(other) — point addition: P + Q
  • sub(other) — point subtraction: P - Q
  • mul(k) — scalar multiplication: k·P (double-and-add)
  • compress() — returns [x, yParity] where yParity is 0n or 1n
  • decompress(x, yParity, curve) — (static) recovers a point from compressed form
  • compressSec1() — SEC 1 compressed format (Uint8Array: 02/03 prefix + x)
  • toUncompressedSec1() — SEC 1 uncompressed format (Uint8Array: 04 prefix + x + y)
  • fromSec1(data, curve) — (static) parses SEC 1 compressed or uncompressed bytes
  • toString() — human-readable representation

CurveParams

Immutable container for elliptic curve domain parameters.

const { CurveParams, CoordinateSystem } = require('js-ecutils')

const curve = new CurveParams({
  p: 23n, a: 1n, b: 1n, n: 28n, h: 1n,
  coord: CoordinateSystem.JACOBIAN  // default
})

Constructor: new CurveParams({ p, a, b, n, h, coord })

  • p — prime modulus of the finite field F_p
  • a, b — curve equation coefficients (y² = x³ + ax + b)
  • n — order of the generator point
  • h — cofactor (default: 1n)
  • coord — coordinate system: CoordinateSystem.AFFINE or CoordinateSystem.JACOBIAN (default)

Validates that the discriminant 4a³ + 27b² ≢ 0 (mod p), ensuring the curve is non-singular.


CoordinateSystem

Enum for selecting the internal arithmetic representation.

  • CoordinateSystem.AFFINE — standard (x, y) coordinates, uses modular inversion per operation
  • CoordinateSystem.JACOBIAN — projective (X, Y, Z) coordinates where x = X/Z², y = Y/Z³, ~3x faster

Curves

getCurve(name) / getGenerator(name)

Look up standard curve parameters and generator points by name.

const { getCurve, getGenerator } = require('js-ecutils')

const curve = getCurve('secp256k1')   // returns CurveParams
const G = getGenerator('secp256k1')   // returns Point (generator)

Supported curves: secp192k1, secp192r1, secp224k1, secp224r1, secp256k1, secp256r1, secp384r1, secp521r1

Lookup is case-insensitive. Throws an error for unknown curve names.


Algorithms

DigitalSignature

ECDSA signature generation and verification.

const { DigitalSignature } = require('js-ecutils')

const ds = new DigitalSignature(123456n, 'secp256k1')

Constructor: new DigitalSignature(privateKey, curveName = 'secp256k1')

Properties:

  • publicKey — the public key point Q = d·G

Methods:

  • sign(messageHash) — generates an ECDSA signature [r, s] for an integer hash
  • verify(publicKey, messageHash, r, s) — verifies a signature, returns boolean
  • signMessage(message, hashFunc?) — hashes a message (SHA-256 by default) and signs it, returns Promise<[r, s]>
  • verifyMessage(publicKey, message, r, s, hashFunc?) — hashes and verifies, returns Promise<boolean>

The nonce k is generated using a cryptographically secure random number generator (crypto.getRandomValues()).


Koblitz

Encode/decode text messages as elliptic curve points using Koblitz's method.

const { Koblitz } = require('js-ecutils')

const kob = new Koblitz('secp521r1')
const [point, j] = kob.encode('Hello, world!')
const text = kob.decode(point, j)
// text === 'Hello, world!'

Constructor: new Koblitz(curveName = 'secp521r1', alphabetSize = 256n)

  • curveName — larger curves can encode longer messages in a single point
  • alphabetSize — 256n for ASCII, 65536n for Unicode

Methods:

  • encode(message) — returns [Point, j] where j is an auxiliary value needed for decoding
  • decode(point, j) — recovers the original text string

Protocols

DiffieHellman

Elliptic Curve Diffie-Hellman (ECDH) key exchange.

const { DiffieHellman } = require('js-ecutils')

const alice = new DiffieHellman(0xAn, 'secp256k1')
const bob   = new DiffieHellman(0xBn, 'secp256k1')

const sharedA = alice.computeSharedSecret(bob.publicKey)
const sharedB = bob.computeSharedSecret(alice.publicKey)
// sharedA.x === sharedB.x && sharedA.y === sharedB.y

Constructor: new DiffieHellman(privateKey, curveName = 'secp256k1')

Properties:

  • publicKey — H = d·G

Methods:

  • computeSharedSecret(otherPublicKey) — returns the shared secret point S = d·Q_other

MasseyOmura

Massey-Omura three-pass protocol for key-free message exchange.

const { MasseyOmura, Koblitz } = require('js-ecutils')

const kob   = new Koblitz('secp521r1')
const alice = new MasseyOmura(0xA1n, 'secp521r1')
const bob   = new MasseyOmura(0xB2n, 'secp521r1')

const [M, j] = kob.encode('Secret message')

const c1 = alice.encrypt(M)        // Alice → Bob
const c2 = bob.encrypt(c1)         // Bob → Alice
const c3 = alice.decrypt(c2)       // Alice → Bob
const plaintext = bob.decrypt(c3)  // Bob recovers M

const text = kob.decode(plaintext, j)
// text === 'Secret message'

Constructor: new MasseyOmura(privateKey, curveName = 'secp521r1')

  • privateKey must be coprime with n (so that e⁻¹ mod n exists)

Methods:

  • encrypt(point) — C = e·P (multiply by private key)
  • decrypt(point) — P = e⁻¹·C (multiply by modular inverse)

Utilities

isQuadraticResidue(a, p)

Tests whether a is a quadratic residue modulo prime p using Euler's criterion: a^((p-1)/2) ≡ 1 (mod p).

modularSqrt(a, p)

Computes √a mod p. Uses the direct formula when p ≡ 3 (mod 4), otherwise falls back to the Tonelli-Shanks algorithm. Returns null if a is not a quadratic residue.


Examples

Point Arithmetic

const { Point, CurveParams, getGenerator } = require('js-ecutils')

// Toy curve: y² = x³ + x + 1 over F₂₃ (order 28)
const curve = new CurveParams({ p: 23n, a: 1n, b: 1n, n: 28n, h: 1n })
const P = new Point(0n, 1n, curve)

// Doubling: 2·P
const P2 = P.mul(2n)
console.log(`2·P = (${P2.x}, ${P2.y})`)  // (6, 19)

// Group order: 28·P = O (identity)
const identity = P.mul(28n)
console.log(`28·P is identity: ${identity.isIdentity}`)  // true

// Negation: -P = (x, p - y)
const neg = P.neg()
console.log(`-P = (${neg.x}, ${neg.y})`)  // (0, 22)

SEC 1 Point Compression

const { getGenerator, Point, getCurve } = require('js-ecutils')

const G = getGenerator('secp256k1')

// Compress to SEC 1 format (33 bytes for 256-bit curve)
const compressed = G.compressSec1()
console.log(`Compressed: ${compressed[0] === 0x02 ? '02' : '03'}...`)

// Decompress back
const curve = getCurve('secp256k1')
const recovered = Point.fromSec1(compressed, curve)
console.log(`Match: ${recovered.x === G.x && recovered.y === G.y}`)  // true

ECDSA Digital Signatures

const { DigitalSignature } = require('js-ecutils')

const ds = new DigitalSignature(123456n, 'secp256k1')

// Sign a raw hash
const hash = 0xDEADBEEFn
const [r, s] = ds.sign(hash)
console.log(`Valid: ${ds.verify(ds.publicKey, hash, r, s)}`)  // true

// Sign a message string (async, uses SHA-256)
async function demo() {
  const [r, s] = await ds.signMessage('Hello, ECDSA!')
  const valid = await ds.verifyMessage(ds.publicKey, 'Hello, ECDSA!', r, s)
  console.log(`Message signature valid: ${valid}`)  // true
}
demo()

Koblitz Encoding

const { Koblitz } = require('js-ecutils')

const kob = new Koblitz('secp521r1')
const [point, j] = kob.encode('Hello, EC!')
const decoded = kob.decode(point, j)
console.log(decoded)  // 'Hello, EC!'

Diffie-Hellman Key Exchange

const { DiffieHellman } = require('js-ecutils')

const alice = new DiffieHellman(12345n, 'secp256k1')
const bob   = new DiffieHellman(67890n, 'secp256k1')

const sharedA = alice.computeSharedSecret(bob.publicKey)
const sharedB = bob.computeSharedSecret(alice.publicKey)

console.log(`Shared secrets match: ${sharedA.x === sharedB.x}`)  // true

Massey-Omura Three-Pass Protocol

const { Koblitz, MasseyOmura } = require('js-ecutils')

const kob   = new Koblitz('secp521r1')
const alice = new MasseyOmura(70604135n, 'secp521r1')
const bob   = new MasseyOmura(48239108668n, 'secp521r1')

// Encode the message as a curve point
const [M, j] = kob.encode('Hello, world!')

// Three-pass exchange
const c1 = alice.encrypt(M)        // Step 1: Alice encrypts
const c2 = bob.encrypt(c1)         // Step 2: Bob encrypts
const c3 = alice.decrypt(c2)       // Step 3: Alice removes her encryption
const recovered = bob.decrypt(c3)  // Step 4: Bob removes his encryption

const text = kob.decode(recovered, j)
console.log(text)  // 'Hello, world!'

Contributing

Contributions are welcome! Please see CONTRIBUTING.md for guidelines.

License

This project is licensed under the MIT License. See the LICENSE file for details.

Related Libraries