@noble/secp256k1
Advanced tools
Comparing version 1.7.0 to 1.7.1
@@ -18,4 +18,35 @@ /*! noble-secp256k1 - MIT License (c) 2019 Paul Miller (paulmillr.com) */ | ||
}); | ||
const divNearest = (a, b) => (a + b / _2n) / b; | ||
const endo = { | ||
beta: BigInt('0x7ae96a2b657c07106e64479eac3434e99cf0497512f58995c1396c28719501ee'), | ||
splitScalar(k) { | ||
const { n } = CURVE; | ||
const a1 = BigInt('0x3086d221a7d46bcde86c90e49284eb15'); | ||
const b1 = -_1n * BigInt('0xe4437ed6010e88286f547fa90abfe4c3'); | ||
const a2 = BigInt('0x114ca50f7a8e2f3f657c1108d9d44cfd8'); | ||
const b2 = a1; | ||
const POW_2_128 = BigInt('0x100000000000000000000000000000000'); | ||
const c1 = divNearest(b2 * k, n); | ||
const c2 = divNearest(-b1 * k, n); | ||
let k1 = mod(k - c1 * a1 - c2 * a2, n); | ||
let k2 = mod(-c1 * b1 - c2 * b2, n); | ||
const k1neg = k1 > POW_2_128; | ||
const k2neg = k2 > POW_2_128; | ||
if (k1neg) | ||
k1 = n - k1; | ||
if (k2neg) | ||
k2 = n - k2; | ||
if (k1 > POW_2_128 || k2 > POW_2_128) { | ||
throw new Error('splitScalarEndo: Endomorphism failed, k=' + k); | ||
} | ||
return { k1neg, k1, k2neg, k2 }; | ||
}, | ||
}; | ||
const fieldLen = 32; | ||
const groupLen = 32; | ||
const hashLen = 32; | ||
const compressedLen = fieldLen + 1; | ||
const uncompressedLen = 2 * fieldLen + 1; | ||
export { CURVE }; | ||
function weistrass(x) { | ||
function weierstrass(x) { | ||
const { a, b } = CURVE; | ||
@@ -32,2 +63,6 @@ const x2 = mod(x * x); | ||
} | ||
function assertJacPoint(other) { | ||
if (!(other instanceof JacobianPoint)) | ||
throw new TypeError('JacobianPoint expected'); | ||
} | ||
class JacobianPoint { | ||
@@ -43,2 +78,4 @@ constructor(x, y, z) { | ||
} | ||
if (p.equals(Point.ZERO)) | ||
return JacobianPoint.ZERO; | ||
return new JacobianPoint(p.x, p.y, _1n); | ||
@@ -54,4 +91,3 @@ } | ||
equals(other) { | ||
if (!(other instanceof JacobianPoint)) | ||
throw new TypeError('JacobianPoint expected'); | ||
assertJacPoint(other); | ||
const { x: X1, y: Y1, z: Z1 } = this; | ||
@@ -85,4 +121,3 @@ const { x: X2, y: Y2, z: Z2 } = other; | ||
add(other) { | ||
if (!(other instanceof JacobianPoint)) | ||
throw new TypeError('JacobianPoint expected'); | ||
assertJacPoint(other); | ||
const { x: X1, y: Y1, z: Z1 } = this; | ||
@@ -139,3 +174,3 @@ const { x: X2, y: Y2, z: Z2 } = other; | ||
} | ||
let { k1neg, k1, k2neg, k2 } = splitScalarEndo(n); | ||
let { k1neg, k1, k2neg, k2 } = endo.splitScalar(n); | ||
let k1p = P0; | ||
@@ -157,3 +192,3 @@ let k2p = P0; | ||
k2p = k2p.negate(); | ||
k2p = new JacobianPoint(mod(k2p.x * CURVE.beta), k2p.y, k2p.z); | ||
k2p = new JacobianPoint(mod(k2p.x * endo.beta), k2p.y, k2p.z); | ||
return k1p.add(k2p); | ||
@@ -193,3 +228,3 @@ } | ||
let p = JacobianPoint.ZERO; | ||
let f = JacobianPoint.ZERO; | ||
let f = JacobianPoint.BASE; | ||
const windows = 1 + (USE_ENDOMORPHISM ? 128 / W : 256 / W); | ||
@@ -208,13 +243,11 @@ const windowSize = 2 ** (W - 1); | ||
} | ||
const offset1 = offset; | ||
const offset2 = offset + Math.abs(wbits) - 1; | ||
const cond1 = window % 2 !== 0; | ||
const cond2 = wbits < 0; | ||
if (wbits === 0) { | ||
let pr = precomputes[offset]; | ||
if (window % 2) | ||
pr = pr.negate(); | ||
f = f.add(pr); | ||
f = f.add(constTimeNegate(cond1, precomputes[offset1])); | ||
} | ||
else { | ||
let cached = precomputes[offset + Math.abs(wbits) - 1]; | ||
if (wbits < 0) | ||
cached = cached.negate(); | ||
p = p.add(cached); | ||
p = p.add(constTimeNegate(cond2, precomputes[offset2])); | ||
} | ||
@@ -229,10 +262,8 @@ } | ||
if (USE_ENDOMORPHISM) { | ||
const { k1neg, k1, k2neg, k2 } = splitScalarEndo(n); | ||
const { k1neg, k1, k2neg, k2 } = endo.splitScalar(n); | ||
let { p: k1p, f: f1p } = this.wNAF(k1, affinePoint); | ||
let { p: k2p, f: f2p } = this.wNAF(k2, affinePoint); | ||
if (k1neg) | ||
k1p = k1p.negate(); | ||
if (k2neg) | ||
k2p = k2p.negate(); | ||
k2p = new JacobianPoint(mod(k2p.x * CURVE.beta), k2p.y, k2p.z); | ||
k1p = constTimeNegate(k1neg, k1p); | ||
k2p = constTimeNegate(k2neg, k2p); | ||
k2p = new JacobianPoint(mod(k2p.x * endo.beta), k2p.y, k2p.z); | ||
point = k1p.add(k2p); | ||
@@ -248,4 +279,7 @@ fake = f1p.add(f2p); | ||
} | ||
toAffine(invZ = invert(this.z)) { | ||
toAffine(invZ) { | ||
const { x, y, z } = this; | ||
const is0 = this.equals(JacobianPoint.ZERO); | ||
if (invZ == null) | ||
invZ = is0 ? _8n : invert(z); | ||
const iz1 = invZ; | ||
@@ -257,2 +291,4 @@ const iz2 = mod(iz1 * iz1); | ||
const zz = mod(z * iz1); | ||
if (is0) | ||
return Point.ZERO; | ||
if (zz !== _1n) | ||
@@ -265,2 +301,6 @@ throw new Error('invZ was invalid'); | ||
JacobianPoint.ZERO = new JacobianPoint(_0n, _1n, _0n); | ||
function constTimeNegate(condition, item) { | ||
const neg = item.negate(); | ||
return condition ? neg : item; | ||
} | ||
const pointPrecomputes = new WeakMap(); | ||
@@ -284,3 +324,3 @@ export class Point { | ||
throw new Error('Point is not on curve'); | ||
const y2 = weistrass(x); | ||
const y2 = weierstrass(x); | ||
let y = sqrtMod(y2); | ||
@@ -302,4 +342,4 @@ const isYOdd = (y & _1n) === _1n; | ||
static fromUncompressedHex(bytes) { | ||
const x = bytesToNumber(bytes.subarray(1, 33)); | ||
const y = bytesToNumber(bytes.subarray(33, 65)); | ||
const x = bytesToNumber(bytes.subarray(1, fieldLen + 1)); | ||
const y = bytesToNumber(bytes.subarray(fieldLen + 1, fieldLen * 2 + 1)); | ||
const point = new Point(x, y); | ||
@@ -313,8 +353,10 @@ point.assertValidity(); | ||
const header = bytes[0]; | ||
if (len === 32 || (len === 33 && (header === 0x02 || header === 0x03))) { | ||
if (len === fieldLen) | ||
return this.fromCompressedHex(bytes); | ||
if (len === compressedLen && (header === 0x02 || header === 0x03)) { | ||
return this.fromCompressedHex(bytes); | ||
} | ||
if (len === 65 && header === 0x04) | ||
if (len === uncompressedLen && header === 0x04) | ||
return this.fromUncompressedHex(bytes); | ||
throw new Error(`Point.fromHex: received invalid point. Expected 32-33 compressed bytes or 65 uncompressed bytes, not ${len}`); | ||
throw new Error(`Point.fromHex: received invalid point. Expected 32-${compressedLen} compressed bytes or ${uncompressedLen} uncompressed bytes, not ${len}`); | ||
} | ||
@@ -325,14 +367,13 @@ static fromPrivateKey(privateKey) { | ||
static fromSignature(msgHash, signature, recovery) { | ||
msgHash = ensureBytes(msgHash); | ||
const h = truncateHash(msgHash); | ||
const { r, s } = normalizeSignature(signature); | ||
if (recovery !== 0 && recovery !== 1) { | ||
throw new Error('Cannot recover signature: invalid recovery bit'); | ||
} | ||
const prefix = recovery & 1 ? '03' : '02'; | ||
const R = Point.fromHex(prefix + numTo32bStr(r)); | ||
if (![0, 1, 2, 3].includes(recovery)) | ||
throw new Error('Cannot recover: invalid recovery bit'); | ||
const h = truncateHash(ensureBytes(msgHash)); | ||
const { n } = CURVE; | ||
const rinv = invert(r, n); | ||
const radj = recovery === 2 || recovery === 3 ? r + n : r; | ||
const rinv = invert(radj, n); | ||
const u1 = mod(-h * rinv, n); | ||
const u2 = mod(s * rinv, n); | ||
const prefix = recovery & 1 ? '03' : '02'; | ||
const R = Point.fromHex(prefix + numTo32bStr(radj)); | ||
const Q = Point.BASE.multiplyAndAddUnsafe(R, u1, u2); | ||
@@ -369,3 +410,3 @@ if (!Q) | ||
const left = mod(y * y); | ||
const right = weistrass(x); | ||
const right = weierstrass(x); | ||
if (mod(left - right) !== _0n) | ||
@@ -471,15 +512,15 @@ throw new Error(msg); | ||
normalizeS() { | ||
return this.hasHighS() ? new Signature(this.r, CURVE.n - this.s) : this; | ||
return this.hasHighS() ? new Signature(this.r, mod(-this.s, CURVE.n)) : this; | ||
} | ||
toDERRawBytes(isCompressed = false) { | ||
return hexToBytes(this.toDERHex(isCompressed)); | ||
toDERRawBytes() { | ||
return hexToBytes(this.toDERHex()); | ||
} | ||
toDERHex(isCompressed = false) { | ||
toDERHex() { | ||
const sHex = sliceDER(numberToHexUnpadded(this.s)); | ||
if (isCompressed) | ||
return sHex; | ||
const rHex = sliceDER(numberToHexUnpadded(this.r)); | ||
const rLen = numberToHexUnpadded(rHex.length / 2); | ||
const sLen = numberToHexUnpadded(sHex.length / 2); | ||
const length = numberToHexUnpadded(rHex.length / 2 + sHex.length / 2 + 4); | ||
const sHexL = sHex.length / 2; | ||
const rHexL = rHex.length / 2; | ||
const sLen = numberToHexUnpadded(sHexL); | ||
const rLen = numberToHexUnpadded(rHexL); | ||
const length = numberToHexUnpadded(rHexL + sHexL + 4); | ||
return `30${length}02${rLen}${rHex}02${sLen}${sHex}`; | ||
@@ -529,3 +570,3 @@ } | ||
if (!(_0n <= num && num < POW_2_256)) | ||
throw new Error('Expected number < 2^256'); | ||
throw new Error('Expected number 0 <= n < 2^256'); | ||
return num.toString(16).padStart(64, '0'); | ||
@@ -613,3 +654,7 @@ } | ||
const t2 = (pow2(t1, _6n) * b2) % P; | ||
return pow2(t2, _2n); | ||
const rt = pow2(t2, _2n); | ||
const xc = (rt * rt) % P; | ||
if (xc !== x) | ||
throw new Error('Cannot find square root'); | ||
return rt; | ||
} | ||
@@ -652,38 +697,13 @@ function invert(number, modulo = CURVE.P) { | ||
} | ||
const divNearest = (a, b) => (a + b / _2n) / b; | ||
const ENDO = { | ||
a1: BigInt('0x3086d221a7d46bcde86c90e49284eb15'), | ||
b1: -_1n * BigInt('0xe4437ed6010e88286f547fa90abfe4c3'), | ||
a2: BigInt('0x114ca50f7a8e2f3f657c1108d9d44cfd8'), | ||
b2: BigInt('0x3086d221a7d46bcde86c90e49284eb15'), | ||
POW_2_128: BigInt('0x100000000000000000000000000000000'), | ||
}; | ||
function splitScalarEndo(k) { | ||
const { n } = CURVE; | ||
const { a1, b1, a2, b2, POW_2_128 } = ENDO; | ||
const c1 = divNearest(b2 * k, n); | ||
const c2 = divNearest(-b1 * k, n); | ||
let k1 = mod(k - c1 * a1 - c2 * a2, n); | ||
let k2 = mod(-c1 * b1 - c2 * b2, n); | ||
const k1neg = k1 > POW_2_128; | ||
const k2neg = k2 > POW_2_128; | ||
if (k1neg) | ||
k1 = n - k1; | ||
if (k2neg) | ||
k2 = n - k2; | ||
if (k1 > POW_2_128 || k2 > POW_2_128) { | ||
throw new Error('splitScalarEndo: Endomorphism failed, k=' + k); | ||
} | ||
return { k1neg, k1, k2neg, k2 }; | ||
function bits2int_2(bytes) { | ||
const delta = bytes.length * 8 - groupLen * 8; | ||
const num = bytesToNumber(bytes); | ||
return delta > 0 ? num >> BigInt(delta) : num; | ||
} | ||
function truncateHash(hash) { | ||
function truncateHash(hash, truncateOnly = false) { | ||
const h = bits2int_2(hash); | ||
if (truncateOnly) | ||
return h; | ||
const { n } = CURVE; | ||
const byteLength = hash.length; | ||
const delta = byteLength * 8 - 256; | ||
let h = bytesToNumber(hash); | ||
if (delta > 0) | ||
h = h >> BigInt(delta); | ||
if (h >= n) | ||
h -= n; | ||
return h; | ||
return h >= n ? h - n : h; | ||
} | ||
@@ -693,5 +713,11 @@ let _sha256Sync; | ||
class HmacDrbg { | ||
constructor() { | ||
this.v = new Uint8Array(32).fill(1); | ||
this.k = new Uint8Array(32).fill(0); | ||
constructor(hashLen, qByteLen) { | ||
this.hashLen = hashLen; | ||
this.qByteLen = qByteLen; | ||
if (typeof hashLen !== 'number' || hashLen < 2) | ||
throw new Error('hashLen must be a number'); | ||
if (typeof qByteLen !== 'number' || qByteLen < 2) | ||
throw new Error('qByteLen must be a number'); | ||
this.v = new Uint8Array(hashLen).fill(1); | ||
this.k = new Uint8Array(hashLen).fill(0); | ||
this.counter = 0; | ||
@@ -733,4 +759,11 @@ } | ||
this.incr(); | ||
this.v = await this.hmac(this.v); | ||
return this.v; | ||
let len = 0; | ||
const out = []; | ||
while (len < this.qByteLen) { | ||
this.v = await this.hmac(this.v); | ||
const sl = this.v.slice(); | ||
out.push(sl); | ||
len += this.v.length; | ||
} | ||
return concatBytes(...out); | ||
} | ||
@@ -740,4 +773,11 @@ generateSync() { | ||
this.incr(); | ||
this.v = this.hmacSync(this.v); | ||
return this.v; | ||
let len = 0; | ||
const out = []; | ||
while (len < this.qByteLen) { | ||
this.v = this.hmacSync(this.v); | ||
const sl = this.v.slice(); | ||
out.push(sl); | ||
len += this.v.length; | ||
} | ||
return concatBytes(...out); | ||
} | ||
@@ -751,7 +791,8 @@ } | ||
} | ||
function kmdToSig(kBytes, m, d) { | ||
const k = bytesToNumber(kBytes); | ||
function kmdToSig(kBytes, m, d, lowS = true) { | ||
const { n } = CURVE; | ||
const k = truncateHash(kBytes, true); | ||
if (!isWithinCurveOrder(k)) | ||
return; | ||
const { n } = CURVE; | ||
const kinv = invert(k, n); | ||
const q = Point.BASE.multiply(k); | ||
@@ -761,7 +802,11 @@ const r = mod(q.x, n); | ||
return; | ||
const s = mod(invert(k, n) * mod(m + d * r, n), n); | ||
const s = mod(kinv * mod(m + d * r, n), n); | ||
if (s === _0n) | ||
return; | ||
const sig = new Signature(r, s); | ||
const recovery = (q.x === sig.r ? 0 : 2) | Number(q.y & _1n); | ||
let sig = new Signature(r, s); | ||
let recovery = (q.x === sig.r ? 0 : 2) | Number(q.y & _1n); | ||
if (lowS && sig.hasHighS()) { | ||
sig = sig.normalizeS(); | ||
recovery ^= 1; | ||
} | ||
return { sig, recovery }; | ||
@@ -778,3 +823,3 @@ } | ||
else if (typeof key === 'string') { | ||
if (key.length !== 64) | ||
if (key.length !== 2 * groupLen) | ||
throw new Error('Expected 32 bytes of private key'); | ||
@@ -784,3 +829,3 @@ num = hexToNumber(key); | ||
else if (key instanceof Uint8Array) { | ||
if (key.length !== 32) | ||
if (key.length !== groupLen) | ||
throw new Error('Expected 32 bytes of private key'); | ||
@@ -828,5 +873,5 @@ num = bytesToNumber(key); | ||
if (arr) | ||
return len === 33 || len === 65; | ||
return len === compressedLen || len === uncompressedLen; | ||
if (str) | ||
return len === 66 || len === 130; | ||
return len === compressedLen * 2 || len === uncompressedLen * 2; | ||
if (item instanceof Point) | ||
@@ -846,3 +891,3 @@ return true; | ||
function bits2int(bytes) { | ||
const slice = bytes.length > 32 ? bytes.slice(0, 32) : bytes; | ||
const slice = bytes.length > fieldLen ? bytes.slice(0, fieldLen) : bytes; | ||
return bytesToNumber(slice); | ||
@@ -866,6 +911,6 @@ } | ||
if (extraEntropy === true) | ||
extraEntropy = utils.randomBytes(32); | ||
extraEntropy = utils.randomBytes(fieldLen); | ||
const e = ensureBytes(extraEntropy); | ||
if (e.length !== 32) | ||
throw new Error('sign: Expected 32 bytes of extra data'); | ||
if (e.length !== fieldLen) | ||
throw new Error(`sign: Expected ${fieldLen} bytes of extra data`); | ||
seedArgs.push(e); | ||
@@ -878,8 +923,4 @@ } | ||
function finalizeSig(recSig, opts) { | ||
let { sig, recovery } = recSig; | ||
const { canonical, der, recovered } = Object.assign({ canonical: true, der: true }, opts); | ||
if (canonical && sig.hasHighS()) { | ||
sig = sig.normalizeS(); | ||
recovery ^= 1; | ||
} | ||
const { sig, recovery } = recSig; | ||
const { der, recovered } = Object.assign({ canonical: true, der: true }, opts); | ||
const hashed = der ? sig.toDERRawBytes() : sig.toCompactRawBytes(); | ||
@@ -890,6 +931,6 @@ return recovered ? [hashed, recovery] : hashed; | ||
const { seed, m, d } = initSigArgs(msgHash, privKey, opts.extraEntropy); | ||
const drbg = new HmacDrbg(hashLen, groupLen); | ||
await drbg.reseed(seed); | ||
let sig; | ||
const drbg = new HmacDrbg(); | ||
await drbg.reseed(seed); | ||
while (!(sig = kmdToSig(await drbg.generate(), m, d))) | ||
while (!(sig = kmdToSig(await drbg.generate(), m, d, opts.canonical))) | ||
await drbg.reseed(); | ||
@@ -900,6 +941,6 @@ return finalizeSig(sig, opts); | ||
const { seed, m, d } = initSigArgs(msgHash, privKey, opts.extraEntropy); | ||
const drbg = new HmacDrbg(hashLen, groupLen); | ||
drbg.reseedSync(seed); | ||
let sig; | ||
const drbg = new HmacDrbg(); | ||
drbg.reseedSync(seed); | ||
while (!(sig = kmdToSig(drbg.generateSync(), m, d))) | ||
while (!(sig = kmdToSig(drbg.generateSync(), m, d, opts.canonical))) | ||
drbg.reseedSync(); | ||
@@ -1111,4 +1152,6 @@ return finalizeSig(sig, opts); | ||
hash = ensureBytes(hash); | ||
if (hash.length < 40 || hash.length > 1024) | ||
throw new Error('Expected 40-1024 bytes of private key as per FIPS 186'); | ||
const minLen = groupLen + 8; | ||
if (hash.length < minLen || hash.length > 1024) { | ||
throw new Error(`Expected valid bytes of private key as per FIPS 186`); | ||
} | ||
const num = mod(bytesToNumber(hash), CURVE.n - _1n) + _1n; | ||
@@ -1129,4 +1172,8 @@ return numTo32b(num); | ||
}, | ||
randomPrivateKey: () => { | ||
return utils.hashToPrivateKey(utils.randomBytes(40)); | ||
randomPrivateKey: () => utils.hashToPrivateKey(utils.randomBytes(groupLen + 8)), | ||
precompute(windowSize = 8, point = Point.BASE) { | ||
const cached = point === Point.BASE ? point : new Point(point.x, point.y); | ||
cached._setWindowSize(windowSize); | ||
cached.multiply(_3n); | ||
return cached; | ||
}, | ||
@@ -1187,8 +1234,3 @@ sha256: async (...messages) => { | ||
}, | ||
precompute(windowSize = 8, point = Point.BASE) { | ||
const cached = point === Point.BASE ? point : new Point(point.x, point.y); | ||
cached._setWindowSize(windowSize); | ||
cached.multiply(_3n); | ||
return cached; | ||
}, | ||
_JacobianPoint: JacobianPoint, | ||
}; | ||
@@ -1195,0 +1237,0 @@ Object.defineProperties(utils, { |
@@ -17,2 +17,23 @@ /*! noble-secp256k1 - MIT License (c) 2019 Paul Miller (paulmillr.com) */ | ||
declare type Sig = Hex | Signature; | ||
declare class JacobianPoint { | ||
readonly x: bigint; | ||
readonly y: bigint; | ||
readonly z: bigint; | ||
constructor(x: bigint, y: bigint, z: bigint); | ||
static readonly BASE: JacobianPoint; | ||
static readonly ZERO: JacobianPoint; | ||
static fromAffine(p: Point): JacobianPoint; | ||
static toAffineBatch(points: JacobianPoint[]): Point[]; | ||
static normalizeZ(points: JacobianPoint[]): JacobianPoint[]; | ||
equals(other: JacobianPoint): boolean; | ||
negate(): JacobianPoint; | ||
double(): JacobianPoint; | ||
add(other: JacobianPoint): JacobianPoint; | ||
subtract(other: JacobianPoint): JacobianPoint; | ||
multiplyUnsafe(scalar: bigint): JacobianPoint; | ||
private precomputeWindow; | ||
private wNAF; | ||
multiply(scalar: number | bigint, affinePoint?: Point): JacobianPoint; | ||
toAffine(invZ?: bigint): Point; | ||
} | ||
export declare class Point { | ||
@@ -55,4 +76,4 @@ readonly x: bigint; | ||
normalizeS(): Signature; | ||
toDERRawBytes(isCompressed?: boolean): Uint8Array; | ||
toDERHex(isCompressed?: boolean): string; | ||
toDERRawBytes(): Uint8Array; | ||
toDERHex(): string; | ||
toRawBytes(): Uint8Array; | ||
@@ -131,2 +152,3 @@ toHex(): string; | ||
randomPrivateKey: () => Uint8Array; | ||
precompute(windowSize?: number, point?: Point): Point; | ||
sha256: (...messages: Uint8Array[]) => Promise<Uint8Array>; | ||
@@ -138,3 +160,3 @@ hmacSha256: (key: Uint8Array, ...messages: Uint8Array[]) => Promise<Uint8Array>; | ||
taggedHashSync: (tag: string, ...messages: Uint8Array[]) => Uint8Array; | ||
precompute(windowSize?: number, point?: Point): Point; | ||
_JacobianPoint: typeof JacobianPoint; | ||
}; |
294
lib/index.js
@@ -22,3 +22,34 @@ "use strict"; | ||
exports.CURVE = CURVE; | ||
function weistrass(x) { | ||
const divNearest = (a, b) => (a + b / _2n) / b; | ||
const endo = { | ||
beta: BigInt('0x7ae96a2b657c07106e64479eac3434e99cf0497512f58995c1396c28719501ee'), | ||
splitScalar(k) { | ||
const { n } = CURVE; | ||
const a1 = BigInt('0x3086d221a7d46bcde86c90e49284eb15'); | ||
const b1 = -_1n * BigInt('0xe4437ed6010e88286f547fa90abfe4c3'); | ||
const a2 = BigInt('0x114ca50f7a8e2f3f657c1108d9d44cfd8'); | ||
const b2 = a1; | ||
const POW_2_128 = BigInt('0x100000000000000000000000000000000'); | ||
const c1 = divNearest(b2 * k, n); | ||
const c2 = divNearest(-b1 * k, n); | ||
let k1 = mod(k - c1 * a1 - c2 * a2, n); | ||
let k2 = mod(-c1 * b1 - c2 * b2, n); | ||
const k1neg = k1 > POW_2_128; | ||
const k2neg = k2 > POW_2_128; | ||
if (k1neg) | ||
k1 = n - k1; | ||
if (k2neg) | ||
k2 = n - k2; | ||
if (k1 > POW_2_128 || k2 > POW_2_128) { | ||
throw new Error('splitScalarEndo: Endomorphism failed, k=' + k); | ||
} | ||
return { k1neg, k1, k2neg, k2 }; | ||
}, | ||
}; | ||
const fieldLen = 32; | ||
const groupLen = 32; | ||
const hashLen = 32; | ||
const compressedLen = fieldLen + 1; | ||
const uncompressedLen = 2 * fieldLen + 1; | ||
function weierstrass(x) { | ||
const { a, b } = CURVE; | ||
@@ -35,2 +66,6 @@ const x2 = mod(x * x); | ||
} | ||
function assertJacPoint(other) { | ||
if (!(other instanceof JacobianPoint)) | ||
throw new TypeError('JacobianPoint expected'); | ||
} | ||
class JacobianPoint { | ||
@@ -46,2 +81,4 @@ constructor(x, y, z) { | ||
} | ||
if (p.equals(Point.ZERO)) | ||
return JacobianPoint.ZERO; | ||
return new JacobianPoint(p.x, p.y, _1n); | ||
@@ -57,4 +94,3 @@ } | ||
equals(other) { | ||
if (!(other instanceof JacobianPoint)) | ||
throw new TypeError('JacobianPoint expected'); | ||
assertJacPoint(other); | ||
const { x: X1, y: Y1, z: Z1 } = this; | ||
@@ -88,4 +124,3 @@ const { x: X2, y: Y2, z: Z2 } = other; | ||
add(other) { | ||
if (!(other instanceof JacobianPoint)) | ||
throw new TypeError('JacobianPoint expected'); | ||
assertJacPoint(other); | ||
const { x: X1, y: Y1, z: Z1 } = this; | ||
@@ -142,3 +177,3 @@ const { x: X2, y: Y2, z: Z2 } = other; | ||
} | ||
let { k1neg, k1, k2neg, k2 } = splitScalarEndo(n); | ||
let { k1neg, k1, k2neg, k2 } = endo.splitScalar(n); | ||
let k1p = P0; | ||
@@ -160,3 +195,3 @@ let k2p = P0; | ||
k2p = k2p.negate(); | ||
k2p = new JacobianPoint(mod(k2p.x * CURVE.beta), k2p.y, k2p.z); | ||
k2p = new JacobianPoint(mod(k2p.x * endo.beta), k2p.y, k2p.z); | ||
return k1p.add(k2p); | ||
@@ -196,3 +231,3 @@ } | ||
let p = JacobianPoint.ZERO; | ||
let f = JacobianPoint.ZERO; | ||
let f = JacobianPoint.BASE; | ||
const windows = 1 + (USE_ENDOMORPHISM ? 128 / W : 256 / W); | ||
@@ -211,13 +246,11 @@ const windowSize = 2 ** (W - 1); | ||
} | ||
const offset1 = offset; | ||
const offset2 = offset + Math.abs(wbits) - 1; | ||
const cond1 = window % 2 !== 0; | ||
const cond2 = wbits < 0; | ||
if (wbits === 0) { | ||
let pr = precomputes[offset]; | ||
if (window % 2) | ||
pr = pr.negate(); | ||
f = f.add(pr); | ||
f = f.add(constTimeNegate(cond1, precomputes[offset1])); | ||
} | ||
else { | ||
let cached = precomputes[offset + Math.abs(wbits) - 1]; | ||
if (wbits < 0) | ||
cached = cached.negate(); | ||
p = p.add(cached); | ||
p = p.add(constTimeNegate(cond2, precomputes[offset2])); | ||
} | ||
@@ -232,10 +265,8 @@ } | ||
if (USE_ENDOMORPHISM) { | ||
const { k1neg, k1, k2neg, k2 } = splitScalarEndo(n); | ||
const { k1neg, k1, k2neg, k2 } = endo.splitScalar(n); | ||
let { p: k1p, f: f1p } = this.wNAF(k1, affinePoint); | ||
let { p: k2p, f: f2p } = this.wNAF(k2, affinePoint); | ||
if (k1neg) | ||
k1p = k1p.negate(); | ||
if (k2neg) | ||
k2p = k2p.negate(); | ||
k2p = new JacobianPoint(mod(k2p.x * CURVE.beta), k2p.y, k2p.z); | ||
k1p = constTimeNegate(k1neg, k1p); | ||
k2p = constTimeNegate(k2neg, k2p); | ||
k2p = new JacobianPoint(mod(k2p.x * endo.beta), k2p.y, k2p.z); | ||
point = k1p.add(k2p); | ||
@@ -251,4 +282,7 @@ fake = f1p.add(f2p); | ||
} | ||
toAffine(invZ = invert(this.z)) { | ||
toAffine(invZ) { | ||
const { x, y, z } = this; | ||
const is0 = this.equals(JacobianPoint.ZERO); | ||
if (invZ == null) | ||
invZ = is0 ? _8n : invert(z); | ||
const iz1 = invZ; | ||
@@ -260,2 +294,4 @@ const iz2 = mod(iz1 * iz1); | ||
const zz = mod(z * iz1); | ||
if (is0) | ||
return Point.ZERO; | ||
if (zz !== _1n) | ||
@@ -268,2 +304,6 @@ throw new Error('invZ was invalid'); | ||
JacobianPoint.ZERO = new JacobianPoint(_0n, _1n, _0n); | ||
function constTimeNegate(condition, item) { | ||
const neg = item.negate(); | ||
return condition ? neg : item; | ||
} | ||
const pointPrecomputes = new WeakMap(); | ||
@@ -287,3 +327,3 @@ class Point { | ||
throw new Error('Point is not on curve'); | ||
const y2 = weistrass(x); | ||
const y2 = weierstrass(x); | ||
let y = sqrtMod(y2); | ||
@@ -305,4 +345,4 @@ const isYOdd = (y & _1n) === _1n; | ||
static fromUncompressedHex(bytes) { | ||
const x = bytesToNumber(bytes.subarray(1, 33)); | ||
const y = bytesToNumber(bytes.subarray(33, 65)); | ||
const x = bytesToNumber(bytes.subarray(1, fieldLen + 1)); | ||
const y = bytesToNumber(bytes.subarray(fieldLen + 1, fieldLen * 2 + 1)); | ||
const point = new Point(x, y); | ||
@@ -316,8 +356,10 @@ point.assertValidity(); | ||
const header = bytes[0]; | ||
if (len === 32 || (len === 33 && (header === 0x02 || header === 0x03))) { | ||
if (len === fieldLen) | ||
return this.fromCompressedHex(bytes); | ||
if (len === compressedLen && (header === 0x02 || header === 0x03)) { | ||
return this.fromCompressedHex(bytes); | ||
} | ||
if (len === 65 && header === 0x04) | ||
if (len === uncompressedLen && header === 0x04) | ||
return this.fromUncompressedHex(bytes); | ||
throw new Error(`Point.fromHex: received invalid point. Expected 32-33 compressed bytes or 65 uncompressed bytes, not ${len}`); | ||
throw new Error(`Point.fromHex: received invalid point. Expected 32-${compressedLen} compressed bytes or ${uncompressedLen} uncompressed bytes, not ${len}`); | ||
} | ||
@@ -328,14 +370,13 @@ static fromPrivateKey(privateKey) { | ||
static fromSignature(msgHash, signature, recovery) { | ||
msgHash = ensureBytes(msgHash); | ||
const h = truncateHash(msgHash); | ||
const { r, s } = normalizeSignature(signature); | ||
if (recovery !== 0 && recovery !== 1) { | ||
throw new Error('Cannot recover signature: invalid recovery bit'); | ||
} | ||
const prefix = recovery & 1 ? '03' : '02'; | ||
const R = Point.fromHex(prefix + numTo32bStr(r)); | ||
if (![0, 1, 2, 3].includes(recovery)) | ||
throw new Error('Cannot recover: invalid recovery bit'); | ||
const h = truncateHash(ensureBytes(msgHash)); | ||
const { n } = CURVE; | ||
const rinv = invert(r, n); | ||
const radj = recovery === 2 || recovery === 3 ? r + n : r; | ||
const rinv = invert(radj, n); | ||
const u1 = mod(-h * rinv, n); | ||
const u2 = mod(s * rinv, n); | ||
const prefix = recovery & 1 ? '03' : '02'; | ||
const R = Point.fromHex(prefix + numTo32bStr(radj)); | ||
const Q = Point.BASE.multiplyAndAddUnsafe(R, u1, u2); | ||
@@ -372,3 +413,3 @@ if (!Q) | ||
const left = mod(y * y); | ||
const right = weistrass(x); | ||
const right = weierstrass(x); | ||
if (mod(left - right) !== _0n) | ||
@@ -475,15 +516,15 @@ throw new Error(msg); | ||
normalizeS() { | ||
return this.hasHighS() ? new Signature(this.r, CURVE.n - this.s) : this; | ||
return this.hasHighS() ? new Signature(this.r, mod(-this.s, CURVE.n)) : this; | ||
} | ||
toDERRawBytes(isCompressed = false) { | ||
return hexToBytes(this.toDERHex(isCompressed)); | ||
toDERRawBytes() { | ||
return hexToBytes(this.toDERHex()); | ||
} | ||
toDERHex(isCompressed = false) { | ||
toDERHex() { | ||
const sHex = sliceDER(numberToHexUnpadded(this.s)); | ||
if (isCompressed) | ||
return sHex; | ||
const rHex = sliceDER(numberToHexUnpadded(this.r)); | ||
const rLen = numberToHexUnpadded(rHex.length / 2); | ||
const sLen = numberToHexUnpadded(sHex.length / 2); | ||
const length = numberToHexUnpadded(rHex.length / 2 + sHex.length / 2 + 4); | ||
const sHexL = sHex.length / 2; | ||
const rHexL = rHex.length / 2; | ||
const sLen = numberToHexUnpadded(sHexL); | ||
const rLen = numberToHexUnpadded(rHexL); | ||
const length = numberToHexUnpadded(rHexL + sHexL + 4); | ||
return `30${length}02${rLen}${rHex}02${sLen}${sHex}`; | ||
@@ -534,3 +575,3 @@ } | ||
if (!(_0n <= num && num < POW_2_256)) | ||
throw new Error('Expected number < 2^256'); | ||
throw new Error('Expected number 0 <= n < 2^256'); | ||
return num.toString(16).padStart(64, '0'); | ||
@@ -618,3 +659,7 @@ } | ||
const t2 = (pow2(t1, _6n) * b2) % P; | ||
return pow2(t2, _2n); | ||
const rt = pow2(t2, _2n); | ||
const xc = (rt * rt) % P; | ||
if (xc !== x) | ||
throw new Error('Cannot find square root'); | ||
return rt; | ||
} | ||
@@ -657,38 +702,13 @@ function invert(number, modulo = CURVE.P) { | ||
} | ||
const divNearest = (a, b) => (a + b / _2n) / b; | ||
const ENDO = { | ||
a1: BigInt('0x3086d221a7d46bcde86c90e49284eb15'), | ||
b1: -_1n * BigInt('0xe4437ed6010e88286f547fa90abfe4c3'), | ||
a2: BigInt('0x114ca50f7a8e2f3f657c1108d9d44cfd8'), | ||
b2: BigInt('0x3086d221a7d46bcde86c90e49284eb15'), | ||
POW_2_128: BigInt('0x100000000000000000000000000000000'), | ||
}; | ||
function splitScalarEndo(k) { | ||
const { n } = CURVE; | ||
const { a1, b1, a2, b2, POW_2_128 } = ENDO; | ||
const c1 = divNearest(b2 * k, n); | ||
const c2 = divNearest(-b1 * k, n); | ||
let k1 = mod(k - c1 * a1 - c2 * a2, n); | ||
let k2 = mod(-c1 * b1 - c2 * b2, n); | ||
const k1neg = k1 > POW_2_128; | ||
const k2neg = k2 > POW_2_128; | ||
if (k1neg) | ||
k1 = n - k1; | ||
if (k2neg) | ||
k2 = n - k2; | ||
if (k1 > POW_2_128 || k2 > POW_2_128) { | ||
throw new Error('splitScalarEndo: Endomorphism failed, k=' + k); | ||
} | ||
return { k1neg, k1, k2neg, k2 }; | ||
function bits2int_2(bytes) { | ||
const delta = bytes.length * 8 - groupLen * 8; | ||
const num = bytesToNumber(bytes); | ||
return delta > 0 ? num >> BigInt(delta) : num; | ||
} | ||
function truncateHash(hash) { | ||
function truncateHash(hash, truncateOnly = false) { | ||
const h = bits2int_2(hash); | ||
if (truncateOnly) | ||
return h; | ||
const { n } = CURVE; | ||
const byteLength = hash.length; | ||
const delta = byteLength * 8 - 256; | ||
let h = bytesToNumber(hash); | ||
if (delta > 0) | ||
h = h >> BigInt(delta); | ||
if (h >= n) | ||
h -= n; | ||
return h; | ||
return h >= n ? h - n : h; | ||
} | ||
@@ -698,5 +718,11 @@ let _sha256Sync; | ||
class HmacDrbg { | ||
constructor() { | ||
this.v = new Uint8Array(32).fill(1); | ||
this.k = new Uint8Array(32).fill(0); | ||
constructor(hashLen, qByteLen) { | ||
this.hashLen = hashLen; | ||
this.qByteLen = qByteLen; | ||
if (typeof hashLen !== 'number' || hashLen < 2) | ||
throw new Error('hashLen must be a number'); | ||
if (typeof qByteLen !== 'number' || qByteLen < 2) | ||
throw new Error('qByteLen must be a number'); | ||
this.v = new Uint8Array(hashLen).fill(1); | ||
this.k = new Uint8Array(hashLen).fill(0); | ||
this.counter = 0; | ||
@@ -738,4 +764,11 @@ } | ||
this.incr(); | ||
this.v = await this.hmac(this.v); | ||
return this.v; | ||
let len = 0; | ||
const out = []; | ||
while (len < this.qByteLen) { | ||
this.v = await this.hmac(this.v); | ||
const sl = this.v.slice(); | ||
out.push(sl); | ||
len += this.v.length; | ||
} | ||
return concatBytes(...out); | ||
} | ||
@@ -745,4 +778,11 @@ generateSync() { | ||
this.incr(); | ||
this.v = this.hmacSync(this.v); | ||
return this.v; | ||
let len = 0; | ||
const out = []; | ||
while (len < this.qByteLen) { | ||
this.v = this.hmacSync(this.v); | ||
const sl = this.v.slice(); | ||
out.push(sl); | ||
len += this.v.length; | ||
} | ||
return concatBytes(...out); | ||
} | ||
@@ -756,7 +796,8 @@ } | ||
} | ||
function kmdToSig(kBytes, m, d) { | ||
const k = bytesToNumber(kBytes); | ||
function kmdToSig(kBytes, m, d, lowS = true) { | ||
const { n } = CURVE; | ||
const k = truncateHash(kBytes, true); | ||
if (!isWithinCurveOrder(k)) | ||
return; | ||
const { n } = CURVE; | ||
const kinv = invert(k, n); | ||
const q = Point.BASE.multiply(k); | ||
@@ -766,7 +807,11 @@ const r = mod(q.x, n); | ||
return; | ||
const s = mod(invert(k, n) * mod(m + d * r, n), n); | ||
const s = mod(kinv * mod(m + d * r, n), n); | ||
if (s === _0n) | ||
return; | ||
const sig = new Signature(r, s); | ||
const recovery = (q.x === sig.r ? 0 : 2) | Number(q.y & _1n); | ||
let sig = new Signature(r, s); | ||
let recovery = (q.x === sig.r ? 0 : 2) | Number(q.y & _1n); | ||
if (lowS && sig.hasHighS()) { | ||
sig = sig.normalizeS(); | ||
recovery ^= 1; | ||
} | ||
return { sig, recovery }; | ||
@@ -783,3 +828,3 @@ } | ||
else if (typeof key === 'string') { | ||
if (key.length !== 64) | ||
if (key.length !== 2 * groupLen) | ||
throw new Error('Expected 32 bytes of private key'); | ||
@@ -789,3 +834,3 @@ num = hexToNumber(key); | ||
else if (key instanceof Uint8Array) { | ||
if (key.length !== 32) | ||
if (key.length !== groupLen) | ||
throw new Error('Expected 32 bytes of private key'); | ||
@@ -835,5 +880,5 @@ num = bytesToNumber(key); | ||
if (arr) | ||
return len === 33 || len === 65; | ||
return len === compressedLen || len === uncompressedLen; | ||
if (str) | ||
return len === 66 || len === 130; | ||
return len === compressedLen * 2 || len === uncompressedLen * 2; | ||
if (item instanceof Point) | ||
@@ -854,3 +899,3 @@ return true; | ||
function bits2int(bytes) { | ||
const slice = bytes.length > 32 ? bytes.slice(0, 32) : bytes; | ||
const slice = bytes.length > fieldLen ? bytes.slice(0, fieldLen) : bytes; | ||
return bytesToNumber(slice); | ||
@@ -874,6 +919,6 @@ } | ||
if (extraEntropy === true) | ||
extraEntropy = exports.utils.randomBytes(32); | ||
extraEntropy = exports.utils.randomBytes(fieldLen); | ||
const e = ensureBytes(extraEntropy); | ||
if (e.length !== 32) | ||
throw new Error('sign: Expected 32 bytes of extra data'); | ||
if (e.length !== fieldLen) | ||
throw new Error(`sign: Expected ${fieldLen} bytes of extra data`); | ||
seedArgs.push(e); | ||
@@ -886,8 +931,4 @@ } | ||
function finalizeSig(recSig, opts) { | ||
let { sig, recovery } = recSig; | ||
const { canonical, der, recovered } = Object.assign({ canonical: true, der: true }, opts); | ||
if (canonical && sig.hasHighS()) { | ||
sig = sig.normalizeS(); | ||
recovery ^= 1; | ||
} | ||
const { sig, recovery } = recSig; | ||
const { der, recovered } = Object.assign({ canonical: true, der: true }, opts); | ||
const hashed = der ? sig.toDERRawBytes() : sig.toCompactRawBytes(); | ||
@@ -898,6 +939,6 @@ return recovered ? [hashed, recovery] : hashed; | ||
const { seed, m, d } = initSigArgs(msgHash, privKey, opts.extraEntropy); | ||
const drbg = new HmacDrbg(hashLen, groupLen); | ||
await drbg.reseed(seed); | ||
let sig; | ||
const drbg = new HmacDrbg(); | ||
await drbg.reseed(seed); | ||
while (!(sig = kmdToSig(await drbg.generate(), m, d))) | ||
while (!(sig = kmdToSig(await drbg.generate(), m, d, opts.canonical))) | ||
await drbg.reseed(); | ||
@@ -909,6 +950,6 @@ return finalizeSig(sig, opts); | ||
const { seed, m, d } = initSigArgs(msgHash, privKey, opts.extraEntropy); | ||
const drbg = new HmacDrbg(hashLen, groupLen); | ||
drbg.reseedSync(seed); | ||
let sig; | ||
const drbg = new HmacDrbg(); | ||
drbg.reseedSync(seed); | ||
while (!(sig = kmdToSig(drbg.generateSync(), m, d))) | ||
while (!(sig = kmdToSig(drbg.generateSync(), m, d, opts.canonical))) | ||
drbg.reseedSync(); | ||
@@ -1121,4 +1162,6 @@ return finalizeSig(sig, opts); | ||
hash = ensureBytes(hash); | ||
if (hash.length < 40 || hash.length > 1024) | ||
throw new Error('Expected 40-1024 bytes of private key as per FIPS 186'); | ||
const minLen = groupLen + 8; | ||
if (hash.length < minLen || hash.length > 1024) { | ||
throw new Error(`Expected valid bytes of private key as per FIPS 186`); | ||
} | ||
const num = mod(bytesToNumber(hash), CURVE.n - _1n) + _1n; | ||
@@ -1139,4 +1182,8 @@ return numTo32b(num); | ||
}, | ||
randomPrivateKey: () => { | ||
return exports.utils.hashToPrivateKey(exports.utils.randomBytes(40)); | ||
randomPrivateKey: () => exports.utils.hashToPrivateKey(exports.utils.randomBytes(groupLen + 8)), | ||
precompute(windowSize = 8, point = Point.BASE) { | ||
const cached = point === Point.BASE ? point : new Point(point.x, point.y); | ||
cached._setWindowSize(windowSize); | ||
cached.multiply(_3n); | ||
return cached; | ||
}, | ||
@@ -1197,8 +1244,3 @@ sha256: async (...messages) => { | ||
}, | ||
precompute(windowSize = 8, point = Point.BASE) { | ||
const cached = point === Point.BASE ? point : new Point(point.x, point.y); | ||
cached._setWindowSize(windowSize); | ||
cached.multiply(_3n); | ||
return cached; | ||
}, | ||
_JacobianPoint: JacobianPoint, | ||
}; | ||
@@ -1205,0 +1247,0 @@ Object.defineProperties(exports.utils, { |
{ | ||
"name": "@noble/secp256k1", | ||
"version": "1.7.0", | ||
"version": "1.7.1", | ||
"description": "Fastest JS implementation of secp256k1. Independently audited, high-security, 0-dependency ECDSA & Schnorr signatures", | ||
@@ -5,0 +5,0 @@ "files": [ |
@@ -138,3 +138,3 @@ # noble-secp256k1 ![Node CI](https://github.com/paulmillr/noble-secp256k1/workflows/Node%20CI/badge.svg) [![code style: prettier](https://img.shields.io/badge/code_style-prettier-ff69b4.svg?style=flat-square)](https://github.com/prettier/prettier) | ||
```ts | ||
import { hmac } = from '@noble/hashes/hmac'; | ||
import { hmac } from '@noble/hashes/hmac'; | ||
import { sha256 } from '@noble/hashes/sha256'; | ||
@@ -355,3 +355,3 @@ secp256k1.utils.hmacSha256Sync = (key, ...msgs) => hmac(sha256, key, secp256k1.utils.concatBytes(...msgs)) | ||
We're using built-in JS `BigInt`, which is "unsuitable for use in cryptography" as [per official spec](https://github.com/tc39/proposal-bigint#cryptography). This means that the lib is potentially vulnerable to [timing attacks](https://en.wikipedia.org/wiki/Timing_attack). 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, [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. Nonetheless we've hardened implementation of ec curve multiplication 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 official spec](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, [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. Nonetheless we've hardened implementation of ec curve multiplication to be algorithmically constant time. | ||
@@ -364,12 +364,12 @@ We however 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. | ||
getPublicKey(utils.randomPrivateKey()) x 7,035 ops/sec @ 142μs/op | ||
sign x 5,452 ops/sec @ 183μs/op | ||
signSync (@noble/hashes) x 5,082 ops/sec @ 196μs/op | ||
verify x 1,042 ops/sec @ 959μs/op | ||
recoverPublicKey x 955 ops/sec @ 1ms/op | ||
getSharedSecret aka ecdh x 624 ops/sec @ 1ms/op | ||
getSharedSecret (precomputed) x 7,357 ops/sec @ 135μs/op | ||
Point.fromHex (decompression) x 13,725 ops/sec @ 72μs/op | ||
schnorr.sign x 770 ops/sec @ 1ms/op | ||
schnorr.verify x 1,093 ops/sec @ 914μs/op | ||
getPublicKey(utils.randomPrivateKey()) x 7,093 ops/sec @ 140μs/op | ||
sign x 5,615 ops/sec @ 178μs/op | ||
signSync (@noble/hashes) x 5,209 ops/sec @ 191μs/op | ||
verify x 1,114 ops/sec @ 896μs/op | ||
recoverPublicKey x 1,018 ops/sec @ 982μs/op | ||
getSharedSecret aka ecdh x 665 ops/sec @ 1ms/op | ||
getSharedSecret (precomputed) x 7,426 ops/sec @ 134μs/op | ||
Point.fromHex (decompression) x 14,582 ops/sec @ 68μs/op | ||
schnorr.sign x 805 ops/sec @ 1ms/op | ||
schnorr.verify x 1,129 ops/sec @ 885μs/op | ||
@@ -376,0 +376,0 @@ Compare to other libraries on M1 (`openssl` uses native bindings, not JS): |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
111256
2608