# ec-compression

Latest version **2.0.0** (published 2026-07-27) · 0 weekly downloads

## Install

```sh
npm install ec-compression
pnpm add ec-compression
yarn add ec-compression
bun add ec-compression
```

## Health

**Score 70/100 (B)** — status: active.

Positive: has types; esm support; no vulnerabilities; recently updated; high maintenance score; high quality score.

Warnings: low downloads.

## Facts

| | |
|---|---|
| Version | 2.0.0 |
| Published | 2026-07-27 |
| First published | 2025-02-07 |
| Weekly downloads | 0 |
| TypeScript types | bundled |
| Module format | ESM + CommonJS |
| Dependencies | 0 |
| Unpacked size | 70.2 KB |
| Known vulnerabilities | 0 |
| Install scripts | no |
| Maintainers | blu3beri |

## Links

- npm: https://www.npmjs.com/package/ec-compression
- npm.io page: https://npm.io/package/ec-compression

## Recent versions

- 2.0.0 (latest) — 2026-07-27
- 0.0.1-alpha.13 (alpha) — 2026-07-02
- 1.0.0 — 2026-07-02
- 0.0.1-alpha.12 — 2025-04-03
- 0.0.1-alpha.11 — 2025-03-05
- 0.0.1-alpha.10 — 2025-02-13
- 0.0.1-alpha.9 — 2025-02-10
- 0.0.1-alpha.8 — 2025-02-10
- 0.0.1-alpha.7 — 2025-02-10
- 0.0.1-alpha.6 — 2025-02-10
- 0.0.1-alpha.5 — 2025-02-10
- 0.0.1-alpha.4 — 2025-02-07
- 0.0.1-alpha.3 — 2025-02-07
- 0.0.1-alpha.2 — 2025-02-07
- 0.0.1-alpha.1 — 2025-02-07
- … 2 more at https://npm.io/package/ec-compression/versions

## README

# ec-compression

Elliptic curve public key point compression and decompression, with no runtime dependencies.

A public key on a short Weierstrass curve is a point `(x, y)`. Because `y` is determined by `x` up to its parity, the point can be stored in compressed form (a parity prefix plus the `x` coordinate) instead of its full uncompressed form. This library converts between the two forms and validates that a given point lies on the curve.

Supported curves: secp256r1 (P-256), secp384r1 (P-384), secp521r1 (P-521), and secp256k1.

## Installation

```sh
npm install ec-compression
```

## Point encoding

Keys are represented as `Uint8Array` using the SEC1 encoding:

- Compressed: `0x02 || x` when `y` is even, `0x03 || x` when `y` is odd.
- Uncompressed: `0x04 || x || y`.

Inputs whose coordinates lost leading zero bytes (as some JWK producers emit
for P-521) are accepted; output is always canonical SEC1, with coordinates
padded to the full curve width.

## Usage

### Compress and decompress

The curve may be a `CurveParams` instance or a curve name (see
[Curve names](#curve-names)).

```ts
import { compressPublicKey, decompressPublicKey } from 'ec-compression'

const compressed = compressPublicKey(uncompressedKey, 'p256')
const uncompressed = decompressPublicKey(compressed, 'p256')
```

Both functions throw if the input is not a valid point for the given curve.

### Best-effort variants

The `*IfPossible` variants return the input unchanged instead of throwing when it
cannot be converted. This is useful when a key may already be in the desired form.

```ts
import {
  compressPublicKeyIfPossible,
  decompressPublicKeyIfPossible,
} from 'ec-compression'

const compressed = compressPublicKeyIfPossible(key, 'p256')
```

### Validation

```ts
import {
  isValidCompressedPublicKeyFormat,
  isValidDecompressedPublicKeyFormat,
  isValidPublicKeyFormat,
} from 'ec-compression'

isValidCompressedPublicKeyFormat(key, 'p256')
isValidDecompressedPublicKeyFormat(key, 'p256')
isValidPublicKeyFormat(key, 'p256') // either form
```

Each check verifies the encoding prefix and length, and that the decoded point lies on the curve.

### Working with points directly

`AffinePoint` exposes the underlying conversion.

```ts
import { AffinePoint, Secp256r1 } from 'ec-compression'

const point = AffinePoint.fromCompressedPoint(compressedKey, Secp256r1)

point.x // bigint
point.y // bigint
point.compressedForm // Uint8Array
point.decompressedForm // Uint8Array
point.isValidPoint(Secp256r1) // boolean

const other = new AffinePoint(xBigintOrBytes, yBigintOrBytes, Secp256r1)
AffinePoint.fromDecompressedPoint(uncompressedKey, Secp256r1)
```

## API

### Compression functions

- `compressPublicKey(uncompressed: Uint8Array, curve: CurveParams | string): Uint8Array`
- `decompressPublicKey(compressed: Uint8Array, curve: CurveParams | string): Uint8Array`
- `compressPublicKeyIfPossible(key: Uint8Array, curve: CurveParams | string): Uint8Array`
- `decompressPublicKeyIfPossible(key: Uint8Array, curve: CurveParams | string): Uint8Array`

### Validation functions

- `isValidCompressedPublicKeyFormat(key: Uint8Array, curve: CurveParams | string): boolean`
- `isValidDecompressedPublicKeyFormat(key: Uint8Array, curve: CurveParams | string): boolean`
- `isValidPublicKeyFormat(key: Uint8Array, curve: CurveParams | string): boolean`

### `AffinePoint`

- `new AffinePoint(x: Uint8Array | bigint, y: Uint8Array | bigint, curve: CurveParams | string)`
- `AffinePoint.fromCompressedPoint(form: Uint8Array | bigint, curve: CurveParams | string): AffinePoint`
- `AffinePoint.fromDecompressedPoint(form: Uint8Array | bigint, curve: CurveParams | string): AffinePoint`
- `point.x` / `point.y` — coordinates as `bigint`
- `point.xBytes` / `point.yBytes` — coordinates as full-width `Uint8Array`
- `point.compressedForm` / `point.decompressedForm` — encoded key as `Uint8Array`
- `point.isValidPoint(curve: CurveParams | string): boolean`

### Curves

- `Secp256r1`, `Secp384r1`, `Secp521r1`, `Secp256k1` — `CurveParams` instances.
- `getCurveParamsByName(name: string): CurveParams | undefined`
- `resolveCurveParams(curve: CurveParams | string): CurveParams` — like `getCurveParamsByName`, but accepts an instance and throws on unknown names.
- `CurveParams` — class holding the curve parameters `p`, `a`, `b`, `pointBitLength`, and `names`.

#### Curve names

Name lookups are case-insensitive. The following aliases are recognised:

| Curve       | Aliases                        |
| ----------- | ------------------------------ |
| `Secp256r1` | `secp256r1`, `p256`, `p-256`   |
| `Secp384r1` | `secp384r1`, `p384`, `p-384`   |
| `Secp521r1` | `secp521r1`, `p521`, `p-521`   |
| `Secp256k1` | `secp256k1`, `k256`, `k-256`   |

### Conversion helpers

- `bigintToBytes(value: bigint, length?: number): Uint8Array` — big-endian, zero-padded to `length` when given; throws on negative input.
- `bytesToBigint(bytes: Uint8Array): bigint` — big-endian; throws on empty input.

## Development

```sh
pnpm install
pnpm test
pnpm lint
pnpm build
```

The number of randomized round-trip cases per curve can be set with the
`TESTS_PER_CURVE` environment variable (default `100`).

## License

MIT

---
_Source: https://npm.io/package/ec-compression · Machine-readable twin of the npm.io package page. Health data is recomputed on every publish._
