@noble/secp256k1
Advanced tools
Comparing version 1.4.0 to 1.5.0
@@ -1,2 +0,2 @@ | ||
/*! noble-secp256k1 - MIT License (c) Paul Miller (paulmillr.com) */ | ||
/*! noble-secp256k1 - MIT License (c) 2019 Paul Miller (paulmillr.com) */ | ||
import nodeCrypto from 'crypto'; | ||
@@ -22,3 +22,5 @@ const _0n = BigInt(0); | ||
const { a, b } = CURVE; | ||
return mod(x ** _3n + a * x + b); | ||
const x2 = mod(x * x); | ||
const x3 = mod(x2 * x); | ||
return mod(x3 + a * x + b); | ||
} | ||
@@ -148,3 +150,3 @@ const USE_ENDOMORPHISM = CURVE.a === _0n; | ||
const windows = USE_ENDOMORPHISM ? 128 / W + 1 : 256 / W + 1; | ||
let points = []; | ||
const points = []; | ||
let p = this; | ||
@@ -326,9 +328,8 @@ let base = p; | ||
const msg = 'Point is not on elliptic curve'; | ||
const { P } = CURVE; | ||
const { x, y } = this; | ||
if (x === _0n || y === _0n || x >= P || y >= P) | ||
if (!isWithinCurvePrime(x) || !isWithinCurvePrime(y)) | ||
throw new Error(msg); | ||
const left = mod(y * y); | ||
const right = weistrass(x); | ||
if ((left - right) % P !== _0n) | ||
if (mod(left - right) !== _0n) | ||
throw new Error(msg); | ||
@@ -455,2 +456,4 @@ } | ||
function concatBytes(...arrays) { | ||
if (!arrays.every((a) => a instanceof Uint8Array)) | ||
throw new Error('Uint8Array list expected'); | ||
if (arrays.length === 1) | ||
@@ -493,10 +496,2 @@ return arrays[0]; | ||
} | ||
function parseHexByte(hexByte) { | ||
if (hexByte.length !== 2) | ||
throw new Error('Invalid byte sequence'); | ||
const byte = Number.parseInt(hexByte, 16); | ||
if (Number.isNaN(byte)) | ||
throw new Error('Invalid byte sequence'); | ||
return byte; | ||
} | ||
function hexToBytes(hex) { | ||
@@ -507,7 +502,11 @@ if (typeof hex !== 'string') { | ||
if (hex.length % 2) | ||
throw new Error('hexToBytes: received invalid unpadded hex'); | ||
throw new Error('hexToBytes: received invalid unpadded hex' + hex.length); | ||
const array = new Uint8Array(hex.length / 2); | ||
for (let i = 0; i < array.length; i++) { | ||
const j = i * 2; | ||
array[i] = parseHexByte(hex.slice(j, j + 2)); | ||
const hexByte = hex.slice(j, j + 2); | ||
const byte = Number.parseInt(hexByte, 16); | ||
if (Number.isNaN(byte)) | ||
throw new Error('Invalid byte sequence'); | ||
array[i] = byte; | ||
} | ||
@@ -520,2 +519,4 @@ return array; | ||
function bytesToNumber(bytes) { | ||
if (!(bytes instanceof Uint8Array)) | ||
throw new Error('Expected Uint8Array'); | ||
return hexToNumber(bytesToHex(bytes)); | ||
@@ -641,76 +642,73 @@ } | ||
} | ||
function _abc6979(msgHash, privateKey, extraEntropy) { | ||
if (msgHash == null) | ||
throw new Error(`sign: expected valid msgHash, not "${msgHash}"`); | ||
const num = typeof msgHash === 'string' ? hexToNumber(msgHash) : bytesToNumber(msgHash); | ||
const h1 = pad32b(num); | ||
const h1n = bytesToNumber(h1); | ||
const x = pad32b(privateKey); | ||
let v = new Uint8Array(32).fill(1); | ||
let k = new Uint8Array(32).fill(0); | ||
const b0 = Uint8Array.from([0x00]); | ||
const b1 = Uint8Array.from([0x01]); | ||
let xh1 = concatBytes(x, h1); | ||
if (extraEntropy != null) { | ||
const e = pad32b(typeof extraEntropy === 'string' ? hexToNumber(extraEntropy) : bytesToNumber(extraEntropy)); | ||
if (e.length !== 32) | ||
throw new Error('secp256k1: Expected 32 bytes of extra data'); | ||
xh1 = concatBytes(xh1, e); | ||
class HmacDrbg { | ||
constructor() { | ||
this.v = new Uint8Array(32).fill(1); | ||
this.k = new Uint8Array(32).fill(0); | ||
this.counter = 0; | ||
} | ||
return { xh1, h1n, v, k, b0, b1 }; | ||
} | ||
async function getQRSrfc6979(msgHash, privateKey, extraEntropy) { | ||
const privKey = normalizePrivateKey(privateKey); | ||
let { xh1, h1n, v, k, b0, b1 } = _abc6979(msgHash, privKey, extraEntropy); | ||
const hmac = utils.hmacSha256; | ||
k = await hmac(k, v, b0, xh1); | ||
v = await hmac(k, v); | ||
k = await hmac(k, v, b1, xh1); | ||
v = await hmac(k, v); | ||
for (let i = 0; i < 1000; i++) { | ||
v = await hmac(k, v); | ||
const qrs = calcQRSFromK(v, h1n, privKey); | ||
if (qrs) | ||
return qrs; | ||
k = await hmac(k, v, b0); | ||
v = await hmac(k, v); | ||
hmac(...values) { | ||
return utils.hmacSha256(this.k, ...values); | ||
} | ||
throw new TypeError('secp256k1: Tried 1,000 k values for sign(), all were invalid'); | ||
} | ||
function getQRSrfc6979Sync(msgHash, privateKey, extraEntropy) { | ||
const privKey = normalizePrivateKey(privateKey); | ||
let { xh1, h1n, v, k, b0, b1 } = _abc6979(msgHash, privKey, extraEntropy); | ||
const hmac = utils.hmacSha256Sync; | ||
if (!hmac) | ||
throw new Error('utils.hmacSha256Sync is undefined, you need to set it'); | ||
k = hmac(k, v, b0, xh1); | ||
if (k instanceof Promise) | ||
throw new Error('To use sync sign(), ensure utils.hmacSha256 is sync'); | ||
v = hmac(k, v); | ||
k = hmac(k, v, b1, xh1); | ||
v = hmac(k, v); | ||
for (let i = 0; i < 1000; i++) { | ||
v = hmac(k, v); | ||
const qrs = calcQRSFromK(v, h1n, privKey); | ||
if (qrs) | ||
return qrs; | ||
k = hmac(k, v, b0); | ||
v = hmac(k, v); | ||
hmacSync(...values) { | ||
if (typeof utils.hmacSha256Sync !== 'function') | ||
throw new Error('utils.hmacSha256Sync is undefined, you need to set it'); | ||
const res = utils.hmacSha256Sync(this.k, ...values); | ||
if (res instanceof Promise) | ||
throw new Error('To use sync sign(), ensure utils.hmacSha256 is sync'); | ||
return res; | ||
} | ||
throw new TypeError('secp256k1: Tried 1,000 k values for sign(), all were invalid'); | ||
incr() { | ||
if (this.counter >= 1000) { | ||
throw new Error('Tried 1,000 k values for sign(), all were invalid'); | ||
} | ||
this.counter += 1; | ||
} | ||
async reseed(seed = new Uint8Array()) { | ||
this.k = await this.hmac(this.v, Uint8Array.from([0x00]), seed); | ||
this.v = await this.hmac(this.v); | ||
if (seed.length === 0) | ||
return; | ||
this.k = await this.hmac(this.v, Uint8Array.from([0x01]), seed); | ||
this.v = await this.hmac(this.v); | ||
} | ||
reseedSync(seed = new Uint8Array()) { | ||
this.k = this.hmacSync(this.v, Uint8Array.from([0x00]), seed); | ||
this.v = this.hmacSync(this.v); | ||
if (seed.length === 0) | ||
return; | ||
this.k = this.hmacSync(this.v, Uint8Array.from([0x01]), seed); | ||
this.v = this.hmacSync(this.v); | ||
} | ||
async generate() { | ||
this.incr(); | ||
this.v = await this.hmac(this.v); | ||
return this.v; | ||
} | ||
generateSync() { | ||
this.incr(); | ||
this.v = this.hmacSync(this.v); | ||
return this.v; | ||
} | ||
} | ||
function isWithinCurveOrder(num) { | ||
return 0 < num && num < CURVE.n; | ||
return _0n < num && num < CURVE.n; | ||
} | ||
function calcQRSFromK(v, msg, priv) { | ||
const k = bytesToNumber(v); | ||
function isWithinCurvePrime(num) { | ||
return 0n < num && num < CURVE.P; | ||
} | ||
function kmdToSig(kBytes, m, d) { | ||
const k = bytesToNumber(kBytes); | ||
if (!isWithinCurveOrder(k)) | ||
return; | ||
const max = CURVE.n; | ||
const { n } = CURVE; | ||
const q = Point.BASE.multiply(k); | ||
const r = mod(q.x, max); | ||
const s = mod(invert(k, max) * (msg + r * priv), max); | ||
if (r === _0n || s === _0n) | ||
const r = mod(q.x, n); | ||
if (r === _0n) | ||
return; | ||
return { q, r, s }; | ||
const s = mod(invert(k, n) * 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); | ||
return { sig, recovery }; | ||
} | ||
@@ -790,8 +788,38 @@ function normalizePrivateKey(key) { | ||
} | ||
function QRSToSig(qrs, opts) { | ||
const { q, r, s } = qrs; | ||
const defaultOpts = { canonical: true, der: true }; | ||
let { canonical, der, recovered } = Object.assign(defaultOpts, opts); | ||
let recovery = (q.x === r ? 0 : 2) | Number(q.y & _1n); | ||
let sig = new Signature(r, s); | ||
function bits2int(bytes) { | ||
const slice = bytes.length > 32 ? bytes.slice(0, 32) : bytes; | ||
return bytesToNumber(slice); | ||
} | ||
function bits2octets(bytes) { | ||
const z1 = bits2int(bytes); | ||
const z2 = mod(z1, CURVE.n); | ||
return int2octets(z2 < _0n ? z1 : z2); | ||
} | ||
function int2octets(num) { | ||
if (typeof num !== 'bigint') | ||
throw new Error('Expected bigint'); | ||
const hex = pad64(num); | ||
return hexToBytes(hex); | ||
} | ||
function initSigArgs(msgHash, privateKey, extraEntropy) { | ||
if (msgHash == null) | ||
throw new Error(`sign: expected valid msgHash, not "${msgHash}"`); | ||
const h1 = ensureBytes(msgHash); | ||
const d = normalizePrivateKey(privateKey); | ||
const seedArgs = [int2octets(d), bits2octets(h1)]; | ||
if (extraEntropy != null) { | ||
if (extraEntropy === true) | ||
extraEntropy = utils.randomBytes(32); | ||
const e = pad32b(bytesToNumber(ensureBytes(extraEntropy))); | ||
if (e.length !== 32) | ||
throw new Error('secp256k1: Expected 32 bytes of extra data'); | ||
seedArgs.push(e); | ||
} | ||
const seed = concatBytes(...seedArgs); | ||
const m = bits2int(h1); | ||
return { seed, m, d }; | ||
} | ||
function finalizeSig(recSig, opts) { | ||
let { sig, recovery } = recSig; | ||
const { canonical, der, recovered } = Object.assign({ canonical: true, der: true }, opts); | ||
if (canonical && sig.hasHighS()) { | ||
@@ -805,6 +833,18 @@ sig = sig.normalizeS(); | ||
async function sign(msgHash, privKey, opts = {}) { | ||
return QRSToSig(await getQRSrfc6979(msgHash, privKey, opts.extraEntropy), opts); | ||
const { seed, m, d } = initSigArgs(msgHash, privKey, opts.extraEntropy); | ||
let sig; | ||
const drbg = new HmacDrbg(); | ||
await drbg.reseed(seed); | ||
while (!(sig = kmdToSig(await drbg.generate(), m, d))) | ||
await drbg.reseed(); | ||
return finalizeSig(sig, opts); | ||
} | ||
function signSync(msgHash, privKey, opts = {}) { | ||
return QRSToSig(getQRSrfc6979Sync(msgHash, privKey, opts.extraEntropy), opts); | ||
const { seed, m, d } = initSigArgs(msgHash, privKey, opts.extraEntropy); | ||
let sig; | ||
const drbg = new HmacDrbg(); | ||
drbg.reseedSync(seed); | ||
while (!(sig = kmdToSig(drbg.generateSync(), m, d))) | ||
drbg.reseedSync(); | ||
return finalizeSig(sig, opts); | ||
} | ||
@@ -862,3 +902,3 @@ export { sign, signSync }; | ||
this.s = s; | ||
if (r <= _0n || s <= _0n || r >= CURVE.P || s >= CURVE.n) | ||
if (!isWithinCurvePrime(r) || !isWithinCurveOrder(s)) | ||
throw new Error('Invalid signature'); | ||
@@ -868,7 +908,6 @@ } | ||
const bytes = ensureBytes(hex); | ||
if (bytes.length !== 64) { | ||
if (bytes.length !== 64) | ||
throw new TypeError(`SchnorrSignature.fromHex: expected 64 bytes, not ${bytes.length}`); | ||
} | ||
const r = bytesToNumber(bytes.slice(0, 32)); | ||
const s = bytesToNumber(bytes.slice(32)); | ||
const s = bytesToNumber(bytes.slice(32, 64)); | ||
return new SchnorrSignature(r, s); | ||
@@ -886,9 +925,7 @@ } | ||
} | ||
async function schnorrSign(msgHash, privateKey, auxRand = utils.randomBytes()) { | ||
if (msgHash == null) | ||
throw new TypeError(`sign: Expected valid message, not "${msgHash}"`); | ||
if (!privateKey) | ||
privateKey = _0n; | ||
async function schnorrSign(message, privateKey, auxRand = utils.randomBytes()) { | ||
if (message == null) | ||
throw new TypeError(`sign: Expected valid message, not "${message}"`); | ||
const { n } = CURVE; | ||
const m = ensureBytes(msgHash); | ||
const m = ensureBytes(message); | ||
const d0 = normalizePrivateKey(privateKey); | ||
@@ -915,5 +952,5 @@ const rand = ensureBytes(auxRand); | ||
} | ||
async function schnorrVerify(signature, msgHash, publicKey) { | ||
async function schnorrVerify(signature, message, publicKey) { | ||
const sig = signature instanceof SchnorrSignature ? signature : SchnorrSignature.fromHex(signature); | ||
const m = typeof msgHash === 'string' ? hexToBytes(msgHash) : msgHash; | ||
const m = ensureBytes(message); | ||
const P = normalizePublicKey(publicKey); | ||
@@ -955,3 +992,3 @@ const e = await createChallenge(sig.r, P, m); | ||
const { randomBytes } = crypto.node; | ||
return new Uint8Array(randomBytes(bytesLength).buffer); | ||
return Uint8Array.from(randomBytes(bytesLength)); | ||
} | ||
@@ -996,5 +1033,3 @@ else { | ||
const hash = createHmac('sha256', key); | ||
for (const message of messages) { | ||
hash.update(message); | ||
} | ||
messages.forEach((m) => hash.update(m)); | ||
return Uint8Array.from(hash.digest()); | ||
@@ -1001,0 +1036,0 @@ } |
@@ -1,2 +0,2 @@ | ||
/*! noble-secp256k1 - MIT License (c) Paul Miller (paulmillr.com) */ | ||
/*! noble-secp256k1 - MIT License (c) 2019 Paul Miller (paulmillr.com) */ | ||
declare const CURVE: { | ||
@@ -64,2 +64,3 @@ a: bigint; | ||
export declare function getSharedSecret(privateA: PrivKey, publicB: PubKey, isCompressed?: boolean): Uint8Array; | ||
declare type Ent = Hex | true; | ||
declare type OptsRecov = { | ||
@@ -69,3 +70,3 @@ recovered: true; | ||
der?: boolean; | ||
extraEntropy?: Hex; | ||
extraEntropy?: Ent; | ||
}; | ||
@@ -76,3 +77,3 @@ declare type OptsNoRecov = { | ||
der?: boolean; | ||
extraEntropy?: Hex; | ||
extraEntropy?: Ent; | ||
}; | ||
@@ -97,4 +98,4 @@ declare function sign(msgHash: Hex, privKey: PrivKey, opts: OptsRecov): Promise<[U8A, number]>; | ||
declare function schnorrGetPublicKey(privateKey: PrivKey): Uint8Array; | ||
declare function schnorrSign(msgHash: Hex, privateKey: PrivKey, auxRand?: Hex): Promise<Uint8Array>; | ||
declare function schnorrVerify(signature: Hex, msgHash: Hex, publicKey: Hex): Promise<boolean>; | ||
declare function schnorrSign(message: Hex, privateKey: PrivKey, auxRand?: Hex): Promise<Uint8Array>; | ||
declare function schnorrVerify(signature: Hex, message: Hex, publicKey: Hex): Promise<boolean>; | ||
export declare const schnorr: { | ||
@@ -101,0 +102,0 @@ Signature: typeof SchnorrSignature; |
243
lib/index.js
"use strict"; | ||
/*! noble-secp256k1 - MIT License (c) Paul Miller (paulmillr.com) */ | ||
/*! noble-secp256k1 - MIT License (c) 2019 Paul Miller (paulmillr.com) */ | ||
var __importDefault = (this && this.__importDefault) || function (mod) { | ||
@@ -28,3 +28,5 @@ return (mod && mod.__esModule) ? mod : { "default": mod }; | ||
const { a, b } = CURVE; | ||
return mod(x ** _3n + a * x + b); | ||
const x2 = mod(x * x); | ||
const x3 = mod(x2 * x); | ||
return mod(x3 + a * x + b); | ||
} | ||
@@ -154,3 +156,3 @@ const USE_ENDOMORPHISM = CURVE.a === _0n; | ||
const windows = USE_ENDOMORPHISM ? 128 / W + 1 : 256 / W + 1; | ||
let points = []; | ||
const points = []; | ||
let p = this; | ||
@@ -332,9 +334,8 @@ let base = p; | ||
const msg = 'Point is not on elliptic curve'; | ||
const { P } = CURVE; | ||
const { x, y } = this; | ||
if (x === _0n || y === _0n || x >= P || y >= P) | ||
if (!isWithinCurvePrime(x) || !isWithinCurvePrime(y)) | ||
throw new Error(msg); | ||
const left = mod(y * y); | ||
const right = weistrass(x); | ||
if ((left - right) % P !== _0n) | ||
if (mod(left - right) !== _0n) | ||
throw new Error(msg); | ||
@@ -463,2 +464,4 @@ } | ||
function concatBytes(...arrays) { | ||
if (!arrays.every((a) => a instanceof Uint8Array)) | ||
throw new Error('Uint8Array list expected'); | ||
if (arrays.length === 1) | ||
@@ -501,10 +504,2 @@ return arrays[0]; | ||
} | ||
function parseHexByte(hexByte) { | ||
if (hexByte.length !== 2) | ||
throw new Error('Invalid byte sequence'); | ||
const byte = Number.parseInt(hexByte, 16); | ||
if (Number.isNaN(byte)) | ||
throw new Error('Invalid byte sequence'); | ||
return byte; | ||
} | ||
function hexToBytes(hex) { | ||
@@ -515,7 +510,11 @@ if (typeof hex !== 'string') { | ||
if (hex.length % 2) | ||
throw new Error('hexToBytes: received invalid unpadded hex'); | ||
throw new Error('hexToBytes: received invalid unpadded hex' + hex.length); | ||
const array = new Uint8Array(hex.length / 2); | ||
for (let i = 0; i < array.length; i++) { | ||
const j = i * 2; | ||
array[i] = parseHexByte(hex.slice(j, j + 2)); | ||
const hexByte = hex.slice(j, j + 2); | ||
const byte = Number.parseInt(hexByte, 16); | ||
if (Number.isNaN(byte)) | ||
throw new Error('Invalid byte sequence'); | ||
array[i] = byte; | ||
} | ||
@@ -528,2 +527,4 @@ return array; | ||
function bytesToNumber(bytes) { | ||
if (!(bytes instanceof Uint8Array)) | ||
throw new Error('Expected Uint8Array'); | ||
return hexToNumber(bytesToHex(bytes)); | ||
@@ -649,76 +650,73 @@ } | ||
} | ||
function _abc6979(msgHash, privateKey, extraEntropy) { | ||
if (msgHash == null) | ||
throw new Error(`sign: expected valid msgHash, not "${msgHash}"`); | ||
const num = typeof msgHash === 'string' ? hexToNumber(msgHash) : bytesToNumber(msgHash); | ||
const h1 = pad32b(num); | ||
const h1n = bytesToNumber(h1); | ||
const x = pad32b(privateKey); | ||
let v = new Uint8Array(32).fill(1); | ||
let k = new Uint8Array(32).fill(0); | ||
const b0 = Uint8Array.from([0x00]); | ||
const b1 = Uint8Array.from([0x01]); | ||
let xh1 = concatBytes(x, h1); | ||
if (extraEntropy != null) { | ||
const e = pad32b(typeof extraEntropy === 'string' ? hexToNumber(extraEntropy) : bytesToNumber(extraEntropy)); | ||
if (e.length !== 32) | ||
throw new Error('secp256k1: Expected 32 bytes of extra data'); | ||
xh1 = concatBytes(xh1, e); | ||
class HmacDrbg { | ||
constructor() { | ||
this.v = new Uint8Array(32).fill(1); | ||
this.k = new Uint8Array(32).fill(0); | ||
this.counter = 0; | ||
} | ||
return { xh1, h1n, v, k, b0, b1 }; | ||
} | ||
async function getQRSrfc6979(msgHash, privateKey, extraEntropy) { | ||
const privKey = normalizePrivateKey(privateKey); | ||
let { xh1, h1n, v, k, b0, b1 } = _abc6979(msgHash, privKey, extraEntropy); | ||
const hmac = exports.utils.hmacSha256; | ||
k = await hmac(k, v, b0, xh1); | ||
v = await hmac(k, v); | ||
k = await hmac(k, v, b1, xh1); | ||
v = await hmac(k, v); | ||
for (let i = 0; i < 1000; i++) { | ||
v = await hmac(k, v); | ||
const qrs = calcQRSFromK(v, h1n, privKey); | ||
if (qrs) | ||
return qrs; | ||
k = await hmac(k, v, b0); | ||
v = await hmac(k, v); | ||
hmac(...values) { | ||
return exports.utils.hmacSha256(this.k, ...values); | ||
} | ||
throw new TypeError('secp256k1: Tried 1,000 k values for sign(), all were invalid'); | ||
} | ||
function getQRSrfc6979Sync(msgHash, privateKey, extraEntropy) { | ||
const privKey = normalizePrivateKey(privateKey); | ||
let { xh1, h1n, v, k, b0, b1 } = _abc6979(msgHash, privKey, extraEntropy); | ||
const hmac = exports.utils.hmacSha256Sync; | ||
if (!hmac) | ||
throw new Error('utils.hmacSha256Sync is undefined, you need to set it'); | ||
k = hmac(k, v, b0, xh1); | ||
if (k instanceof Promise) | ||
throw new Error('To use sync sign(), ensure utils.hmacSha256 is sync'); | ||
v = hmac(k, v); | ||
k = hmac(k, v, b1, xh1); | ||
v = hmac(k, v); | ||
for (let i = 0; i < 1000; i++) { | ||
v = hmac(k, v); | ||
const qrs = calcQRSFromK(v, h1n, privKey); | ||
if (qrs) | ||
return qrs; | ||
k = hmac(k, v, b0); | ||
v = hmac(k, v); | ||
hmacSync(...values) { | ||
if (typeof exports.utils.hmacSha256Sync !== 'function') | ||
throw new Error('utils.hmacSha256Sync is undefined, you need to set it'); | ||
const res = exports.utils.hmacSha256Sync(this.k, ...values); | ||
if (res instanceof Promise) | ||
throw new Error('To use sync sign(), ensure utils.hmacSha256 is sync'); | ||
return res; | ||
} | ||
throw new TypeError('secp256k1: Tried 1,000 k values for sign(), all were invalid'); | ||
incr() { | ||
if (this.counter >= 1000) { | ||
throw new Error('Tried 1,000 k values for sign(), all were invalid'); | ||
} | ||
this.counter += 1; | ||
} | ||
async reseed(seed = new Uint8Array()) { | ||
this.k = await this.hmac(this.v, Uint8Array.from([0x00]), seed); | ||
this.v = await this.hmac(this.v); | ||
if (seed.length === 0) | ||
return; | ||
this.k = await this.hmac(this.v, Uint8Array.from([0x01]), seed); | ||
this.v = await this.hmac(this.v); | ||
} | ||
reseedSync(seed = new Uint8Array()) { | ||
this.k = this.hmacSync(this.v, Uint8Array.from([0x00]), seed); | ||
this.v = this.hmacSync(this.v); | ||
if (seed.length === 0) | ||
return; | ||
this.k = this.hmacSync(this.v, Uint8Array.from([0x01]), seed); | ||
this.v = this.hmacSync(this.v); | ||
} | ||
async generate() { | ||
this.incr(); | ||
this.v = await this.hmac(this.v); | ||
return this.v; | ||
} | ||
generateSync() { | ||
this.incr(); | ||
this.v = this.hmacSync(this.v); | ||
return this.v; | ||
} | ||
} | ||
function isWithinCurveOrder(num) { | ||
return 0 < num && num < CURVE.n; | ||
return _0n < num && num < CURVE.n; | ||
} | ||
function calcQRSFromK(v, msg, priv) { | ||
const k = bytesToNumber(v); | ||
function isWithinCurvePrime(num) { | ||
return 0n < num && num < CURVE.P; | ||
} | ||
function kmdToSig(kBytes, m, d) { | ||
const k = bytesToNumber(kBytes); | ||
if (!isWithinCurveOrder(k)) | ||
return; | ||
const max = CURVE.n; | ||
const { n } = CURVE; | ||
const q = Point.BASE.multiply(k); | ||
const r = mod(q.x, max); | ||
const s = mod(invert(k, max) * (msg + r * priv), max); | ||
if (r === _0n || s === _0n) | ||
const r = mod(q.x, n); | ||
if (r === _0n) | ||
return; | ||
return { q, r, s }; | ||
const s = mod(invert(k, n) * 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); | ||
return { sig, recovery }; | ||
} | ||
@@ -801,8 +799,38 @@ function normalizePrivateKey(key) { | ||
exports.getSharedSecret = getSharedSecret; | ||
function QRSToSig(qrs, opts) { | ||
const { q, r, s } = qrs; | ||
const defaultOpts = { canonical: true, der: true }; | ||
let { canonical, der, recovered } = Object.assign(defaultOpts, opts); | ||
let recovery = (q.x === r ? 0 : 2) | Number(q.y & _1n); | ||
let sig = new Signature(r, s); | ||
function bits2int(bytes) { | ||
const slice = bytes.length > 32 ? bytes.slice(0, 32) : bytes; | ||
return bytesToNumber(slice); | ||
} | ||
function bits2octets(bytes) { | ||
const z1 = bits2int(bytes); | ||
const z2 = mod(z1, CURVE.n); | ||
return int2octets(z2 < _0n ? z1 : z2); | ||
} | ||
function int2octets(num) { | ||
if (typeof num !== 'bigint') | ||
throw new Error('Expected bigint'); | ||
const hex = pad64(num); | ||
return hexToBytes(hex); | ||
} | ||
function initSigArgs(msgHash, privateKey, extraEntropy) { | ||
if (msgHash == null) | ||
throw new Error(`sign: expected valid msgHash, not "${msgHash}"`); | ||
const h1 = ensureBytes(msgHash); | ||
const d = normalizePrivateKey(privateKey); | ||
const seedArgs = [int2octets(d), bits2octets(h1)]; | ||
if (extraEntropy != null) { | ||
if (extraEntropy === true) | ||
extraEntropy = exports.utils.randomBytes(32); | ||
const e = pad32b(bytesToNumber(ensureBytes(extraEntropy))); | ||
if (e.length !== 32) | ||
throw new Error('secp256k1: Expected 32 bytes of extra data'); | ||
seedArgs.push(e); | ||
} | ||
const seed = concatBytes(...seedArgs); | ||
const m = bits2int(h1); | ||
return { seed, m, d }; | ||
} | ||
function finalizeSig(recSig, opts) { | ||
let { sig, recovery } = recSig; | ||
const { canonical, der, recovered } = Object.assign({ canonical: true, der: true }, opts); | ||
if (canonical && sig.hasHighS()) { | ||
@@ -816,7 +844,19 @@ sig = sig.normalizeS(); | ||
async function sign(msgHash, privKey, opts = {}) { | ||
return QRSToSig(await getQRSrfc6979(msgHash, privKey, opts.extraEntropy), opts); | ||
const { seed, m, d } = initSigArgs(msgHash, privKey, opts.extraEntropy); | ||
let sig; | ||
const drbg = new HmacDrbg(); | ||
await drbg.reseed(seed); | ||
while (!(sig = kmdToSig(await drbg.generate(), m, d))) | ||
await drbg.reseed(); | ||
return finalizeSig(sig, opts); | ||
} | ||
exports.sign = sign; | ||
function signSync(msgHash, privKey, opts = {}) { | ||
return QRSToSig(getQRSrfc6979Sync(msgHash, privKey, opts.extraEntropy), opts); | ||
const { seed, m, d } = initSigArgs(msgHash, privKey, opts.extraEntropy); | ||
let sig; | ||
const drbg = new HmacDrbg(); | ||
drbg.reseedSync(seed); | ||
while (!(sig = kmdToSig(drbg.generateSync(), m, d))) | ||
drbg.reseedSync(); | ||
return finalizeSig(sig, opts); | ||
} | ||
@@ -875,3 +915,3 @@ exports.signSync = signSync; | ||
this.s = s; | ||
if (r <= _0n || s <= _0n || r >= CURVE.P || s >= CURVE.n) | ||
if (!isWithinCurvePrime(r) || !isWithinCurveOrder(s)) | ||
throw new Error('Invalid signature'); | ||
@@ -881,7 +921,6 @@ } | ||
const bytes = ensureBytes(hex); | ||
if (bytes.length !== 64) { | ||
if (bytes.length !== 64) | ||
throw new TypeError(`SchnorrSignature.fromHex: expected 64 bytes, not ${bytes.length}`); | ||
} | ||
const r = bytesToNumber(bytes.slice(0, 32)); | ||
const s = bytesToNumber(bytes.slice(32)); | ||
const s = bytesToNumber(bytes.slice(32, 64)); | ||
return new SchnorrSignature(r, s); | ||
@@ -899,9 +938,7 @@ } | ||
} | ||
async function schnorrSign(msgHash, privateKey, auxRand = exports.utils.randomBytes()) { | ||
if (msgHash == null) | ||
throw new TypeError(`sign: Expected valid message, not "${msgHash}"`); | ||
if (!privateKey) | ||
privateKey = _0n; | ||
async function schnorrSign(message, privateKey, auxRand = exports.utils.randomBytes()) { | ||
if (message == null) | ||
throw new TypeError(`sign: Expected valid message, not "${message}"`); | ||
const { n } = CURVE; | ||
const m = ensureBytes(msgHash); | ||
const m = ensureBytes(message); | ||
const d0 = normalizePrivateKey(privateKey); | ||
@@ -928,5 +965,5 @@ const rand = ensureBytes(auxRand); | ||
} | ||
async function schnorrVerify(signature, msgHash, publicKey) { | ||
async function schnorrVerify(signature, message, publicKey) { | ||
const sig = signature instanceof SchnorrSignature ? signature : SchnorrSignature.fromHex(signature); | ||
const m = typeof msgHash === 'string' ? hexToBytes(msgHash) : msgHash; | ||
const m = ensureBytes(message); | ||
const P = normalizePublicKey(publicKey); | ||
@@ -968,3 +1005,3 @@ const e = await createChallenge(sig.r, P, m); | ||
const { randomBytes } = crypto.node; | ||
return new Uint8Array(randomBytes(bytesLength).buffer); | ||
return Uint8Array.from(randomBytes(bytesLength)); | ||
} | ||
@@ -1009,5 +1046,3 @@ else { | ||
const hash = createHmac('sha256', key); | ||
for (const message of messages) { | ||
hash.update(message); | ||
} | ||
messages.forEach((m) => hash.update(m)); | ||
return Uint8Array.from(hash.digest()); | ||
@@ -1014,0 +1049,0 @@ } |
{ | ||
"name": "@noble/secp256k1", | ||
"version": "1.4.0", | ||
"version": "1.5.0", | ||
"description": "Fastest JS implementation of secp256k1. Independently audited, high-security, 0-dependency ECDSA & Schnorr signatures", | ||
@@ -13,3 +13,3 @@ "files": [ | ||
"build": "tsc -d && tsc -p tsconfig.esm.json", | ||
"build-release": "rollup -c rollup.config.js", | ||
"build:release": "rollup -c rollup.config.js", | ||
"lint": "prettier --print-width 100 --single-quote --check index.ts", | ||
@@ -31,3 +31,3 @@ "test": "jest", | ||
"devDependencies": { | ||
"@noble/hashes": "^0.5.6", | ||
"@noble/hashes": "~1.0.0", | ||
"@rollup/plugin-commonjs": "^21.0.0", | ||
@@ -34,0 +34,0 @@ "@rollup/plugin-node-resolve": "^13.0.0", |
@@ -37,5 +37,6 @@ # 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) | ||
(async () => { | ||
// You pass either a hex string, or Uint8Array | ||
// You pass a hex string, or Uint8Array | ||
const privateKey = "6b911fd37cdf5c81d4c0adb1ab7fa822ed253ab0ad9aa18d77257c88b29b718e"; | ||
const messageHash = "a33321f98e4ff1c283c76998f14f57447545d339b3db534c6d886decb4209f28"; | ||
const message = "hello world"; | ||
const messageHash = await secp.utils.sha256(message); | ||
const publicKey = secp.getPublicKey(privateKey); | ||
@@ -45,9 +46,13 @@ const signature = await secp.sign(messageHash, privateKey); | ||
// Signatures compatible with openssl | ||
const signatureS = await secp.sign(messageHash, privateKey, { canonical: false }); | ||
// Sigs with improved security (see README) | ||
const signatureE = await secp.sign(messageHash, privateKey, { extraEntropy: true }); | ||
// Malleable signatures, compatible with openssl | ||
const signatureM = await secp.sign(messageHash, privateKey, { canonical: false }); | ||
// Supports Schnorr signatures | ||
const rpub = secp.schnorr.getPublicKey(privateKey); | ||
const rsignature = await secp.schnorr.sign(messageHash, privateKey); | ||
const risSigned = await secp.schnorr.verify(rsignature, messageHash, rpub); | ||
const rsignature = await secp.schnorr.sign(message, privateKey); | ||
const risSigned = await secp.schnorr.verify(rsignature, message, rpub); | ||
})(); | ||
@@ -122,2 +127,12 @@ ``` | ||
It's strongly recommended to pass `{extraEntropy: true}` to improve security of signatures: | ||
- In case the entropy generator is broken, signatures would be just like they are without the option | ||
- It would help a lot in case there is an error somewhere in `k` generation. Exposing `k` could leak private keys | ||
- Schnorr signatures are adding extra entropy every time | ||
- The only disadvantage to this is the fact signatures won't be exactly equal to fully-deterministic sigs; | ||
think backwards-compatibility with test vectors. They would still be valid, though | ||
`sign` arguments: | ||
- `msgHash: Uint8Array | string` - message hash which would be signed | ||
@@ -130,3 +145,3 @@ - `privateKey: Uint8Array | string | bigint` - private key which will sign the hash | ||
`false` makes signatures compatible with openssl | ||
- `options?.extraEntropy: Uint8Array | string` - additional entropy `k'` for deterministic signature, follows section 3.6 of RFC6979. [Could be reused](https://crypto.stackexchange.com/questions/97911/reusing-additional-data-k-nonce-from-rfc6979-ecdsa). | ||
- `options?.extraEntropy: Uint8Array | string | true` - additional entropy `k'` for deterministic signature, follows section 3.6 of RFC6979. When `true`, it would automatically be filled with 32 bytes of cryptographically secure entropy | ||
- `options?.der: boolean = true` - whether the returned signature should be in DER format. If `false`, it would be in Compact format (32-byte r + 32-byte s) | ||
@@ -196,3 +211,3 @@ | ||
- `privateKey: Uint8Array | string | bigint` - private key which will sign the hash | ||
- `auxilaryRandom?: Uint8Array` — optional 32 random bytes. By default, the method gathers cryptogarphically secure random. | ||
- `auxilaryRandom?: Uint8Array` — optional 32 random bytes. By default, the method gathers cryptogarphically secure entropy | ||
- Returns Schnorr signature in Hex format. | ||
@@ -213,2 +228,8 @@ | ||
###### `utils.randomBytes(): Uint8Array` | ||
Returns `Uint8Array` of 32 cryptographically secure random bytes. | ||
Uses `crypto.web.getRandomValues` in browser, `require('crypto').randomBytes` in node.js. | ||
###### `utils.randomPrivateKey(): Uint8Array` | ||
@@ -300,3 +321,3 @@ | ||
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 rootkits with every `npm install`. Our goal is to minimize this attack vector. | ||
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. | ||
@@ -307,3 +328,3 @@ ## Speed | ||
getPublicKey(utils.randomPrivateKey()) x 6,121 ops/sec @ 163μs/op | ||
getPublicKey(utils.randomPrivateKey()) x 6,216 ops/sec @ 160μs/op | ||
sign x 4,789 ops/sec @ 208μs/op | ||
@@ -310,0 +331,0 @@ verify x 923 ops/sec @ 1ms/op |
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
94214
2194
365