ec-compression
v2.0.0
Published
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
npm install ec-compressionPoint encoding
Keys are represented as Uint8Array using the SEC1 encoding:
- Compressed:
0x02 || xwhenyis even,0x03 || xwhenyis 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).
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.
import {
compressPublicKeyIfPossible,
decompressPublicKeyIfPossible,
} from 'ec-compression'
const compressed = compressPublicKeyIfPossible(key, 'p256')Validation
import {
isValidCompressedPublicKeyFormat,
isValidDecompressedPublicKeyFormat,
isValidPublicKeyFormat,
} from 'ec-compression'
isValidCompressedPublicKeyFormat(key, 'p256')
isValidDecompressedPublicKeyFormat(key, 'p256')
isValidPublicKeyFormat(key, 'p256') // either formEach 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.
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): Uint8ArraydecompressPublicKey(compressed: Uint8Array, curve: CurveParams | string): Uint8ArraycompressPublicKeyIfPossible(key: Uint8Array, curve: CurveParams | string): Uint8ArraydecompressPublicKeyIfPossible(key: Uint8Array, curve: CurveParams | string): Uint8Array
Validation functions
isValidCompressedPublicKeyFormat(key: Uint8Array, curve: CurveParams | string): booleanisValidDecompressedPublicKeyFormat(key: Uint8Array, curve: CurveParams | string): booleanisValidPublicKeyFormat(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): AffinePointAffinePoint.fromDecompressedPoint(form: Uint8Array | bigint, curve: CurveParams | string): AffinePointpoint.x/point.y— coordinates asbigintpoint.xBytes/point.yBytes— coordinates as full-widthUint8Arraypoint.compressedForm/point.decompressedForm— encoded key asUint8Arraypoint.isValidPoint(curve: CurveParams | string): boolean
Curves
Secp256r1,Secp384r1,Secp521r1,Secp256k1—CurveParamsinstances.getCurveParamsByName(name: string): CurveParams | undefinedresolveCurveParams(curve: CurveParams | string): CurveParams— likegetCurveParamsByName, but accepts an instance and throws on unknown names.CurveParams— class holding the curve parametersp,a,b,pointBitLength, andnames.
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 tolengthwhen given; throws on negative input.bytesToBigint(bytes: Uint8Array): bigint— big-endian; throws on empty input.
Development
pnpm install
pnpm test
pnpm lint
pnpm buildThe number of randomized round-trip cases per curve can be set with the
TESTS_PER_CURVE environment variable (default 100).
License
MIT
