Socket
Socket
Sign inDemoInstall

@noble/ed25519

Package Overview
Dependencies
Maintainers
1
Versions
17
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@noble/ed25519 - npm Package Compare versions

Comparing version 2.0.0 to 2.1.0

12

index.d.ts

@@ -25,3 +25,3 @@ declare const CURVE: {

static fromAffine(p: AffinePoint): Point;
static fromHex(hex: Hex, strict?: boolean): Point;
static fromHex(hex: Hex, zip215?: boolean): Point;
get x(): bigint;

@@ -55,4 +55,8 @@ get y(): bigint;

declare const sign: (msg: Hex, privKey: Hex) => Bytes;
declare const verifyAsync: (s: Hex, m: Hex, p: Hex) => Promise<boolean>;
declare const verify: (s: Hex, m: Hex, p: Hex) => boolean;
declare const verifyAsync: (s: Hex, m: Hex, p: Hex, opts?: {
zip215: boolean;
}) => Promise<boolean>;
declare const verify: (s: Hex, m: Hex, p: Hex, opts?: {
zip215: boolean;
}) => boolean;
declare const etc: {

@@ -64,3 +68,3 @@ bytesToHex: (b: Bytes) => string;

invert: (num: bigint, md?: bigint) => bigint;
randomBytes: (len: number) => Bytes;
randomBytes: (len?: number) => Bytes;
sha512Async: (...messages: Bytes[]) => Promise<Bytes>;

@@ -67,0 +71,0 @@ sha512Sync: Sha512FnSync;

@@ -7,3 +7,3 @@ /*! noble-ed25519 - MIT License (c) 2019 Paul Miller (paulmillr.com) */

const CURVE = {
a: -1n,
a: -1n, // where a=-1, d = -(121665/121666) == -(121665 * inv(121666)) mod P
d: 37095705934669439343138083508754565189542113879843219016388785533085940283555n,

@@ -14,10 +14,11 @@ p: P, n: N, h: 8, Gx, Gy // field prime, curve (group) order, cofactor

const str = (s) => typeof s === 'string'; // is string
const isu8 = (a) => (a instanceof Uint8Array ||
(a != null && typeof a === 'object' && a.constructor.name === 'Uint8Array'));
const au8 = (a, l) => // is Uint8Array (of specific length)
!(a instanceof Uint8Array) || (typeof l === 'number' && l > 0 && a.length !== l) ?
err('Uint8Array expected') : a;
!isu8(a) || (typeof l === 'number' && l > 0 && a.length !== l) ?
err('Uint8Array of valid length expected') : a;
const u8n = (data) => new Uint8Array(data); // creates Uint8Array
const toU8 = (a, len) => au8(str(a) ? h2b(a) : u8n(a), len); // norm(hex/u8a) to u8a
const toU8 = (a, len) => au8(str(a) ? h2b(a) : u8n(au8(a)), len); // norm(hex/u8a) to u8a
const mod = (a, b = P) => { let r = a % b; return r >= 0n ? r : b + r; }; // mod division
const isPoint = (p) => (p instanceof Point ? p : err('Point expected')); // is xyzt point
let Gpows = undefined; // precomputes for base point G
class Point {

@@ -31,16 +32,13 @@ constructor(ex, ey, ez, et) {

static fromAffine(p) { return new Point(p.x, p.y, 1n, mod(p.x * p.y)); }
static fromHex(hex, strict = true) {
static fromHex(hex, zip215 = false) {
const { d } = CURVE;
hex = toU8(hex, 32);
const normed = hex.slice(); // copy the array to not mess it up
normed[31] = hex[31] & ~0x80; // adjust first LE byte = last BE byte
const lastByte = hex[31];
normed[31] = lastByte & ~0x80; // adjust first LE byte = last BE byte
const y = b2n_LE(normed); // decode as little-endian, convert to num
if (y === 0n) { // y=0 is valid, proceed
}
else {
if (strict && !(0n < y && y < P))
err('bad y coord 1'); // strict=true [1..P-1]
if (!strict && !(0n < y && y < 2n ** 256n))
err('bad y coord 2'); // strict=false [1..2^256-1]
}
if (zip215 && !(0n <= y && y < 2n ** 256n))
err('bad y coord 1'); // zip215=true [1..2^256-1]
if (!zip215 && !(0n <= y && y < P))
err('bad y coord 2'); // zip215=false [1..P-1]
const y2 = mod(y * y); // y²

@@ -53,4 +51,6 @@ const u = mod(y2 - 1n); // u=y²-1

const isXOdd = (x & 1n) === 1n; // adjust sign of x coordinate
const isHeadOdd = (hex[31] & 0x80) !== 0;
if (isHeadOdd !== isXOdd)
const isLastByteOdd = (lastByte & 0x80) !== 0; // x_0, last bit
if (!zip215 && x === 0n && isLastByteOdd)
err('bad y coord 3'); // x=0 and x_0 = 1
if (isLastByteOdd !== isXOdd)
x = mod(-x);

@@ -137,4 +137,4 @@ return new Point(x, y, 1n, mod(x * y)); // Z=1, T=xy

const { ex: x, ey: y, ez: z } = this; // (x, y, z, t) ∋ (x=x/z, y=y/z, t=xy)
if (this.is0())
return { x: 0n, y: 0n }; // fast-path for zero point
if (this.equals(I))
return { x: 0n, y: 1n }; // fast-path for zero point
const iz = invert(z); // z^-1: invert z

@@ -284,11 +284,21 @@ if (mod(z * iz) !== 1n)

};
const _verify = (sig, msg, pub) => {
const dvo = { zip215: true };
const _verify = (sig, msg, pub, opts = dvo) => {
msg = toU8(msg); // Message hex str/Bytes
sig = toU8(sig, 64); // Signature hex str/Bytes, must be 64 bytes
const A = Point.fromHex(pub, false); // public key A decoded
const R = Point.fromHex(sig.slice(0, 32), false); // 0 <= R < 2^256: ZIP215 R can be >= P
const s = b2n_LE(sig.slice(32, 64)); // Decode second half as an integer S
const SB = G.mul(s, false); // in the range 0 <= s < L
const hashable = concatB(R.toRawBytes(), A.toRawBytes(), msg); // dom2(F, C) || R || A || PH(M)
const { zip215 } = opts; // switch between zip215 and rfc8032 verif
let A, R, s, SB, hashable = new Uint8Array();
try {
A = Point.fromHex(pub, zip215); // public key A decoded
R = Point.fromHex(sig.slice(0, 32), zip215); // 0 <= R < 2^256: ZIP215 R can be >= P
s = b2n_LE(sig.slice(32, 64)); // Decode second half as an integer S
SB = G.mul(s, false); // in the range 0 <= s < L
hashable = concatB(R.toRawBytes(), A.toRawBytes(), msg); // dom2(F, C) || R || A || PH(M)
}
catch (error) { }
const finish = (hashed) => {
if (SB == null)
return false; // false if try-catch catched an error
if (!zip215 && A.isSmallOrder())
return false; // false for SBS: Strongly Binding Signature
const k = modL_LE(hashed); // decode in little-endian, modulo L

@@ -301,4 +311,4 @@ const RkA = R.add(A.mul(k, false)); // [8]R + [8][k]A'

// RFC8032 5.1.7: verification async, sync
const verifyAsync = async (s, m, p) => hashFinish(true, _verify(s, m, p));
const verify = (s, m, p) => hashFinish(false, _verify(s, m, p));
const verifyAsync = async (s, m, p, opts = dvo) => hashFinish(true, _verify(s, m, p, opts));
const verify = (s, m, p, opts = dvo) => hashFinish(false, _verify(s, m, p, opts));
const cr = () => // We support: 1) browsers 2) node.js 19+

@@ -309,7 +319,7 @@ typeof globalThis === 'object' && 'crypto' in globalThis ? globalThis.crypto : undefined;

mod, invert,
randomBytes: (len) => {
randomBytes: (len = 32) => {
const crypto = cr(); // Can be shimmed in node.js <= 18 to prevent error:
// import { webcrypto } from 'node:crypto';
// if (!globalThis.crypto) globalThis.crypto = webcrypto;
if (!crypto)
if (!crypto || !crypto.getRandomValues)
err('crypto.getRandomValues must be defined');

@@ -320,3 +330,3 @@ return crypto.getRandomValues(u8n(len));

const crypto = cr();
if (!crypto)
if (!crypto || !crypto.subtle)
err('crypto.subtle or etc.sha512Async must be defined');

@@ -335,3 +345,3 @@ const m = concatB(...messages);

randomPrivateKey: () => etc.randomBytes(32),
precompute(w = 8, p = G) { p.multiply(3n); return p; }, // no-op
precompute(w = 8, p = G) { p.multiply(3n); w; return p; }, // no-op
};

@@ -354,2 +364,3 @@ const W = 8; // Precomputes-related code. W = window size

};
let Gpows = undefined; // precomputes for base point G
const wNAF = (n) => {

@@ -356,0 +367,0 @@ // Compared to other point mult methods,

@@ -6,3 +6,3 @@ /*! noble-ed25519 - MIT License (c) 2019 Paul Miller (paulmillr.com) */

const Gy = 0x6666666666666666666666666666666666666666666666666666666666666658n; // base point y
const CURVE = { // Curve's formula is −x² + y² = -a + dx²y²
const CURVE = { // Curve's formula is −x² + y² = -a + dx²y²
a: -1n, // where a=-1, d = -(121665/121666) == -(121665 * inv(121666)) mod P

@@ -15,10 +15,13 @@ d: 37095705934669439343138083508754565189542113879843219016388785533085940283555n,

const str = (s: unknown): s is string => typeof s === 'string'; // is string
const isu8 = (a: unknown): a is Uint8Array => (
a instanceof Uint8Array ||
(a != null && typeof a === 'object' && a.constructor.name === 'Uint8Array')
);
const au8 = (a: unknown, l?: number): Bytes => // is Uint8Array (of specific length)
!(a instanceof Uint8Array) || (typeof l === 'number' && l > 0 && a.length !== l) ?
err('Uint8Array expected') : a;
!isu8(a) || (typeof l === 'number' && l > 0 && a.length !== l) ?
err('Uint8Array of valid length expected') : a;
const u8n = (data?: any) => new Uint8Array(data); // creates Uint8Array
const toU8 = (a: Hex, len?: number) => au8(str(a) ? h2b(a) : u8n(a), len); // norm(hex/u8a) to u8a
const toU8 = (a: Hex, len?: number) => au8(str(a) ? h2b(a) : u8n(au8(a)), len); // norm(hex/u8a) to u8a
const mod = (a: bigint, b = P) => { let r = a % b; return r >= 0n ? r : b + r; }; // mod division
const isPoint = (p: any) => (p instanceof Point ? p : err('Point expected')); // is xyzt point
let Gpows: Point[] | undefined = undefined; // precomputes for base point G
interface AffinePoint { x: bigint, y: bigint } // Point in 2d xy affine coordinates

@@ -30,13 +33,11 @@ class Point { // Point in xyzt extended coordinates

static fromAffine(p: AffinePoint) { return new Point(p.x, p.y, 1n, mod(p.x * p.y)); }
static fromHex(hex: Hex, strict = true) { // RFC8032 5.1.3: hex / Uint8Array to Point.
static fromHex(hex: Hex, zip215 = false) { // RFC8032 5.1.3: hex / Uint8Array to Point.
const { d } = CURVE;
hex = toU8(hex, 32);
const normed = hex.slice(); // copy the array to not mess it up
normed[31] = hex[31] & ~0x80; // adjust first LE byte = last BE byte
const lastByte = hex[31];
normed[31] = lastByte & ~0x80; // adjust first LE byte = last BE byte
const y = b2n_LE(normed); // decode as little-endian, convert to num
if (y === 0n) { // y=0 is valid, proceed
} else {
if (strict && !(0n < y && y < P)) err('bad y coord 1'); // strict=true [1..P-1]
if (!strict && !(0n < y && y < 2n ** 256n)) err('bad y coord 2'); // strict=false [1..2^256-1]
}
if (zip215 && !(0n <= y && y < 2n ** 256n)) err('bad y coord 1'); // zip215=true [1..2^256-1]
if (!zip215 && !(0n <= y && y < P)) err('bad y coord 2'); // zip215=false [1..P-1]
const y2 = mod(y * y); // y²

@@ -48,4 +49,5 @@ const u = mod(y2 - 1n); // u=y²-1

const isXOdd = (x & 1n) === 1n; // adjust sign of x coordinate
const isHeadOdd = (hex[31] & 0x80) !== 0;
if (isHeadOdd !== isXOdd) x = mod(-x);
const isLastByteOdd = (lastByte & 0x80) !== 0; // x_0, last bit
if (!zip215 && x === 0n && isLastByteOdd) err('bad y coord 3'); // x=0 and x_0 = 1
if (isLastByteOdd !== isXOdd) x = mod(-x);
return new Point(x, y, 1n, mod(x * y)); // Z=1, T=xy

@@ -107,3 +109,3 @@ }

const { ex: x, ey: y, ez: z } = this; // (x, y, z, t) ∋ (x=x/z, y=y/z, t=xy)
if (this.is0()) return { x: 0n, y: 0n }; // fast-path for zero point
if (this.equals(I)) return { x: 0n, y: 1n }; // fast-path for zero point
const iz = invert(z); // z^-1: invert z

@@ -120,3 +122,3 @@ if (mod(z * iz) !== 1n) err('invalid inverse'); // (z * z^-1) must be 1, otherwise bad math

toHex(): string { return b2h(this.toRawBytes()); } // encode to hex string
}

@@ -250,14 +252,21 @@ const { BASE: G, ZERO: I } = Point; // Generator, identity points

};
const _verify = (sig: Hex, msg: Hex, pub: Hex): Finishable<boolean> => { // sig verification
const dvo = { zip215: true };
const _verify = (sig: Hex, msg: Hex, pub: Hex, opts = dvo): Finishable<boolean> => {
msg = toU8(msg); // Message hex str/Bytes
sig = toU8(sig, 64); // Signature hex str/Bytes, must be 64 bytes
const A = Point.fromHex(pub, false); // public key A decoded
const R = Point.fromHex(sig.slice(0, 32), false); // 0 <= R < 2^256: ZIP215 R can be >= P
const s = b2n_LE(sig.slice(32, 64)); // Decode second half as an integer S
const SB = G.mul(s, false); // in the range 0 <= s < L
const hashable = concatB(R.toRawBytes(), A.toRawBytes(), msg); // dom2(F, C) || R || A || PH(M)
const { zip215 } = opts; // switch between zip215 and rfc8032 verif
let A: Point, R: Point, s: bigint, SB: Point, hashable = new Uint8Array();
try {
A = Point.fromHex(pub, zip215); // public key A decoded
R = Point.fromHex(sig.slice(0, 32), zip215); // 0 <= R < 2^256: ZIP215 R can be >= P
s = b2n_LE(sig.slice(32, 64)); // Decode second half as an integer S
SB = G.mul(s, false); // in the range 0 <= s < L
hashable = concatB(R.toRawBytes(), A.toRawBytes(), msg); // dom2(F, C) || R || A || PH(M)
} catch (error) {}
const finish = (hashed: Bytes): boolean => { // k = SHA512(dom2(F, C) || R || A || PH(M))
if (SB == null) return false; // false if try-catch catched an error
if (!zip215 && A.isSmallOrder()) return false; // false for SBS: Strongly Binding Signature
const k = modL_LE(hashed); // decode in little-endian, modulo L
const RkA = R.add(A.mul(k, false)); // [8]R + [8][k]A'
return RkA.add(SB.negate()).clearCofactor().is0(); // [8][S]B = [8]R + [8][k]A'
return RkA.add(SB.negate()).clearCofactor().is0(); // [8][S]B = [8]R + [8][k]A'
}

@@ -267,4 +276,6 @@ return { hashable, finish };

// RFC8032 5.1.7: verification async, sync
const verifyAsync = async (s: Hex, m: Hex, p: Hex) => hashFinish(true, _verify(s, m, p));
const verify = (s: Hex, m: Hex, p: Hex) => hashFinish(false, _verify(s, m, p));
const verifyAsync = async (s: Hex, m: Hex, p: Hex, opts = dvo) =>
hashFinish(true, _verify(s, m, p, opts));
const verify = (s: Hex, m: Hex, p: Hex, opts = dvo) =>
hashFinish(false, _verify(s, m, p, opts));
declare const globalThis: Record<string, any> | undefined; // Typescript symbol present in browsers

@@ -276,7 +287,7 @@ const cr = () => // We support: 1) browsers 2) node.js 19+

mod, invert,
randomBytes: (len: number): Bytes => { // CSPRNG (random number generator)
randomBytes: (len = 32): Bytes => { // CSPRNG (random number generator)
const crypto = cr(); // Can be shimmed in node.js <= 18 to prevent error:
// import { webcrypto } from 'node:crypto';
// if (!globalThis.crypto) globalThis.crypto = webcrypto;
if (!crypto) err('crypto.getRandomValues must be defined');
if (!crypto || !crypto.getRandomValues) err('crypto.getRandomValues must be defined');
return crypto.getRandomValues(u8n(len));

@@ -286,3 +297,3 @@ },

const crypto = cr();
if (!crypto) err('crypto.subtle or etc.sha512Async must be defined');
if (!crypto || !crypto.subtle) err('crypto.subtle or etc.sha512Async must be defined');
const m = concatB(...messages);

@@ -299,3 +310,3 @@ return u8n(await crypto.subtle.digest('SHA-512', m.buffer));

randomPrivateKey: (): Bytes => etc.randomBytes(32),
precompute(w=8, p: Point = G) { p.multiply(3n); return p; }, // no-op
precompute(w=8, p: Point = G) { p.multiply(3n); w; return p; }, // no-op
}

@@ -315,2 +326,3 @@ const W = 8; // Precomputes-related code. W = window size

}
let Gpows: Point[] | undefined = undefined; // precomputes for base point G
const wNAF = (n: bigint): { p: Point; f: Point } => { // w-ary non-adjacent form (wNAF) method.

@@ -317,0 +329,0 @@ // Compared to other point mult methods,

{
"name": "@noble/ed25519",
"version": "2.0.0",
"description": "Fastest 4KB JS implementation of ed25519 elliptic curve. Auditable, high-security, 0-dependency EDDSA signatures compliant with RFC8032 & ZIP215",
"version": "2.1.0",
"description": "Fastest 4KB JS implementation of ed25519 EDDSA signatures compliant with RFC8032, FIPS 186-5 & ZIP215",
"files": [

@@ -14,9 +14,12 @@ "index.js",

"types": "index.d.ts",
"sideEffects": false,
"scripts": {
"build": "tsc",
"build:release": "rollup -c rollup.config.js",
"test": "node test/ed25519.test.mjs",
"build:min": "cd test/build; npm install; npm run terser",
"build:mingz": "npm run --silent build:min | gzip -c8",
"build:release": "npm run --silent build:min > test/build/noble-ed25519.min.js; npm run --silent build:mingz > test/build/noble-ed25519.min.js.gz",
"test": "node test/index.test.js",
"test:webcrypto": "node test/ed25519.webcrypto.test.js",
"bench": "node test/benchmark/benchmark.js",
"min": "cd test/build; npm install; npm run terser",
"loc": "echo \"`npm run --silent min | wc -c` symbols `wc -l < index.ts` LOC, `npm run --silent min | gzip -c8 | wc -c`B gzipped\""
"loc": "echo \"`npm run --silent build:min | wc -c` symbols `wc -l < index.ts` LOC, `npm run --silent build:mingz | wc -c`B gzipped\""
},

@@ -27,11 +30,12 @@ "author": "Paul Miller (https://paulmillr.com)",

"type": "git",
"url": "https://github.com/paulmillr/noble-ed25519.git"
"url": "git+https://github.com/paulmillr/noble-ed25519.git"
},
"license": "MIT",
"devDependencies": {
"@noble/hashes": "1.3.0",
"@noble/hashes": "1.4.0",
"@paulmillr/jsbt": "0.1.0",
"fast-check": "3.0.0",
"micro-bmark": "0.3.0",
"micro-should": "0.4.0",
"typescript": "5.0.2"
"typescript": "5.3.2"
},

@@ -41,2 +45,3 @@ "keywords": [

"rfc8032",
"fips186",
"signature",

@@ -47,7 +52,4 @@ "eddsa",

"elliptic curve",
"ecc",
"curve",
"rfc7748",
"zip215",
"ristretto255",
"x25519",

@@ -62,8 +64,3 @@ "curve25519"

},
"funding": [
{
"type": "individual",
"url": "https://paulmillr.com/funding/"
}
]
"funding": "https://paulmillr.com/funding/"
}
# noble-ed25519
[Fastest](#speed) 4KB JS implementation of [ed25519](https://en.wikipedia.org/wiki/EdDSA)
elliptic curve. Auditable, high-security, 0-dependency EdDSA signatures compliant with
[RFC8032](https://tools.ietf.org/html/rfc8032) and [ZIP215](https://zips.z.cash/zip-0215).
Fastest 4KB JS implementation of ed25519 signatures.
The library is a tiny single-feature version of
[noble-curves](https://github.com/paulmillr/noble-curves), with some features
removed. Check out curves as a drop-in replacement with
[ristretto255](https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-ristretto255-decaf448),
X25519 / curve25519, ed25519ph and ed25519ctx.
- ✍️ [EDDSA](https://en.wikipedia.org/wiki/EdDSA) signatures compliant with [RFC8032](https://tools.ietf.org/html/rfc8032),
FIPS 186-5
- 🪢 Consensus-friendly, compliant with [ZIP215](https://zips.z.cash/zip-0215)
- 🔖 SUF-CMA (strong unforgeability under chosen message attacks) and SBS (non-repudiation / exclusive ownership)
- 📦 Pure ESM, can be imported without transpilers
- 🪶 4KB gzipped, 350 lines of code
Take a look at: [Upgrading](#upgrading) section for v1 to v2 transition instructions,
[the online demo](https://paulmillr.com/noble/) and
[ed25519-keygen](https://github.com/paulmillr/ed25519-keygen) if you need
SSH/PGP/HDKey implementation using the library.
Use larger drop-in replacement [noble-curves](https://github.com/paulmillr/noble-curves) instead,
if you need additional features such as common.js support, ristretto255, X25519, curve25519, ed25519ph, ed25519ctx.
To upgrade from v1 to v2, see [Upgrading](#upgrading). [Online demo](https://paulmillr.com/noble/).
### This library belongs to _noble_ crypto
### This library belongs to _noble_ cryptography
> **noble-crypto** — high-security, easily auditable set of contained cryptographic libraries and tools.
> **noble-cryptography** — high-security, easily auditable set of contained cryptographic libraries and tools.
- No dependencies, protection against supply chain attacks
- Auditable TypeScript / JS code
- Supported in all major browsers and stable node.js versions
- All releases are signed with PGP keys
- Zero or minimal dependencies
- Highly readable TypeScript / JS code
- PGP-signed releases and transparent NPM builds with provenance
- Check out [homepage](https://paulmillr.com/noble/) & all libraries:
[curves](https://github.com/paulmillr/noble-curves)
(4kb versions [secp256k1](https://github.com/paulmillr/noble-secp256k1),
[ed25519](https://github.com/paulmillr/noble-ed25519)),
[hashes](https://github.com/paulmillr/noble-hashes)
[ciphers](https://github.com/paulmillr/noble-ciphers),
[curves](https://github.com/paulmillr/noble-curves),
[hashes](https://github.com/paulmillr/noble-hashes),
[post-quantum](https://github.com/paulmillr/noble-post-quantum),
4kb [secp256k1](https://github.com/paulmillr/noble-secp256k1) /
[ed25519](https://github.com/paulmillr/noble-ed25519)
## Usage
Browser, deno, node.js and unpkg are supported:
> npm install @noble/ed25519
We support all major platforms and runtimes. For node.js <= 18 and React Native, additional polyfills are needed: see below.
```js
import * as ed from '@noble/ed25519'; // ESM-only. Use bundler for common.js
import * as ed from '@noble/ed25519';
// import * as ed from "https://deno.land/x/ed25519/mod.ts"; // Deno

@@ -47,3 +46,3 @@ // import * as ed from "https://unpkg.com/@noble/ed25519"; // Unpkg

const message = Uint8Array.from([0xab, 0xbc, 0xcd, 0xde]);
const pubKey = await ed.getPublicKeyAsync(privKey);
const pubKey = await ed.getPublicKeyAsync(privKey); // Sync methods below
const signature = await ed.signAsync(message, privKey);

@@ -54,19 +53,22 @@ const isValid = await ed.verifyAsync(signature, message, pubKey);

Advanced examples:
Additional polyfills for some environments:
```ts
// 1. Use the shim to enable synchronous methods.
// Only async methods are available by default to keep library dependency-free.
// 1. Enable synchronous methods.
// Only async methods are available by default, to keep the library dependency-free.
import { sha512 } from '@noble/hashes/sha512';
ed.etc.sha512Sync = (...m) => sha512(ed.etc.concatBytes(...m));
ed.getPublicKey(privateKey); // sync methods can be used now
ed.sign(message, privateKey);
ed.verify(signature, message, publicKey);
// Sync methods can be used now:
// ed.getPublicKey(privKey); ed.sign(msg, privKey); ed.verify(signature, msg, pubKey);
// 2. Use the shim only for node.js <= 18 BEFORE importing noble-secp256k1.
// The library depends on global variable crypto to work. It is available in
// all browsers and many environments, but node.js <= 18 don't have it.
// 2. node.js 18 and older, requires polyfilling globalThis.crypto
import { webcrypto } from 'node:crypto';
// @ts-ignore
if (!globalThis.crypto) globalThis.crypto = webcrypto;
// 3. React Native needs crypto.getRandomValues polyfill and sha512
import 'react-native-get-random-values';
import { sha512 } from '@noble/hashes/sha512';
ed.etc.sha512Sync = (...m) => sha512(ed.etc.concatBytes(...m));
ed.etc.sha512Async = (...m) => Promise.resolve(ed.etc.sha512Sync(...m));
```

@@ -77,17 +79,26 @@

There are 3 main methods: `getPublicKey(privateKey)`, `sign(message, privateKey)`
and `verify(signature, message, publicKey)`.
and `verify(signature, message, publicKey)`. We accept Hex type everywhere:
```ts
type Hex = Uint8Array | string
```
### getPublicKey
```typescript
type Hex = Uint8Array | string;
// Generates 32-byte public key from 32-byte private key.
// - Some libraries have 64-byte private keys. Don't worry, those are just
// priv+pub concatenated. Slice it: `priv64b.slice(0, 32)`
// - Use `Point.fromPrivateKey(privateKey)` if you want `Point` instance instead
// - Use `Point.fromHex(publicKey)` if you want to convert hex / bytes into Point.
// It will use decompression algorithm 5.1.3 of RFC 8032.
// - Use `utils.getExtendedPublicKey` if you need full SHA512 hash of seed
function getPublicKey(privateKey: Hex): Uint8Array;
function getPublicKeyAsync(privateKey: Hex): Promise<Uint8Array>;
```
// Generates EdDSA signature.
Generates 32-byte public key from 32-byte private key.
- Some libraries have 64-byte private keys. Don't worry, those are just
priv+pub concatenated. Slice it: `priv64b.slice(0, 32)`
- Use `Point.fromPrivateKey(privateKey)` if you want `Point` instance instead
- Use `Point.fromHex(publicKey)` if you want to convert hex / bytes into Point.
It will use decompression algorithm 5.1.3 of RFC 8032.
- Use `utils.getExtendedPublicKey` if you need full SHA512 hash of seed
### sign
```ts
function sign(

@@ -98,15 +109,17 @@ message: Hex, // message which would be signed

function signAsync(message: Hex, privateKey: Hex): Promise<Uint8Array>;
```
// Verifies EdDSA signature. Compatible with [ZIP215](https://zips.z.cash/zip-0215):
// - `0 <= sig.R/publicKey < 2**256` (can be `>= curve.P` aka non-canonical encoding)
// - `0 <= sig.s < l`
// - There is no security risk in ZIP behavior, and there is no effect on
// honestly generated sigs, but it is verify important for consensus-critical
// apps. See [It’s 255:19AM](https://hdevalence.ca/blog/2020-10-04-its-25519am).
// - _Not compatible with RFC8032_ because RFC enforces canonical encoding of
// R/publicKey.
Generates EdDSA signature. Always deterministic.
Assumes unhashed `message`: it would be hashed by ed25519 internally.
For prehashed ed25519ph, switch to noble-curves.
### verify
```ts
function verify(
signature: Hex, // returned by the `sign` function
message: Hex, // message that needs to be verified
publicKey: Hex // public (not private) key
publicKey: Hex // public (not private) key,
options = { zip215: true } // ZIP215 or RFC8032 verification type
): boolean;

@@ -116,35 +129,50 @@ function verifyAsync(signature: Hex, message: Hex, publicKey: Hex): Promise<boolean>;

Verifies EdDSA signature. Has SUF-CMA (strong unforgeability under chosen message attacks).
By default, follows ZIP215 [1] and can be used in consensus-critical apps [2].
`zip215: false` option switches verification criteria to strict
RFC8032 / FIPS 186-5 and provides non-repudiation with SBS (Strongly Binding Signatures) [3].
[1]: https://zips.z.cash/zip-0215
[2]: https://hdevalence.ca/blog/2020-10-04-its-25519am
[3]: https://eprint.iacr.org/2020/1244
### utils
A bunch of useful **utilities** are also exposed:
```typescript
export const etc: {
bytesToHex: (b: Bytes) => string;
hexToBytes: (hex: string) => Bytes;
concatBytes: (...arrs: Bytes[]) => Uint8Array;
mod: (a: bigint, b?: bigint) => bigint;
invert: (num: bigint, md?: bigint) => bigint;
randomBytes: (len: number) => Bytes;
sha512Async: (...messages: Bytes[]) => Promise<Bytes>;
sha512Sync: Sha512FnSync;
const etc: {
bytesToHex: (b: Bytes) => string;
hexToBytes: (hex: string) => Bytes;
concatBytes: (...arrs: Bytes[]) => Uint8Array;
mod: (a: bigint, b?: bigint) => bigint;
invert: (num: bigint, md?: bigint) => bigint;
randomBytes: (len: number) => Bytes;
sha512Async: (...messages: Bytes[]) => Promise<Bytes>;
sha512Sync: Sha512FnSync;
};
export const utils: {
getExtendedPublicKeyAsync: (priv: Hex) => Promise<ExtK>;
getExtendedPublicKey: (priv: Hex) => ExtK;
precompute(p: Point, w?: number): Point;
randomPrivateKey: () => Bytes;
const utils: {
getExtendedPublicKeyAsync: (priv: Hex) => Promise<ExtK>;
getExtendedPublicKey: (priv: Hex) => ExtK;
precompute(p: Point, w?: number): Point;
randomPrivateKey: () => Bytes; // Uses CSPRNG https://developer.mozilla.org/en-US/docs/Web/API/Crypto/getRandomValues
};
export class ExtendedPoint { // Elliptic curve point in Extended (x, y, z, t) coordinates.
constructor(x: bigint, y: bigint, z: bigint, t: bigint);
class ExtendedPoint { // Elliptic curve point in Extended (x, y, z, t) coordinates.
constructor(ex: bigint, ey: bigint, ez: bigint, et: bigint);
static readonly BASE: Point;
static readonly ZERO: Point;
static fromAffine(point: AffinePoint): ExtendedPoint;
static fromHex(hash: string);
toRawBytes(): Uint8Array;
toHex(): string; // Compact representation of a Point
isTorsionFree(): boolean; // Multiplies the point by curve order
toAffine(): Point;
equals(other: ExtendedPoint): boolean;
get x(): bigint;
get y(): bigint;
// Note: It does not check whether the `other` point is valid point on curve.
add(other: ExtendedPoint): ExtendedPoint;
equals(other: ExtendedPoint): boolean;
isTorsionFree(): boolean; // Multiplies the point by curve order
multiply(scalar: bigint): ExtendedPoint;
subtract(other: ExtendedPoint): ExtendedPoint;
multiply(scalar: bigint): ExtendedPoint;
toAffine(): Point;
toRawBytes(): Uint8Array;
toHex(): string; // Compact representation of a Point
}

@@ -161,37 +189,58 @@ // Curve params

The module is production-ready.
It is cross-tested against [noble-curves](https://github.com/paulmillr/noble-curves),
and has similar security.
The library has not been independently audited as of v2, which is a rewrite of v1.
v1 has been audited by [Cure53](https://cure53.de/pentest-report_ed25519.pdf) in Feb 2022.
1. The current version is rewrite of v1, which has been audited by cure53:
[PDF](https://cure53.de/pentest-report_ed25519.pdf).
2. It's being fuzzed by [Guido Vranken's cryptofuzz](https://github.com/guidovranken/cryptofuzz):
run the fuzzer by yourself to check.
The code is identical to [noble-curves](https://github.com/paulmillr/noble-curves), which *has* been audited.
Our EC multiplication is hardened to be algorithmically constant time.
We're using built-in JS `BigInt`, which is potentially vulnerable to
[timing attacks](https://en.wikipedia.org/wiki/Timing_attack) as
[per MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt#cryptography).
But, _JIT-compiler_ and _Garbage Collector_ make "constant time" extremely hard
to achieve in a scripting language. Which means _any other JS library doesn't
use constant-time bigints_. Including bn.js or anything else.
Even statically typed Rust, a language without GC,
It is tested against property-based, cross-library and Wycheproof vectors,
and has fuzzing by [Guido Vranken's cryptofuzz](https://github.com/guidovranken/cryptofuzz).
### Constant-timeness
_JIT-compiler_ and _Garbage Collector_ make "constant time" extremely hard to
achieve [timing attack](https://en.wikipedia.org/wiki/Timing_attack) resistance
in a scripting language. Which means _any other JS library can't have
constant-timeness_. Even statically typed Rust, a language without GC,
[makes it harder to achieve constant-time](https://www.chosenplaintext.ca/open-source/rust-timing-shield/security)
for some cases. If your goal is absolute security, don't use any JS lib —
including bindings to native ones. Use low-level libraries & languages.
for some cases. If your goal is absolute security, don't use any JS lib — including bindings to native ones.
Use low-level libraries & languages. Nonetheless we're targetting algorithmic constant time.
### Supply chain security
1. **Commits** are signed with PGP keys, to prevent forgery. Make sure to verify commit signatures.
2. **Releases** are transparent and built on GitHub CI. Make sure to verify [provenance](https://docs.npmjs.com/generating-provenance-statements) logs
3. **Rare releasing** is followed.
The less often it is done, the less code dependents would need to audit
4. **Dependencies** are minimal:
- All deps are prevented from automatic updates and have locked-down version ranges. Every update is checked with `npm-diff`
- Updates themselves are rare, to ensure rogue updates are not catched accidentally
5. devDependencies are only used if you want to contribute to the repo. They are disabled for end-users:
- [noble-hashes](https://github.com/paulmillr/noble-hashes) is used, by the same author, to provide hashing functionality tests
- micro-bmark and micro-should are developed by the same author and follow identical security practices
- fast-check (property-based testing) and typescript are used for code quality, vector generation and ts compilation.
The packages are big, which makes it hard to audit their source code thoroughly and fully
We consider infrastructure attacks like rogue NPM modules very important;
that's why it's crucial to minimize the amount of 3rd-party dependencies & native
bindings. If your app uses 500 dependencies, any dep could get hacked and you'll
be downloading malware with every `npm install`. Our goal is to minimize this attack vector.
that's why it's crucial to minimize the amount of 3rd-party dependencies & native bindings.
If your app uses 500 dependencies, any dep could get hacked and you'll be
downloading malware with every install. Our goal is to minimize this attack vector.
If you see anything unusual: investigate and report.
### Randomness
We're deferring to built-in
[crypto.getRandomValues](https://developer.mozilla.org/en-US/docs/Web/API/Crypto/getRandomValues)
which is considered cryptographically secure (CSPRNG).
In the past, browsers had bugs that made it weak: it may happen again.
## Speed
Benchmarks done with Apple M2 on macOS 13 with Node.js 19.
Benchmarks done with Apple M2 on macOS 13 with Node.js 20.
getPublicKey 1 bit x 8,260 ops/sec @ 121μs/op
getPublicKey(utils.randomPrivateKey()) x 8,096 ops/sec @ 123μs/op
sign x 4,084 ops/sec @ 244μs/op
verify x 872 ops/sec @ 1ms/op
Point.fromHex decompression x 14,523 ops/sec @ 68μs/op
getPublicKey(utils.randomPrivateKey()) x 9,173 ops/sec @ 109μs/op
sign x 4,567 ops/sec @ 218μs/op
verify x 994 ops/sec @ 1ms/op
Point.fromHex decompression x 16,164 ops/sec @ 61μs/op

@@ -210,3 +259,3 @@ Compare to alternative implementations:

3. `npm run build` to compile TypeScript code
4. `npm run test` to run jest on `test/index.ts`
4. `npm run test` to run tests

@@ -213,0 +262,0 @@ ## Upgrading

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc