New Case Study:See how Anthropic automated 95% of dependency reviews with Socket.Learn More
Socket
Sign inDemoInstall
Socket

@noble/secp256k1

Package Overview
Dependencies
Maintainers
0
Versions
24
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@noble/secp256k1 - npm Package Compare versions

Comparing version 2.1.0 to 2.2.0

44

index.d.ts

@@ -40,5 +40,5 @@ declare const CURVE: {

toHex(isCompressed?: boolean): string;
toRawBytes(isCompressed?: boolean): Uint8Array;
toRawBytes(isCompressed?: boolean): Bytes;
}
declare function getPublicKey(privKey: PrivKey, isCompressed?: boolean): Uint8Array;
declare const getPublicKey: (privKey: PrivKey, isCompressed?: boolean) => Bytes;
type SignatureWithRecovery = Signature & {

@@ -53,3 +53,3 @@ recovery: number;

static fromCompact(hex: Hex): Signature;
assertValidity(): this;
assertValidity(): Signature;
addRecoveryBit(rec: number): SignatureWithRecovery;

@@ -59,14 +59,15 @@ hasHighS(): boolean;

recoverPublicKey(msgh: Hex): Point;
toCompactRawBytes(): Uint8Array;
toCompactRawBytes(): Bytes;
toCompactHex(): string;
}
type HmacFnSync = undefined | ((key: Bytes, ...msgs: Bytes[]) => Bytes);
declare function signAsync(msgh: Hex, priv: PrivKey, opts?: {
lowS?: boolean | undefined;
extraEntropy?: boolean | Hex | undefined;
}): Promise<SignatureWithRecovery>;
declare function sign(msgh: Hex, priv: PrivKey, opts?: {
lowS?: boolean | undefined;
extraEntropy?: boolean | Hex | undefined;
}): SignatureWithRecovery;
type OptS = {
lowS?: boolean;
extraEntropy?: boolean | Hex;
};
type OptV = {
lowS?: boolean;
};
declare const signAsync: (msgh: Hex, priv: PrivKey, opts?: OptS) => Promise<SignatureWithRecovery>;
declare const sign: (msgh: Hex, priv: PrivKey, opts?: OptS) => SignatureWithRecovery;
type SigLike = {

@@ -76,18 +77,15 @@ r: bigint;

};
declare function verify(sig: Hex | SigLike, msgh: Hex, pub: Hex, opts?: {
lowS?: boolean | undefined;
}): boolean;
declare function getSharedSecret(privA: Hex, pubB: Hex, isCompressed?: boolean): Bytes;
declare function hashToPrivateKey(hash: Hex): Bytes;
declare const verify: (sig: Hex | SigLike, msgh: Hex, pub: Hex, opts?: OptV) => boolean;
declare const getSharedSecret: (privA: Hex, pubB: Hex, isCompressed?: boolean) => Bytes;
declare const etc: {
hexToBytes: (hex: string) => Bytes;
bytesToHex: (b: Bytes) => string;
concatBytes: (...arrs: Bytes[]) => Uint8Array;
bytesToNumberBE: (b: Bytes) => bigint;
numberToBytesBE: (num: bigint) => Bytes;
mod: (a: bigint, b?: bigint) => bigint;
bytesToHex: (bytes: Bytes) => string;
concatBytes: (...arrs: Bytes[]) => Bytes;
bytesToNumberBE: (a: Bytes) => bigint;
numberToBytesBE: (n: bigint) => Bytes;
mod: (a: bigint, md?: bigint) => bigint;
invert: (num: bigint, md?: bigint) => bigint;
hmacSha256Async: (key: Bytes, ...msgs: Bytes[]) => Promise<Bytes>;
hmacSha256Sync: HmacFnSync;
hashToPrivateKey: typeof hashToPrivateKey;
hashToPrivateKey: (hash: Hex) => Bytes;
randomBytes: (len?: number) => Bytes;

@@ -94,0 +92,0 @@ };

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

const Gy = 0x483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8n; // base point y
const CURVE = { p: P, n: N, a: 0n, b: 7n, Gx, Gy }; // exported variables incl. a, b
const CURVE = {
p: P, n: N, a: 0n, b: 7n, Gx, Gy
}; // exported variables incl. a, b
const fLen = 32; // field / group byte length
const crv = (x) => mod(mod(x * x) * x + CURVE.b); // x³ + ax + b weierstrass formula; a=0
const curve = (x) => M(M(x * x) * x + CURVE.b); // x³ + ax + b weierstrass formula; a=0
const err = (m = '') => { throw new Error(m); }; // error helper, messes-up stack trace
const big = (n) => typeof n === 'bigint'; // is big integer
const str = (s) => typeof s === 'string'; // is string
const fe = (n) => big(n) && 0n < n && n < P; // is field element (invertible)
const ge = (n) => big(n) && 0n < n && n < N; // is group element
const isu8 = (a) => (a instanceof Uint8Array ||
(a != null && typeof a === 'object' && a.constructor.name === 'Uint8Array'));
const isB = (n) => typeof n === 'bigint'; // is big integer
const isS = (s) => typeof s === 'string'; // is string
const fe = (n) => isB(n) && 0n < n && n < P; // is field element (invertible)
const ge = (n) => isB(n) && 0n < n && n < N; // is group element
const isu8 = (a) => (a instanceof Uint8Array || (ArrayBuffer.isView(a) && a.constructor.name === 'Uint8Array'));
const au8 = (a, l) => // assert is Uint8Array (of specific length)

@@ -22,5 +23,8 @@ !isu8(a) || (typeof l === 'number' && l > 0 && a.length !== l) ?

const u8n = (data) => new Uint8Array(data); // creates Uint8Array
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 3d point
const toU8 = (a, len) => au8(isS(a) ? h2b(a) : u8n(au8(a)), len); // norm(hex/u8a) to u8a
const M = (a, b = P) => {
const r = a % b;
return r >= 0n ? r : b + r;
};
const aPoint = (p) => (p instanceof Point ? p : err('Point expected')); // is 3d point
class Point {

@@ -31,5 +35,6 @@ constructor(px, py, pz) {

this.pz = pz;
} //3d=less inversions
Object.freeze(this);
}
static fromAffine(p) {
return ((p.x === 0n) && (p.y === 0n)) ? Point.ZERO : new Point(p.x, p.y, 1n);
return ((p.x === 0n) && (p.y === 0n)) ? I : new Point(p.x, p.y, 1n);
}

@@ -40,16 +45,16 @@ static fromHex(hex) {

const head = hex[0], tail = hex.subarray(1); // first byte is prefix, rest is data
const x = slcNum(tail, 0, fLen), len = hex.length; // next 32 bytes are x coordinate
const x = slc(tail, 0, fLen), len = hex.length; // next 32 bytes are x coordinate
if (len === 33 && [0x02, 0x03].includes(head)) { // compressed points: 33b, start
if (!fe(x))
err('Point hex invalid: x not FE'); // with byte 0x02 or 0x03. Check if 0<x<P
let y = sqrt(crv(x)); // x³ + ax + b is right side of equation
let y = sqrt(curve(x)); // x³ + ax + b is right side of equation
const isYOdd = (y & 1n) === 1n; // y² is equivalent left-side. Calculate y²:
const headOdd = (head & 1) === 1; // y = √y²; there are two solutions: y, -y
if (headOdd !== isYOdd)
y = mod(-y); // determine proper solution
y = M(-y); // determine proper solution
p = new Point(x, y, 1n); // create point
} // Uncompressed points: 65b, start with 0x04
if (len === 65 && head === 0x04)
p = new Point(x, slcNum(tail, fLen, 2 * fLen), 1n);
return p ? p.ok() : err('Point is not on curve'); // Verify the result
p = new Point(x, slc(tail, fLen, 2 * fLen), 1n);
return p ? p.ok() : err('Point invalid: not on curve'); // Verify the result
}

@@ -61,52 +66,52 @@ static fromPrivateKey(k) { return G.mul(toPriv(k)); } // Create point from a private key.

const { px: X1, py: Y1, pz: Z1 } = this;
const { px: X2, py: Y2, pz: Z2 } = isPoint(other); // isPoint() checks class equality
const X1Z2 = mod(X1 * Z2), X2Z1 = mod(X2 * Z1);
const Y1Z2 = mod(Y1 * Z2), Y2Z1 = mod(Y2 * Z1);
const { px: X2, py: Y2, pz: Z2 } = aPoint(other); // isPoint() checks class equality
const X1Z2 = M(X1 * Z2), X2Z1 = M(X2 * Z1);
const Y1Z2 = M(Y1 * Z2), Y2Z1 = M(Y2 * Z1);
return X1Z2 === X2Z1 && Y1Z2 === Y2Z1;
}
negate() { return new Point(this.px, mod(-this.py), this.pz); } // Flip point over y coord
negate() { return new Point(this.px, M(-this.py), this.pz); } // Flip point over y coord
double() { return this.add(this); } // Point doubling: P+P, complete formula.
add(other) {
const { px: X1, py: Y1, pz: Z1 } = this; // free formula from Renes-Costello-Batina
const { px: X2, py: Y2, pz: Z2 } = isPoint(other); // https://eprint.iacr.org/2015/1060, algo 1
const { px: X2, py: Y2, pz: Z2 } = aPoint(other); // https://eprint.iacr.org/2015/1060, algo 1
const { a, b } = CURVE; // Cost: 12M + 0S + 3*a + 3*b3 + 23add
let X3 = 0n, Y3 = 0n, Z3 = 0n;
const b3 = mod(b * 3n);
let t0 = mod(X1 * X2), t1 = mod(Y1 * Y2), t2 = mod(Z1 * Z2), t3 = mod(X1 + Y1); // step 1
let t4 = mod(X2 + Y2); // step 5
t3 = mod(t3 * t4);
t4 = mod(t0 + t1);
t3 = mod(t3 - t4);
t4 = mod(X1 + Z1);
let t5 = mod(X2 + Z2); // step 10
t4 = mod(t4 * t5);
t5 = mod(t0 + t2);
t4 = mod(t4 - t5);
t5 = mod(Y1 + Z1);
X3 = mod(Y2 + Z2); // step 15
t5 = mod(t5 * X3);
X3 = mod(t1 + t2);
t5 = mod(t5 - X3);
Z3 = mod(a * t4);
X3 = mod(b3 * t2); // step 20
Z3 = mod(X3 + Z3);
X3 = mod(t1 - Z3);
Z3 = mod(t1 + Z3);
Y3 = mod(X3 * Z3);
t1 = mod(t0 + t0); // step 25
t1 = mod(t1 + t0);
t2 = mod(a * t2);
t4 = mod(b3 * t4);
t1 = mod(t1 + t2);
t2 = mod(t0 - t2); // step 30
t2 = mod(a * t2);
t4 = mod(t4 + t2);
t0 = mod(t1 * t4);
Y3 = mod(Y3 + t0);
t0 = mod(t5 * t4); // step 35
X3 = mod(t3 * X3);
X3 = mod(X3 - t0);
t0 = mod(t3 * t1);
Z3 = mod(t5 * Z3);
Z3 = mod(Z3 + t0); // step 40
const b3 = M(b * 3n);
let t0 = M(X1 * X2), t1 = M(Y1 * Y2), t2 = M(Z1 * Z2), t3 = M(X1 + Y1); // step 1
let t4 = M(X2 + Y2); // step 5
t3 = M(t3 * t4);
t4 = M(t0 + t1);
t3 = M(t3 - t4);
t4 = M(X1 + Z1);
let t5 = M(X2 + Z2); // step 10
t4 = M(t4 * t5);
t5 = M(t0 + t2);
t4 = M(t4 - t5);
t5 = M(Y1 + Z1);
X3 = M(Y2 + Z2); // step 15
t5 = M(t5 * X3);
X3 = M(t1 + t2);
t5 = M(t5 - X3);
Z3 = M(a * t4);
X3 = M(b3 * t2); // step 20
Z3 = M(X3 + Z3);
X3 = M(t1 - Z3);
Z3 = M(t1 + Z3);
Y3 = M(X3 * Z3);
t1 = M(t0 + t0); // step 25
t1 = M(t1 + t0);
t2 = M(a * t2);
t4 = M(b3 * t4);
t1 = M(t1 + t2);
t2 = M(t0 - t2); // step 30
t2 = M(a * t2);
t4 = M(t4 + t2);
t0 = M(t1 * t4);
Y3 = M(Y3 + t0);
t0 = M(t5 * t4); // step 35
X3 = M(t3 * X3);
X3 = M(X3 - t0);
t0 = M(t3 * t1);
Z3 = M(t5 * Z3);
Z3 = M(Z3 + t0); // step 40
return new Point(X3, Y3, Z3);

@@ -118,3 +123,3 @@ }

if (!ge(n))
err('invalid scalar'); // must be 0 < n < CURVE.n
err('scalar invalid'); // must be 0 < n < CURVE.n
if (this.equals(G))

@@ -140,6 +145,6 @@ return wNAF(n).p; // use precomputes for base point

return { x, y }; // if z is 1, pass affine coordinates as-is
const iz = inv(z); // z^-1: invert z
if (mod(z * iz) !== 1n)
err('invalid inverse'); // (z * z^-1) must be 1, otherwise bad math
return { x: mod(x * iz), y: mod(y * iz) }; // x = x*z^-1; y = y*z^-1
const iz = inv(z, P); // z^-1: invert z
if (M(z * iz) !== 1n)
err('inverse invalid'); // (z * z^-1) must be 1, otherwise bad math
return { x: M(x * iz), y: M(y * iz) }; // x = x*z^-1; y = y*z^-1
}

@@ -150,3 +155,3 @@ assertValidity() {

err('Point invalid: x or y'); // x and y must be in range 0 < n < P
return mod(y * y) === crv(x) ? // y² = x³ + ax + b, must be equal
return M(y * y) === curve(x) ? // y² = x³ + ax + b, must be equal
this : err('Point invalid: not on curve');

@@ -170,22 +175,34 @@ }

const padh = (n, pad) => n.toString(16).padStart(pad, '0');
const b2h = (b) => Array.from(b).map(e => padh(e, 2)).join(''); // bytes to hex
const b2h = (b) => Array.from(au8(b)).map(e => padh(e, 2)).join(''); // bytes to hex
const C = { _0: 48, _9: 57, A: 65, F: 70, a: 97, f: 102 }; // ASCII characters
const _ch = (ch) => {
if (ch >= C._0 && ch <= C._9)
return ch - C._0; // '2' => 50-48
if (ch >= C.A && ch <= C.F)
return ch - (C.A - 10); // 'B' => 66-(65-10)
if (ch >= C.a && ch <= C.f)
return ch - (C.a - 10); // 'b' => 98-(97-10)
return;
};
const h2b = (hex) => {
const l = hex.length; // error if not string,
if (!str(hex) || l % 2)
err('hex invalid 1'); // or has odd length like 3, 5.
const arr = u8n(l / 2); // create result array
for (let i = 0; i < arr.length; i++) {
const j = i * 2;
const h = hex.slice(j, j + 2); // hexByte. slice is faster than substr
const b = Number.parseInt(h, 16); // byte, created from string part
if (Number.isNaN(b) || b < 0)
err('hex invalid 2'); // byte must be valid 0 <= byte < 256
arr[i] = b;
const e = 'hex invalid';
if (!isS(hex))
return err(e);
const hl = hex.length, al = hl / 2;
if (hl % 2)
return err(e);
const array = u8n(al);
for (let ai = 0, hi = 0; ai < al; ai++, hi += 2) { // treat each char as ASCII
const n1 = _ch(hex.charCodeAt(hi)); // parse first char, multiply it by 16
const n2 = _ch(hex.charCodeAt(hi + 1)); // parse second char
if (n1 === undefined || n2 === undefined)
return err(e);
array[ai] = n1 * 16 + n2; // example: 'A9' => 10*16 + 9
}
return arr;
return array;
};
const b2n = (b) => BigInt('0x' + (b2h(b) || '0')); // bytes to number
const slcNum = (b, from, to) => b2n(b.slice(from, to)); // slice bytes num
const slc = (b, from, to) => b2n(b.slice(from, to)); // slice bytes num
const n2b = (num) => {
return big(num) && num >= 0n && num < B256 ? h2b(padh(num, 2 * fLen)) : err('bigint expected');
return isB(num) && num >= 0n && num < B256 ? h2b(padh(num, 2 * fLen)) : err('bigint expected');
};

@@ -199,6 +216,6 @@ const n2h = (num) => b2h(n2b(num)); // number to 32b hex

};
const inv = (num, md = P) => {
const inv = (num, md) => {
if (num === 0n || md <= 0n)
err('no inverse n=' + num + ' mod=' + md); // no neg exponent for now
let a = mod(num, md), b = md, x = 0n, y = 1n, u = 1n, v = 0n;
let a = M(num, md), b = md, x = 0n, y = 1n, u = 1n, v = 0n;
while (a !== 0n) { // uses euclidean gcd algorithm

@@ -209,3 +226,3 @@ const q = b / a, r = b % a; // not constant-time

}
return b === 1n ? mod(x, md) : err('no inverse'); // b is gcd at this point
return b === 1n ? M(x, md) : err('no inverse'); // b is gcd at this point
};

@@ -219,10 +236,10 @@ const sqrt = (n) => {

}
return mod(r * r) === n ? r : err('sqrt invalid'); // check if result is valid
return M(r * r) === n ? r : err('sqrt invalid'); // check if result is valid
};
const toPriv = (p) => {
if (!big(p))
if (!isB(p))
p = b2n(toU8(p, fLen)); // convert to bigint when bytes
return ge(p) ? p : err('private key out of range'); // check if bigint is in range
return ge(p) ? p : err('private key invalid 3'); // check if bigint is in range
};
const moreThanHalfN = (n) => n > (N >> 1n); // if a number is bigger than CURVE.n/2
const high = (n) => n > (N >> 1n); // if a number is bigger than CURVE.n/2
const getPublicKey = (privKey, isCompressed = true) => {

@@ -240,3 +257,3 @@ return Point.fromPrivateKey(privKey).toRawBytes(isCompressed); // 33b or 65b output

hex = toU8(hex, 64); // compact repr is (32b r)||(32b s)
return new Signature(slcNum(hex, 0, fLen), slcNum(hex, fLen, 2 * fLen));
return new Signature(slc(hex, 0, fLen), slc(hex, fLen, 2 * fLen));
}

@@ -247,5 +264,5 @@ assertValidity() { return ge(this.r) && ge(this.s) ? this : err(); } // 0 < r or s < CURVE.n

}
hasHighS() { return moreThanHalfN(this.s); }
hasHighS() { return high(this.s); }
normalizeS() {
return this.hasHighS() ? new Signature(this.r, mod(this.s, N), this.recovery) : this;
return high(this.s) ? new Signature(this.r, M(-this.s, N), this.recovery) : this;
}

@@ -263,4 +280,4 @@ recoverPublicKey(msgh) {

const ir = inv(radj, N); // r^-1
const u1 = mod(-h * ir, N); // -hr^-1
const u2 = mod(s * ir, N); // sr^-1
const u1 = M(-h * ir, N); // -hr^-1
const u2 = M(s * ir, N); // sr^-1
return G.mulAddQUns(R, u1, u2); // (sr^-1)R-(hr^-1)G = -(hr^-1)G + (sr^-1)

@@ -273,2 +290,4 @@ }

const delta = bytes.length * 8 - 256; // RFC suggests optional truncating via bits2octets
if (delta > 1024)
err('msg invalid'); // our CUSTOM check, "just-in-case"
const num = b2n(bytes); // FIPS 186-4 4.6 suggests the leftmost min(nBitLen, outLen) bits, which

@@ -278,7 +297,7 @@ return delta > 0 ? num >> BigInt(delta) : num; // matches bits2int. bits2int can produce res>N.

const bits2int_modN = (bytes) => {
return mod(bits2int(bytes), N); // with 0: BAD for trunc as per RFC vectors
return M(bits2int(bytes), N); // with 0: BAD for trunc as per RFC vectors
};
const i2o = (num) => n2b(num); // int to octets
const cr = () => // We support: 1) browsers 2) node.js 19+ 3) deno, other envs with crypto
typeof globalThis === 'object' && 'crypto' in globalThis ? globalThis.crypto : undefined;
typeof globalThis === 'object' && 'crypto' in globalThis && 'subtle' in globalThis.crypto ? globalThis.crypto : undefined;
let _hmacSync; // Can be redefined by use in utils; built-ins don't provide it

@@ -288,4 +307,4 @@ const optS = { lowS: true }; // opts for sign()

const prepSig = (msgh, priv, opts = optS) => {
if (['der', 'recovered', 'canonical'].some(k => k in opts)) // Ban legacy options
err('sign() legacy options not supported');
if (['der', 'recovered', 'canonical'].some(k => k in opts))
err('option not supported'); // legacy opts
let { lowS } = opts; // generates low-s sigs by default

@@ -299,10 +318,4 @@ if (lowS == null)

let ent = opts.extraEntropy; // RFC6979 3.6: additional k' (optional)
if (ent) { // K = HMAC_K(V || 0x00 || int2octets(x) || bits2octets(h1) || k')
if (ent === true)
ent = etc.randomBytes(fLen); // if true, use CSPRNG to generate data
const e = toU8(ent); // convert Hex|Bytes to Bytes
if (e.length !== fLen)
err(); // Expected 32 bytes of extra data
seed.push(e);
}
if (ent) // K = HMAC_K(V || 0x00 || int2octets(x) || bits2octets(h1) || k')
seed.push(ent === true ? etc.randomBytes(fLen) : toU8(ent)); // true == fetch from CSPRNG
const m = h1i; // convert msg to bigint

@@ -315,6 +328,6 @@ const k2sig = (kBytes) => {

const q = G.mul(k).aff(); // q = Gk
const r = mod(q.x, N); // r = q.x mod n
const r = M(q.x, N); // r = q.x mod n
if (r === 0n)
return; // r=0 invalid
const s = mod(ik * mod(m + mod(d * r, N), N), N); // s = k^-1(m + rd) mod n
const s = M(ik * M(m + M(d * r, N), N), N); // s = k^-1(m + rd) mod n
if (s === 0n)

@@ -324,4 +337,4 @@ return; // s=0 invalid

let rec = (q.x === r ? 0 : 2) | Number(q.y & 1n); // recovery bit
if (lowS && moreThanHalfN(s)) { // if lowS was passed, ensure s is always
normS = mod(-s, N); // in the bottom half of CURVE.n
if (lowS && high(s)) { // if lowS was passed, ensure s is always
normS = M(-s, N); // in the bottom half of CURVE.n
rec ^= 1;

@@ -397,2 +410,3 @@ }

}
;
// ECDSA signature generation. via secg.org/sec1-v2.pdf 4.1.2 + RFC6979 deterministic k

@@ -412,3 +426,3 @@ const signAsync = async (msgh, priv, opts = optS) => {

if ('strict' in opts)
err('verify() legacy options not supported'); // legacy param
err('option not supported'); // legacy param
let sig_, h, P; // secg.org/sec1-v2.pdf 4.1.4

@@ -429,3 +443,3 @@ const rs = sig && typeof sig === 'object' && 'r' in sig; // Previous ver supported DER sigs. We

const { r, s } = sig_;
if (lowS && moreThanHalfN(s))
if (lowS && high(s))
return false; // lowS bans sig.s >= CURVE.n/2

@@ -435,4 +449,4 @@ let R;

const is = inv(s, N); // s^-1
const u1 = mod(h * is, N); // u1 = hs^-1 mod n
const u2 = mod(r * is, N); // u2 = rs^-1 mod n
const u1 = M(h * is, N); // u1 = hs^-1 mod n
const u2 = M(r * is, N); // u2 = rs^-1 mod n
R = G.mulAddQUns(P, u1, u2).aff(); // R = u1⋅G + u2⋅P

@@ -445,3 +459,3 @@ }

return false; // stop if R is identity / zero point
const v = mod(R.x, N); // R.x must be in N's field, not P's
const v = M(R.x, N); // R.x must be in N's field, not P's
return v === r; // mod(R.x, n) == r

@@ -454,12 +468,15 @@ };

hash = toU8(hash); // produces private keys with modulo bias
const minLen = fLen + 8; // being neglible.
if (hash.length < minLen || hash.length > 1024)
err('expected proper params');
const num = mod(b2n(hash), N - 1n) + 1n; // takes at least n+8 bytes
return n2b(num);
if (hash.length < fLen + 8 || hash.length > 1024)
err('expected 40-1024b'); // being neglible.
const num = M(b2n(hash), N - 1n); // takes n+8 bytes
return n2b(num + 1n); // returns (hash mod n-1)+1
};
const etc = {
hexToBytes: h2b, bytesToHex: b2h, // share API with noble-curves.
concatBytes: concatB, bytesToNumberBE: b2n, numberToBytesBE: n2b,
mod, invert: inv, // math utilities
hexToBytes: h2b, // share API with noble-curves.
bytesToHex: b2h,
concatBytes: concatB,
bytesToNumberBE: b2n,
numberToBytesBE: n2b,
mod: M,
invert: inv, // math utilities
hmacSha256Async: async (key, ...msgs) => {

@@ -474,3 +491,3 @@ const c = cr(); // async HMAC-SHA256, no sync built-in!

hmacSha256Sync: _hmacSync, // For TypeScript. Actual logic is below
hashToPrivateKey,
hashToPrivateKey: hashToPrivateKey,
randomBytes: (len = 32) => {

@@ -477,0 +494,0 @@ const crypto = cr(); // Must be shimmed in node.js <= 18 to prevent error. See README.

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

const Gy = 0x483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8n; // base point y
const CURVE = {p: P, n: N, a: 0n, b: 7n, Gx, Gy}; // exported variables incl. a, b
const CURVE: { p: bigint; n: bigint; a: bigint; b: bigint; Gx: bigint; Gy: bigint } = {
p: P, n: N, a: 0n, b: 7n, Gx, Gy }; // exported variables incl. a, b
const fLen = 32; // field / group byte length
type Bytes = Uint8Array; type Hex = Bytes | string; type PrivKey = Hex | bigint;
const crv = (x: bigint) => mod(mod(x * x) * x + CURVE.b); // x³ + ax + b weierstrass formula; a=0
const curve = (x: bigint) => M(M(x * x) * x + CURVE.b); // x³ + ax + b weierstrass formula; a=0
const err = (m = ''): never => { throw new Error(m); }; // error helper, messes-up stack trace
const big = (n: unknown): n is bigint => typeof n === 'bigint'; // is big integer
const str = (s: unknown): s is string => typeof s === 'string'; // is string
const fe = (n: bigint) => big(n) && 0n < n && n < P; // is field element (invertible)
const ge = (n: bigint) => big(n) && 0n < n && n < N; // is group element
const isB = (n: unknown): n is bigint => typeof n === 'bigint'; // is big integer
const isS = (s: unknown): s is string => typeof s === 'string'; // is string
const fe = (n: bigint) => isB(n) && 0n < n && n < P; // is field element (invertible)
const ge = (n: bigint) => isB(n) && 0n < n && n < N; // is group element
const isu8 = (a: unknown): a is Uint8Array => (
a instanceof Uint8Array ||
(a != null && typeof a === 'object' && a.constructor.name === 'Uint8Array')
a instanceof Uint8Array || (ArrayBuffer.isView(a) && a.constructor.name === 'Uint8Array')
);

@@ -25,13 +25,17 @@ const au8 = (a: unknown, l?: number): Bytes => // assert is Uint8Array (of specific length)

const u8n = (data?: any) => new Uint8Array(data); // creates Uint8Array
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: unknown) => (p instanceof Point ? p : err('Point expected')); // is 3d point
const toU8 = (a: Hex, len?: number) => au8(isS(a) ? h2b(a) : u8n(au8(a)), len); // norm(hex/u8a) to u8a
const M = (a: bigint, b: bigint = P) => { // mod division
const r = a % b; return r >= 0n ? r : b + r;
};
const aPoint = (p: unknown) => (p instanceof Point ? p : err('Point expected')); // is 3d point
interface AffinePoint { x: bigint, y: bigint } // Point in 2d xy affine coordinates
class Point { // Point in 3d xyz projective coordinates
constructor(readonly px: bigint, readonly py: bigint, readonly pz: bigint) {} //3d=less inversions
static readonly BASE = new Point(Gx, Gy, 1n); // Generator / base point
static readonly ZERO = new Point(0n, 1n, 0n); // Identity / zero point
static fromAffine (p: AffinePoint) { // (0, 0) => (0, 1, 0), not (0, 0, 1)
return ((p.x === 0n) && (p.y === 0n)) ? Point.ZERO : new Point(p.x, p.y, 1n);
constructor(readonly px: bigint, readonly py: bigint, readonly pz: bigint) { //3d=less inversions
Object.freeze(this);
}
static readonly BASE: Point = new Point(Gx, Gy, 1n); // Generator / base point
static readonly ZERO: Point = new Point(0n, 1n, 0n); // Identity / zero point
static fromAffine(p: AffinePoint): Point { // (0, 0) => (0, 1, 0), not (0, 0, 1)
return ((p.x === 0n) && (p.y === 0n)) ? I : new Point(p.x, p.y, 1n);
}
static fromHex(hex: Hex): Point { // Convert Uint8Array or hex string to Point

@@ -41,53 +45,53 @@ hex = toU8(hex); // convert hex string to Uint8Array

const head = hex[0], tail = hex.subarray(1); // first byte is prefix, rest is data
const x = slcNum(tail, 0, fLen), len = hex.length; // next 32 bytes are x coordinate
const x = slc(tail, 0, fLen), len = hex.length; // next 32 bytes are x coordinate
if (len === 33 && [0x02, 0x03].includes(head)) { // compressed points: 33b, start
if (!fe(x)) err('Point hex invalid: x not FE'); // with byte 0x02 or 0x03. Check if 0<x<P
let y = sqrt(crv(x)); // x³ + ax + b is right side of equation
let y = sqrt(curve(x)); // x³ + ax + b is right side of equation
const isYOdd = (y & 1n) === 1n; // y² is equivalent left-side. Calculate y²:
const headOdd = (head & 1) === 1; // y = √y²; there are two solutions: y, -y
if (headOdd !== isYOdd) y = mod(-y); // determine proper solution
if (headOdd !== isYOdd) y = M(-y); // determine proper solution
p = new Point(x, y, 1n); // create point
} // Uncompressed points: 65b, start with 0x04
if (len === 65 && head === 0x04) p = new Point(x, slcNum(tail, fLen, 2 * fLen), 1n);
return p ? p.ok() : err('Point is not on curve'); // Verify the result
if (len === 65 && head === 0x04) p = new Point(x, slc(tail, fLen, 2 * fLen), 1n);
return p ? p.ok() : err('Point invalid: not on curve'); // Verify the result
}
static fromPrivateKey(k: PrivKey) { return G.mul(toPriv(k)); } // Create point from a private key.
get x() { return this.aff().x; } // .x, .y will call expensive toAffine:
get y() { return this.aff().y; } // should be used with care.
static fromPrivateKey(k: PrivKey): Point { return G.mul(toPriv(k)); } // Create point from a private key.
get x(): bigint { return this.aff().x; } // .x, .y will call expensive toAffine:
get y(): bigint { return this.aff().y; } // should be used with care.
equals(other: Point): boolean { // Equality check: compare points
const { px: X1, py: Y1, pz: Z1 } = this;
const { px: X2, py: Y2, pz: Z2 } = isPoint(other); // isPoint() checks class equality
const X1Z2 = mod(X1 * Z2), X2Z1 = mod(X2 * Z1);
const Y1Z2 = mod(Y1 * Z2), Y2Z1 = mod(Y2 * Z1);
const { px: X2, py: Y2, pz: Z2 } = aPoint(other); // isPoint() checks class equality
const X1Z2 = M(X1 * Z2), X2Z1 = M(X2 * Z1);
const Y1Z2 = M(Y1 * Z2), Y2Z1 = M(Y2 * Z1);
return X1Z2 === X2Z1 && Y1Z2 === Y2Z1;
}
negate() { return new Point(this.px, mod(-this.py), this.pz); } // Flip point over y coord
double() { return this.add(this); } // Point doubling: P+P, complete formula.
add(other: Point) { // Point addition: P+Q, complete, exception
negate(): Point { return new Point(this.px, M(-this.py), this.pz); } // Flip point over y coord
double(): Point { return this.add(this); } // Point doubling: P+P, complete formula.
add(other: Point): Point { // Point addition: P+Q, complete, exception
const { px: X1, py: Y1, pz: Z1 } = this; // free formula from Renes-Costello-Batina
const { px: X2, py: Y2, pz: Z2 } = isPoint(other); // https://eprint.iacr.org/2015/1060, algo 1
const { px: X2, py: Y2, pz: Z2 } = aPoint(other); // https://eprint.iacr.org/2015/1060, algo 1
const { a, b } = CURVE; // Cost: 12M + 0S + 3*a + 3*b3 + 23add
let X3 = 0n, Y3 = 0n, Z3 = 0n;
const b3 = mod(b * 3n);
let t0 = mod(X1 * X2), t1 = mod(Y1 * Y2), t2 = mod(Z1 * Z2), t3 = mod(X1 + Y1); // step 1
let t4 = mod(X2 + Y2); // step 5
t3 = mod(t3 * t4); t4 = mod(t0 + t1); t3 = mod(t3 - t4); t4 = mod(X1 + Z1);
let t5 = mod(X2 + Z2); // step 10
t4 = mod(t4 * t5); t5 = mod(t0 + t2); t4 = mod(t4 - t5); t5 = mod(Y1 + Z1);
X3 = mod(Y2 + Z2); // step 15
t5 = mod(t5 * X3); X3 = mod(t1 + t2); t5 = mod(t5 - X3); Z3 = mod(a * t4);
X3 = mod(b3 * t2); // step 20
Z3 = mod(X3 + Z3); X3 = mod(t1 - Z3); Z3 = mod(t1 + Z3); Y3 = mod(X3 * Z3);
t1 = mod(t0 + t0); // step 25
t1 = mod(t1 + t0); t2 = mod(a * t2); t4 = mod(b3 * t4); t1 = mod(t1 + t2);
t2 = mod(t0 - t2); // step 30
t2 = mod(a * t2); t4 = mod(t4 + t2); t0 = mod(t1 * t4); Y3 = mod(Y3 + t0);
t0 = mod(t5 * t4); // step 35
X3 = mod(t3 * X3); X3 = mod(X3 - t0); t0 = mod(t3 * t1); Z3 = mod(t5 * Z3);
Z3 = mod(Z3 + t0); // step 40
const b3 = M(b * 3n);
let t0 = M(X1 * X2), t1 = M(Y1 * Y2), t2 = M(Z1 * Z2), t3 = M(X1 + Y1); // step 1
let t4 = M(X2 + Y2); // step 5
t3 = M(t3 * t4); t4 = M(t0 + t1); t3 = M(t3 - t4); t4 = M(X1 + Z1);
let t5 = M(X2 + Z2); // step 10
t4 = M(t4 * t5); t5 = M(t0 + t2); t4 = M(t4 - t5); t5 = M(Y1 + Z1);
X3 = M(Y2 + Z2); // step 15
t5 = M(t5 * X3); X3 = M(t1 + t2); t5 = M(t5 - X3); Z3 = M(a * t4);
X3 = M(b3 * t2); // step 20
Z3 = M(X3 + Z3); X3 = M(t1 - Z3); Z3 = M(t1 + Z3); Y3 = M(X3 * Z3);
t1 = M(t0 + t0); // step 25
t1 = M(t1 + t0); t2 = M(a * t2); t4 = M(b3 * t4); t1 = M(t1 + t2);
t2 = M(t0 - t2); // step 30
t2 = M(a * t2); t4 = M(t4 + t2); t0 = M(t1 * t4); Y3 = M(Y3 + t0);
t0 = M(t5 * t4); // step 35
X3 = M(t3 * X3); X3 = M(X3 - t0); t0 = M(t3 * t1); Z3 = M(t5 * Z3);
Z3 = M(Z3 + t0); // step 40
return new Point(X3, Y3, Z3);
}
mul(n: bigint, safe = true) { // Point scalar multiplication.
mul(n: bigint, safe = true): Point { // Point scalar multiplication.
if (!safe && n === 0n) return I; // in unsafe mode, allow zero
if (!ge(n)) err('invalid scalar'); // must be 0 < n < CURVE.n
if (!ge(n)) err('scalar invalid'); // must be 0 < n < CURVE.n
if (this.equals(G)) return wNAF(n).p; // use precomputes for base point

@@ -101,3 +105,3 @@ let p = I, f = G; // init result point & fake point

}
mulAddQUns(R: Point, u1: bigint, u2: bigint) { // Double scalar mult. Q = u1⋅G + u2⋅R.
mulAddQUns(R: Point, u1: bigint, u2: bigint): Point { // Double scalar mult. Q = u1⋅G + u2⋅R.
return this.mul(u1, false).add(R.mul(u2, false)).ok(); // Unsafe: do NOT use for stuff related

@@ -109,5 +113,5 @@ } // to private keys. Doesn't use Shamir trick

if (z === 1n) return { x, y }; // if z is 1, pass affine coordinates as-is
const iz = inv(z); // z^-1: invert z
if (mod(z * iz) !== 1n) err('invalid inverse'); // (z * z^-1) must be 1, otherwise bad math
return { x: mod(x * iz), y: mod(y * iz) }; // x = x*z^-1; y = y*z^-1
const iz = inv(z, P); // z^-1: invert z
if (M(z * iz) !== 1n) err('inverse invalid'); // (z * z^-1) must be 1, otherwise bad math
return { x: M(x * iz), y: M(y * iz) }; // x = x*z^-1; y = y*z^-1
}

@@ -117,9 +121,9 @@ assertValidity(): Point { // Checks if the point is valid and on-curve

if (!fe(x) || !fe(y)) err('Point invalid: x or y'); // x and y must be in range 0 < n < P
return mod(y * y) === crv(x) ? // y² = x³ + ax + b, must be equal
return M(y * y) === curve(x) ? // y² = x³ + ax + b, must be equal
this : err('Point invalid: not on curve');
}
multiply(n: bigint) { return this.mul(n); } // Aliases to compress code
aff() { return this.toAffine(); }
ok() { return this.assertValidity(); }
toHex(isCompressed = true) { // Encode point to hex string.
multiply(n: bigint): Point { return this.mul(n); } // Aliases to compress code
aff(): AffinePoint { return this.toAffine(); }
ok(): Point { return this.assertValidity(); }
toHex(isCompressed = true): string { // Encode point to hex string.
const { x, y } = this.aff(); // convert to 2d xy affine point

@@ -129,3 +133,3 @@ const head = isCompressed ? ((y & 1n) === 0n ? '02' : '03') : '04'; // 0x02, 0x03, 0x04 prefix

}
toRawBytes(isCompressed = true) { // Encode point to Uint8Array.
toRawBytes(isCompressed = true): Bytes { // Encode point to Uint8Array.
return h2b(this.toHex(isCompressed)); // re-use toHex(), convert hex to bytes

@@ -136,23 +140,31 @@ }

const padh = (n: number | bigint, pad: number) => n.toString(16).padStart(pad, '0');
const b2h = (b: Bytes): string => Array.from(b).map(e => padh(e, 2)).join(''); // bytes to hex
const b2h = (b: Bytes): string => Array.from(au8(b)).map(e => padh(e, 2)).join(''); // bytes to hex
const C = { _0: 48, _9: 57, A: 65, F: 70, a: 97, f: 102 } as const; // ASCII characters
const _ch = (ch: number): number | undefined => {
if (ch >= C._0 && ch <= C._9) return ch - C._0; // '2' => 50-48
if (ch >= C.A && ch <= C.F) return ch - (C.A - 10); // 'B' => 66-(65-10)
if (ch >= C.a && ch <= C.f) return ch - (C.a - 10); // 'b' => 98-(97-10)
return;
};
const h2b = (hex: string): Bytes => { // hex to bytes
const l = hex.length; // error if not string,
if (!str(hex) || l % 2) err('hex invalid 1'); // or has odd length like 3, 5.
const arr = u8n(l / 2); // create result array
for (let i = 0; i < arr.length; i++) {
const j = i * 2;
const h = hex.slice(j, j + 2); // hexByte. slice is faster than substr
const b = Number.parseInt(h, 16); // byte, created from string part
if (Number.isNaN(b) || b < 0) err('hex invalid 2'); // byte must be valid 0 <= byte < 256
arr[i] = b;
const e = 'hex invalid';
if (!isS(hex)) return err(e);
const hl = hex.length, al = hl / 2;
if (hl % 2) return err(e);
const array = u8n(al);
for (let ai = 0, hi = 0; ai < al; ai++, hi += 2) { // treat each char as ASCII
const n1 = _ch(hex.charCodeAt(hi)); // parse first char, multiply it by 16
const n2 = _ch(hex.charCodeAt(hi + 1)); // parse second char
if (n1 === undefined || n2 === undefined) return err(e);
array[ai] = n1 * 16 + n2; // example: 'A9' => 10*16 + 9
}
return arr;
return array;
};
const b2n = (b: Bytes): bigint => BigInt('0x' + (b2h(b) || '0')); // bytes to number
const slcNum = (b: Bytes, from: number, to: number) => b2n(b.slice(from, to)); // slice bytes num
const slc = (b: Bytes, from: number, to: number) => b2n(b.slice(from, to)); // slice bytes num
const n2b = (num: bigint): Bytes => { // number to 32b. Must be 0 <= num < B256
return big(num) && num >= 0n && num < B256 ? h2b(padh(num, 2 * fLen)) : err('bigint expected');
return isB(num) && num >= 0n && num < B256 ? h2b(padh(num, 2 * fLen)) : err('bigint expected');
};
const n2h = (num: bigint): string => b2h(n2b(num)); // number to 32b hex
const concatB = (...arrs: Bytes[]) => { // concatenate Uint8Array-s
const concatB = (...arrs: Bytes[]): Bytes => { // concatenate Uint8Array-s
const r = u8n(arrs.reduce((sum, a) => sum + au8(a).length, 0)); // create u8a of summed length

@@ -163,5 +175,5 @@ let pad = 0; // walk through each array,

};
const inv = (num: bigint, md = P): bigint => { // modular inversion
const inv = (num: bigint, md: bigint): bigint => { // modular inversion
if (num === 0n || md <= 0n) err('no inverse n=' + num + ' mod=' + md); // no neg exponent for now
let a = mod(num, md), b = md, x = 0n, y = 1n, u = 1n, v = 0n;
let a = M(num, md), b = md, x = 0n, y = 1n, u = 1n, v = 0n;
while (a !== 0n) { // uses euclidean gcd algorithm

@@ -172,3 +184,3 @@ const q = b / a, r = b % a; // not constant-time

}
return b === 1n ? mod(x, md) : err('no inverse'); // b is gcd at this point
return b === 1n ? M(x, md) : err('no inverse'); // b is gcd at this point
};

@@ -181,11 +193,11 @@ const sqrt = (n: bigint) => { // √n = n^((p+1)/4) for fields p = 3 mod 4

}
return mod(r * r) === n ? r : err('sqrt invalid'); // check if result is valid
return M(r * r) === n ? r : err('sqrt invalid'); // check if result is valid
};
const toPriv = (p: PrivKey): bigint => { // normalize private key to bigint
if (!big(p)) p = b2n(toU8(p, fLen)); // convert to bigint when bytes
return ge(p) ? p : err('private key out of range'); // check if bigint is in range
if (!isB(p)) p = b2n(toU8(p, fLen)); // convert to bigint when bytes
return ge(p) ? p : err('private key invalid 3'); // check if bigint is in range
};
const moreThanHalfN = (n: bigint): boolean => n > (N >> 1n); // if a number is bigger than CURVE.n/2
const getPublicKey = (privKey: PrivKey, isCompressed = true) => { // Make public key from priv
return Point.fromPrivateKey(privKey).toRawBytes(isCompressed); // 33b or 65b output
const high = (n: bigint): boolean => n > (N >> 1n); // if a number is bigger than CURVE.n/2
const getPublicKey = (privKey: PrivKey, isCompressed = true): Bytes => { // Make public key from priv
return Point.fromPrivateKey(privKey).toRawBytes(isCompressed); // 33b or 65b output
}

@@ -197,13 +209,13 @@ type SignatureWithRecovery = Signature & { recovery: number }

} // constructed outside.
static fromCompact(hex: Hex) { // create signature from 64b compact repr
static fromCompact(hex: Hex): Signature { // create signature from 64b compact repr
hex = toU8(hex, 64); // compact repr is (32b r)||(32b s)
return new Signature(slcNum(hex, 0, fLen), slcNum(hex, fLen, 2 * fLen));
return new Signature(slc(hex, 0, fLen), slc(hex, fLen, 2 * fLen));
}
assertValidity() { return ge(this.r) && ge(this.s) ? this : err(); } // 0 < r or s < CURVE.n
assertValidity(): Signature { return ge(this.r) && ge(this.s) ? this : err(); } // 0 < r or s < CURVE.n
addRecoveryBit(rec: number): SignatureWithRecovery {
return new Signature(this.r, this.s, rec) as SignatureWithRecovery;
}
hasHighS() { return moreThanHalfN(this.s); }
normalizeS() {
return this.hasHighS() ? new Signature(this.r, mod(this.s, N), this.recovery) : this
hasHighS(): boolean { return high(this.s); }
normalizeS(): Signature {
return high(this.s) ? new Signature(this.r, M(-this.s, N), this.recovery) : this;
}

@@ -219,16 +231,17 @@ recoverPublicKey(msgh: Hex): Point { // ECDSA public key recovery

const ir = inv(radj, N); // r^-1
const u1 = mod(-h * ir, N); // -hr^-1
const u2 = mod(s * ir, N); // sr^-1
const u1 = M(-h * ir, N); // -hr^-1
const u2 = M(s * ir, N); // sr^-1
return G.mulAddQUns(R, u1, u2); // (sr^-1)R-(hr^-1)G = -(hr^-1)G + (sr^-1)
}
toCompactRawBytes() { return h2b(this.toCompactHex()); } // Uint8Array 64b compact repr
toCompactHex() { return n2h(this.r) + n2h(this.s); } // hex 64b compact repr
toCompactRawBytes(): Bytes { return h2b(this.toCompactHex()); } // Uint8Array 64b compact repr
toCompactHex(): string { return n2h(this.r) + n2h(this.s); } // hex 64b compact repr
}
const bits2int = (bytes: Uint8Array): bigint => { // RFC6979: ensure ECDSA msg is X bytes.
const bits2int = (bytes: Bytes): bigint => { // RFC6979: ensure ECDSA msg is X bytes.
const delta = bytes.length * 8 - 256; // RFC suggests optional truncating via bits2octets
if (delta > 1024) err('msg invalid'); // our CUSTOM check, "just-in-case"
const num = b2n(bytes); // FIPS 186-4 4.6 suggests the leftmost min(nBitLen, outLen) bits, which
return delta > 0 ? num >> BigInt(delta) : num; // matches bits2int. bits2int can produce res>N.
};
const bits2int_modN = (bytes: Uint8Array): bigint => { // int2octets can't be used; pads small msgs
return mod(bits2int(bytes), N); // with 0: BAD for trunc as per RFC vectors
const bits2int_modN = (bytes: Bytes): bigint => { // int2octets can't be used; pads small msgs
return M(bits2int(bytes), N); // with 0: BAD for trunc as per RFC vectors
};

@@ -238,11 +251,12 @@ const i2o = (num: bigint): Bytes => n2b(num); // int to octets

const cr = () => // We support: 1) browsers 2) node.js 19+ 3) deno, other envs with crypto
typeof globalThis === 'object' && 'crypto' in globalThis ? globalThis.crypto : undefined;
typeof globalThis === 'object' && 'crypto' in globalThis && 'subtle' in globalThis.crypto ? globalThis.crypto : undefined;
type HmacFnSync = undefined | ((key: Bytes, ...msgs: Bytes[]) => Bytes);
let _hmacSync: HmacFnSync; // Can be redefined by use in utils; built-ins don't provide it
const optS: { lowS?: boolean; extraEntropy?: boolean | Hex; } = { lowS: true }; // opts for sign()
const optV: { lowS?: boolean } = { lowS: true }; // standard opts for verify()
type OptS = { lowS?: boolean; extraEntropy?: boolean | Hex; };
type OptV = { lowS?: boolean };
const optS: OptS = { lowS: true }; // opts for sign()
const optV: OptV = { lowS: true }; // standard opts for verify()
type BC = { seed: Bytes, k2sig : (kb: Bytes) => SignatureWithRecovery | undefined }; // Bytes+predicate checker
const prepSig = (msgh: Hex, priv: PrivKey, opts=optS): BC => {// prepare for RFC6979 sig generation
if (['der', 'recovered', 'canonical'].some(k => k in opts)) // Ban legacy options
err('sign() legacy options not supported');
const prepSig = (msgh: Hex, priv: PrivKey, opts: OptS = optS): BC => {// prepare for RFC6979 sig generation
if (['der', 'recovered', 'canonical'].some(k => k in opts)) err('option not supported'); // legacy opts
let { lowS } = opts; // generates low-s sigs by default

@@ -255,8 +269,4 @@ if (lowS == null) lowS = true; // RFC6979 3.2: we skip step A

let ent = opts.extraEntropy; // RFC6979 3.6: additional k' (optional)
if (ent) { // K = HMAC_K(V || 0x00 || int2octets(x) || bits2octets(h1) || k')
if (ent === true) ent = etc.randomBytes(fLen); // if true, use CSPRNG to generate data
const e = toU8(ent); // convert Hex|Bytes to Bytes
if (e.length !== fLen) err(); // Expected 32 bytes of extra data
seed.push(e);
}
if (ent) // K = HMAC_K(V || 0x00 || int2octets(x) || bits2octets(h1) || k')
seed.push(ent === true ? etc.randomBytes(fLen) : toU8(ent)); // true == fetch from CSPRNG
const m = h1i; // convert msg to bigint

@@ -268,10 +278,10 @@ const k2sig = (kBytes: Bytes): SignatureWithRecovery | undefined => { // Transform k => Signature.

const q = G.mul(k).aff(); // q = Gk
const r = mod(q.x, N); // r = q.x mod n
const r = M(q.x, N); // r = q.x mod n
if (r === 0n) return; // r=0 invalid
const s = mod(ik * mod(m + mod(d * r, N), N), N); // s = k^-1(m + rd) mod n
const s = M(ik * M(m + M(d * r, N), N), N); // s = k^-1(m + rd) mod n
if (s === 0n) return; // s=0 invalid
let normS = s; // normalized S
let rec = (q.x === r ? 0 : 2) | Number(q.y & 1n); // recovery bit
if (lowS && moreThanHalfN(s)) { // if lowS was passed, ensure s is always
normS = mod(-s, N); // in the bottom half of CURVE.n
if (lowS && high(s)) { // if lowS was passed, ensure s is always
normS = M(-s, N); // in the bottom half of CURVE.n
rec ^= 1;

@@ -341,17 +351,17 @@ }

}
}
};
// ECDSA signature generation. via secg.org/sec1-v2.pdf 4.1.2 + RFC6979 deterministic k
const signAsync = async (msgh: Hex, priv: PrivKey, opts = optS): Promise<SignatureWithRecovery> => {
const signAsync = async (msgh: Hex, priv: PrivKey, opts: OptS = optS): Promise<SignatureWithRecovery> => {
const { seed, k2sig } = prepSig(msgh, priv, opts); // Extract arguments for hmac-drbg
return hmacDrbg<SignatureWithRecovery>(true)(seed, k2sig); // Re-run drbg until k2sig returns ok
}
const sign = (msgh: Hex, priv: PrivKey, opts = optS): SignatureWithRecovery => {
};
const sign = (msgh: Hex, priv: PrivKey, opts: OptS = optS): SignatureWithRecovery => {
const { seed, k2sig } = prepSig(msgh, priv, opts); // Extract arguments for hmac-drbg
return hmacDrbg<SignatureWithRecovery>(false)(seed, k2sig); // Re-run drbg until k2sig returns ok
}
};
type SigLike = { r: bigint, s: bigint };
const verify = (sig: Hex | SigLike, msgh: Hex, pub: Hex, opts = optV): boolean => {
const verify = (sig: Hex | SigLike, msgh: Hex, pub: Hex, opts: OptV = optV): boolean => {
let { lowS } = opts; // ECDSA signature verification
if (lowS == null) lowS = true; // Default lowS=true
if ('strict' in opts) err('verify() legacy options not supported'); // legacy param
if ('strict' in opts) err('option not supported'); // legacy param
let sig_: Signature, h: bigint, P: Point; // secg.org/sec1-v2.pdf 4.1.4

@@ -363,3 +373,3 @@ const rs = sig && typeof sig === 'object' && 'r' in sig; // Previous ver supported DER sigs. We

sig_ = rs ? new Signature(sig.r, sig.s).assertValidity() : Signature.fromCompact(sig);
h = bits2int_modN(toU8(msgh)); // Truncate hash
h = bits2int_modN(toU8(msgh)); // Truncate hash
P = pub instanceof Point ? pub.ok() : Point.fromHex(pub); // Validate public key

@@ -369,28 +379,31 @@ } catch (e) { return false; } // Check sig for validity in both cases

const { r, s } = sig_;
if (lowS && moreThanHalfN(s)) return false; // lowS bans sig.s >= CURVE.n/2
if (lowS && high(s)) return false; // lowS bans sig.s >= CURVE.n/2
let R: AffinePoint;
try {
const is = inv(s, N); // s^-1
const u1 = mod(h * is, N); // u1 = hs^-1 mod n
const u2 = mod(r * is, N); // u2 = rs^-1 mod n
const u1 = M(h * is, N); // u1 = hs^-1 mod n
const u2 = M(r * is, N); // u2 = rs^-1 mod n
R = G.mulAddQUns(P, u1, u2).aff(); // R = u1⋅G + u2⋅P
} catch (error) { return false; }
if (!R) return false; // stop if R is identity / zero point
const v = mod(R.x, N); // R.x must be in N's field, not P's
const v = M(R.x, N); // R.x must be in N's field, not P's
return v === r; // mod(R.x, n) == r
}
};
const getSharedSecret = (privA: Hex, pubB: Hex, isCompressed = true): Bytes => {
return Point.fromHex(pubB).mul(toPriv(privA)).toRawBytes(isCompressed); // ECDH
}
};
const hashToPrivateKey = (hash: Hex): Bytes => { // FIPS 186 B.4.1 compliant key generation
hash = toU8(hash); // produces private keys with modulo bias
const minLen = fLen + 8; // being neglible.
if (hash.length < minLen || hash.length > 1024) err('expected proper params');
const num = mod(b2n(hash), N - 1n) + 1n; // takes at least n+8 bytes
return n2b(num);
if (hash.length < fLen + 8 || hash.length > 1024) err('expected 40-1024b'); // being neglible.
const num = M(b2n(hash), N - 1n); // takes n+8 bytes
return n2b(num + 1n); // returns (hash mod n-1)+1
}
const etc = { // Not placed in utils because they
hexToBytes: h2b, bytesToHex: b2h, // share API with noble-curves.
concatBytes: concatB, bytesToNumberBE: b2n, numberToBytesBE: n2b,
mod, invert: inv, // math utilities
hexToBytes: h2b as (hex: string) => Bytes, // share API with noble-curves.
bytesToHex: b2h as (bytes: Bytes) => string,
concatBytes: concatB as (...arrs: Bytes[]) => Bytes,
bytesToNumberBE: b2n as (a: Bytes) => bigint,
numberToBytesBE: n2b as (n: bigint) => Bytes,
mod: M as (a: bigint, md?: bigint) => bigint,
invert: inv as (num: bigint, md?: bigint) => bigint, // math utilities
hmacSha256Async: async (key: Bytes, ...msgs: Bytes[]): Promise<Bytes> => {

@@ -403,4 +416,4 @@ const c = cr(); // async HMAC-SHA256, no sync built-in!

},
hmacSha256Sync: _hmacSync, // For TypeScript. Actual logic is below
hashToPrivateKey,
hmacSha256Sync: _hmacSync as HmacFnSync, // For TypeScript. Actual logic is below
hashToPrivateKey: hashToPrivateKey as (hash: Hex) => Bytes,
randomBytes: (len = 32): Bytes => { // CSPRNG (random number generator)

@@ -411,8 +424,8 @@ const crypto = cr(); // Must be shimmed in node.js <= 18 to prevent error. See README.

},
}
};
const utils = { // utilities
normPrivateKeyToScalar: toPriv,
isValidPrivateKey: (key: Hex) => { try { return !!toPriv(key); } catch (e) { return false; } },
normPrivateKeyToScalar: toPriv as (p: PrivKey) => bigint,
isValidPrivateKey: (key: Hex): boolean => { try { return !!toPriv(key); } catch (e) { return false; } },
randomPrivateKey: (): Bytes => hashToPrivateKey(etc.randomBytes(fLen + 16)), // FIPS 186 B.4.1.
precompute(w=8, p: Point = G) { p.multiply(3n); w; return p; }, // no-op
precompute(w=8, p: Point = G): Point { p.multiply(3n); w; return p; }, // no-op
};

@@ -419,0 +432,0 @@ Object.defineProperties(etc, { hmacSha256Sync: { // Allow setting it once, ignore then

{
"name": "@noble/secp256k1",
"version": "2.1.0",
"version": "2.2.0",
"description": "Fastest 4KB JS implementation of secp256k1 ECDH & ECDSA signatures compliant with RFC6979",

@@ -20,4 +20,4 @@ "files": [

"build:release": "npm run --silent build:min > test/build/noble-secp256k1.min.js; npm run --silent build:mingz > test/build/noble-secp256k1.min.js.gz",
"test": "node test/index.test.js",
"test:webcrypto": "node test/secp256k1.webcrypto.test.js",
"test": "node test/index.js",
"test:webcrypto": "node test/index.webcrypto.js",
"bench": "node test/benchmark.js",

@@ -34,8 +34,8 @@ "loc": "echo \"`npm run --silent build:min | wc -c` symbols `wc -l < index.ts` LOC, `npm run --silent build:mingz | wc -c`B gzipped\""

"devDependencies": {
"@noble/hashes": "1.4.0",
"@paulmillr/jsbt": "0.1.0",
"@noble/hashes": "1.6.1",
"@paulmillr/jsbt": "0.2.1",
"fast-check": "3.0.0",
"micro-bmark": "0.3.0",
"micro-bmark": "0.3.1",
"micro-should": "0.4.0",
"typescript": "5.3.2"
"typescript": "5.5.2"
},

@@ -42,0 +42,0 @@ "keywords": [

@@ -5,3 +5,3 @@ # noble-secp256k1

- ✍️ Deterministic [ECDSA](https://en.wikipedia.org/wiki/Elliptic_Curve_Digital_Signature_Algorithm)
- ✍️ [ECDSA](https://en.wikipedia.org/wiki/Elliptic_Curve_Digital_Signature_Algorithm)
signatures compliant with [RFC6979](https://www.rfc-editor.org/rfc/rfc6979)

@@ -12,4 +12,5 @@ - 🤝 Elliptic Curve Diffie-Hellman [ECDH](https://en.wikipedia.org/wiki/Elliptic-curve_Diffie–Hellman)

Use larger drop-in replacement [noble-curves](https://github.com/paulmillr/noble-curves) instead,
if you need additional features such as common.js, Schnorr signatures, DER encoding or support for different hash functions. To upgrade from v1 to v2, see [Upgrading](#upgrading).
The module is a sister project of [noble-curves](https://github.com/paulmillr/noble-curves),
focusing on smaller attack surface & better auditability.
Curves are drop-in replacement and have more features: Common.js, Schnorr signatures, DER encoding or support for different hash functions. To upgrade from v1 to v2, see [Upgrading](#upgrading).

@@ -33,4 +34,6 @@ ### This library belongs to _noble_ cryptography

> npm install @noble/secp256k1
> `npm install @noble/secp256k1`
> `deno add @noble/secp256k1`
We support all major platforms and runtimes. For node.js <= 18 and React Native, additional polyfills are needed: see below.

@@ -40,7 +43,6 @@

import * as secp from '@noble/secp256k1';
// import * as secp from "https://deno.land/x/secp256k1/mod.ts"; // Deno
// import * as secp from "https://unpkg.com/@noble/secp256k1"; // Unpkg
(async () => {
// keys, messages & other inputs can be Uint8Arrays or hex strings
// Uint8Array.from([0xde, 0xad, 0xbe, 0xef]) === 'deadbeef'
// Uint8Arrays or hex strings are accepted:
// Uint8Array.from([0xde, 0xad, 0xbe, 0xef]) is equal to 'deadbeef'
const privKey = secp.utils.randomPrivateKey(); // Secure random private key

@@ -66,3 +68,3 @@ // sha256 of 'hello world'

import { sha256 } from '@noble/hashes/sha256';
secp.etc.hmacSha256Sync = (k, ...m) => hmac(sha256, k, secp.etc.concatBytes(...m))
secp.etc.hmacSha256Sync = (k, ...m) => hmac(sha256, k, secp.etc.concatBytes(...m));
// Sync methods can be used now:

@@ -92,3 +94,3 @@ // secp.sign(msgHash, privKey);

```ts
type Hex = Uint8Array | string
type Hex = Uint8Array | string;
```

@@ -114,3 +116,3 @@

privateKey: Hex, // private key which will sign the hash
opts?: { lowS: boolean, extraEntropy: boolean | Hex } // optional params
opts?: { lowS: boolean; extraEntropy: boolean | Hex } // optional params
): Signature;

@@ -133,9 +135,9 @@ function signAsync(

2. `extraEntropy: true` improves security by adding entropy, follows section 3.6 of RFC6979:
- No disadvantage: if an entropy generator is broken, sigs would be the same
as they are without the option
- It would help a lot in case there is an error somewhere in `k` gen.
Exposing `k` could leak private keys
- Sigs with extra entropy would have different `r` / `s`, which means they
would still be valid, but may break some test vectors if you're
cross-testing against other libs
- No disadvantage: if an entropy generator is broken, sigs would be the same
as they are without the option
- It would help a lot in case there is an error somewhere in `k` gen.
Exposing `k` could leak private keys
- Sigs with extra entropy would have different `r` / `s`, which means they
would still be valid, but may break some test vectors if you're
cross-testing against other libs

@@ -238,3 +240,3 @@ ### verify

}
CURVE // curve prime; order; equation params, generator coordinates
CURVE; // curve prime; order; equation params, generator coordinates
```

@@ -245,31 +247,50 @@

The module is production-ready.
It is cross-tested against [noble-curves](https://github.com/paulmillr/noble-curves),
and has similar security.
While [noble-curves](https://github.com/paulmillr/noble-curves) provide improved security,
we cross-test against curves.
1. The current version is rewrite of v1, which has been audited by cure53:
[PDF](https://cure53.de/pentest-report_noble-lib.pdf) (funded by [Umbra.cash](https://umbra.cash) & community).
1. The current version has not been independently audited. It is a rewrite of v1, which has been audited by cure53 in Apr 2021:
[PDF](https://cure53.de/pentest-report_noble-lib.pdf) (funded by [Umbra.cash](https://umbra.cash) & community).
2. It's being fuzzed by [Guido Vranken's cryptofuzz](https://github.com/guidovranken/cryptofuzz):
run the fuzzer by yourself to check.
you can also run the fuzzer by yourself.
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,
### 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.
As for key generation, we're deferring to built-in
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

@@ -305,12 +326,2 @@

## Contributing
1. Clone the repository.
2. `npm install` to install build dependencies like TypeScript
3. `npm run build` to compile TypeScript code
4. `npm test` to run jest on `test/index.ts`
Special thanks to [Roman Koblov](https://github.com/romankoblov), who have
helped to improve scalar multiplication speed.
## Upgrading

@@ -337,20 +348,20 @@

- `getPublicKey`
- now produce 33-byte compressed signatures by default
- to use old behavior, which produced 65-byte uncompressed keys, set
argument `isCompressed` to `false`: `getPublicKey(priv, false)`
- now produce 33-byte compressed signatures by default
- to use old behavior, which produced 65-byte uncompressed keys, set
argument `isCompressed` to `false`: `getPublicKey(priv, false)`
- `sign`
- is now sync; use `signAsync` for async version
- now returns `Signature` instance with `{ r, s, recovery }` properties
- `canonical` option was renamed to `lowS`
- `recovered` option has been removed because recovery bit is always returned now
- `der` option has been removed. There are 2 options:
1. Use compact encoding: `fromCompact`, `toCompactRawBytes`, `toCompactHex`.
Compact encoding is simply a concatenation of 32-byte r and 32-byte s.
2. If you must use DER encoding, switch to noble-curves (see above).
- is now sync; use `signAsync` for async version
- now returns `Signature` instance with `{ r, s, recovery }` properties
- `canonical` option was renamed to `lowS`
- `recovered` option has been removed because recovery bit is always returned now
- `der` option has been removed. There are 2 options:
1. Use compact encoding: `fromCompact`, `toCompactRawBytes`, `toCompactHex`.
Compact encoding is simply a concatenation of 32-byte r and 32-byte s.
2. If you must use DER encoding, switch to noble-curves (see above).
- `verify`
- `strict` option was renamed to `lowS`
- `strict` option was renamed to `lowS`
- `getSharedSecret`
- now produce 33-byte compressed signatures by default
- to use old behavior, which produced 65-byte uncompressed keys, set
argument `isCompressed` to `false`: `getSharedSecret(a, b, false)`
- now produce 33-byte compressed signatures by default
- to use old behavior, which produced 65-byte uncompressed keys, set
argument `isCompressed` to `false`: `getSharedSecret(a, b, false)`
- `recoverPublicKey(msg, sig, rec)` was changed to `sig.recoverPublicKey(msg)`

@@ -362,4 +373,17 @@ - `number` type for private keys have been removed: use `bigint` instead

## Contributing & testing
* `npm install && npm run build && npm test` will build the code and run tests.
* `npm run bench` will run benchmarks, which may need their deps first (`npm run bench:install`)
* `npm run loc` will count total output size, important to be less than 4KB
Check out [github.com/paulmillr/guidelines](https://github.com/paulmillr/guidelines)
for general coding practices and rules.
See [paulmillr.com/noble](https://paulmillr.com/noble/)
for useful resources, articles, documentation and demos
related to the library.
## License
MIT (c) Paul Miller [(https://paulmillr.com)](https://paulmillr.com), see LICENSE file.
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