@noble/secp256k1
Advanced tools
Comparing version 1.3.4 to 1.4.0
@@ -292,3 +292,3 @@ /*! noble-secp256k1 - MIT License (c) Paul Miller (paulmillr.com) */ | ||
if (recovery !== 0 && recovery !== 1) { | ||
throw new Error('Cannot recover signature: invalid yParity bit'); | ||
throw new Error('Cannot recover signature: invalid recovery bit'); | ||
} | ||
@@ -355,5 +355,33 @@ const prefix = 2 + (recovery & 1); | ||
Point.ZERO = new Point(_0n, _0n); | ||
function sliceDer(s) { | ||
function sliceDER(s) { | ||
return Number.parseInt(s[0], 16) >= 8 ? '00' + s : s; | ||
} | ||
function parseDERInt(data) { | ||
if (data.length < 2 || data[0] !== 0x02) { | ||
throw new Error(`Invalid signature integer tag: ${bytesToHex(data)}`); | ||
} | ||
const len = data[1]; | ||
const res = data.subarray(2, len + 2); | ||
if (!len || res.length !== len) { | ||
throw new Error(`Invalid signature integer: wrong length`); | ||
} | ||
if (res[0] === 0x00 && res[1] <= 0x7f) { | ||
throw new Error('Invalid signature integer: trailing length'); | ||
} | ||
return { data: bytesToNumber(res), left: data.subarray(len + 2) }; | ||
} | ||
function parseDERSignature(data) { | ||
if (data.length < 2 || data[0] != 0x30) { | ||
throw new Error(`Invalid signature tag: ${bytesToHex(data)}`); | ||
} | ||
if (data[1] !== data.length - 2) { | ||
throw new Error('Invalid signature: incorrect length'); | ||
} | ||
const { data: r, left: sBytes } = parseDERInt(data.subarray(2)); | ||
const { data: s, left: rBytesLeft } = parseDERInt(sBytes); | ||
if (rBytesLeft.length) { | ||
throw new Error(`Invalid signature: left bytes after parsing: ${bytesToHex(rBytesLeft)}`); | ||
} | ||
return { r, s }; | ||
} | ||
export class Signature { | ||
@@ -363,52 +391,20 @@ constructor(r, s) { | ||
this.s = s; | ||
this.assertValidity(); | ||
} | ||
static fromCompact(hex) { | ||
if (typeof hex !== 'string' && !(hex instanceof Uint8Array)) { | ||
throw new TypeError(`Signature.fromCompact: Expected string or Uint8Array`); | ||
} | ||
const str = hex instanceof Uint8Array ? bytesToHex(hex) : hex; | ||
const arr = hex instanceof Uint8Array; | ||
const name = 'Signature.fromCompact'; | ||
if (typeof hex !== 'string' && !arr) | ||
throw new TypeError(`${name}: Expected string or Uint8Array`); | ||
const str = arr ? bytesToHex(hex) : hex; | ||
if (str.length !== 128) | ||
throw new Error('Signature.fromCompact: Expected 64-byte hex'); | ||
const sig = new Signature(hexToNumber(str.slice(0, 64)), hexToNumber(str.slice(64, 128))); | ||
sig.assertValidity(); | ||
return sig; | ||
throw new Error(`${name}: Expected 64-byte hex`); | ||
return new Signature(hexToNumber(str.slice(0, 64)), hexToNumber(str.slice(64, 128))); | ||
} | ||
static fromDER(hex) { | ||
const fn = 'Signature.fromDER'; | ||
if (typeof hex !== 'string' && !(hex instanceof Uint8Array)) { | ||
throw new TypeError(`${fn}: Expected string or Uint8Array`); | ||
} | ||
const str = hex instanceof Uint8Array ? bytesToHex(hex) : hex; | ||
const length = parseByte(str.slice(2, 4)); | ||
if (str.slice(0, 2) !== '30' || length !== str.length - 4 || str.slice(4, 6) !== '02') { | ||
throw new Error(`${fn}: Invalid signature ${str}`); | ||
} | ||
const rLen = parseByte(str.slice(6, 8)); | ||
const rEnd = 8 + rLen; | ||
const rr = str.slice(8, rEnd); | ||
if (rr.startsWith('00') && parseByte(rr.slice(2, 4)) <= 0x7f) { | ||
throw new Error(`${fn}: Invalid r with trailing length`); | ||
} | ||
const r = hexToNumber(rr); | ||
const separator = str.slice(rEnd, rEnd + 2); | ||
if (separator !== '02') { | ||
throw new Error(`${fn}: Invalid r-s separator`); | ||
} | ||
const sLen = parseByte(str.slice(rEnd + 2, rEnd + 4)); | ||
const diff = length - sLen - rLen - 10; | ||
if (diff > 0 || diff === -4) { | ||
throw new Error(`${fn}: Invalid total length`); | ||
} | ||
if (sLen > length - rLen - 4) { | ||
throw new Error(`${fn}: Invalid s`); | ||
} | ||
const sStart = rEnd + 4; | ||
const ss = str.slice(sStart, sStart + sLen); | ||
if (ss.startsWith('00') && parseByte(ss.slice(2, 4)) <= 0x7f) { | ||
throw new Error(`${fn}: Invalid s with trailing length`); | ||
} | ||
const s = hexToNumber(ss); | ||
const sig = new Signature(r, s); | ||
sig.assertValidity(); | ||
return sig; | ||
const arr = hex instanceof Uint8Array; | ||
if (typeof hex !== 'string' && !arr) | ||
throw new TypeError(`Signature.fromDER: Expected string or Uint8Array`); | ||
const { r, s } = parseDERSignature(arr ? hex : hexToBytes(hex)); | ||
return new Signature(r, s); | ||
} | ||
@@ -425,2 +421,9 @@ static fromHex(hex) { | ||
} | ||
hasHighS() { | ||
const HALF = CURVE.n >> _1n; | ||
return this.s > HALF; | ||
} | ||
normalizeS() { | ||
return this.hasHighS() ? new Signature(this.r, CURVE.n - this.s) : this; | ||
} | ||
toDERRawBytes(isCompressed = false) { | ||
@@ -430,6 +433,6 @@ return hexToBytes(this.toDERHex(isCompressed)); | ||
toDERHex(isCompressed = false) { | ||
const sHex = sliceDer(numberToHex(this.s)); | ||
const sHex = sliceDER(numberToHex(this.s)); | ||
if (isCompressed) | ||
return sHex; | ||
const rHex = sliceDer(numberToHex(this.r)); | ||
const rHex = sliceDER(numberToHex(this.r)); | ||
const rLen = numberToHex(rHex.length / 2); | ||
@@ -453,3 +456,2 @@ const sLen = numberToHex(sHex.length / 2); | ||
} | ||
export const SignResult = Signature; | ||
function concatBytes(...arrays) { | ||
@@ -467,6 +469,7 @@ if (arrays.length === 1) | ||
} | ||
const hexes = Array.from({ length: 256 }, (v, i) => i.toString(16).padStart(2, '0')); | ||
function bytesToHex(uint8a) { | ||
let hex = ''; | ||
for (let i = 0; i < uint8a.length; i++) { | ||
hex += uint8a[i].toString(16).padStart(2, '0'); | ||
hex += hexes[uint8a[i]]; | ||
} | ||
@@ -476,2 +479,4 @@ return hex; | ||
function pad64(num) { | ||
if (num > POW_2_256) | ||
throw new Error('pad64: invalid number'); | ||
return num.toString(16).padStart(64, '0'); | ||
@@ -492,2 +497,10 @@ } | ||
} | ||
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) { | ||
@@ -502,3 +515,3 @@ if (typeof hex !== 'string') { | ||
const j = i * 2; | ||
array[i] = Number.parseInt(hex.slice(j, j + 2), 16); | ||
array[i] = parseHexByte(hex.slice(j, j + 2)); | ||
} | ||
@@ -513,5 +526,2 @@ return array; | ||
} | ||
function parseByte(str) { | ||
return Number.parseInt(str, 16) * 2; | ||
} | ||
function normalizeScalar(num) { | ||
@@ -635,3 +645,3 @@ if (typeof num === 'number' && num > 0 && Number.isSafeInteger(num)) | ||
} | ||
function _abc6979(msgHash, privateKey) { | ||
function _abc6979(msgHash, privateKey, extraEntropy) { | ||
if (msgHash == null) | ||
@@ -647,15 +657,22 @@ throw new Error(`sign: expected valid msgHash, not "${msgHash}"`); | ||
const b1 = Uint8Array.from([0x01]); | ||
return { h1, h1n, x, v, k, b0, b1 }; | ||
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); | ||
} | ||
return { xh1, h1n, v, k, b0, b1 }; | ||
} | ||
async function getQRSrfc6979(msgHash, privateKey) { | ||
async function getQRSrfc6979(msgHash, privateKey, extraEntropy) { | ||
const privKey = normalizePrivateKey(privateKey); | ||
let { h1, h1n, x, v, k, b0, b1 } = _abc6979(msgHash, privKey); | ||
let { xh1, h1n, v, k, b0, b1 } = _abc6979(msgHash, privKey, extraEntropy); | ||
const hmac = utils.hmacSha256; | ||
k = await hmac(k, v, b0, x, h1); | ||
k = await hmac(k, v, b0, xh1); | ||
v = await hmac(k, v); | ||
k = await hmac(k, v, b1, x, h1); | ||
k = await hmac(k, v, b1, xh1); | ||
v = await hmac(k, v); | ||
for (let i = 0; i < 1000; i++) { | ||
v = await hmac(k, v); | ||
let qrs = calcQRSFromK(v, h1n, privKey); | ||
const qrs = calcQRSFromK(v, h1n, privKey); | ||
if (qrs) | ||
@@ -668,17 +685,17 @@ return qrs; | ||
} | ||
function getQRSrfc6979Sync(msgHash, privateKey) { | ||
function getQRSrfc6979Sync(msgHash, privateKey, extraEntropy) { | ||
const privKey = normalizePrivateKey(privateKey); | ||
let { h1, h1n, x, v, k, b0, b1 } = _abc6979(msgHash, privKey); | ||
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, x, h1); | ||
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, x, h1); | ||
k = hmac(k, v, b1, xh1); | ||
v = hmac(k, v); | ||
for (let i = 0; i < 1000; i++) { | ||
v = hmac(k, v); | ||
let qrs = calcQRSFromK(v, h1n, privKey); | ||
const qrs = calcQRSFromK(v, h1n, privKey); | ||
if (qrs) | ||
@@ -704,3 +721,3 @@ return qrs; | ||
return; | ||
return [q, r, s]; | ||
return { q, r, s }; | ||
} | ||
@@ -746,16 +763,14 @@ function normalizePrivateKey(key) { | ||
} | ||
else { | ||
try { | ||
return Signature.fromDER(signature); | ||
} | ||
catch (error) { | ||
return Signature.fromCompact(signature); | ||
} | ||
} | ||
export function getPublicKey(privateKey, isCompressed = false) { | ||
const point = Point.fromPrivateKey(privateKey); | ||
if (typeof privateKey === 'string') { | ||
return point.toHex(isCompressed); | ||
} | ||
return point.toRawBytes(isCompressed); | ||
return Point.fromPrivateKey(privateKey).toRawBytes(isCompressed); | ||
} | ||
export function recoverPublicKey(msgHash, signature, recovery) { | ||
const point = Point.fromSignature(msgHash, signature, recovery); | ||
return typeof msgHash === 'string' ? point.toHex() : point.toRawBytes(); | ||
return Point.fromSignature(msgHash, signature, recovery).toRawBytes(); | ||
} | ||
@@ -781,32 +796,26 @@ function isPub(item) { | ||
b.assertValidity(); | ||
const shared = b.multiply(normalizePrivateKey(privateA)); | ||
return typeof privateA === 'string' | ||
? shared.toHex(isCompressed) | ||
: shared.toRawBytes(isCompressed); | ||
return b.multiply(normalizePrivateKey(privateA)).toRawBytes(isCompressed); | ||
} | ||
function QRSToSig(qrs, opts, str = false) { | ||
const [q, r, s] = qrs; | ||
let { canonical, der, recovered } = opts; | ||
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 adjustedS = s; | ||
const HIGH_NUMBER = CURVE.n >> _1n; | ||
if (s > HIGH_NUMBER && canonical) { | ||
adjustedS = CURVE.n - s; | ||
let sig = new Signature(r, s); | ||
if (canonical && sig.hasHighS()) { | ||
sig = sig.normalizeS(); | ||
recovery ^= 1; | ||
} | ||
const sig = new Signature(r, adjustedS); | ||
sig.assertValidity(); | ||
const hex = der === false ? sig.toCompactHex() : sig.toDERHex(); | ||
const hashed = str ? hex : hexToBytes(hex); | ||
const hashed = der ? sig.toDERRawBytes() : sig.toCompactRawBytes(); | ||
return recovered ? [hashed, recovery] : hashed; | ||
} | ||
async function sign(msgHash, privKey, opts = {}) { | ||
return QRSToSig(await getQRSrfc6979(msgHash, privKey), opts, typeof msgHash === 'string'); | ||
return QRSToSig(await getQRSrfc6979(msgHash, privKey, opts.extraEntropy), opts); | ||
} | ||
function signSync(msgHash, privKey, opts = {}) { | ||
return QRSToSig(getQRSrfc6979Sync(msgHash, privKey), opts, typeof msgHash === 'string'); | ||
return QRSToSig(getQRSrfc6979Sync(msgHash, privKey, opts.extraEntropy), opts); | ||
} | ||
export { sign, signSync }; | ||
export function verify(signature, msgHash, publicKey) { | ||
const { n } = CURVE; | ||
const vopts = { strict: true }; | ||
export function verify(signature, msgHash, publicKey, opts = vopts) { | ||
let sig; | ||
@@ -820,6 +829,15 @@ try { | ||
const { r, s } = sig; | ||
if (opts.strict && sig.hasHighS()) | ||
return false; | ||
const h = truncateHash(msgHash); | ||
if (h === _0n) | ||
return false; | ||
const pubKey = JacobianPoint.fromAffine(normalizePublicKey(publicKey)); | ||
let pubKey; | ||
try { | ||
pubKey = JacobianPoint.fromAffine(normalizePublicKey(publicKey)); | ||
} | ||
catch (error) { | ||
return false; | ||
} | ||
const { n } = CURVE; | ||
const s1 = invert(s, n); | ||
@@ -872,4 +890,3 @@ const u1 = mod(h * s1, n); | ||
function schnorrGetPublicKey(privateKey) { | ||
const P = Point.fromPrivateKey(privateKey); | ||
return typeof privateKey === 'string' ? P.toHexX() : P.toRawX(); | ||
return Point.fromPrivateKey(privateKey).toRawX(); | ||
} | ||
@@ -902,3 +919,3 @@ async function schnorrSign(msgHash, privateKey, auxRand = utils.randomBytes()) { | ||
throw new Error('sign: Invalid signature produced'); | ||
return typeof msgHash === 'string' ? sig.toHex() : sig.toRawBytes(); | ||
return sig.toRawBytes(); | ||
} | ||
@@ -960,2 +977,3 @@ async function schnorrVerify(signature, msgHash, publicKey) { | ||
}, | ||
bytesToHex, | ||
sha256: async (message) => { | ||
@@ -984,3 +1002,3 @@ if (crypto.web) { | ||
const hash = createHmac('sha256', key); | ||
for (let message of messages) { | ||
for (const message of messages) { | ||
hash.update(message); | ||
@@ -987,0 +1005,0 @@ } |
@@ -18,4 +18,4 @@ /*! noble-secp256k1 - MIT License (c) Paul Miller (paulmillr.com) */ | ||
export declare class Point { | ||
x: bigint; | ||
y: bigint; | ||
readonly x: bigint; | ||
readonly y: bigint; | ||
static BASE: Point; | ||
@@ -44,4 +44,4 @@ static ZERO: Point; | ||
export declare class Signature { | ||
r: bigint; | ||
s: bigint; | ||
readonly r: bigint; | ||
readonly s: bigint; | ||
constructor(r: bigint, s: bigint); | ||
@@ -52,2 +52,4 @@ static fromCompact(hex: Hex): Signature; | ||
assertValidity(): void; | ||
hasHighS(): boolean; | ||
normalizeS(): Signature; | ||
toDERRawBytes(isCompressed?: boolean): Uint8Array; | ||
@@ -60,31 +62,28 @@ toDERHex(isCompressed?: boolean): string; | ||
} | ||
export declare const SignResult: typeof Signature; | ||
declare function bytesToHex(uint8a: Uint8Array): string; | ||
declare type U8A = Uint8Array; | ||
export declare function getPublicKey(privateKey: Uint8Array | number | bigint, isCompressed?: boolean): Uint8Array; | ||
export declare function getPublicKey(privateKey: string, isCompressed?: boolean): string; | ||
export declare function recoverPublicKey(msgHash: string, signature: string, recovery: number): string | undefined; | ||
export declare function recoverPublicKey(msgHash: Uint8Array, signature: Uint8Array, recovery: number): Uint8Array | undefined; | ||
export declare function getSharedSecret(privateA: PrivKey, publicB: PubKey, isCompressed?: boolean): Hex; | ||
export declare function getPublicKey(privateKey: PrivKey, isCompressed?: boolean): Uint8Array; | ||
export declare function recoverPublicKey(msgHash: Hex, signature: Sig, recovery: number): Uint8Array | undefined; | ||
export declare function getSharedSecret(privateA: PrivKey, publicB: PubKey, isCompressed?: boolean): Uint8Array; | ||
declare type OptsRecov = { | ||
recovered: true; | ||
canonical?: true; | ||
canonical?: boolean; | ||
der?: boolean; | ||
extraEntropy?: Hex; | ||
}; | ||
declare type OptsNoRecov = { | ||
recovered?: false; | ||
canonical?: true; | ||
canonical?: boolean; | ||
der?: boolean; | ||
extraEntropy?: Hex; | ||
}; | ||
declare function sign(msgHash: U8A, privKey: PrivKey, opts: OptsRecov): Promise<[U8A, number]>; | ||
declare function sign(msgHash: string, privKey: PrivKey, opts: OptsRecov): Promise<[string, number]>; | ||
declare function sign(msgHash: U8A, privKey: PrivKey, opts?: OptsNoRecov): Promise<U8A>; | ||
declare function sign(msgHash: string, privKey: PrivKey, opts?: OptsNoRecov): Promise<string>; | ||
declare function sign(msgHash: string, privKey: PrivKey, opts?: OptsNoRecov): Promise<string>; | ||
declare function signSync(msgHash: U8A, privKey: PrivKey, opts: OptsRecov): [U8A, number]; | ||
declare function signSync(msgHash: string, privKey: PrivKey, opts: OptsRecov): [string, number]; | ||
declare function signSync(msgHash: U8A, privKey: PrivKey, opts?: OptsNoRecov): U8A; | ||
declare function signSync(msgHash: string, privKey: PrivKey, opts?: OptsNoRecov): string; | ||
declare function signSync(msgHash: string, privKey: PrivKey, opts?: OptsNoRecov): string; | ||
declare function sign(msgHash: Hex, privKey: PrivKey, opts: OptsRecov): Promise<[U8A, number]>; | ||
declare function sign(msgHash: Hex, privKey: PrivKey, opts?: OptsNoRecov): Promise<U8A>; | ||
declare function signSync(msgHash: Hex, privKey: PrivKey, opts: OptsRecov): [U8A, number]; | ||
declare function signSync(msgHash: Hex, privKey: PrivKey, opts?: OptsNoRecov): U8A; | ||
export { sign, signSync }; | ||
export declare function verify(signature: Sig, msgHash: Hex, publicKey: PubKey): boolean; | ||
declare type VOpts = { | ||
strict?: boolean; | ||
}; | ||
export declare function verify(signature: Sig, msgHash: Hex, publicKey: PubKey, opts?: VOpts): boolean; | ||
declare class SchnorrSignature { | ||
@@ -98,6 +97,4 @@ readonly r: bigint; | ||
} | ||
declare function schnorrGetPublicKey(privateKey: Uint8Array): Uint8Array; | ||
declare function schnorrGetPublicKey(privateKey: string): string; | ||
declare function schnorrSign(msgHash: string, privateKey: string, auxRand?: Hex): Promise<string>; | ||
declare function schnorrSign(msgHash: Uint8Array, privateKey: Uint8Array, auxRand?: Hex): Promise<Uint8Array>; | ||
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>; | ||
@@ -116,2 +113,3 @@ export declare const schnorr: { | ||
randomPrivateKey: () => Uint8Array; | ||
bytesToHex: typeof bytesToHex; | ||
sha256: (message: Uint8Array) => Promise<Uint8Array>; | ||
@@ -118,0 +116,0 @@ hmacSha256: (key: Uint8Array, ...messages: Uint8Array[]) => Promise<Uint8Array>; |
220
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.SignResult = exports.Signature = exports.Point = 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")); | ||
@@ -299,3 +299,3 @@ const _0n = BigInt(0); | ||
if (recovery !== 0 && recovery !== 1) { | ||
throw new Error('Cannot recover signature: invalid yParity bit'); | ||
throw new Error('Cannot recover signature: invalid recovery bit'); | ||
} | ||
@@ -363,5 +363,33 @@ const prefix = 2 + (recovery & 1); | ||
Point.ZERO = new Point(_0n, _0n); | ||
function sliceDer(s) { | ||
function sliceDER(s) { | ||
return Number.parseInt(s[0], 16) >= 8 ? '00' + s : s; | ||
} | ||
function parseDERInt(data) { | ||
if (data.length < 2 || data[0] !== 0x02) { | ||
throw new Error(`Invalid signature integer tag: ${bytesToHex(data)}`); | ||
} | ||
const len = data[1]; | ||
const res = data.subarray(2, len + 2); | ||
if (!len || res.length !== len) { | ||
throw new Error(`Invalid signature integer: wrong length`); | ||
} | ||
if (res[0] === 0x00 && res[1] <= 0x7f) { | ||
throw new Error('Invalid signature integer: trailing length'); | ||
} | ||
return { data: bytesToNumber(res), left: data.subarray(len + 2) }; | ||
} | ||
function parseDERSignature(data) { | ||
if (data.length < 2 || data[0] != 0x30) { | ||
throw new Error(`Invalid signature tag: ${bytesToHex(data)}`); | ||
} | ||
if (data[1] !== data.length - 2) { | ||
throw new Error('Invalid signature: incorrect length'); | ||
} | ||
const { data: r, left: sBytes } = parseDERInt(data.subarray(2)); | ||
const { data: s, left: rBytesLeft } = parseDERInt(sBytes); | ||
if (rBytesLeft.length) { | ||
throw new Error(`Invalid signature: left bytes after parsing: ${bytesToHex(rBytesLeft)}`); | ||
} | ||
return { r, s }; | ||
} | ||
class Signature { | ||
@@ -371,52 +399,20 @@ constructor(r, s) { | ||
this.s = s; | ||
this.assertValidity(); | ||
} | ||
static fromCompact(hex) { | ||
if (typeof hex !== 'string' && !(hex instanceof Uint8Array)) { | ||
throw new TypeError(`Signature.fromCompact: Expected string or Uint8Array`); | ||
} | ||
const str = hex instanceof Uint8Array ? bytesToHex(hex) : hex; | ||
const arr = hex instanceof Uint8Array; | ||
const name = 'Signature.fromCompact'; | ||
if (typeof hex !== 'string' && !arr) | ||
throw new TypeError(`${name}: Expected string or Uint8Array`); | ||
const str = arr ? bytesToHex(hex) : hex; | ||
if (str.length !== 128) | ||
throw new Error('Signature.fromCompact: Expected 64-byte hex'); | ||
const sig = new Signature(hexToNumber(str.slice(0, 64)), hexToNumber(str.slice(64, 128))); | ||
sig.assertValidity(); | ||
return sig; | ||
throw new Error(`${name}: Expected 64-byte hex`); | ||
return new Signature(hexToNumber(str.slice(0, 64)), hexToNumber(str.slice(64, 128))); | ||
} | ||
static fromDER(hex) { | ||
const fn = 'Signature.fromDER'; | ||
if (typeof hex !== 'string' && !(hex instanceof Uint8Array)) { | ||
throw new TypeError(`${fn}: Expected string or Uint8Array`); | ||
} | ||
const str = hex instanceof Uint8Array ? bytesToHex(hex) : hex; | ||
const length = parseByte(str.slice(2, 4)); | ||
if (str.slice(0, 2) !== '30' || length !== str.length - 4 || str.slice(4, 6) !== '02') { | ||
throw new Error(`${fn}: Invalid signature ${str}`); | ||
} | ||
const rLen = parseByte(str.slice(6, 8)); | ||
const rEnd = 8 + rLen; | ||
const rr = str.slice(8, rEnd); | ||
if (rr.startsWith('00') && parseByte(rr.slice(2, 4)) <= 0x7f) { | ||
throw new Error(`${fn}: Invalid r with trailing length`); | ||
} | ||
const r = hexToNumber(rr); | ||
const separator = str.slice(rEnd, rEnd + 2); | ||
if (separator !== '02') { | ||
throw new Error(`${fn}: Invalid r-s separator`); | ||
} | ||
const sLen = parseByte(str.slice(rEnd + 2, rEnd + 4)); | ||
const diff = length - sLen - rLen - 10; | ||
if (diff > 0 || diff === -4) { | ||
throw new Error(`${fn}: Invalid total length`); | ||
} | ||
if (sLen > length - rLen - 4) { | ||
throw new Error(`${fn}: Invalid s`); | ||
} | ||
const sStart = rEnd + 4; | ||
const ss = str.slice(sStart, sStart + sLen); | ||
if (ss.startsWith('00') && parseByte(ss.slice(2, 4)) <= 0x7f) { | ||
throw new Error(`${fn}: Invalid s with trailing length`); | ||
} | ||
const s = hexToNumber(ss); | ||
const sig = new Signature(r, s); | ||
sig.assertValidity(); | ||
return sig; | ||
const arr = hex instanceof Uint8Array; | ||
if (typeof hex !== 'string' && !arr) | ||
throw new TypeError(`Signature.fromDER: Expected string or Uint8Array`); | ||
const { r, s } = parseDERSignature(arr ? hex : hexToBytes(hex)); | ||
return new Signature(r, s); | ||
} | ||
@@ -433,2 +429,9 @@ static fromHex(hex) { | ||
} | ||
hasHighS() { | ||
const HALF = CURVE.n >> _1n; | ||
return this.s > HALF; | ||
} | ||
normalizeS() { | ||
return this.hasHighS() ? new Signature(this.r, CURVE.n - this.s) : this; | ||
} | ||
toDERRawBytes(isCompressed = false) { | ||
@@ -438,6 +441,6 @@ return hexToBytes(this.toDERHex(isCompressed)); | ||
toDERHex(isCompressed = false) { | ||
const sHex = sliceDer(numberToHex(this.s)); | ||
const sHex = sliceDER(numberToHex(this.s)); | ||
if (isCompressed) | ||
return sHex; | ||
const rHex = sliceDer(numberToHex(this.r)); | ||
const rHex = sliceDER(numberToHex(this.r)); | ||
const rLen = numberToHex(rHex.length / 2); | ||
@@ -462,3 +465,2 @@ const sLen = numberToHex(sHex.length / 2); | ||
exports.Signature = Signature; | ||
exports.SignResult = Signature; | ||
function concatBytes(...arrays) { | ||
@@ -476,6 +478,7 @@ if (arrays.length === 1) | ||
} | ||
const hexes = Array.from({ length: 256 }, (v, i) => i.toString(16).padStart(2, '0')); | ||
function bytesToHex(uint8a) { | ||
let hex = ''; | ||
for (let i = 0; i < uint8a.length; i++) { | ||
hex += uint8a[i].toString(16).padStart(2, '0'); | ||
hex += hexes[uint8a[i]]; | ||
} | ||
@@ -485,2 +488,4 @@ return hex; | ||
function pad64(num) { | ||
if (num > POW_2_256) | ||
throw new Error('pad64: invalid number'); | ||
return num.toString(16).padStart(64, '0'); | ||
@@ -501,2 +506,10 @@ } | ||
} | ||
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) { | ||
@@ -511,3 +524,3 @@ if (typeof hex !== 'string') { | ||
const j = i * 2; | ||
array[i] = Number.parseInt(hex.slice(j, j + 2), 16); | ||
array[i] = parseHexByte(hex.slice(j, j + 2)); | ||
} | ||
@@ -522,5 +535,2 @@ return array; | ||
} | ||
function parseByte(str) { | ||
return Number.parseInt(str, 16) * 2; | ||
} | ||
function normalizeScalar(num) { | ||
@@ -644,3 +654,3 @@ if (typeof num === 'number' && num > 0 && Number.isSafeInteger(num)) | ||
} | ||
function _abc6979(msgHash, privateKey) { | ||
function _abc6979(msgHash, privateKey, extraEntropy) { | ||
if (msgHash == null) | ||
@@ -656,15 +666,22 @@ throw new Error(`sign: expected valid msgHash, not "${msgHash}"`); | ||
const b1 = Uint8Array.from([0x01]); | ||
return { h1, h1n, x, v, k, b0, b1 }; | ||
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); | ||
} | ||
return { xh1, h1n, v, k, b0, b1 }; | ||
} | ||
async function getQRSrfc6979(msgHash, privateKey) { | ||
async function getQRSrfc6979(msgHash, privateKey, extraEntropy) { | ||
const privKey = normalizePrivateKey(privateKey); | ||
let { h1, h1n, x, v, k, b0, b1 } = _abc6979(msgHash, privKey); | ||
let { xh1, h1n, v, k, b0, b1 } = _abc6979(msgHash, privKey, extraEntropy); | ||
const hmac = exports.utils.hmacSha256; | ||
k = await hmac(k, v, b0, x, h1); | ||
k = await hmac(k, v, b0, xh1); | ||
v = await hmac(k, v); | ||
k = await hmac(k, v, b1, x, h1); | ||
k = await hmac(k, v, b1, xh1); | ||
v = await hmac(k, v); | ||
for (let i = 0; i < 1000; i++) { | ||
v = await hmac(k, v); | ||
let qrs = calcQRSFromK(v, h1n, privKey); | ||
const qrs = calcQRSFromK(v, h1n, privKey); | ||
if (qrs) | ||
@@ -677,17 +694,17 @@ return qrs; | ||
} | ||
function getQRSrfc6979Sync(msgHash, privateKey) { | ||
function getQRSrfc6979Sync(msgHash, privateKey, extraEntropy) { | ||
const privKey = normalizePrivateKey(privateKey); | ||
let { h1, h1n, x, v, k, b0, b1 } = _abc6979(msgHash, privKey); | ||
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, x, h1); | ||
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, x, h1); | ||
k = hmac(k, v, b1, xh1); | ||
v = hmac(k, v); | ||
for (let i = 0; i < 1000; i++) { | ||
v = hmac(k, v); | ||
let qrs = calcQRSFromK(v, h1n, privKey); | ||
const qrs = calcQRSFromK(v, h1n, privKey); | ||
if (qrs) | ||
@@ -713,3 +730,3 @@ return qrs; | ||
return; | ||
return [q, r, s]; | ||
return { q, r, s }; | ||
} | ||
@@ -755,17 +772,15 @@ function normalizePrivateKey(key) { | ||
} | ||
else { | ||
try { | ||
return Signature.fromDER(signature); | ||
} | ||
catch (error) { | ||
return Signature.fromCompact(signature); | ||
} | ||
} | ||
function getPublicKey(privateKey, isCompressed = false) { | ||
const point = Point.fromPrivateKey(privateKey); | ||
if (typeof privateKey === 'string') { | ||
return point.toHex(isCompressed); | ||
} | ||
return point.toRawBytes(isCompressed); | ||
return Point.fromPrivateKey(privateKey).toRawBytes(isCompressed); | ||
} | ||
exports.getPublicKey = getPublicKey; | ||
function recoverPublicKey(msgHash, signature, recovery) { | ||
const point = Point.fromSignature(msgHash, signature, recovery); | ||
return typeof msgHash === 'string' ? point.toHex() : point.toRawBytes(); | ||
return Point.fromSignature(msgHash, signature, recovery).toRawBytes(); | ||
} | ||
@@ -792,34 +807,28 @@ exports.recoverPublicKey = recoverPublicKey; | ||
b.assertValidity(); | ||
const shared = b.multiply(normalizePrivateKey(privateA)); | ||
return typeof privateA === 'string' | ||
? shared.toHex(isCompressed) | ||
: shared.toRawBytes(isCompressed); | ||
return b.multiply(normalizePrivateKey(privateA)).toRawBytes(isCompressed); | ||
} | ||
exports.getSharedSecret = getSharedSecret; | ||
function QRSToSig(qrs, opts, str = false) { | ||
const [q, r, s] = qrs; | ||
let { canonical, der, recovered } = opts; | ||
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 adjustedS = s; | ||
const HIGH_NUMBER = CURVE.n >> _1n; | ||
if (s > HIGH_NUMBER && canonical) { | ||
adjustedS = CURVE.n - s; | ||
let sig = new Signature(r, s); | ||
if (canonical && sig.hasHighS()) { | ||
sig = sig.normalizeS(); | ||
recovery ^= 1; | ||
} | ||
const sig = new Signature(r, adjustedS); | ||
sig.assertValidity(); | ||
const hex = der === false ? sig.toCompactHex() : sig.toDERHex(); | ||
const hashed = str ? hex : hexToBytes(hex); | ||
const hashed = der ? sig.toDERRawBytes() : sig.toCompactRawBytes(); | ||
return recovered ? [hashed, recovery] : hashed; | ||
} | ||
async function sign(msgHash, privKey, opts = {}) { | ||
return QRSToSig(await getQRSrfc6979(msgHash, privKey), opts, typeof msgHash === 'string'); | ||
return QRSToSig(await getQRSrfc6979(msgHash, privKey, opts.extraEntropy), opts); | ||
} | ||
exports.sign = sign; | ||
function signSync(msgHash, privKey, opts = {}) { | ||
return QRSToSig(getQRSrfc6979Sync(msgHash, privKey), opts, typeof msgHash === 'string'); | ||
return QRSToSig(getQRSrfc6979Sync(msgHash, privKey, opts.extraEntropy), opts); | ||
} | ||
exports.signSync = signSync; | ||
function verify(signature, msgHash, publicKey) { | ||
const { n } = CURVE; | ||
const vopts = { strict: true }; | ||
function verify(signature, msgHash, publicKey, opts = vopts) { | ||
let sig; | ||
@@ -833,6 +842,15 @@ try { | ||
const { r, s } = sig; | ||
if (opts.strict && sig.hasHighS()) | ||
return false; | ||
const h = truncateHash(msgHash); | ||
if (h === _0n) | ||
return false; | ||
const pubKey = JacobianPoint.fromAffine(normalizePublicKey(publicKey)); | ||
let pubKey; | ||
try { | ||
pubKey = JacobianPoint.fromAffine(normalizePublicKey(publicKey)); | ||
} | ||
catch (error) { | ||
return false; | ||
} | ||
const { n } = CURVE; | ||
const s1 = invert(s, n); | ||
@@ -886,4 +904,3 @@ const u1 = mod(h * s1, n); | ||
function schnorrGetPublicKey(privateKey) { | ||
const P = Point.fromPrivateKey(privateKey); | ||
return typeof privateKey === 'string' ? P.toHexX() : P.toRawX(); | ||
return Point.fromPrivateKey(privateKey).toRawX(); | ||
} | ||
@@ -916,3 +933,3 @@ async function schnorrSign(msgHash, privateKey, auxRand = exports.utils.randomBytes()) { | ||
throw new Error('sign: Invalid signature produced'); | ||
return typeof msgHash === 'string' ? sig.toHex() : sig.toRawBytes(); | ||
return sig.toRawBytes(); | ||
} | ||
@@ -974,2 +991,3 @@ async function schnorrVerify(signature, msgHash, publicKey) { | ||
}, | ||
bytesToHex, | ||
sha256: async (message) => { | ||
@@ -998,3 +1016,3 @@ if (crypto.web) { | ||
const hash = createHmac('sha256', key); | ||
for (let message of messages) { | ||
for (const message of messages) { | ||
hash.update(message); | ||
@@ -1001,0 +1019,0 @@ } |
{ | ||
"name": "@noble/secp256k1", | ||
"version": "1.3.4", | ||
"version": "1.4.0", | ||
"description": "Fastest JS implementation of secp256k1. Independently audited, high-security, 0-dependency ECDSA & Schnorr signatures", | ||
@@ -19,10 +19,4 @@ "files": [ | ||
}, | ||
"jest": { | ||
"testRegex": "/test/.*?\\.ts", | ||
"transform": { | ||
"^.+\\.ts$": "ts-jest" | ||
} | ||
}, | ||
"author": "Paul Miller (https://paulmillr.com)", | ||
"homepage": "https://paulmillr.com/posts/noble-secp256k1-fast-ecc/", | ||
"homepage": "https://paulmillr.com/noble/", | ||
"repository": { | ||
@@ -37,13 +31,14 @@ "type": "git", | ||
"devDependencies": { | ||
"@noble/hashes": "^0.5.6", | ||
"@rollup/plugin-commonjs": "^21.0.0", | ||
"@rollup/plugin-node-resolve": "^13.0.0", | ||
"@types/jest": "^27", | ||
"@types/node": "^16.3.3", | ||
"@types/node": "^16", | ||
"fast-check": "^2.14.0", | ||
"jest": "^27", | ||
"jest": "^27.4.5", | ||
"micro-bmark": "^0.1.3", | ||
"noble-hashes": "^0.2.0", | ||
"prettier": "^2.2.1", | ||
"rollup": "^2.53.2", | ||
"ts-jest": "^27", | ||
"typescript": "~4.3.5" | ||
"prettier": "^2.5.1", | ||
"rollup": "^2.62.0", | ||
"ts-jest": "^27.1.2", | ||
"typescript": "4.5.4" | ||
}, | ||
@@ -76,3 +71,9 @@ "keywords": [ | ||
"./index.d.ts": "./lib/index.d.ts" | ||
}, | ||
"jest": { | ||
"testRegex": "/test/.*?\\.ts", | ||
"transform": { | ||
"^.+\\.ts$": "ts-jest" | ||
} | ||
} | ||
} |
102
README.md
@@ -17,3 +17,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) | ||
- All releases are signed with PGP keys | ||
- Check out all libraries: | ||
- Check out [homepage](https://paulmillr.com/noble/) & all libraries: | ||
[secp256k1](https://github.com/paulmillr/noble-secp256k1), | ||
@@ -45,4 +45,4 @@ [ed25519](https://github.com/paulmillr/noble-ed25519), | ||
// Canonical signatures | ||
const signatureC = await secp.sign(messageHash, privateKey, { canonical: true }); | ||
// Signatures compatible with openssl | ||
const signatureS = await secp.sign(messageHash, privateKey, { canonical: false }); | ||
@@ -56,9 +56,24 @@ // Supports Schnorr signatures | ||
Deno: | ||
To use the module with [Deno](https://deno.land), | ||
you will need [import map](https://deno.land/manual/linking_to_external_code/import_maps): | ||
```typescript | ||
import * as secp from "https://deno.land/x/secp256k1/mod.ts"; | ||
const publicKey = secp.getPublicKey("6b911fd37cdf5c81d4c0adb1ab7fa822ed253ab0ad9aa18d77257c88b29b718e"); | ||
``` | ||
- `deno run --import-map=imports.json app.ts` | ||
- `app.ts` | ||
```typescript | ||
import * as secp from "https://deno.land/x/secp256k1/mod.ts"; | ||
const publicKey = secp.getPublicKey(secp.utils.randomPrivateKey()); | ||
console.log(publicKey); | ||
``` | ||
- `imports.json` | ||
```json | ||
{ | ||
"imports": { | ||
"crypto": "https://deno.land/std@0.119.0/node/crypto.ts" | ||
} | ||
} | ||
``` | ||
## API | ||
@@ -68,4 +83,4 @@ | ||
- [`getSharedSecret(privateKeyA, publicKeyB)`](#getsharedsecretprivatekeya-publickeyb) | ||
- [`sign(hash, privateKey)`](#signhash-privatekey) | ||
- [`verify(signature, hash, publicKey)`](#verifysignature-hash-publickey) | ||
- [`sign(msgHash, privateKey)`](#signmsghash-privatekey) | ||
- [`verify(signature, msgHash, publicKey)`](#verifysignature-msghash-publickey) | ||
- [`recoverPublicKey(hash, signature, recovery)`](#recoverpublickeyhash-signature-recovery) | ||
@@ -79,5 +94,3 @@ - [`schnorr.getPublicKey(privateKey)`](#schnorrgetpublickeyprivatekey) | ||
```typescript | ||
function getPublicKey(privateKey: Uint8Array, isCompressed?: false): Uint8Array; | ||
function getPublicKey(privateKey: string, isCompressed?: false): string; | ||
function getPublicKey(privateKey: bigint): Uint8Array; | ||
function getPublicKey(privateKey: Uint8Array | string | bigint, isCompressed = false): Uint8Array; | ||
``` | ||
@@ -93,5 +106,3 @@ `privateKey` will be used to generate public key. | ||
```typescript | ||
function getSharedSecret(privateKeyA: Uint8Array, publicKeyB: Uint8Array): Uint8Array; | ||
function getSharedSecret(privateKeyA: string, publicKeyB: string): string; | ||
function getSharedSecret(privateKeyA: bigint, publicKeyB: Point): Uint8Array; | ||
function getSharedSecret(privateKeyA: Uint8Array | string | bigint, publicKeyB: Uint8Array | string | Point): Uint8Array; | ||
``` | ||
@@ -107,10 +118,9 @@ | ||
##### `sign(hash, privateKey)` | ||
##### `sign(msgHash, privateKey)` | ||
```typescript | ||
function sign(msgHash: Uint8Array, privateKey: Uint8Array, opts?: Options): Promise<Uint8Array>; | ||
function sign(msgHash: string, privateKey: string, opts?: Options): Promise<string>; | ||
function sign(msgHash: Uint8Array, privateKey: Uint8Array, opts?: Options): Promise<[Uint8Array | string, number]>; | ||
function sign(msgHash: Uint8Array | string, privateKey: Uint8Array | string, opts?: Options): Promise<Uint8Array>; | ||
function sign(msgHash: Uint8Array | string, privateKey: Uint8Array | string, opts?: Options): Promise<[Uint8Array, number]>; | ||
``` | ||
Generates deterministic ECDSA signature as per RFC6979. | ||
Generates low-s deterministic ECDSA signature as per RFC6979. | ||
@@ -121,3 +131,6 @@ - `msgHash: Uint8Array | string` - message hash which would be signed | ||
- `options?.recovered: boolean = false` - whether the recovered bit should be included in the result. In this case, the result would be an array of two items. | ||
- `options?.canonical: boolean = false` - whether a signature `s` should be no more than 1/2 prime order | ||
- `options?.canonical: boolean = true` - whether a signature `s` should be no more than 1/2 prime order. | ||
`true` makes signatures compatible with libsecp256k1, | ||
`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?.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) | ||
@@ -130,4 +143,4 @@ | ||
```ts | ||
const { hmac } = require('noble-hashes/lib/hmac'); | ||
const { sha256 } = require('noble-hashes/lib/sha256'); | ||
const { hmac } = require('@noble/hashes/hmac'); | ||
const { sha256 } = require('@noble/hashes/sha256'); | ||
secp256k1.utils.hmacSha256Sync = (key: Uint8Array, ...msgs: Uint8Array[]) => { | ||
@@ -143,6 +156,6 @@ const h = hmac.create(sha256, key); | ||
##### `verify(signature, hash, publicKey)` | ||
##### `verify(signature, msgHash, publicKey)` | ||
```typescript | ||
function verify(signature: Uint8Array, msgHash: Uint8Array, publicKey: Uint8Array): boolean | ||
function verify(signature: string, msgHash: string, publicKey: string): boolean | ||
function verify(signature: Uint8Array | string, msgHash: Uint8Array | string, publicKey: Uint8Array | string): boolean | ||
function verify(signature: Signature, msgHash: Uint8Array | string, publicKey: Point): boolean | ||
``` | ||
@@ -152,2 +165,6 @@ - `signature: Uint8Array | string | { r: bigint, s: bigint }` - object returned by the `sign` function | ||
- `publicKey: Uint8Array | string | Point` - e.g. that was generated from `privateKey` by `getPublicKey` | ||
- `options?: Options` - *optional* object related to signature value and format | ||
- `options?.strict: boolean = true` - whether a signature `s` should be no more than 1/2 prime order. | ||
`true` makes signatures compatible with libsecp256k1, | ||
`false` makes signatures compatible with openssl | ||
- Returns `boolean`: `true` if `signature == hash`; otherwise `false` | ||
@@ -157,4 +174,3 @@ | ||
```typescript | ||
function recoverPublicKey(msgHash: Uint8Array, signature: Uint8Array, recovery: number): Uint8Array | undefined; | ||
function recoverPublicKey(msgHash: string, signature: string, recovery: number): string | undefined; | ||
function recoverPublicKey(msgHash: Uint8Array | string, signature: Uint8Array | string, recovery: number): Uint8Array | undefined; | ||
``` | ||
@@ -172,4 +188,3 @@ - `msgHash: Uint8Array | string` - message hash which would be signed | ||
```typescript | ||
function schnorrGetPublicKey(privateKey: Uint8Array): Uint8Array; | ||
function schnorrGetPublicKey(privateKey: string): string; | ||
function schnorrGetPublicKey(privateKey: Uint8Array | string): Uint8Array; | ||
``` | ||
@@ -179,8 +194,7 @@ | ||
Specifically, its *y* coordinate may be flipped. See BIP0340 for clarification. | ||
Specifically, its *y* coordinate may be flipped. See BIP340 for clarification. | ||
##### `schnorr.sign(hash, privateKey)` | ||
```typescript | ||
function schnorrSign(msgHash: Uint8Array, privateKey: Uint8Array, auxilaryRandom?: Uint8Array): Promise<Uint8Array>; | ||
function schnorrSign(msgHash: string, privateKey: string, auxilaryRandom?: string): Promise<string>; | ||
function schnorrSign(msgHash: Uint8Array | string, privateKey: Uint8Array | string, auxilaryRandom?: Uint8Array): Promise<Uint8Array>; | ||
``` | ||
@@ -216,6 +230,14 @@ | ||
###### `utils.hmacSha256Sync` | ||
###### `utils.bytesToHex(bytes: Uint8Array): string` | ||
The function is not defined by default, but could be used to implement `signSync` method (see above). | ||
Converts a byte array to hex string. | ||
###### `utils.sha256` and `utils.hmacSha256` | ||
Asynchronous methods that calculate `SHA256` and `HMAC-SHA256`. Use browser built-ins by default. | ||
###### `utils.sha256Sync` and `utils.hmacSha256Sync` | ||
The functions are not defined by default, but could be used to implement `signSync` method (see above). | ||
###### `utils.precompute(W = 8, point = BASE_POINT): Point` | ||
@@ -269,2 +291,4 @@ | ||
static fromCompact(hex: Uint8Array | string); | ||
assertValidity(): void; | ||
hasHighS(): boolean; // high-S sigs cannot be produced using { canonical: true } | ||
toDERRawBytes(): Uint8Array; | ||
@@ -290,9 +314,9 @@ toDERHex(): string; | ||
Benchmarks measured with Apple M1. | ||
Benchmarks measured with Apple M1 on MacOS 12. | ||
getPublicKey(utils.randomPrivateKey()) x 6,121 ops/sec @ 163μs/op | ||
sign x 4,679 ops/sec @ 213μs/op | ||
sign x 4,789 ops/sec @ 208μs/op | ||
verify x 923 ops/sec @ 1ms/op | ||
recoverPublicKey x 491 ops/sec @ 2ms/op | ||
getSharedSecret aka ecdh x 534 ops/sec @ 1ms/op | ||
getSharedSecret aka ecdh x 558 ops/sec @ 1790μs/op | ||
getSharedSecret (precomputed) x 7,105 ops/sec @ 140μs/op | ||
@@ -299,0 +323,0 @@ Point.fromHex (decompression) x 12,171 ops/sec @ 82μs/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
91521
2123
344
12