peerpigeon
Version:
WebRTC-based peer-to-peer mesh networking library with intelligent routing and signaling server
1,476 lines (1,474 loc) • 530 kB
JavaScript
// PeerPigeon Browser Bundle - includes UnSEA crypto library
// Generated automatically - do not edit directly
var PeerPigeon = (() => {
var __create = Object.create;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
}) : x)(function(x) {
if (typeof require !== "undefined") return require.apply(this, arguments);
throw Error('Dynamic require of "' + x + '" is not supported');
});
var __esm = (fn, res) => function __init() {
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
};
var __commonJS = (cb, mod2) => function __require2() {
return mod2 || (0, cb[__getOwnPropNames(cb)[0]])((mod2 = { exports: {} }).exports, mod2), mod2.exports;
};
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toESM = (mod2, isNodeMode, target) => (target = mod2 != null ? __create(__getProtoOf(mod2)) : {}, __copyProps(
// If the importer is in node compatibility mode or this is not an ESM
// file that has been converted to a CommonJS file using a Babel-
// compatible transform (i.e. "__esModule" has not been set), then set
// "default" to the CommonJS "module.exports" for node compatibility.
isNodeMode || !mod2 || !mod2.__esModule ? __defProp(target, "default", { value: mod2, enumerable: true }) : target,
mod2
));
var __toCommonJS = (mod2) => __copyProps(__defProp({}, "__esModule", { value: true }), mod2);
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
// node_modules/unsea/dist/unsea.mjs
var unsea_exports = {};
__export(unsea_exports, {
SECURITY_CONFIG: () => SECURITY_CONFIG,
clearKeys: () => clearKeys,
decryptMessageWithMeta: () => decryptMessageWithMeta,
encryptMessageWithMeta: () => encryptMessageWithMeta,
exportToJWK: () => exportToJWK,
exportToPEM: () => exportToPEM,
generateRandomPair: () => generateRandomPair,
generateSignedWork: () => generateSignedWork,
generateWork: () => generateWork,
getSecurityInfo: () => getSecurityInfo,
importFromJWK: () => importFromJWK,
importFromPEM: () => importFromPEM,
loadKeys: () => loadKeys,
saveKeys: () => saveKeys,
signMessage: () => signMessage,
verifyMessage: () => verifyMessage,
verifySignedWork: () => verifySignedWork,
verifyWork: () => verifyWork
});
function isBytes(a) {
return a instanceof Uint8Array || ArrayBuffer.isView(a) && a.constructor.name === "Uint8Array";
}
function anumber(n) {
if (!Number.isSafeInteger(n) || n < 0)
throw new Error("positive integer expected, got " + n);
}
function abytes(b, ...lengths) {
if (!isBytes(b))
throw new Error("Uint8Array expected");
if (lengths.length > 0 && !lengths.includes(b.length))
throw new Error("Uint8Array expected of length " + lengths + ", got length=" + b.length);
}
function ahash(h) {
if (typeof h !== "function" || typeof h.create !== "function")
throw new Error("Hash should be wrapped by utils.createHasher");
anumber(h.outputLen);
anumber(h.blockLen);
}
function aexists(instance, checkFinished = true) {
if (instance.destroyed)
throw new Error("Hash instance has been destroyed");
if (checkFinished && instance.finished)
throw new Error("Hash#digest() has already been called");
}
function aoutput(out, instance) {
abytes(out);
const min = instance.outputLen;
if (out.length < min) {
throw new Error("digestInto() expects output buffer of length at least " + min);
}
}
function clean(...arrays) {
for (let i = 0; i < arrays.length; i++) {
arrays[i].fill(0);
}
}
function createView(arr) {
return new DataView(arr.buffer, arr.byteOffset, arr.byteLength);
}
function rotr(word, shift) {
return word << 32 - shift | word >>> shift;
}
function bytesToHex(bytes) {
abytes(bytes);
if (hasHexBuiltin)
return bytes.toHex();
let hex = "";
for (let i = 0; i < bytes.length; i++) {
hex += hexes[bytes[i]];
}
return hex;
}
function asciiToBase16(ch) {
if (ch >= asciis._0 && ch <= asciis._9)
return ch - asciis._0;
if (ch >= asciis.A && ch <= asciis.F)
return ch - (asciis.A - 10);
if (ch >= asciis.a && ch <= asciis.f)
return ch - (asciis.a - 10);
return;
}
function hexToBytes(hex) {
if (typeof hex !== "string")
throw new Error("hex string expected, got " + typeof hex);
if (hasHexBuiltin)
return Uint8Array.fromHex(hex);
const hl = hex.length;
const al = hl / 2;
if (hl % 2)
throw new Error("hex string expected, got unpadded hex of length " + hl);
const array = new Uint8Array(al);
for (let ai = 0, hi = 0; ai < al; ai++, hi += 2) {
const n1 = asciiToBase16(hex.charCodeAt(hi));
const n2 = asciiToBase16(hex.charCodeAt(hi + 1));
if (n1 === void 0 || n2 === void 0) {
const char = hex[hi] + hex[hi + 1];
throw new Error('hex string expected, got non-hex character "' + char + '" at index ' + hi);
}
array[ai] = n1 * 16 + n2;
}
return array;
}
function utf8ToBytes(str) {
if (typeof str !== "string")
throw new Error("string expected");
return new Uint8Array(new TextEncoder().encode(str));
}
function toBytes(data) {
if (typeof data === "string")
data = utf8ToBytes(data);
abytes(data);
return data;
}
function concatBytes(...arrays) {
let sum = 0;
for (let i = 0; i < arrays.length; i++) {
const a = arrays[i];
abytes(a);
sum += a.length;
}
const res = new Uint8Array(sum);
for (let i = 0, pad = 0; i < arrays.length; i++) {
const a = arrays[i];
res.set(a, pad);
pad += a.length;
}
return res;
}
function createHasher(hashCons) {
const hashC = (msg) => hashCons().update(toBytes(msg)).digest();
const tmp = hashCons();
hashC.outputLen = tmp.outputLen;
hashC.blockLen = tmp.blockLen;
hashC.create = () => hashCons();
return hashC;
}
function randomBytes(bytesLength = 32) {
if (crypto2 && typeof crypto2.getRandomValues === "function") {
return crypto2.getRandomValues(new Uint8Array(bytesLength));
}
if (crypto2 && typeof crypto2.randomBytes === "function") {
return Uint8Array.from(crypto2.randomBytes(bytesLength));
}
throw new Error("crypto.getRandomValues must be defined");
}
function abool(title, value) {
if (typeof value !== "boolean")
throw new Error(title + " boolean expected, got " + value);
}
function numberToHexUnpadded(num) {
const hex = num.toString(16);
return hex.length & 1 ? "0" + hex : hex;
}
function hexToNumber(hex) {
if (typeof hex !== "string")
throw new Error("hex string expected, got " + typeof hex);
return hex === "" ? _0n$3 : BigInt("0x" + hex);
}
function bytesToNumberBE(bytes) {
return hexToNumber(bytesToHex(bytes));
}
function bytesToNumberLE(bytes) {
abytes(bytes);
return hexToNumber(bytesToHex(Uint8Array.from(bytes).reverse()));
}
function numberToBytesBE(n, len) {
return hexToBytes(n.toString(16).padStart(len * 2, "0"));
}
function numberToBytesLE(n, len) {
return numberToBytesBE(n, len).reverse();
}
function ensureBytes(title, hex, expectedLength) {
let res;
if (typeof hex === "string") {
try {
res = hexToBytes(hex);
} catch (e) {
throw new Error(title + " must be hex string or Uint8Array, cause: " + e);
}
} else if (isBytes(hex)) {
res = Uint8Array.from(hex);
} else {
throw new Error(title + " must be hex string or Uint8Array");
}
res.length;
return res;
}
function inRange(n, min, max) {
return isPosBig(n) && isPosBig(min) && isPosBig(max) && min <= n && n < max;
}
function aInRange(title, n, min, max) {
if (!inRange(n, min, max))
throw new Error("expected valid " + title + ": " + min + " <= n < " + max + ", got " + n);
}
function bitLen(n) {
let len;
for (len = 0; n > _0n$3; n >>= _1n$3, len += 1)
;
return len;
}
function createHmacDrbg(hashLen, qByteLen, hmacFn) {
if (typeof hashLen !== "number" || hashLen < 2)
throw new Error("hashLen must be a number");
if (typeof qByteLen !== "number" || qByteLen < 2)
throw new Error("qByteLen must be a number");
if (typeof hmacFn !== "function")
throw new Error("hmacFn must be a function");
const u8n = (len) => new Uint8Array(len);
const u8of = (byte) => Uint8Array.of(byte);
let v = u8n(hashLen);
let k = u8n(hashLen);
let i = 0;
const reset = () => {
v.fill(1);
k.fill(0);
i = 0;
};
const h = (...b) => hmacFn(k, v, ...b);
const reseed = (seed = u8n(0)) => {
k = h(u8of(0), seed);
v = h();
if (seed.length === 0)
return;
k = h(u8of(1), seed);
v = h();
};
const gen = () => {
if (i++ >= 1e3)
throw new Error("drbg: tried 1000 values");
let len = 0;
const out = [];
while (len < qByteLen) {
v = h();
const sl = v.slice();
out.push(sl);
len += v.length;
}
return concatBytes(...out);
};
const genUntil = (seed, pred) => {
reset();
reseed(seed);
let res = void 0;
while (!(res = pred(gen())))
reseed();
reset();
return res;
};
return genUntil;
}
function _validateObject(object, fields, optFields = {}) {
if (!object || typeof object !== "object")
throw new Error("expected valid options object");
function checkField(fieldName, expectedType, isOpt) {
const val = object[fieldName];
if (isOpt && val === void 0)
return;
const current = typeof val;
if (current !== expectedType || val === null)
throw new Error(`param "${fieldName}" is invalid: expected ${expectedType}, got ${current}`);
}
Object.entries(fields).forEach(([k, v]) => checkField(k, v, false));
Object.entries(optFields).forEach(([k, v]) => checkField(k, v, true));
}
function memoized(fn) {
const map = /* @__PURE__ */ new WeakMap();
return (arg, ...args) => {
const val = map.get(arg);
if (val !== void 0)
return val;
const computed = fn(arg, ...args);
map.set(arg, computed);
return computed;
};
}
function mod(a, b) {
const result = a % b;
return result >= _0n$2 ? result : b + result;
}
function invert(number, modulo) {
if (number === _0n$2)
throw new Error("invert: expected non-zero number");
if (modulo <= _0n$2)
throw new Error("invert: expected positive modulus, got " + modulo);
let a = mod(number, modulo);
let b = modulo;
let x = _0n$2, u = _1n$2;
while (a !== _0n$2) {
const q = b / a;
const r = b % a;
const m = x - u * q;
b = a, a = r, x = u, u = m;
}
const gcd = b;
if (gcd !== _1n$2)
throw new Error("invert: does not exist");
return mod(x, modulo);
}
function assertIsSquare(Fp, root, n) {
if (!Fp.eql(Fp.sqr(root), n))
throw new Error("Cannot find square root");
}
function sqrt3mod4(Fp, n) {
const p1div4 = (Fp.ORDER + _1n$2) / _4n$1;
const root = Fp.pow(n, p1div4);
assertIsSquare(Fp, root, n);
return root;
}
function sqrt5mod8(Fp, n) {
const p5div8 = (Fp.ORDER - _5n) / _8n;
const n2 = Fp.mul(n, _2n$1);
const v = Fp.pow(n2, p5div8);
const nv = Fp.mul(n, v);
const i = Fp.mul(Fp.mul(nv, _2n$1), v);
const root = Fp.mul(nv, Fp.sub(i, Fp.ONE));
assertIsSquare(Fp, root, n);
return root;
}
function sqrt9mod16(P) {
const Fp_ = Field(P);
const tn = tonelliShanks(P);
const c1 = tn(Fp_, Fp_.neg(Fp_.ONE));
const c2 = tn(Fp_, c1);
const c3 = tn(Fp_, Fp_.neg(c1));
const c4 = (P + _7n) / _16n;
return (Fp, n) => {
let tv1 = Fp.pow(n, c4);
let tv2 = Fp.mul(tv1, c1);
const tv3 = Fp.mul(tv1, c2);
const tv4 = Fp.mul(tv1, c3);
const e1 = Fp.eql(Fp.sqr(tv2), n);
const e2 = Fp.eql(Fp.sqr(tv3), n);
tv1 = Fp.cmov(tv1, tv2, e1);
tv2 = Fp.cmov(tv4, tv3, e2);
const e3 = Fp.eql(Fp.sqr(tv2), n);
const root = Fp.cmov(tv1, tv2, e3);
assertIsSquare(Fp, root, n);
return root;
};
}
function tonelliShanks(P) {
if (P < _3n$1)
throw new Error("sqrt is not defined for small field");
let Q = P - _1n$2;
let S = 0;
while (Q % _2n$1 === _0n$2) {
Q /= _2n$1;
S++;
}
let Z = _2n$1;
const _Fp = Field(P);
while (FpLegendre(_Fp, Z) === 1) {
if (Z++ > 1e3)
throw new Error("Cannot find square root: probably non-prime P");
}
if (S === 1)
return sqrt3mod4;
let cc = _Fp.pow(Z, Q);
const Q1div2 = (Q + _1n$2) / _2n$1;
return function tonelliSlow(Fp, n) {
if (Fp.is0(n))
return n;
if (FpLegendre(Fp, n) !== 1)
throw new Error("Cannot find square root");
let M = S;
let c = Fp.mul(Fp.ONE, cc);
let t = Fp.pow(n, Q);
let R = Fp.pow(n, Q1div2);
while (!Fp.eql(t, Fp.ONE)) {
if (Fp.is0(t))
return Fp.ZERO;
let i = 1;
let t_tmp = Fp.sqr(t);
while (!Fp.eql(t_tmp, Fp.ONE)) {
i++;
t_tmp = Fp.sqr(t_tmp);
if (i === M)
throw new Error("Cannot find square root");
}
const exponent = _1n$2 << BigInt(M - i - 1);
const b = Fp.pow(c, exponent);
M = i;
c = Fp.sqr(b);
t = Fp.mul(t, c);
R = Fp.mul(R, b);
}
return R;
};
}
function FpSqrt(P) {
if (P % _4n$1 === _3n$1)
return sqrt3mod4;
if (P % _8n === _5n)
return sqrt5mod8;
if (P % _16n === _9n)
return sqrt9mod16(P);
return tonelliShanks(P);
}
function validateField(field) {
const initial = {
ORDER: "bigint",
MASK: "bigint",
BYTES: "number",
BITS: "number"
};
const opts = FIELD_FIELDS.reduce((map, val) => {
map[val] = "function";
return map;
}, initial);
_validateObject(field, opts);
return field;
}
function FpPow(Fp, num, power) {
if (power < _0n$2)
throw new Error("invalid exponent, negatives unsupported");
if (power === _0n$2)
return Fp.ONE;
if (power === _1n$2)
return num;
let p = Fp.ONE;
let d = num;
while (power > _0n$2) {
if (power & _1n$2)
p = Fp.mul(p, d);
d = Fp.sqr(d);
power >>= _1n$2;
}
return p;
}
function FpInvertBatch(Fp, nums, passZero = false) {
const inverted = new Array(nums.length).fill(passZero ? Fp.ZERO : void 0);
const multipliedAcc = nums.reduce((acc, num, i) => {
if (Fp.is0(num))
return acc;
inverted[i] = acc;
return Fp.mul(acc, num);
}, Fp.ONE);
const invertedAcc = Fp.inv(multipliedAcc);
nums.reduceRight((acc, num, i) => {
if (Fp.is0(num))
return acc;
inverted[i] = Fp.mul(acc, inverted[i]);
return Fp.mul(acc, num);
}, invertedAcc);
return inverted;
}
function FpLegendre(Fp, n) {
const p1mod2 = (Fp.ORDER - _1n$2) / _2n$1;
const powered = Fp.pow(n, p1mod2);
const yes = Fp.eql(powered, Fp.ONE);
const zero = Fp.eql(powered, Fp.ZERO);
const no = Fp.eql(powered, Fp.neg(Fp.ONE));
if (!yes && !zero && !no)
throw new Error("invalid Legendre symbol result");
return yes ? 1 : zero ? 0 : -1;
}
function nLength(n, nBitLength) {
if (nBitLength !== void 0)
anumber(nBitLength);
const _nBitLength = nBitLength !== void 0 ? nBitLength : n.toString(2).length;
const nByteLength = Math.ceil(_nBitLength / 8);
return { nBitLength: _nBitLength, nByteLength };
}
function Field(ORDER, bitLenOrOpts, isLE = false, opts = {}) {
if (ORDER <= _0n$2)
throw new Error("invalid field: expected ORDER > 0, got " + ORDER);
let _nbitLength = void 0;
let _sqrt = void 0;
let modOnDecode = false;
let allowedLengths = void 0;
if (typeof bitLenOrOpts === "object" && bitLenOrOpts != null) {
if (opts.sqrt || isLE)
throw new Error("cannot specify opts in two arguments");
const _opts = bitLenOrOpts;
if (_opts.BITS)
_nbitLength = _opts.BITS;
if (_opts.sqrt)
_sqrt = _opts.sqrt;
if (typeof _opts.isLE === "boolean")
isLE = _opts.isLE;
if (typeof _opts.modOnDecode === "boolean")
modOnDecode = _opts.modOnDecode;
allowedLengths = _opts.allowedLengths;
} else {
if (typeof bitLenOrOpts === "number")
_nbitLength = bitLenOrOpts;
if (opts.sqrt)
_sqrt = opts.sqrt;
}
const { nBitLength: BITS, nByteLength: BYTES } = nLength(ORDER, _nbitLength);
if (BYTES > 2048)
throw new Error("invalid field: expected ORDER of <= 2048 bytes");
let sqrtP;
const f = Object.freeze({
ORDER,
isLE,
BITS,
BYTES,
MASK: bitMask(BITS),
ZERO: _0n$2,
ONE: _1n$2,
allowedLengths,
create: (num) => mod(num, ORDER),
isValid: (num) => {
if (typeof num !== "bigint")
throw new Error("invalid field element: expected bigint, got " + typeof num);
return _0n$2 <= num && num < ORDER;
},
is0: (num) => num === _0n$2,
// is valid and invertible
isValidNot0: (num) => !f.is0(num) && f.isValid(num),
isOdd: (num) => (num & _1n$2) === _1n$2,
neg: (num) => mod(-num, ORDER),
eql: (lhs, rhs) => lhs === rhs,
sqr: (num) => mod(num * num, ORDER),
add: (lhs, rhs) => mod(lhs + rhs, ORDER),
sub: (lhs, rhs) => mod(lhs - rhs, ORDER),
mul: (lhs, rhs) => mod(lhs * rhs, ORDER),
pow: (num, power) => FpPow(f, num, power),
div: (lhs, rhs) => mod(lhs * invert(rhs, ORDER), ORDER),
// Same as above, but doesn't normalize
sqrN: (num) => num * num,
addN: (lhs, rhs) => lhs + rhs,
subN: (lhs, rhs) => lhs - rhs,
mulN: (lhs, rhs) => lhs * rhs,
inv: (num) => invert(num, ORDER),
sqrt: _sqrt || ((n) => {
if (!sqrtP)
sqrtP = FpSqrt(ORDER);
return sqrtP(f, n);
}),
toBytes: (num) => isLE ? numberToBytesLE(num, BYTES) : numberToBytesBE(num, BYTES),
fromBytes: (bytes, skipValidation = true) => {
if (allowedLengths) {
if (!allowedLengths.includes(bytes.length) || bytes.length > BYTES) {
throw new Error("Field.fromBytes: expected " + allowedLengths + " bytes, got " + bytes.length);
}
const padded = new Uint8Array(BYTES);
padded.set(bytes, isLE ? 0 : padded.length - bytes.length);
bytes = padded;
}
if (bytes.length !== BYTES)
throw new Error("Field.fromBytes: expected " + BYTES + " bytes, got " + bytes.length);
let scalar = isLE ? bytesToNumberLE(bytes) : bytesToNumberBE(bytes);
if (modOnDecode)
scalar = mod(scalar, ORDER);
if (!skipValidation) {
if (!f.isValid(scalar))
throw new Error("invalid field element: outside of range 0..ORDER");
}
return scalar;
},
// TODO: we don't need it here, move out to separate fn
invertBatch: (lst) => FpInvertBatch(f, lst),
// We can't move this out because Fp6, Fp12 implement it
// and it's unclear what to return in there.
cmov: (a, b, c) => c ? b : a
});
return Object.freeze(f);
}
function getFieldBytesLength(fieldOrder) {
if (typeof fieldOrder !== "bigint")
throw new Error("field order must be bigint");
const bitLength = fieldOrder.toString(2).length;
return Math.ceil(bitLength / 8);
}
function getMinHashLength(fieldOrder) {
const length = getFieldBytesLength(fieldOrder);
return length + Math.ceil(length / 2);
}
function mapHashToField(key, fieldOrder, isLE = false) {
const len = key.length;
const fieldLen = getFieldBytesLength(fieldOrder);
const minLen = getMinHashLength(fieldOrder);
if (len < 16 || len < minLen || len > 1024)
throw new Error("expected " + minLen + "-1024 bytes of input, got " + len);
const num = isLE ? bytesToNumberLE(key) : bytesToNumberBE(key);
const reduced = mod(num, fieldOrder - _1n$2) + _1n$2;
return isLE ? numberToBytesLE(reduced, fieldLen) : numberToBytesBE(reduced, fieldLen);
}
function setBigUint64(view, byteOffset, value, isLE) {
if (typeof view.setBigUint64 === "function")
return view.setBigUint64(byteOffset, value, isLE);
const _32n2 = BigInt(32);
const _u32_max = BigInt(4294967295);
const wh = Number(value >> _32n2 & _u32_max);
const wl = Number(value & _u32_max);
const h = isLE ? 4 : 0;
const l = isLE ? 0 : 4;
view.setUint32(byteOffset + h, wh, isLE);
view.setUint32(byteOffset + l, wl, isLE);
}
function Chi(a, b, c) {
return a & b ^ ~a & c;
}
function Maj(a, b, c) {
return a & b ^ a & c ^ b & c;
}
function fromBig(n, le = false) {
if (le)
return { h: Number(n & U32_MASK64), l: Number(n >> _32n & U32_MASK64) };
return { h: Number(n >> _32n & U32_MASK64) | 0, l: Number(n & U32_MASK64) | 0 };
}
function split(lst, le = false) {
const len = lst.length;
let Ah = new Uint32Array(len);
let Al = new Uint32Array(len);
for (let i = 0; i < len; i++) {
const { h, l } = fromBig(lst[i], le);
[Ah[i], Al[i]] = [h, l];
}
return [Ah, Al];
}
function add(Ah, Al, Bh, Bl) {
const l = (Al >>> 0) + (Bl >>> 0);
return { h: Ah + Bh + (l / 2 ** 32 | 0) | 0, l: l | 0 };
}
function negateCt(condition, item) {
const neg = item.negate();
return condition ? neg : item;
}
function normalizeZ(c, points) {
const invertedZs = FpInvertBatch(c.Fp, points.map((p) => p.Z));
return points.map((p, i) => c.fromAffine(p.toAffine(invertedZs[i])));
}
function validateW(W, bits) {
if (!Number.isSafeInteger(W) || W <= 0 || W > bits)
throw new Error("invalid window size, expected [1.." + bits + "], got W=" + W);
}
function calcWOpts(W, scalarBits) {
validateW(W, scalarBits);
const windows = Math.ceil(scalarBits / W) + 1;
const windowSize = 2 ** (W - 1);
const maxNumber = 2 ** W;
const mask = bitMask(W);
const shiftBy = BigInt(W);
return { windows, windowSize, mask, maxNumber, shiftBy };
}
function calcOffsets(n, window2, wOpts) {
const { windowSize, mask, maxNumber, shiftBy } = wOpts;
let wbits = Number(n & mask);
let nextN = n >> shiftBy;
if (wbits > windowSize) {
wbits -= maxNumber;
nextN += _1n$1;
}
const offsetStart = window2 * windowSize;
const offset = offsetStart + Math.abs(wbits) - 1;
const isZero = wbits === 0;
const isNeg = wbits < 0;
const isNegF = window2 % 2 !== 0;
const offsetF = offsetStart;
return { nextN, offset, isZero, isNeg, isNegF, offsetF };
}
function validateMSMPoints(points, c) {
if (!Array.isArray(points))
throw new Error("array expected");
points.forEach((p, i) => {
if (!(p instanceof c))
throw new Error("invalid point at index " + i);
});
}
function validateMSMScalars(scalars, field) {
if (!Array.isArray(scalars))
throw new Error("array of scalars expected");
scalars.forEach((s, i) => {
if (!field.isValid(s))
throw new Error("invalid scalar at index " + i);
});
}
function getW(P) {
return pointWindowSizes.get(P) || 1;
}
function assert0(n) {
if (n !== _0n$1)
throw new Error("invalid wNAF");
}
function mulEndoUnsafe(Point, point, k1, k2) {
let acc = point;
let p1 = Point.ZERO;
let p2 = Point.ZERO;
while (k1 > _0n$1 || k2 > _0n$1) {
if (k1 & _1n$1)
p1 = p1.add(acc);
if (k2 & _1n$1)
p2 = p2.add(acc);
acc = acc.double();
k1 >>= _1n$1;
k2 >>= _1n$1;
}
return { p1, p2 };
}
function pippenger(c, fieldN, points, scalars) {
validateMSMPoints(points, c);
validateMSMScalars(scalars, fieldN);
const plength = points.length;
const slength = scalars.length;
if (plength !== slength)
throw new Error("arrays of points and scalars must have equal length");
const zero = c.ZERO;
const wbits = bitLen(BigInt(plength));
let windowSize = 1;
if (wbits > 12)
windowSize = wbits - 3;
else if (wbits > 4)
windowSize = wbits - 2;
else if (wbits > 0)
windowSize = 2;
const MASK = bitMask(windowSize);
const buckets = new Array(Number(MASK) + 1).fill(zero);
const lastBits = Math.floor((fieldN.BITS - 1) / windowSize) * windowSize;
let sum = zero;
for (let i = lastBits; i >= 0; i -= windowSize) {
buckets.fill(zero);
for (let j = 0; j < slength; j++) {
const scalar = scalars[j];
const wbits2 = Number(scalar >> BigInt(i) & MASK);
buckets[wbits2] = buckets[wbits2].add(points[j]);
}
let resI = zero;
for (let j = buckets.length - 1, sumI = zero; j > 0; j--) {
sumI = sumI.add(buckets[j]);
resI = resI.add(sumI);
}
sum = sum.add(resI);
if (i !== 0)
for (let j = 0; j < windowSize; j++)
sum = sum.double();
}
return sum;
}
function createField(order, field) {
if (field) {
if (field.ORDER !== order)
throw new Error("Field.ORDER must match order: Fp == p, Fn == n");
validateField(field);
return field;
} else {
return Field(order);
}
}
function _createCurveFields(type, CURVE, curveOpts = {}) {
if (!CURVE || typeof CURVE !== "object")
throw new Error(`expected valid ${type} CURVE object`);
for (const p of ["p", "n", "h"]) {
const val = CURVE[p];
if (!(typeof val === "bigint" && val > _0n$1))
throw new Error(`CURVE.${p} must be positive bigint`);
}
const Fp = createField(CURVE.p, curveOpts.Fp);
const Fn = createField(CURVE.n, curveOpts.Fn);
const _b = "b";
const params = ["Gx", "Gy", "a", _b];
for (const p of params) {
if (!Fp.isValid(CURVE[p]))
throw new Error(`CURVE.${p} must be valid field element of CURVE.Fp`);
}
return { Fp, Fn };
}
function _splitEndoScalar(k, basis, n) {
const [[a1, b1], [a2, b2]] = basis;
const c1 = divNearest(b2 * k, n);
const c2 = divNearest(-b1 * k, n);
let k1 = k - c1 * a1 - c2 * a2;
let k2 = -c1 * b1 - c2 * b2;
const k1neg = k1 < _0n;
const k2neg = k2 < _0n;
if (k1neg)
k1 = -k1;
if (k2neg)
k2 = -k2;
const MAX_NUM = bitMask(Math.ceil(bitLen(n) / 2)) + _1n;
if (k1 < _0n || k1 >= MAX_NUM || k2 < _0n || k2 >= MAX_NUM) {
throw new Error("splitScalar (endomorphism): failed, k=" + k);
}
return { k1neg, k1, k2neg, k2 };
}
function validateSigVerOpts(opts) {
if (opts.lowS !== void 0)
abool("lowS", opts.lowS);
if (opts.prehash !== void 0)
abool("prehash", opts.prehash);
}
function _legacyHelperEquat(Fp, a, b) {
function weierstrassEquation(x) {
const x2 = Fp.sqr(x);
const x3 = Fp.mul(x2, x);
return Fp.add(Fp.add(x3, Fp.mul(x, a)), b);
}
return weierstrassEquation;
}
function _normFnElement(Fn, key) {
const { BYTES: expected } = Fn;
let num;
if (typeof key === "bigint") {
num = key;
} else {
let bytes = ensureBytes("private key", key);
try {
num = Fn.fromBytes(bytes);
} catch (error) {
throw new Error(`invalid private key: expected ui8a of size ${expected}, got ${typeof key}`);
}
}
if (!Fn.isValidNot0(num))
throw new Error("invalid private key: out of range [1..N-1]");
return num;
}
function weierstrassN(CURVE, curveOpts = {}) {
const { Fp, Fn } = _createCurveFields("weierstrass", CURVE, curveOpts);
const { h: cofactor, n: CURVE_ORDER } = CURVE;
_validateObject(curveOpts, {}, {
allowInfinityPoint: "boolean",
clearCofactor: "function",
isTorsionFree: "function",
fromBytes: "function",
toBytes: "function",
endo: "object",
wrapPrivateKey: "boolean"
});
const { endo } = curveOpts;
if (endo) {
if (!Fp.is0(CURVE.a) || typeof endo.beta !== "bigint" || !Array.isArray(endo.basises)) {
throw new Error('invalid endo: expected "beta": bigint and "basises": array');
}
}
function assertCompressionIsSupported() {
if (!Fp.isOdd)
throw new Error("compression is not supported: Field does not have .isOdd()");
}
function pointToBytes(_c, point, isCompressed) {
const { x, y } = point.toAffine();
const bx = Fp.toBytes(x);
abool("isCompressed", isCompressed);
if (isCompressed) {
assertCompressionIsSupported();
const hasEvenY = !Fp.isOdd(y);
return concatBytes(pprefix(hasEvenY), bx);
} else {
return concatBytes(Uint8Array.of(4), bx, Fp.toBytes(y));
}
}
function pointFromBytes(bytes) {
abytes(bytes);
const L = Fp.BYTES;
const LC = L + 1;
const LU = 2 * L + 1;
const length = bytes.length;
const head = bytes[0];
const tail = bytes.subarray(1);
if (length === LC && (head === 2 || head === 3)) {
const x = Fp.fromBytes(tail);
if (!Fp.isValid(x))
throw new Error("bad point: is not on curve, wrong x");
const y2 = weierstrassEquation(x);
let y;
try {
y = Fp.sqrt(y2);
} catch (sqrtError) {
const err = sqrtError instanceof Error ? ": " + sqrtError.message : "";
throw new Error("bad point: is not on curve, sqrt error" + err);
}
assertCompressionIsSupported();
const isYOdd = Fp.isOdd(y);
const isHeadOdd = (head & 1) === 1;
if (isHeadOdd !== isYOdd)
y = Fp.neg(y);
return { x, y };
} else if (length === LU && head === 4) {
const x = Fp.fromBytes(tail.subarray(L * 0, L * 1));
const y = Fp.fromBytes(tail.subarray(L * 1, L * 2));
if (!isValidXY(x, y))
throw new Error("bad point: is not on curve");
return { x, y };
} else {
throw new Error(`bad point: got length ${length}, expected compressed=${LC} or uncompressed=${LU}`);
}
}
const toBytes2 = curveOpts.toBytes || pointToBytes;
const fromBytes = curveOpts.fromBytes || pointFromBytes;
const weierstrassEquation = _legacyHelperEquat(Fp, CURVE.a, CURVE.b);
function isValidXY(x, y) {
const left = Fp.sqr(y);
const right = weierstrassEquation(x);
return Fp.eql(left, right);
}
if (!isValidXY(CURVE.Gx, CURVE.Gy))
throw new Error("bad curve params: generator point");
const _4a3 = Fp.mul(Fp.pow(CURVE.a, _3n), _4n);
const _27b2 = Fp.mul(Fp.sqr(CURVE.b), BigInt(27));
if (Fp.is0(Fp.add(_4a3, _27b2)))
throw new Error("bad curve params: a or b");
function acoord(title, n, banZero = false) {
if (!Fp.isValid(n) || banZero && Fp.is0(n))
throw new Error(`bad point coordinate ${title}`);
return n;
}
function aprjpoint(other) {
if (!(other instanceof Point))
throw new Error("ProjectivePoint expected");
}
function splitEndoScalarN(k) {
if (!endo || !endo.basises)
throw new Error("no endo");
return _splitEndoScalar(k, endo.basises, Fn.ORDER);
}
const toAffineMemo = memoized((p, iz) => {
const { X, Y, Z } = p;
if (Fp.eql(Z, Fp.ONE))
return { x: X, y: Y };
const is0 = p.is0();
if (iz == null)
iz = is0 ? Fp.ONE : Fp.inv(Z);
const x = Fp.mul(X, iz);
const y = Fp.mul(Y, iz);
const zz = Fp.mul(Z, iz);
if (is0)
return { x: Fp.ZERO, y: Fp.ZERO };
if (!Fp.eql(zz, Fp.ONE))
throw new Error("invZ was invalid");
return { x, y };
});
const assertValidMemo = memoized((p) => {
if (p.is0()) {
if (curveOpts.allowInfinityPoint && !Fp.is0(p.Y))
return;
throw new Error("bad point: ZERO");
}
const { x, y } = p.toAffine();
if (!Fp.isValid(x) || !Fp.isValid(y))
throw new Error("bad point: x or y not field elements");
if (!isValidXY(x, y))
throw new Error("bad point: equation left != right");
if (!p.isTorsionFree())
throw new Error("bad point: not in prime-order subgroup");
return true;
});
function finishEndo(endoBeta, k1p, k2p, k1neg, k2neg) {
k2p = new Point(Fp.mul(k2p.X, endoBeta), k2p.Y, k2p.Z);
k1p = negateCt(k1neg, k1p);
k2p = negateCt(k2neg, k2p);
return k1p.add(k2p);
}
class Point {
/** Does NOT validate if the point is valid. Use `.assertValidity()`. */
constructor(X, Y, Z) {
this.X = acoord("x", X);
this.Y = acoord("y", Y, true);
this.Z = acoord("z", Z);
Object.freeze(this);
}
/** Does NOT validate if the point is valid. Use `.assertValidity()`. */
static fromAffine(p) {
const { x, y } = p || {};
if (!p || !Fp.isValid(x) || !Fp.isValid(y))
throw new Error("invalid affine point");
if (p instanceof Point)
throw new Error("projective point not allowed");
if (Fp.is0(x) && Fp.is0(y))
return Point.ZERO;
return new Point(x, y, Fp.ONE);
}
get x() {
return this.toAffine().x;
}
get y() {
return this.toAffine().y;
}
// TODO: remove
get px() {
return this.X;
}
get py() {
return this.X;
}
get pz() {
return this.Z;
}
static normalizeZ(points) {
return normalizeZ(Point, points);
}
static fromBytes(bytes) {
abytes(bytes);
return Point.fromHex(bytes);
}
/** Converts hash string or Uint8Array to Point. */
static fromHex(hex) {
const P = Point.fromAffine(fromBytes(ensureBytes("pointHex", hex)));
P.assertValidity();
return P;
}
/** Multiplies generator point by privateKey. */
static fromPrivateKey(privateKey) {
return Point.BASE.multiply(_normFnElement(Fn, privateKey));
}
// TODO: remove
static msm(points, scalars) {
return pippenger(Point, Fn, points, scalars);
}
_setWindowSize(windowSize) {
this.precompute(windowSize);
}
/**
*
* @param windowSize
* @param isLazy true will defer table computation until the first multiplication
* @returns
*/
precompute(windowSize = 8, isLazy = true) {
wnaf.createCache(this, windowSize);
if (!isLazy)
this.multiply(_3n);
return this;
}
// TODO: return `this`
/** A point on curve is valid if it conforms to equation. */
assertValidity() {
assertValidMemo(this);
}
hasEvenY() {
const { y } = this.toAffine();
if (!Fp.isOdd)
throw new Error("Field doesn't support isOdd");
return !Fp.isOdd(y);
}
/** Compare one point to another. */
equals(other) {
aprjpoint(other);
const { X: X1, Y: Y1, Z: Z1 } = this;
const { X: X2, Y: Y2, Z: Z2 } = other;
const U1 = Fp.eql(Fp.mul(X1, Z2), Fp.mul(X2, Z1));
const U2 = Fp.eql(Fp.mul(Y1, Z2), Fp.mul(Y2, Z1));
return U1 && U2;
}
/** Flips point to one corresponding to (x, -y) in Affine coordinates. */
negate() {
return new Point(this.X, Fp.neg(this.Y), this.Z);
}
// Renes-Costello-Batina exception-free doubling formula.
// There is 30% faster Jacobian formula, but it is not complete.
// https://eprint.iacr.org/2015/1060, algorithm 3
// Cost: 8M + 3S + 3*a + 2*b3 + 15add.
double() {
const { a, b } = CURVE;
const b3 = Fp.mul(b, _3n);
const { X: X1, Y: Y1, Z: Z1 } = this;
let X3 = Fp.ZERO, Y3 = Fp.ZERO, Z3 = Fp.ZERO;
let t0 = Fp.mul(X1, X1);
let t1 = Fp.mul(Y1, Y1);
let t2 = Fp.mul(Z1, Z1);
let t3 = Fp.mul(X1, Y1);
t3 = Fp.add(t3, t3);
Z3 = Fp.mul(X1, Z1);
Z3 = Fp.add(Z3, Z3);
X3 = Fp.mul(a, Z3);
Y3 = Fp.mul(b3, t2);
Y3 = Fp.add(X3, Y3);
X3 = Fp.sub(t1, Y3);
Y3 = Fp.add(t1, Y3);
Y3 = Fp.mul(X3, Y3);
X3 = Fp.mul(t3, X3);
Z3 = Fp.mul(b3, Z3);
t2 = Fp.mul(a, t2);
t3 = Fp.sub(t0, t2);
t3 = Fp.mul(a, t3);
t3 = Fp.add(t3, Z3);
Z3 = Fp.add(t0, t0);
t0 = Fp.add(Z3, t0);
t0 = Fp.add(t0, t2);
t0 = Fp.mul(t0, t3);
Y3 = Fp.add(Y3, t0);
t2 = Fp.mul(Y1, Z1);
t2 = Fp.add(t2, t2);
t0 = Fp.mul(t2, t3);
X3 = Fp.sub(X3, t0);
Z3 = Fp.mul(t2, t1);
Z3 = Fp.add(Z3, Z3);
Z3 = Fp.add(Z3, Z3);
return new Point(X3, Y3, Z3);
}
// Renes-Costello-Batina exception-free addition formula.
// There is 30% faster Jacobian formula, but it is not complete.
// https://eprint.iacr.org/2015/1060, algorithm 1
// Cost: 12M + 0S + 3*a + 3*b3 + 23add.
add(other) {
aprjpoint(other);
const { X: X1, Y: Y1, Z: Z1 } = this;
const { X: X2, Y: Y2, Z: Z2 } = other;
let X3 = Fp.ZERO, Y3 = Fp.ZERO, Z3 = Fp.ZERO;
const a = CURVE.a;
const b3 = Fp.mul(CURVE.b, _3n);
let t0 = Fp.mul(X1, X2);
let t1 = Fp.mul(Y1, Y2);
let t2 = Fp.mul(Z1, Z2);
let t3 = Fp.add(X1, Y1);
let t4 = Fp.add(X2, Y2);
t3 = Fp.mul(t3, t4);
t4 = Fp.add(t0, t1);
t3 = Fp.sub(t3, t4);
t4 = Fp.add(X1, Z1);
let t5 = Fp.add(X2, Z2);
t4 = Fp.mul(t4, t5);
t5 = Fp.add(t0, t2);
t4 = Fp.sub(t4, t5);
t5 = Fp.add(Y1, Z1);
X3 = Fp.add(Y2, Z2);
t5 = Fp.mul(t5, X3);
X3 = Fp.add(t1, t2);
t5 = Fp.sub(t5, X3);
Z3 = Fp.mul(a, t4);
X3 = Fp.mul(b3, t2);
Z3 = Fp.add(X3, Z3);
X3 = Fp.sub(t1, Z3);
Z3 = Fp.add(t1, Z3);
Y3 = Fp.mul(X3, Z3);
t1 = Fp.add(t0, t0);
t1 = Fp.add(t1, t0);
t2 = Fp.mul(a, t2);
t4 = Fp.mul(b3, t4);
t1 = Fp.add(t1, t2);
t2 = Fp.sub(t0, t2);
t2 = Fp.mul(a, t2);
t4 = Fp.add(t4, t2);
t0 = Fp.mul(t1, t4);
Y3 = Fp.add(Y3, t0);
t0 = Fp.mul(t5, t4);
X3 = Fp.mul(t3, X3);
X3 = Fp.sub(X3, t0);
t0 = Fp.mul(t3, t1);
Z3 = Fp.mul(t5, Z3);
Z3 = Fp.add(Z3, t0);
return new Point(X3, Y3, Z3);
}
subtract(other) {
return this.add(other.negate());
}
is0() {
return this.equals(Point.ZERO);
}
/**
* Constant time multiplication.
* Uses wNAF method. Windowed method may be 10% faster,
* but takes 2x longer to generate and consumes 2x memory.
* Uses precomputes when available.
* Uses endomorphism for Koblitz curves.
* @param scalar by which the point would be multiplied
* @returns New point
*/
multiply(scalar) {
const { endo: endo2 } = curveOpts;
if (!Fn.isValidNot0(scalar))
throw new Error("invalid scalar: out of range");
let point, fake;
const mul = (n) => wnaf.cached(this, n, (p) => normalizeZ(Point, p));
if (endo2) {
const { k1neg, k1, k2neg, k2 } = splitEndoScalarN(scalar);
const { p: k1p, f: k1f } = mul(k1);
const { p: k2p, f: k2f } = mul(k2);
fake = k1f.add(k2f);
point = finishEndo(endo2.beta, k1p, k2p, k1neg, k2neg);
} else {
const { p, f } = mul(scalar);
point = p;
fake = f;
}
return normalizeZ(Point, [point, fake])[0];
}
/**
* Non-constant-time multiplication. Uses double-and-add algorithm.
* It's faster, but should only be used when you don't care about
* an exposed secret key e.g. sig verification, which works over *public* keys.
*/
multiplyUnsafe(sc) {
const { endo: endo2 } = curveOpts;
const p = this;
if (!Fn.isValid(sc))
throw new Error("invalid scalar: out of range");
if (sc === _0n || p.is0())
return Point.ZERO;
if (sc === _1n)
return p;
if (wnaf.hasCache(this))
return this.multiply(sc);
if (endo2) {
const { k1neg, k1, k2neg, k2 } = splitEndoScalarN(sc);
const { p1, p2 } = mulEndoUnsafe(Point, p, k1, k2);
return finishEndo(endo2.beta, p1, p2, k1neg, k2neg);
} else {
return wnaf.unsafe(p, sc);
}
}
multiplyAndAddUnsafe(Q, a, b) {
const sum = this.multiplyUnsafe(a).add(Q.multiplyUnsafe(b));
return sum.is0() ? void 0 : sum;
}
/**
* Converts Projective point to affine (x, y) coordinates.
* @param invertedZ Z^-1 (inverted zero) - optional, precomputation is useful for invertBatch
*/
toAffine(invertedZ) {
return toAffineMemo(this, invertedZ);
}
/**
* Checks whether Point is free of torsion elements (is in prime subgroup).
* Always torsion-free for cofactor=1 curves.
*/
isTorsionFree() {
const { isTorsionFree } = curveOpts;
if (cofactor === _1n)
return true;
if (isTorsionFree)
return isTorsionFree(Point, this);
return wnaf.unsafe(this, CURVE_ORDER).is0();
}
clearCofactor() {
const { clearCofactor } = curveOpts;
if (cofactor === _1n)
return this;
if (clearCofactor)
return clearCofactor(Point, this);
return this.multiplyUnsafe(cofactor);
}
isSmallOrder() {
return this.multiplyUnsafe(cofactor).is0();
}
toBytes(isCompressed = true) {
abool("isCompressed", isCompressed);
this.assertValidity();
return toBytes2(Point, this, isCompressed);
}
/** @deprecated use `toBytes` */
toRawBytes(isCompressed = true) {
return this.toBytes(isCompressed);
}
toHex(isCompressed = true) {
return bytesToHex(this.toBytes(isCompressed));
}
toString() {
return `<Point ${this.is0() ? "ZERO" : this.toHex()}>`;
}
}
Point.BASE = new Point(CURVE.Gx, CURVE.Gy, Fp.ONE);
Point.ZERO = new Point(Fp.ZERO, Fp.ONE, Fp.ZERO);
Point.Fp = Fp;
Point.Fn = Fn;
const bits = Fn.BITS;
const wnaf = new wNAF(Point, curveOpts.endo ? Math.ceil(bits / 2) : bits);
return Point;
}
function pprefix(hasEvenY) {
return Uint8Array.of(hasEvenY ? 2 : 3);
}
function ecdsa(Point, hash, ecdsaOpts = {}) {
ahash(hash);
_validateObject(ecdsaOpts, {}, {
hmac: "function",
lowS: "boolean",
randomBytes: "function",
bits2int: "function",
bits2int_modN: "function"
});
const randomBytes_ = ecdsaOpts.randomBytes || randomBytes;
const hmac_ = ecdsaOpts.hmac || ((key, ...msgs) => hmac(hash, key, concatBytes(...msgs)));
const { Fp, Fn } = Point;
const { ORDER: CURVE_ORDER, BITS: fnBits } = Fn;
const seedLen = getMinHashLength(CURVE_ORDER);
const lengths = {
secret: Fn.BYTES,
public: 1 + Fp.BYTES,
publicUncompressed: 1 + 2 * Fp.BYTES,
signature: 2 * Fn.BYTES,
seed: seedLen
};
function isBiggerThanHalfOrder(number) {
const HALF = CURVE_ORDER >> _1n;
return number > HALF;
}
function normalizeS(s) {
return isBiggerThanHalfOrder(s) ? Fn.neg(s) : s;
}
function aValidRS(title, num) {
if (!Fn.isValidNot0(num))
throw new Error(`invalid signature ${title}: out of range 1..CURVE.n`);
}
class Signature {
constructor(r, s, recovery) {
aValidRS("r", r);
aValidRS("s", s);
this.r = r;
this.s = s;
if (recovery != null)
this.recovery = recovery;
Object.freeze(this);
}
static fromBytes(bytes, format = "compact") {
if (format === "compact") {
const L = Fn.BYTES;
abytes(bytes, L * 2);
const r = bytes.subarray(0, L);
const s = bytes.subarray(L, L * 2);
return new Signature(Fn.fromBytes(r), Fn.fromBytes(s));
}
if (format === "der") {
abytes(bytes);
const { r, s } = DER.toSig(bytes);
return new Signature(r, s);
}
throw new Error("invalid format");
}
static fromHex(hex, format) {
return this.fromBytes(hexToBytes(hex), format);
}
addRecoveryBit(recovery) {
return new Signature(this.r, this.s, recovery);
}
// ProjPointType<bigint>
recoverPublicKey(msgHash) {
const FIELD_ORDER = Fp.ORDER;
const { r, s, recovery: rec } = this;
if (rec == null || ![0, 1, 2, 3].includes(rec))
throw new Error("recovery id invalid");
const hasCofactor = CURVE_ORDER * _2n < FIELD_ORDER;
if (hasCofactor && rec > 1)
throw new Error("recovery id is ambiguous for h>1 curve");
const radj = rec === 2 || rec === 3 ? r + CURVE_ORDER : r;
if (!Fp.isValid(radj))
throw new Error("recovery id 2 or 3 invalid");
const x = Fp.toBytes(radj);
const R = Point.fromHex(concatBytes(pprefix((rec & 1) === 0), x));
const ir = Fn.inv(radj);
const h = bits2int_modN(ensureBytes("msgHash", msgHash));
const u1 = Fn.create(-h * ir);
const u2 = Fn.create(s * ir);
const Q = Point.BASE.multiplyUnsafe(u1).add(R.multiplyUnsafe(u2));
if (Q.is0())
throw new Error("point at infinify");
Q.assertValidity();
return Q;
}
// Signatures should be low-s, to prevent malleability.
hasHighS() {
return isBiggerThanHalfOrder(this.s);
}
normalizeS() {
return this.hasHighS() ? new Signature(this.r, Fn.neg(this.s), this.recovery) : this;
}
toBytes(format = "compact") {
if (format === "compact")
return concatBytes(Fn.toBytes(this.r), Fn.toBytes(this.s));
if (format === "der")
return hexToBytes(DER.hexFromSig(this));
throw new Error("invalid format");
}
toHex(format) {
return bytesToHex(this.toBytes(format));
}
// TODO: remove
assertValidity() {
}
static fromCompact(hex) {
return Signature.fromBytes(ensureBytes("sig", hex), "compact");
}
static fromDER(hex) {
return Signature.fromBytes(ensureBytes("sig", hex), "der");
}
toDERRawBytes() {
return this.toBytes("der");
}
toDERHex() {
return bytesToHex(this.toBytes("der"));
}
toCompactRawBytes() {
return this.toBytes("compact");
}
toCompactHex() {
return bytesToHex(this.toBytes("compact"));
}
}
function isValidSecretKey(privateKey) {
try {
return !!_normFnElement(Fn, privateKey);
} catch (error) {
return false;
}
}
function isValidPublicKey(publicKey, isCompressed) {
try {
const l = publicKey.length;
if (isCompressed === true && l !== lengths.public)
return false;
if (isCompressed === false && l !== lengths.publicUncompressed)
return false;
return !!Point.fromBytes(publicKey);
} catch (error) {
return false;
}
}
function randomSecretKey(seed = randomBytes_(seedLen)) {
return mapHashToField(seed, CURVE_ORDER);
}
const utils = {
isValidSecretKey,
isValidPublicKey,
randomSecretKey,
// TODO: remove
isValidPrivateKey: isValidSecretKey,
randomPrivateKey: randomSecretKey,
normPrivateKeyToScalar: (key) => _normFnElement(Fn, key),
precompute(windowSize = 8, point = Point.BASE) {
return point.precompute(windowSize, false);
}
};
function getPublicKey(secretKey, isCompressed = true) {
return Point.BASE.multiply(_normFnElement(Fn, secretKey)).toBytes(isCompressed);
}
function isProbPub(item) {
if (typeof item === "bigint")
return false;
if (item instanceof Point)
return true;
if (Fn.allowedLengths || lengths.secret === lengths.public)
return void 0;
const l = ensureBytes("key", item).length;
return l === lengths.public || l === lengths.publicUncompressed;
}
function getSharedSecret(secretKeyA, publicKeyB, isCompressed = true) {
if (isProbPub(sec