@noble/secp256k1
Advanced tools
Comparing version 1.5.4 to 1.5.5
@@ -27,3 +27,3 @@ /*! noble-secp256k1 - MIT License (c) 2019 Paul Miller (paulmillr.com) */ | ||
const USE_ENDOMORPHISM = CURVE.a === _0n; | ||
export class JacobianPoint { | ||
class JacobianPoint { | ||
constructor(x, y, z) { | ||
@@ -262,3 +262,3 @@ this.x = x; | ||
const isShort = bytes.length === 32; | ||
const x = bytesToNumber(isShort ? bytes : bytes.slice(1)); | ||
const x = bytesToNumber(isShort ? bytes : bytes.subarray(1)); | ||
if (!isValidFieldElement(x)) | ||
@@ -283,4 +283,4 @@ throw new Error('Point is not on curve'); | ||
static fromUncompressedHex(bytes) { | ||
const x = bytesToNumber(bytes.slice(1, 33)); | ||
const y = bytesToNumber(bytes.slice(33)); | ||
const x = bytesToNumber(bytes.subarray(1, 33)); | ||
const y = bytesToNumber(bytes.subarray(33, 65)); | ||
const point = new Point(x, y); | ||
@@ -292,9 +292,10 @@ point.assertValidity(); | ||
const bytes = ensureBytes(hex); | ||
const len = bytes.length; | ||
const header = bytes[0]; | ||
if (bytes.length === 32 || (bytes.length === 33 && (header === 0x02 || header === 0x03))) { | ||
if (len === 32 || (len === 33 && (header === 0x02 || header === 0x03))) { | ||
return this.fromCompressedHex(bytes); | ||
} | ||
if (bytes.length === 65 && header === 0x04) | ||
if (len === 65 && header === 0x04) | ||
return this.fromUncompressedHex(bytes); | ||
throw new Error(`Point.fromHex: received invalid point. Expected 32-33 compressed bytes or 65 uncompressed bytes, not ${bytes.length}`); | ||
throw new Error(`Point.fromHex: received invalid point. Expected 32-33 compressed bytes or 65 uncompressed bytes, not ${len}`); | ||
} | ||
@@ -305,5 +306,4 @@ static fromPrivateKey(privateKey) { | ||
static fromSignature(msgHash, signature, recovery) { | ||
const { n } = CURVE; | ||
msgHash = ensureBytes(msgHash); | ||
const z = truncateHash(msgHash); | ||
const h = truncateHash(msgHash); | ||
const { r, s } = normalizeSignature(signature); | ||
@@ -313,18 +313,15 @@ if (recovery !== 0 && recovery !== 1) { | ||
} | ||
if (z === _0n) | ||
if (h === _0n) | ||
throw new Error('Cannot recover signature: msgHash cannot be 0'); | ||
const prefix = recovery & 1 ? '03' : '02'; | ||
const Ra = Point.fromHex(prefix + numTo32bStr(r)); | ||
const R = JacobianPoint.fromAffine(Ra); | ||
const R = Point.fromHex(prefix + numTo32bStr(r)); | ||
const { n } = CURVE; | ||
const rinv = invert(r, n); | ||
const u1 = mod(-z * rinv, n); | ||
const u1 = mod(-h * rinv, n); | ||
const u2 = mod(s * rinv, n); | ||
const u1G = JacobianPoint.BASE.multiply(u1); | ||
const u2R = R.multiplyUnsafe(u2); | ||
const Q = u1G.add(u2R); | ||
if (Q.equals(JacobianPoint.ZERO)) | ||
const Q = Point.BASE.multiplyAndAddUnsafe(R, u1, u2); | ||
if (!Q) | ||
throw new Error('Cannot recover signature: point at infinify'); | ||
const Qa = Q.toAffine(); | ||
Qa.assertValidity(); | ||
return Qa; | ||
Q.assertValidity(); | ||
return Q; | ||
} | ||
@@ -337,3 +334,4 @@ toRawBytes(isCompressed = false) { | ||
if (isCompressed) { | ||
return `${this.y & _1n ? '03' : '02'}${x}`; | ||
const prefix = this.y & _1n ? '03' : '02'; | ||
return `${prefix}${x}`; | ||
} | ||
@@ -378,2 +376,9 @@ else { | ||
} | ||
multiplyAndAddUnsafe(Q, a, b) { | ||
const P = JacobianPoint.fromAffine(this); | ||
const aP = P.multiply(a); | ||
const bQ = JacobianPoint.fromAffine(Q).multiplyUnsafe(b); | ||
const sum = aP.add(bQ); | ||
return sum.equals(JacobianPoint.ZERO) ? undefined : sum.toAffine(); | ||
} | ||
} | ||
@@ -883,5 +888,5 @@ Point.BASE = new Point(CURVE.Gx, CURVE.Gy); | ||
return false; | ||
let pubKey; | ||
let P; | ||
try { | ||
pubKey = JacobianPoint.fromAffine(normalizePublicKey(publicKey)); | ||
P = normalizePublicKey(publicKey); | ||
} | ||
@@ -892,8 +897,8 @@ catch (error) { | ||
const { n } = CURVE; | ||
const s1 = invert(s, n); | ||
const u1 = mod(h * s1, n); | ||
const u2 = mod(r * s1, n); | ||
const Ghs1 = JacobianPoint.BASE.multiply(u1); | ||
const Prs1 = pubKey.multiplyUnsafe(u2); | ||
const R = Ghs1.add(Prs1).toAffine(); | ||
const sinv = invert(s, n); | ||
const u1 = mod(h * sinv, n); | ||
const u2 = mod(r * sinv, n); | ||
const R = Point.BASE.multiplyAndAddUnsafe(P, u1, u2); | ||
if (!R) | ||
return false; | ||
const v = mod(R.x, n); | ||
@@ -914,3 +919,3 @@ return v === r; | ||
function hasEvenY(point) { | ||
return mod(point.y, _2n) === _0n; | ||
return (point.y & _1n) === _0n; | ||
} | ||
@@ -921,4 +926,3 @@ class SchnorrSignature { | ||
this.s = s; | ||
if (!isValidFieldElement(r) || !isWithinCurveOrder(s)) | ||
throw new Error('Invalid signature'); | ||
this.assertValidity(); | ||
} | ||
@@ -929,6 +933,11 @@ static fromHex(hex) { | ||
throw new TypeError(`SchnorrSignature.fromHex: expected 64 bytes, not ${bytes.length}`); | ||
const r = bytesToNumber(bytes.slice(0, 32)); | ||
const s = bytesToNumber(bytes.slice(32, 64)); | ||
const r = bytesToNumber(bytes.subarray(0, 32)); | ||
const s = bytesToNumber(bytes.subarray(32, 64)); | ||
return new SchnorrSignature(r, s); | ||
} | ||
assertValidity() { | ||
const { r, s } = this; | ||
if (!isValidFieldElement(r) || !isWithinCurveOrder(s)) | ||
throw new Error('Invalid signature'); | ||
} | ||
toHex() { | ||
@@ -964,18 +973,32 @@ return numTo32bStr(this.r) + numTo32bStr(this.s); | ||
const e = await createChallenge(R.x, P, m); | ||
const sig = new SchnorrSignature(R.x, mod(k + e * d, n)); | ||
const isValid = await schnorrVerify(sig.toRawBytes(), m, P.toRawX()); | ||
const sig = new SchnorrSignature(R.x, mod(k + e * d, n)).toRawBytes(); | ||
const isValid = await schnorrVerify(sig, m, P.toRawX()); | ||
if (!isValid) | ||
throw new Error('sign: Invalid signature produced'); | ||
return sig.toRawBytes(); | ||
return sig; | ||
} | ||
async function schnorrVerify(signature, message, publicKey) { | ||
const sig = signature instanceof SchnorrSignature ? signature : SchnorrSignature.fromHex(signature); | ||
const raw = signature instanceof SchnorrSignature; | ||
let sig; | ||
try { | ||
sig = raw ? signature : SchnorrSignature.fromHex(signature); | ||
if (raw) | ||
sig.assertValidity(); | ||
} | ||
catch (error) { | ||
return false; | ||
} | ||
const { r, s } = sig; | ||
const m = ensureBytes(message); | ||
const P = normalizePublicKey(publicKey); | ||
const e = await createChallenge(sig.r, P, m); | ||
const sG = Point.fromPrivateKey(sig.s); | ||
const eP = P.multiply(e); | ||
const R = sG.subtract(eP); | ||
if (R.equals(Point.BASE) || !hasEvenY(R) || R.x !== sig.r) | ||
let P; | ||
try { | ||
P = normalizePublicKey(publicKey); | ||
} | ||
catch (error) { | ||
return false; | ||
} | ||
const e = await createChallenge(r, P, m); | ||
const R = Point.BASE.multiplyAndAddUnsafe(P, normalizePrivateKey(s), mod(-e, CURVE.n)); | ||
if (!R || !hasEvenY(R) || R.x !== r) | ||
return false; | ||
return true; | ||
@@ -982,0 +1005,0 @@ } |
@@ -17,23 +17,2 @@ /*! noble-secp256k1 - MIT License (c) 2019 Paul Miller (paulmillr.com) */ | ||
declare type Sig = Hex | Signature; | ||
export 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 { | ||
@@ -63,2 +42,3 @@ readonly x: bigint; | ||
multiply(scalar: number | bigint): Point; | ||
multiplyAndAddUnsafe(Q: Point, a: bigint, b: bigint): Point | undefined; | ||
} | ||
@@ -115,2 +95,3 @@ export declare class Signature { | ||
static fromHex(hex: Hex): SchnorrSignature; | ||
assertValidity(): void; | ||
toHex(): string; | ||
@@ -117,0 +98,0 @@ toRawBytes(): Uint8Array; |
112
lib/index.js
@@ -7,3 +7,3 @@ "use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.utils = exports.schnorr = exports.verify = exports.signSync = exports.sign = exports.getSharedSecret = exports.recoverPublicKey = exports.getPublicKey = exports.Signature = exports.Point = exports.JacobianPoint = exports.CURVE = void 0; | ||
exports.utils = exports.schnorr = exports.verify = exports.signSync = exports.sign = exports.getSharedSecret = exports.recoverPublicKey = exports.getPublicKey = exports.Signature = exports.Point = exports.CURVE = void 0; | ||
const crypto_1 = __importDefault(require("crypto")); | ||
@@ -254,3 +254,2 @@ const _0n = BigInt(0); | ||
} | ||
exports.JacobianPoint = JacobianPoint; | ||
JacobianPoint.BASE = new JacobianPoint(CURVE.Gx, CURVE.Gy, _1n); | ||
@@ -270,3 +269,3 @@ JacobianPoint.ZERO = new JacobianPoint(_0n, _1n, _0n); | ||
const isShort = bytes.length === 32; | ||
const x = bytesToNumber(isShort ? bytes : bytes.slice(1)); | ||
const x = bytesToNumber(isShort ? bytes : bytes.subarray(1)); | ||
if (!isValidFieldElement(x)) | ||
@@ -291,4 +290,4 @@ throw new Error('Point is not on curve'); | ||
static fromUncompressedHex(bytes) { | ||
const x = bytesToNumber(bytes.slice(1, 33)); | ||
const y = bytesToNumber(bytes.slice(33)); | ||
const x = bytesToNumber(bytes.subarray(1, 33)); | ||
const y = bytesToNumber(bytes.subarray(33, 65)); | ||
const point = new Point(x, y); | ||
@@ -300,9 +299,10 @@ point.assertValidity(); | ||
const bytes = ensureBytes(hex); | ||
const len = bytes.length; | ||
const header = bytes[0]; | ||
if (bytes.length === 32 || (bytes.length === 33 && (header === 0x02 || header === 0x03))) { | ||
if (len === 32 || (len === 33 && (header === 0x02 || header === 0x03))) { | ||
return this.fromCompressedHex(bytes); | ||
} | ||
if (bytes.length === 65 && header === 0x04) | ||
if (len === 65 && header === 0x04) | ||
return this.fromUncompressedHex(bytes); | ||
throw new Error(`Point.fromHex: received invalid point. Expected 32-33 compressed bytes or 65 uncompressed bytes, not ${bytes.length}`); | ||
throw new Error(`Point.fromHex: received invalid point. Expected 32-33 compressed bytes or 65 uncompressed bytes, not ${len}`); | ||
} | ||
@@ -313,5 +313,4 @@ static fromPrivateKey(privateKey) { | ||
static fromSignature(msgHash, signature, recovery) { | ||
const { n } = CURVE; | ||
msgHash = ensureBytes(msgHash); | ||
const z = truncateHash(msgHash); | ||
const h = truncateHash(msgHash); | ||
const { r, s } = normalizeSignature(signature); | ||
@@ -321,18 +320,15 @@ if (recovery !== 0 && recovery !== 1) { | ||
} | ||
if (z === _0n) | ||
if (h === _0n) | ||
throw new Error('Cannot recover signature: msgHash cannot be 0'); | ||
const prefix = recovery & 1 ? '03' : '02'; | ||
const Ra = Point.fromHex(prefix + numTo32bStr(r)); | ||
const R = JacobianPoint.fromAffine(Ra); | ||
const R = Point.fromHex(prefix + numTo32bStr(r)); | ||
const { n } = CURVE; | ||
const rinv = invert(r, n); | ||
const u1 = mod(-z * rinv, n); | ||
const u1 = mod(-h * rinv, n); | ||
const u2 = mod(s * rinv, n); | ||
const u1G = JacobianPoint.BASE.multiply(u1); | ||
const u2R = R.multiplyUnsafe(u2); | ||
const Q = u1G.add(u2R); | ||
if (Q.equals(JacobianPoint.ZERO)) | ||
const Q = Point.BASE.multiplyAndAddUnsafe(R, u1, u2); | ||
if (!Q) | ||
throw new Error('Cannot recover signature: point at infinify'); | ||
const Qa = Q.toAffine(); | ||
Qa.assertValidity(); | ||
return Qa; | ||
Q.assertValidity(); | ||
return Q; | ||
} | ||
@@ -345,3 +341,4 @@ toRawBytes(isCompressed = false) { | ||
if (isCompressed) { | ||
return `${this.y & _1n ? '03' : '02'}${x}`; | ||
const prefix = this.y & _1n ? '03' : '02'; | ||
return `${prefix}${x}`; | ||
} | ||
@@ -386,2 +383,9 @@ else { | ||
} | ||
multiplyAndAddUnsafe(Q, a, b) { | ||
const P = JacobianPoint.fromAffine(this); | ||
const aP = P.multiply(a); | ||
const bQ = JacobianPoint.fromAffine(Q).multiplyUnsafe(b); | ||
const sum = aP.add(bQ); | ||
return sum.equals(JacobianPoint.ZERO) ? undefined : sum.toAffine(); | ||
} | ||
} | ||
@@ -897,5 +901,5 @@ exports.Point = Point; | ||
return false; | ||
let pubKey; | ||
let P; | ||
try { | ||
pubKey = JacobianPoint.fromAffine(normalizePublicKey(publicKey)); | ||
P = normalizePublicKey(publicKey); | ||
} | ||
@@ -906,8 +910,8 @@ catch (error) { | ||
const { n } = CURVE; | ||
const s1 = invert(s, n); | ||
const u1 = mod(h * s1, n); | ||
const u2 = mod(r * s1, n); | ||
const Ghs1 = JacobianPoint.BASE.multiply(u1); | ||
const Prs1 = pubKey.multiplyUnsafe(u2); | ||
const R = Ghs1.add(Prs1).toAffine(); | ||
const sinv = invert(s, n); | ||
const u1 = mod(h * sinv, n); | ||
const u2 = mod(r * sinv, n); | ||
const R = Point.BASE.multiplyAndAddUnsafe(P, u1, u2); | ||
if (!R) | ||
return false; | ||
const v = mod(R.x, n); | ||
@@ -929,3 +933,3 @@ return v === r; | ||
function hasEvenY(point) { | ||
return mod(point.y, _2n) === _0n; | ||
return (point.y & _1n) === _0n; | ||
} | ||
@@ -936,4 +940,3 @@ class SchnorrSignature { | ||
this.s = s; | ||
if (!isValidFieldElement(r) || !isWithinCurveOrder(s)) | ||
throw new Error('Invalid signature'); | ||
this.assertValidity(); | ||
} | ||
@@ -944,6 +947,11 @@ static fromHex(hex) { | ||
throw new TypeError(`SchnorrSignature.fromHex: expected 64 bytes, not ${bytes.length}`); | ||
const r = bytesToNumber(bytes.slice(0, 32)); | ||
const s = bytesToNumber(bytes.slice(32, 64)); | ||
const r = bytesToNumber(bytes.subarray(0, 32)); | ||
const s = bytesToNumber(bytes.subarray(32, 64)); | ||
return new SchnorrSignature(r, s); | ||
} | ||
assertValidity() { | ||
const { r, s } = this; | ||
if (!isValidFieldElement(r) || !isWithinCurveOrder(s)) | ||
throw new Error('Invalid signature'); | ||
} | ||
toHex() { | ||
@@ -979,18 +987,32 @@ return numTo32bStr(this.r) + numTo32bStr(this.s); | ||
const e = await createChallenge(R.x, P, m); | ||
const sig = new SchnorrSignature(R.x, mod(k + e * d, n)); | ||
const isValid = await schnorrVerify(sig.toRawBytes(), m, P.toRawX()); | ||
const sig = new SchnorrSignature(R.x, mod(k + e * d, n)).toRawBytes(); | ||
const isValid = await schnorrVerify(sig, m, P.toRawX()); | ||
if (!isValid) | ||
throw new Error('sign: Invalid signature produced'); | ||
return sig.toRawBytes(); | ||
return sig; | ||
} | ||
async function schnorrVerify(signature, message, publicKey) { | ||
const sig = signature instanceof SchnorrSignature ? signature : SchnorrSignature.fromHex(signature); | ||
const raw = signature instanceof SchnorrSignature; | ||
let sig; | ||
try { | ||
sig = raw ? signature : SchnorrSignature.fromHex(signature); | ||
if (raw) | ||
sig.assertValidity(); | ||
} | ||
catch (error) { | ||
return false; | ||
} | ||
const { r, s } = sig; | ||
const m = ensureBytes(message); | ||
const P = normalizePublicKey(publicKey); | ||
const e = await createChallenge(sig.r, P, m); | ||
const sG = Point.fromPrivateKey(sig.s); | ||
const eP = P.multiply(e); | ||
const R = sG.subtract(eP); | ||
if (R.equals(Point.BASE) || !hasEvenY(R) || R.x !== sig.r) | ||
let P; | ||
try { | ||
P = normalizePublicKey(publicKey); | ||
} | ||
catch (error) { | ||
return false; | ||
} | ||
const e = await createChallenge(r, P, m); | ||
const R = Point.BASE.multiplyAndAddUnsafe(P, normalizePrivateKey(s), mod(-e, CURVE.n)); | ||
if (!R || !hasEvenY(R) || R.x !== r) | ||
return false; | ||
return true; | ||
@@ -997,0 +1019,0 @@ } |
{ | ||
"name": "@noble/secp256k1", | ||
"version": "1.5.4", | ||
"version": "1.5.5", | ||
"description": "Fastest JS implementation of secp256k1. Independently audited, high-security, 0-dependency ECDSA & Schnorr signatures", | ||
@@ -5,0 +5,0 @@ "files": [ |
@@ -310,4 +310,4 @@ # 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) | ||
Point.fromHex (decompression) x 12,553 ops/sec @ 79μs/op | ||
schnorr.sign x 426 ops/sec @ 2ms/op | ||
schnorr.verify x 520 ops/sec @ 1ms/op | ||
schnorr.sign x 687 ops/sec @ 1ms/op | ||
schnorr.verify x 990 ops/sec @ 1ms/op | ||
@@ -314,0 +314,0 @@ Compare to other libraries (`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
2287
98083