UNPKG

@pact-toolbox/crypto

Version:
1,357 lines (1,343 loc) 68.7 kB
//#region rolldown:runtime 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 __commonJS = (cb, mod) => function() { return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports; }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) { key = keys[i]; if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: ((k) => from[k]).bind(null, key), enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, mod)); //#endregion const node_crypto = __toESM(require("node:crypto")); const blakejs = __toESM(require("blakejs")); //#region ../../node_modules/.pnpm/uncrypto@0.1.3/node_modules/uncrypto/dist/crypto.node.mjs const subtle$1 = node_crypto.default.webcrypto?.subtle || {}; const randomUUID = () => { return node_crypto.default.randomUUID(); }; const getRandomValues = (array) => { return node_crypto.default.webcrypto.getRandomValues(array); }; const _crypto = { randomUUID, getRandomValues, subtle: subtle$1 }; //#endregion //#region ../../node_modules/.pnpm/@oxc-project+runtime@0.72.2/node_modules/@oxc-project/runtime/src/helpers/typeof.js var require_typeof = __commonJS({ "../../node_modules/.pnpm/@oxc-project+runtime@0.72.2/node_modules/@oxc-project/runtime/src/helpers/typeof.js"(exports, module) { function _typeof$2(o) { "@babel/helpers - typeof"; return module.exports = _typeof$2 = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function(o$1) { return typeof o$1; } : function(o$1) { return o$1 && "function" == typeof Symbol && o$1.constructor === Symbol && o$1 !== Symbol.prototype ? "symbol" : typeof o$1; }, module.exports.__esModule = true, module.exports["default"] = module.exports, _typeof$2(o); } module.exports = _typeof$2, module.exports.__esModule = true, module.exports["default"] = module.exports; } }); //#endregion //#region ../../node_modules/.pnpm/@oxc-project+runtime@0.72.2/node_modules/@oxc-project/runtime/src/helpers/toPrimitive.js var require_toPrimitive = __commonJS({ "../../node_modules/.pnpm/@oxc-project+runtime@0.72.2/node_modules/@oxc-project/runtime/src/helpers/toPrimitive.js"(exports, module) { var _typeof$1 = require_typeof()["default"]; function toPrimitive$1(t, r) { if ("object" != _typeof$1(t) || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != _typeof$1(i)) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); } module.exports = toPrimitive$1, module.exports.__esModule = true, module.exports["default"] = module.exports; } }); //#endregion //#region ../../node_modules/.pnpm/@oxc-project+runtime@0.72.2/node_modules/@oxc-project/runtime/src/helpers/toPropertyKey.js var require_toPropertyKey = __commonJS({ "../../node_modules/.pnpm/@oxc-project+runtime@0.72.2/node_modules/@oxc-project/runtime/src/helpers/toPropertyKey.js"(exports, module) { var _typeof = require_typeof()["default"]; var toPrimitive = require_toPrimitive(); function toPropertyKey$1(t) { var i = toPrimitive(t, "string"); return "symbol" == _typeof(i) ? i : i + ""; } module.exports = toPropertyKey$1, module.exports.__esModule = true, module.exports["default"] = module.exports; } }); //#endregion //#region ../../node_modules/.pnpm/@oxc-project+runtime@0.72.2/node_modules/@oxc-project/runtime/src/helpers/defineProperty.js var require_defineProperty = __commonJS({ "../../node_modules/.pnpm/@oxc-project+runtime@0.72.2/node_modules/@oxc-project/runtime/src/helpers/defineProperty.js"(exports, module) { var toPropertyKey = require_toPropertyKey(); function _defineProperty$1(e, r, t) { return (r = toPropertyKey(r)) in e ? Object.defineProperty(e, r, { value: t, enumerable: !0, configurable: !0, writable: !0 }) : e[r] = t, e; } module.exports = _defineProperty$1, module.exports.__esModule = true, module.exports["default"] = module.exports; } }); //#endregion //#region ../../node_modules/.pnpm/@noble+ed25519@2.3.0/node_modules/@noble/ed25519/index.js var import_defineProperty = __toESM(require_defineProperty(), 1); /*! noble-ed25519 - MIT License (c) 2019 Paul Miller (paulmillr.com) */ /** * 4KB JS implementation of ed25519 EdDSA signatures. * Compliant with RFC8032, FIPS 186-5 & ZIP215. * @module * @example * ```js import * as ed from '@noble/ed25519'; (async () => { const privKey = ed.utils.randomPrivateKey(); const message = Uint8Array.from([0xab, 0xbc, 0xcd, 0xde]); const pubKey = await ed.getPublicKeyAsync(privKey); // Sync methods are also present const signature = await ed.signAsync(message, privKey); const isValid = await ed.verifyAsync(signature, message, pubKey); })(); ``` */ /** * Curve params. ed25519 is twisted edwards curve. Equation is −x² + y² = -a + dx²y². * * P = `2n**255n - 19n` // field over which calculations are done * * N = `2n**252n + 27742317777372353535851937790883648493n` // group order, amount of curve points * * h = 8 // cofactor * * a = `Fp.create(BigInt(-1))` // equation param * * d = -121665/121666 a.k.a. `Fp.neg(121665 * Fp.inv(121666))` // equation param * * Gx, Gy are coordinates of Generator / base point */ const ed25519_CURVE = { p: 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffedn, n: 0x1000000000000000000000000000000014def9dea2f79cd65812631a5cf5d3edn, h: 8n, a: 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffecn, d: 0x52036cee2b6ffe738cc740797779e89800700a4d4141d8ab75eb4dca135978a3n, Gx: 0x216936d3cd6e53fec0a4e231fdd6dc5c692cc7609525a7b2c9562d608f25d51an, Gy: 0x6666666666666666666666666666666666666666666666666666666666666658n }; const { p: P, n: N, Gx, Gy, a: _a, d: _d } = ed25519_CURVE; const h = 8n; const L = 32; const L2 = 64; const err = (m = "") => { throw new Error(m); }; const isBig = (n) => typeof n === "bigint"; const isStr = (s) => typeof s === "string"; const isBytes = (a) => a instanceof Uint8Array || ArrayBuffer.isView(a) && a.constructor.name === "Uint8Array"; /** assert is Uint8Array (of specific length) */ const abytes = (a, l) => !isBytes(a) || typeof l === "number" && l > 0 && a.length !== l ? err("Uint8Array expected") : a; /** create Uint8Array */ const u8n = (len) => new Uint8Array(len); const u8fr = (buf) => Uint8Array.from(buf); const padh = (n, pad) => n.toString(16).padStart(pad, "0"); const bytesToHex = (b) => Array.from(abytes(b)).map((e) => padh(e, 2)).join(""); const C = { _0: 48, _9: 57, A: 65, F: 70, a: 97, f: 102 }; const _ch = (ch) => { if (ch >= C._0 && ch <= C._9) return ch - C._0; if (ch >= C.A && ch <= C.F) return ch - (C.A - 10); if (ch >= C.a && ch <= C.f) return ch - (C.a - 10); return; }; const hexToBytes = (hex) => { const e = "hex invalid"; if (!isStr(hex)) return err(e); const hl = hex.length; const al = hl / 2; if (hl % 2) return err(e); const array = u8n(al); for (let ai = 0, hi = 0; ai < al; ai++, hi += 2) { const n1 = _ch(hex.charCodeAt(hi)); const n2 = _ch(hex.charCodeAt(hi + 1)); if (n1 === void 0 || n2 === void 0) return err(e); array[ai] = n1 * 16 + n2; } return array; }; /** normalize hex or ui8a to ui8a */ const toU8 = (a, len) => abytes(isStr(a) ? hexToBytes(a) : u8fr(abytes(a)), len); const cr = () => globalThis?.crypto; const subtle = () => cr()?.subtle ?? err("crypto.subtle must be defined"); const concatBytes = (...arrs) => { const r = u8n(arrs.reduce((sum, a) => sum + abytes(a).length, 0)); let pad = 0; arrs.forEach((a) => { r.set(a, pad); pad += a.length; }); return r; }; /** WebCrypto OS-level CSPRNG (random number generator). Will throw when not available. */ const randomBytes = (len = L) => { const c = cr(); return c.getRandomValues(u8n(len)); }; const big = BigInt; const arange = (n, min, max, msg = "bad number: out of range") => isBig(n) && min <= n && n < max ? n : err(msg); /** modular division */ const M = (a, b = P) => { const r = a % b; return r >= 0n ? r : b + r; }; const modN = (a) => M(a, N); /** Modular inversion using eucledian GCD (non-CT). No negative exponent for now. */ const invert = (num, md) => { if (num === 0n || md <= 0n) err("no inverse n=" + num + " mod=" + md); let a = M(num, md), b = md, x = 0n, y = 1n, u = 1n, v = 0n; while (a !== 0n) { const q = b / a, r = b % a; const m = x - u * q, n = y - v * q; b = a, a = r, x = u, y = v, u = m, v = n; } return b === 1n ? M(x, md) : err("no inverse"); }; const callHash = (name) => { const fn = etc[name]; if (typeof fn !== "function") err("hashes." + name + " not set"); return fn; }; const apoint = (p) => p instanceof Point ? p : err("Point expected"); const B256 = 2n ** 256n; /** Point in XYZT extended coordinates. */ var Point = class Point { constructor(ex, ey, ez, et) { (0, import_defineProperty.default)(this, "ex", void 0); (0, import_defineProperty.default)(this, "ey", void 0); (0, import_defineProperty.default)(this, "ez", void 0); (0, import_defineProperty.default)(this, "et", void 0); const max = B256; this.ex = arange(ex, 0n, max); this.ey = arange(ey, 0n, max); this.ez = arange(ez, 1n, max); this.et = arange(et, 0n, max); Object.freeze(this); } static fromAffine(p) { return new Point(p.x, p.y, 1n, M(p.x * p.y)); } /** RFC8032 5.1.3: Uint8Array to Point. */ static fromBytes(hex, zip215 = false) { const d = _d; const normed = u8fr(abytes(hex, L)); const lastByte = hex[31]; normed[31] = lastByte & -129; const y = bytesToNumLE(normed); const max = zip215 ? B256 : P; arange(y, 0n, max); const y2 = M(y * y); const u = M(y2 - 1n); const v = M(d * y2 + 1n); let { isValid, value: x } = uvRatio(u, v); if (!isValid) err("bad point: y not sqrt"); const isXOdd = (x & 1n) === 1n; const isLastByteOdd = (lastByte & 128) !== 0; if (!zip215 && x === 0n && isLastByteOdd) err("bad point: x==0, isLastByteOdd"); if (isLastByteOdd !== isXOdd) x = M(-x); return new Point(x, y, 1n, M(x * y)); } /** Checks if the point is valid and on-curve. */ assertValidity() { const a = _a; const d = _d; const p = this; if (p.is0()) throw new Error("bad point: ZERO"); const { ex: X, ey: Y, ez: Z, et: T } = p; const X2 = M(X * X); const Y2 = M(Y * Y); const Z2 = M(Z * Z); const Z4 = M(Z2 * Z2); const aX2 = M(X2 * a); const left = M(Z2 * M(aX2 + Y2)); const right = M(Z4 + M(d * M(X2 * Y2))); if (left !== right) throw new Error("bad point: equation left != right (1)"); const XY = M(X * Y); const ZT = M(Z * T); if (XY !== ZT) throw new Error("bad point: equation left != right (2)"); return this; } /** Equality check: compare points P&Q. */ equals(other) { const { ex: X1, ey: Y1, ez: Z1 } = this; const { ex: X2, ey: Y2, ez: Z2 } = apoint(other); const X1Z2 = M(X1 * Z2); const X2Z1 = M(X2 * Z1); const Y1Z2 = M(Y1 * Z2); const Y2Z1 = M(Y2 * Z1); return X1Z2 === X2Z1 && Y1Z2 === Y2Z1; } is0() { return this.equals(I); } /** Flip point over y coordinate. */ negate() { return new Point(M(-this.ex), this.ey, this.ez, M(-this.et)); } /** Point doubling. Complete formula. Cost: `4M + 4S + 1*a + 6add + 1*2`. */ double() { const { ex: X1, ey: Y1, ez: Z1 } = this; const a = _a; const A = M(X1 * X1); const B = M(Y1 * Y1); const C$1 = M(2n * M(Z1 * Z1)); const D = M(a * A); const x1y1 = X1 + Y1; const E = M(M(x1y1 * x1y1) - A - B); const G$1 = D + B; const F = G$1 - C$1; const H = D - B; const X3 = M(E * F); const Y3 = M(G$1 * H); const T3 = M(E * H); const Z3 = M(F * G$1); return new Point(X3, Y3, Z3, T3); } /** Point addition. Complete formula. Cost: `8M + 1*k + 8add + 1*2`. */ add(other) { const { ex: X1, ey: Y1, ez: Z1, et: T1 } = this; const { ex: X2, ey: Y2, ez: Z2, et: T2 } = apoint(other); const a = _a; const d = _d; const A = M(X1 * X2); const B = M(Y1 * Y2); const C$1 = M(T1 * d * T2); const D = M(Z1 * Z2); const E = M((X1 + Y1) * (X2 + Y2) - A - B); const F = M(D - C$1); const G$1 = M(D + C$1); const H = M(B - a * A); const X3 = M(E * F); const Y3 = M(G$1 * H); const T3 = M(E * H); const Z3 = M(F * G$1); return new Point(X3, Y3, Z3, T3); } /** * Point-by-scalar multiplication. Scalar must be in range 1 <= n < CURVE.n. * Uses {@link wNAF} for base point. * Uses fake point to mitigate side-channel leakage. * @param n scalar by which point is multiplied * @param safe safe mode guards against timing attacks; unsafe mode is faster */ multiply(n, safe = true) { if (!safe && (n === 0n || this.is0())) return I; arange(n, 1n, N); if (n === 1n) return this; if (this.equals(G)) return wNAF(n).p; let p = I; let f = G; for (let d = this; n > 0n; d = d.double(), n >>= 1n) if (n & 1n) p = p.add(d); else if (safe) f = f.add(d); return p; } /** Convert point to 2d xy affine point. (X, Y, Z) ∋ (x=X/Z, y=Y/Z) */ toAffine() { const { ex: x, ey: y, ez: z } = this; if (this.equals(I)) return { x: 0n, y: 1n }; const iz = invert(z, P); if (M(z * iz) !== 1n) err("invalid inverse"); return { x: M(x * iz), y: M(y * iz) }; } toBytes() { const { x, y } = this.assertValidity().toAffine(); const b = numTo32bLE(y); b[31] |= x & 1n ? 128 : 0; return b; } toHex() { return bytesToHex(this.toBytes()); } clearCofactor() { return this.multiply(big(h), false); } isSmallOrder() { return this.clearCofactor().is0(); } isTorsionFree() { let p = this.multiply(N / 2n, false).double(); if (N % 2n) p = p.add(this); return p.is0(); } static fromHex(hex, zip215) { return Point.fromBytes(toU8(hex), zip215); } get x() { return this.toAffine().x; } get y() { return this.toAffine().y; } toRawBytes() { return this.toBytes(); } }; (0, import_defineProperty.default)(Point, "BASE", void 0); (0, import_defineProperty.default)(Point, "ZERO", void 0); /** Generator / base point */ const G = new Point(Gx, Gy, 1n, M(Gx * Gy)); /** Identity / zero point */ const I = new Point(0n, 1n, 1n, 0n); Point.BASE = G; Point.ZERO = I; const numTo32bLE = (num) => hexToBytes(padh(arange(num, 0n, B256), L2)).reverse(); const bytesToNumLE = (b) => big("0x" + bytesToHex(u8fr(abytes(b)).reverse())); const pow2 = (x, power) => { let r = x; while (power-- > 0n) { r *= r; r %= P; } return r; }; const pow_2_252_3 = (x) => { const x2 = x * x % P; const b2 = x2 * x % P; const b4 = pow2(b2, 2n) * b2 % P; const b5 = pow2(b4, 1n) * x % P; const b10 = pow2(b5, 5n) * b5 % P; const b20 = pow2(b10, 10n) * b10 % P; const b40 = pow2(b20, 20n) * b20 % P; const b80 = pow2(b40, 40n) * b40 % P; const b160 = pow2(b80, 80n) * b80 % P; const b240 = pow2(b160, 80n) * b80 % P; const b250 = pow2(b240, 10n) * b10 % P; const pow_p_5_8 = pow2(b250, 2n) * x % P; return { pow_p_5_8, b2 }; }; const RM1 = 0x2b8324804fc1df0b2b4d00993dfbd7a72f431806ad2fe478c4ee1b274a0ea0b0n; const uvRatio = (u, v) => { const v3 = M(v * v * v); const v7 = M(v3 * v3 * v); const pow = pow_2_252_3(u * v7).pow_p_5_8; let x = M(u * v3 * pow); const vx2 = M(v * x * x); const root1 = x; const root2 = M(x * RM1); const useRoot1 = vx2 === u; const useRoot2 = vx2 === M(-u); const noRoot = vx2 === M(-u * RM1); if (useRoot1) x = root1; if (useRoot2 || noRoot) x = root2; if ((M(x) & 1n) === 1n) x = M(-x); return { isValid: useRoot1 || useRoot2, value: x }; }; const modL_LE = (hash) => modN(bytesToNumLE(hash)); const sha512a = (...m) => etc.sha512Async(...m); const sha512s = (...m) => callHash("sha512Sync")(...m); const hash2extK = (hashed) => { const head = hashed.slice(0, L); head[0] &= 248; head[31] &= 127; head[31] |= 64; const prefix = hashed.slice(L, L2); const scalar = modL_LE(head); const point = G.multiply(scalar); const pointBytes = point.toBytes(); return { head, prefix, scalar, point, pointBytes }; }; const getExtendedPublicKeyAsync = (priv) => sha512a(toU8(priv, L)).then(hash2extK); const getExtendedPublicKey = (priv) => hash2extK(sha512s(toU8(priv, L))); /** Creates 32-byte ed25519 public key from 32-byte private key. Async. */ const getPublicKeyAsync = (priv) => getExtendedPublicKeyAsync(priv).then((p) => p.pointBytes); const hashFinishA = (res) => sha512a(res.hashable).then(res.finish); const _sign = (e, rBytes, msg) => { const { pointBytes: P$1, scalar: s } = e; const r = modL_LE(rBytes); const R = G.multiply(r).toBytes(); const hashable = concatBytes(R, P$1, msg); const finish = (hashed) => { const S = modN(r + modL_LE(hashed) * s); return abytes(concatBytes(R, numTo32bLE(S)), L2); }; return { hashable, finish }; }; /** * Signs message (NOT message hash) using private key. Async. * Follows RFC8032 5.1.6. */ const signAsync = async (msg, privKey) => { const m = toU8(msg); const e = await getExtendedPublicKeyAsync(privKey); const rBytes = await sha512a(e.prefix, m); return hashFinishA(_sign(e, rBytes, m)); }; const veriOpts = { zip215: true }; const _verify = (sig, msg, pub, opts = veriOpts) => { sig = toU8(sig, L2); msg = toU8(msg); pub = toU8(pub, L); const { zip215 } = opts; let A; let R; let s; let SB; let hashable = Uint8Array.of(); try { A = Point.fromHex(pub, zip215); R = Point.fromHex(sig.slice(0, L), zip215); s = bytesToNumLE(sig.slice(L, L2)); SB = G.multiply(s, false); hashable = concatBytes(R.toBytes(), A.toBytes(), msg); } catch (error) {} const finish = (hashed) => { if (SB == null) return false; if (!zip215 && A.isSmallOrder()) return false; const k = modL_LE(hashed); const RkA = R.add(A.multiply(k, false)); return RkA.add(SB.negate()).clearCofactor().is0(); }; return { hashable, finish }; }; /** Verifies signature on message and public key. Async. Follows RFC8032 5.1.7. */ const verifyAsync = async (s, m, p, opts = veriOpts) => hashFinishA(_verify(s, m, p, opts)); /** Math, hex, byte helpers. Not in `utils` because utils share API with noble-curves. */ const etc = { sha512Async: async (...messages) => { const s = subtle(); const m = concatBytes(...messages); return u8n(await s.digest("SHA-512", m.buffer)); }, sha512Sync: void 0, bytesToHex, hexToBytes, concatBytes, mod: M, invert, randomBytes }; /** ed25519-specific key utilities. */ const utils = { getExtendedPublicKeyAsync, getExtendedPublicKey, randomPrivateKey: () => randomBytes(L), precompute: (w = 8, p = G) => { p.multiply(3n); return p; } }; const W = 8; const scalarBits = 256; const pwindows = Math.ceil(scalarBits / W) + 1; const pwindowSize = 2 ** (W - 1); const precompute = () => { const points = []; let p = G; let b = p; for (let w = 0; w < pwindows; w++) { b = p; points.push(b); for (let i = 1; i < pwindowSize; i++) { b = b.add(p); points.push(b); } p = b.double(); } return points; }; let Gpows = void 0; const ctneg = (cnd, p) => { const n = p.negate(); return cnd ? n : p; }; /** * Precomputes give 12x faster getPublicKey(), 10x sign(), 2x verify() by * caching multiples of G (base point). Cache is stored in 32MB of RAM. * Any time `G.multiply` is done, precomputes are used. * Not used for getSharedSecret, which instead multiplies random pubkey `P.multiply`. * * w-ary non-adjacent form (wNAF) precomputation method is 10% slower than windowed method, * but takes 2x less RAM. RAM reduction is possible by utilizing `.subtract`. * * !! Precomputes can be disabled by commenting-out call of the wNAF() inside Point#multiply(). */ const wNAF = (n) => { const comp = Gpows || (Gpows = precompute()); let p = I; let f = G; const pow_2_w = 2 ** W; const maxNum = pow_2_w; const mask = big(pow_2_w - 1); const shiftBy = big(W); for (let w = 0; w < pwindows; w++) { let wbits = Number(n & mask); n >>= shiftBy; if (wbits > pwindowSize) { wbits -= maxNum; n += 1n; } const off = w * pwindowSize; const offF = off; const offP = off + Math.abs(wbits) - 1; const isEven = w % 2 !== 0; const isNeg = wbits < 0; if (wbits === 0) f = f.add(ctneg(isEven, comp[offF])); else p = p.add(ctneg(isNeg, comp[offP])); } return { p, f }; }; //#endregion //#region src/polyfill/secrets.ts const PROHIBITED_KEY_USAGES = new Set([ "decrypt", "deriveBits", "deriveKey", "encrypt", "unwrapKey", "wrapKey" ]); const ED25519_PKCS8_HEADER = [ 48, 46, 2, 1, 0, 48, 5, 6, 3, 43, 101, 112, 4, 34, 4, 32 ]; function bufferSourceToUint8Array(data) { return data instanceof Uint8Array ? data : new Uint8Array(ArrayBuffer.isView(data) ? data.buffer : data); } let storageKeyBySecretKey_INTERNAL_ONLY_DO_NOT_EXPORT; let publicKeyBytesStore; function createKeyPairFromBytes$1(bytes, extractable, keyUsages) { const keyPair = createKeyPair_INTERNAL_ONLY_DO_NOT_EXPORT(extractable, keyUsages); const cache = storageKeyBySecretKey_INTERNAL_ONLY_DO_NOT_EXPORT ||= /* @__PURE__ */ new WeakMap(); cache.set(keyPair.privateKey, bytes); cache.set(keyPair.publicKey, bytes); return keyPair; } function createKeyPair_INTERNAL_ONLY_DO_NOT_EXPORT(extractable, keyUsages) { if (keyUsages.length === 0) throw new DOMException("Usages cannot be empty when creating a key.", "SyntaxError"); if (keyUsages.some((usage) => PROHIBITED_KEY_USAGES.has(usage))) throw new DOMException("Unsupported key usage for an Ed25519 key.", "SyntaxError"); const base = { [Symbol.toStringTag]: "CryptoKey", algorithm: Object.freeze({ name: "Ed25519" }) }; const privateKey = { ...base, extractable, type: "private", usages: Object.freeze(keyUsages.filter((usage) => usage === "sign")) }; const publicKey = { ...base, extractable: true, type: "public", usages: Object.freeze(keyUsages.filter((usage) => usage === "verify")) }; return Object.freeze({ privateKey: Object.freeze(privateKey), publicKey: Object.freeze(publicKey) }); } function getSecretKeyBytes_INTERNAL_ONLY_DO_NOT_EXPORT(key) { const secretKeyBytes = storageKeyBySecretKey_INTERNAL_ONLY_DO_NOT_EXPORT?.get(key); if (secretKeyBytes === void 0) throw new Error("Could not find secret key material associated with this `CryptoKey`"); return secretKeyBytes; } async function getPublicKeyBytes(key) { const publicKeyStore = publicKeyBytesStore ||= /* @__PURE__ */ new WeakMap(); const fromPublicStore = publicKeyStore.get(key); if (fromPublicStore) return fromPublicStore; const publicKeyBytes = await getPublicKeyAsync(getSecretKeyBytes_INTERNAL_ONLY_DO_NOT_EXPORT(key)); publicKeyStore.set(key, publicKeyBytes); return publicKeyBytes; } function base64UrlEncode(bytes) { return btoa(Array.from(bytes, (b) => String.fromCharCode(b)).join("")).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, ""); } function base64UrlDecode(value) { const m = value.length % 4; const base64Value = value.replace(/-/g, "+").replace(/_/g, "/").padEnd(value.length + (m === 0 ? 0 : 4 - m), "="); return Uint8Array.from(atob(base64Value), (c) => c.charCodeAt(0)); } async function exportKeyPolyfill(format, key) { if (key.extractable === false) throw new DOMException("key is not extractable", "InvalidAccessException"); switch (format) { case "raw": { if (key.type !== "public") throw new DOMException(`Unable to export a raw Ed25519 ${key.type} key`, "InvalidAccessError"); const publicKeyBytes = await getPublicKeyBytes(key); return publicKeyBytes; } case "pkcs8": { if (key.type !== "private") throw new DOMException(`Unable to export a pkcs8 Ed25519 ${key.type} key`, "InvalidAccessError"); const secretKeyBytes = getSecretKeyBytes_INTERNAL_ONLY_DO_NOT_EXPORT(key); return new Uint8Array([...ED25519_PKCS8_HEADER, ...secretKeyBytes]); } case "jwk": { const publicKeyBytes = await getPublicKeyBytes(key); const base = { crv: "Ed25519", ext: key.extractable, key_ops: key.usages, kty: "OKP", x: base64UrlEncode(publicKeyBytes) }; if (key.type === "private") { const secretKeyBytes = getSecretKeyBytes_INTERNAL_ONLY_DO_NOT_EXPORT(key); return Object.freeze({ ...base, d: base64UrlEncode(secretKeyBytes) }); } return Object.freeze({ ...base }); } default: throw new Error(`Exporting polyfilled Ed25519 keys in the "${format}" format is unimplemented`); } } /** * This function generates a key pair and stores the secret bytes associated with it in a * module-private cache. Instead of vending the actual secret bytes, it returns a `CryptoKeyPair` * that you can use with other methods in this package to produce signatures and derive public keys * associated with the secret. */ function generateKeyPolyfill(extractable, keyUsages) { const privateKeyBytes = utils.randomPrivateKey(); const keyPair = createKeyPairFromBytes$1(privateKeyBytes, extractable, keyUsages); return keyPair; } function isPolyfilledKey(key) { return !!storageKeyBySecretKey_INTERNAL_ONLY_DO_NOT_EXPORT?.has(key) || !!publicKeyBytesStore?.has(key); } async function signPolyfill(key, data) { if (key.type !== "private" || !key.usages.includes("sign")) throw new DOMException("Unable to use this key to sign", "InvalidAccessError"); const privateKeyBytes = getSecretKeyBytes_INTERNAL_ONLY_DO_NOT_EXPORT(key); const payload = bufferSourceToUint8Array(data); const signature$1 = await signAsync(payload, privateKeyBytes); return signature$1; } async function verifyPolyfill(key, signature$1, data) { if (key.type !== "public" || !key.usages.includes("verify")) throw new DOMException("Unable to use this key to verify", "InvalidAccessError"); const publicKeyBytes = await getPublicKeyBytes(key); try { return await verifyAsync(bufferSourceToUint8Array(signature$1), bufferSourceToUint8Array(data), publicKeyBytes); } catch { return false; } } function assertValidKeyUsages(keyUsages, type) { const prohibitedKeyUses = new Set([...type === "private" ? ["verify"] : ["sign"], ...PROHIBITED_KEY_USAGES]); if (keyUsages.some((usage) => prohibitedKeyUses.has(usage))) throw new DOMException("Unsupported key usage for a Ed25519 key", "SyntaxError"); } function importKeyPolyfill(format, keyData, extractable, keyUsages) { if (format === "raw") { const bytes = bufferSourceToUint8Array(keyData); assertValidKeyUsages(keyUsages, "public"); if (bytes.length !== 32) throw new DOMException("Ed25519 raw keys must be exactly 32-bytes", "DataError"); const publicKey = { [Symbol.toStringTag]: "CryptoKey", algorithm: Object.freeze({ name: "Ed25519" }), extractable, type: "public", usages: Object.freeze(keyUsages.filter((usage) => usage === "verify")) }; const cache = publicKeyBytesStore ||= /* @__PURE__ */ new WeakMap(); cache.set(publicKey, bytes); return publicKey; } if (format === "pkcs8") { const bytes = bufferSourceToUint8Array(keyData); assertValidKeyUsages(keyUsages, "private"); if (bytes.length !== 48) throw new DOMException("Invalid keyData", "DataError"); const header = bytes.slice(0, 16); if (!header.every((val, i) => val === ED25519_PKCS8_HEADER[i])) throw new DOMException("Invalid keyData", "DataError"); const secretKeyBytes = bytes.slice(16); const privateKey = { [Symbol.toStringTag]: "CryptoKey", algorithm: Object.freeze({ name: "Ed25519" }), extractable, type: "private", usages: Object.freeze(keyUsages.filter((usage) => usage === "sign")) }; const cache = storageKeyBySecretKey_INTERNAL_ONLY_DO_NOT_EXPORT ||= /* @__PURE__ */ new WeakMap(); cache.set(privateKey, secretKeyBytes); return privateKey; } if (format === "jwk") { const jwk = keyData; const type = "d" in jwk ? "private" : "public"; assertValidKeyUsages(keyUsages, type); const keyOps = new Set(jwk.key_ops ?? []); const sameKeyUsages = keyUsages.length === keyOps.size && [...keyUsages].every((x) => keyOps.has(x)); if (jwk.kty !== "OKP" || jwk.crv !== "Ed25519" || jwk.ext !== extractable || !sameKeyUsages) throw new DOMException("Invalid Ed25519 JWK", "DataError"); if (type === "public" && !jwk.x) throw new DOMException("Ed25519 JWK is missing public key coordinates", "DataError"); if (type === "private" && !jwk.d) throw new DOMException("Ed25519 JWK is missing private key coordinates", "DataError"); const usageToKeep = type === "public" ? "verify" : "sign"; const key = Object.freeze({ [Symbol.toStringTag]: "CryptoKey", algorithm: Object.freeze({ name: "Ed25519" }), extractable, type, usages: Object.freeze(keyUsages.filter((usage) => usage === usageToKeep)) }); if (type === "public") { const cache = publicKeyBytesStore ||= /* @__PURE__ */ new WeakMap(); cache.set(key, base64UrlDecode(jwk.x)); } else { const cache = storageKeyBySecretKey_INTERNAL_ONLY_DO_NOT_EXPORT ||= /* @__PURE__ */ new WeakMap(); cache.set(key, base64UrlDecode(jwk.d)); } return key; } throw new Error(`Importing Ed25519 keys in the "${format}" format is unimplemented`); } //#endregion //#region src/polyfill/install.ts function install() { if (globalThis.isSecureContext) { /** * Create `crypto.subtle` if it doesn't exist. */ const originalCryptoObject = globalThis.crypto ||= {}; const originalSubtleCrypto = originalCryptoObject.subtle ||= {}; /** * Override `SubtleCrypto#exportKey` */ const originalExportKey = originalSubtleCrypto.exportKey; originalSubtleCrypto.exportKey = async (...args) => { const [_, key] = args; if (isPolyfilledKey(key)) return await exportKeyPolyfill(...args); else if (originalExportKey) return await originalExportKey.apply(originalSubtleCrypto, args); else throw new TypeError("No native `exportKey` function exists to handle this call"); }; /** * Override `SubtleCrypto#generateKey` */ const originalGenerateKey = originalSubtleCrypto.generateKey; let originalGenerateKeySupportsEd25519; originalSubtleCrypto.generateKey = async (...args) => { const [algorithm] = args; if (algorithm !== "Ed25519") if (originalGenerateKey) return await originalGenerateKey.apply(originalSubtleCrypto, args); else throw new TypeError("No native `generateKey` function exists to handle this call"); let optimisticallyGeneratedKeyPair; if (originalGenerateKeySupportsEd25519 === void 0) originalGenerateKeySupportsEd25519 = new Promise((resolve) => { if (!originalGenerateKey) { resolve(originalGenerateKeySupportsEd25519 = false); return; } originalGenerateKey.apply(originalSubtleCrypto, args).then((keyPair) => { if (process.env.NODE_ENV !== "production") console.warn("`webcrypto-ed25519-polyfill` was installed in an environment that supports Ed25519 key manipulation natively. Falling back to the native implementation. Consider installing this polyfill only in environments where Ed25519 is not supported."); if (originalSubtleCrypto.generateKey !== originalGenerateKey) originalSubtleCrypto.generateKey = originalGenerateKey; optimisticallyGeneratedKeyPair = keyPair; resolve(originalGenerateKeySupportsEd25519 = true); }).catch(() => { resolve(originalGenerateKeySupportsEd25519 = false); }); }); if (typeof originalGenerateKeySupportsEd25519 === "boolean" ? originalGenerateKeySupportsEd25519 : await originalGenerateKeySupportsEd25519) if (optimisticallyGeneratedKeyPair) return optimisticallyGeneratedKeyPair; else if (originalGenerateKey) return await originalGenerateKey.apply(originalSubtleCrypto, args); else throw new TypeError("No native `generateKey` function exists to handle this call"); else { const [_, extractable, keyUsages] = args; return generateKeyPolyfill(extractable, keyUsages); } }; /** * Override `SubtleCrypto#sign` */ const originalSign = originalSubtleCrypto.sign; originalSubtleCrypto.sign = async (...args) => { const [_, key] = args; if (isPolyfilledKey(key)) { const [_$1, ...rest] = args; return await signPolyfill(...rest); } else if (originalSign) return await originalSign.apply(originalSubtleCrypto, args); else throw new TypeError("No native `sign` function exists to handle this call"); }; /** * Override `SubtleCrypto#verify` */ const originalVerify = originalSubtleCrypto.verify; originalSubtleCrypto.verify = async (...args) => { const [_, key] = args; if (isPolyfilledKey(key)) { const [_$1, ...rest] = args; return await verifyPolyfill(...rest); } else if (originalVerify) return await originalVerify.apply(originalSubtleCrypto, args); else throw new TypeError("No native `verify` function exists to handle this call"); }; /** * Override `SubtleCrypto#importKey` */ const originalImportKey = originalSubtleCrypto.importKey; let originalImportKeySupportsEd25519; originalSubtleCrypto.importKey = async (...args) => { const [format, keyData, algorithm] = args; if (algorithm !== "Ed25519") if (originalImportKey) return await originalImportKey.apply(originalSubtleCrypto, args); else throw new TypeError("No native `importKey` function exists to handle this call"); let optimisticallyImportedKey; if (originalImportKeySupportsEd25519 === void 0) originalImportKeySupportsEd25519 = new Promise((resolve) => { if (!originalImportKey) { resolve(originalImportKeySupportsEd25519 = false); return; } originalImportKey.apply(originalSubtleCrypto, args).then((key) => { if (process.env.NODE_ENV !== "production") console.warn("`webcrypto-ed25519-polyfill` was included in an environment that supports Ed25519 key manipulation natively. Falling back to the native implementation. Consider including this polyfill only in environments where Ed25519 is not supported."); if (originalSubtleCrypto.importKey !== originalImportKey) originalSubtleCrypto.importKey = originalImportKey; optimisticallyImportedKey = key; resolve(originalImportKeySupportsEd25519 = true); }).catch(() => { resolve(originalImportKeySupportsEd25519 = false); }); }); if (typeof originalImportKey === "boolean" ? originalImportKeySupportsEd25519 : await originalImportKeySupportsEd25519) if (optimisticallyImportedKey) return optimisticallyImportedKey; else if (originalImportKey) return await originalImportKey.apply(originalSubtleCrypto, args); else throw new TypeError("No native `importKey` function exists to handle this call"); else { const [_format, _keyData, _algorithm, extractable, keyUsages] = args; return importKeyPolyfill(format, keyData, extractable, keyUsages); } }; } } //#endregion //#region src/assertions.ts function assertIsSecureContext() { if (!globalThis.isSecureContext) throw new Error("Must be in a secure context (HTTPS, localhost, file://)"); } let cachedEd25519Decision; async function isEd25519CurveSupported(subtle$2) { if (cachedEd25519Decision === void 0) cachedEd25519Decision = new Promise((resolve) => { subtle$2.generateKey("Ed25519", false, ["sign", "verify"]).catch(() => { resolve(cachedEd25519Decision = false); }).then(() => { resolve(cachedEd25519Decision = true); }); }); if (typeof cachedEd25519Decision === "boolean") return cachedEd25519Decision; else return await cachedEd25519Decision; } function assertDigestCapabilityIsAvailable() { assertIsSecureContext(); if (typeof _crypto === "undefined" || typeof _crypto.subtle?.digest !== "function") throw new Error("SubtleCrypto.digest is not available"); } async function assertKeyGenerationIsAvailable() { assertIsSecureContext(); if (typeof _crypto === "undefined" || typeof _crypto.subtle?.generateKey !== "function") throw new Error("SubtleCrypto.generateKey is not available"); if (!await isEd25519CurveSupported(_crypto.subtle)) throw new Error("Ed25519 curve is not supported"); } function assertKeyExporterIsAvailable() { assertIsSecureContext(); if (typeof _crypto === "undefined" || typeof _crypto.subtle?.exportKey !== "function") throw new Error("SubtleCrypto.exportKey is not available"); } function assertSigningCapabilityIsAvailable() { assertIsSecureContext(); if (typeof _crypto === "undefined" || typeof _crypto.subtle?.sign !== "function") throw new Error("SubtleCrypto.sign is not available"); } function assertVerificationCapabilityIsAvailable() { assertIsSecureContext(); if (typeof _crypto === "undefined" || typeof _crypto.subtle?.verify !== "function") throw new Error("SubtleCrypto.verify is not available"); } function assertPRNGIsAvailable() { if (typeof _crypto === "undefined" || typeof _crypto.getRandomValues !== "function") throw new Error("Crypto.getRandomValues is not available"); } /** * Asserts that a given byte array is not empty. */ function assertByteArrayIsNotEmptyForCodec(codecDescription, bytes, offset = 0) { if (bytes.length - offset <= 0) throw new Error(`Empty byte array for ${codecDescription}`); } /** * Asserts that a given byte array has enough bytes to decode. */ function assertByteArrayHasEnoughBytesForCodec(codecDescription, expected, bytes, offset = 0) { const bytesLength = bytes.length - offset; if (bytesLength < expected) throw new Error(`Not enough bytes to decode ${codecDescription}. Expected: ${expected}, Actual: ${bytesLength}`); } /** * Asserts that a given offset is within the byte array bounds. * This range is between 0 and the byte array length and is inclusive. * An offset equals to the byte array length is considered a valid offset * as it allows the post-offset of codecs to signal the end of the byte array. */ function assertByteArrayOffsetIsNotOutOfRange(codecDescription, offset, bytesLength) { if (offset < 0 || offset > bytesLength) throw new Error(`Offset is out of range for ${codecDescription}: ${offset}`); } /** * Asserts that a given string matches a given alphabet. */ function assertValidBaseString(alphabet$2, testValue, givenValue = testValue) { if (!testValue.match(new RegExp(`^[${alphabet$2}]*$`))) throw new Error(`Invalid base${alphabet$2.length} string: ${givenValue}`); } //#endregion //#region src/codecs/core.ts /** * Get the encoded size of a given value in bytes. */ function getEncodedSize(value, encoder) { return "fixedSize" in encoder ? encoder.fixedSize : encoder.getSizeFromValue(value); } function createEncoder(encoder) { return Object.freeze({ ...encoder, encode: (value) => { const bytes = new Uint8Array(getEncodedSize(value, encoder)); encoder.write(value, bytes, 0); return bytes; } }); } function createDecoder(decoder) { return Object.freeze({ ...decoder, decode: (bytes, offset = 0) => decoder.read(bytes, offset)[0] }); } function createCodec(codec) { return Object.freeze({ ...codec, decode: (bytes, offset = 0) => codec.read(bytes, offset)[0], encode: (value) => { const bytes = new Uint8Array(getEncodedSize(value, codec)); codec.write(value, bytes, 0); return bytes; } }); } function isFixedSize(codec) { return "fixedSize" in codec && typeof codec.fixedSize === "number"; } function assertIsFixedSize(codec) { if (!isFixedSize(codec)) throw new Error("expected a fixed size codec"); } function isVariableSize(codec) { return !isFixedSize(codec); } function assertIsVariableSize(codec) { if (!isVariableSize(codec)) throw new Error("expected a variable size codec"); } function transformEncoder(encoder, unmap) { return createEncoder({ ...isVariableSize(encoder) ? { ...encoder, getSizeFromValue: (value) => encoder.getSizeFromValue(unmap(value)) } : encoder, write: (value, bytes, offset) => encoder.write(unmap(value), bytes, offset) }); } function transformDecoder(decoder, map) { return createDecoder({ ...decoder, read: (bytes, offset) => { const [value, newOffset] = decoder.read(bytes, offset); return [map(value, bytes, offset), newOffset]; } }); } function combineCodec(encoder, decoder) { if (isFixedSize(encoder) !== isFixedSize(decoder)) throw new Error("encoder and decoder size compatibility mismatch"); if (isFixedSize(encoder) && isFixedSize(decoder) && encoder.fixedSize !== decoder.fixedSize) throw new Error(`encoder and decoder fixed size mismatch ${encoder.fixedSize} !== ${decoder.fixedSize}`); if (!isFixedSize(encoder) && !isFixedSize(decoder) && encoder.maxSize !== decoder.maxSize) throw new Error(`encoder and decoder max size mismatch ${encoder.maxSize} !== ${decoder.maxSize}`); return { ...decoder, ...encoder, decode: decoder.decode, encode: encoder.encode, read: decoder.read, write: encoder.write }; } function transformCodec(codec, unmap, map) { return createCodec({ ...transformEncoder(codec, unmap), read: map ? transformDecoder(codec, map).read : codec.read }); } /** * Concatenates an array of `Uint8Array`s into a single `Uint8Array`. * Reuses the original byte array when applicable. */ const mergeBytes = (byteArrays) => { const nonEmptyByteArrays = byteArrays.filter((arr) => arr.length); if (nonEmptyByteArrays.length === 0) return byteArrays.length ? byteArrays[0] : new Uint8Array(); if (nonEmptyByteArrays.length === 1) return nonEmptyByteArrays[0]; const totalLength = nonEmptyByteArrays.reduce((total, arr) => total + arr.length, 0); const result = new Uint8Array(totalLength); let offset = 0; nonEmptyByteArrays.forEach((arr) => { result.set(arr, offset); offset += arr.length; }); return result; }; /** * Pads a `Uint8Array` with zeroes to the specified length. * If the array is longer than the specified length, it is returned as-is. */ const padBytes = (bytes, length) => { if (bytes.length >= length) return bytes; const paddedBytes = new Uint8Array(length).fill(0); paddedBytes.set(bytes); return paddedBytes; }; /** * Fixes a `Uint8Array` to the specified length. * If the array is longer than the specified length, it is truncated. * If the array is shorter than the specified length, it is padded with zeroes. */ const fixBytes = (bytes, length) => padBytes(bytes.length <= length ? bytes : bytes.slice(0, length), length); /** * Returns true if and only if the provided `data` byte array contains * the provided `bytes` byte array at the specified `offset`. */ function containsBytes(data, bytes, offset) { const slice = offset === 0 && data.length === bytes.length ? data : data.slice(offset, offset + bytes.length); if (slice.length !== bytes.length) return false; return bytes.every((b, i) => b === slice[i]); } /** * Creates a fixed-size encoder from a given encoder. * * @param encoder - The encoder to wrap into a fixed-size encoder. * @param fixedBytes - The fixed number of bytes to write. */ function fixEncoderSize(encoder, fixedBytes) { return createEncoder({ fixedSize: fixedBytes, write: (value, bytes, offset) => { const variableByteArray = encoder.encode(value); const fixedByteArray = variableByteArray.length > fixedBytes ? variableByteArray.slice(0, fixedBytes) : variableByteArray; bytes.set(fixedByteArray, offset); return offset + fixedBytes; } }); } /** * Creates a fixed-size decoder from a given decoder. * * @param decoder - The decoder to wrap into a fixed-size decoder. * @param fixedBytes - The fixed number of bytes to read. */ function fixDecoderSize(decoder, fixedBytes) { return createDecoder({ fixedSize: fixedBytes, read: (bytes, offset) => { assertByteArrayHasEnoughBytesForCodec("fixCodecSize", fixedBytes, bytes, offset); if (offset > 0 || bytes.length > fixedBytes) bytes = bytes.slice(offset, offset + fixedBytes); if (isFixedSize(decoder)) bytes = fixBytes(bytes, decoder.fixedSize); const [value] = decoder.read(bytes, 0); return [value, offset + fixedBytes]; } }); } /** * Creates a fixed-size codec from a given codec. * * @param codec - The codec to wrap into a fixed-size codec. * @param fixedBytes - The fixed number of bytes to read/write. */ function fixCodecSize(codec, fixedBytes) { return combineCodec(fixEncoderSize(codec, fixedBytes), fixDecoderSize(codec, fixedBytes)); } //#endregion //#region src/codecs/strings/base16.ts var HexC = /* @__PURE__ */ function(HexC$1) { HexC$1[HexC$1["ZERO"] = 48] = "ZERO"; HexC$1[HexC$1["NINE"] = 57] = "NINE"; HexC$1[HexC$1["A_UP"] = 65] = "A_UP"; HexC$1[HexC$1["F_UP"] = 70] = "F_UP"; HexC$1[HexC$1["A_LO"] = 97] = "A_LO"; HexC$1[HexC$1["F_LO"] = 102] = "F_LO"; return HexC$1; }(HexC || {}); function charCodeToBase16(char) { if (char >= HexC.ZERO && char <= HexC.NINE) return char - HexC.ZERO; if (char >= HexC.A_UP && char <= HexC.F_UP) return char - (HexC.A_UP - 10); if (char >= HexC.A_LO && char <= HexC.F_LO) return char - (HexC.A_LO - 10); } /** Encodes strings in base16. */ function getBase16Encoder() { return createEncoder({ getSizeFromValue: (value) => Math.ceil(value.length / 2), write(value, bytes, offset) { const len = value.length; const al = len / 2; if (len === 1) { const c = value.charCodeAt(0); const n = charCodeToBase16(c); if (n === void 0) throw new Error(`Invalid string for base16: ${value}`); bytes.set([n], offset); return 1 + offset; } const hexBytes = new Uint8Array(al); for (let i = 0, j = 0; i < al; i++) { const c1 = value.charCodeAt(j++); const c2 = value.charCodeAt(j++); const n1 = charCodeToBase16(c1); const n2 = charCodeToBase16(c2); if (n1 === void 0 || n2 === void 0 && !Number.isNaN(c2)) throw new Error(`Invalid string for base16: ${value}`); hexBytes[i] = !Number.isNaN(c2) ? n1 << 4 | (n2 ?? 0) : n1; } bytes.set(hexBytes, offset); return hexBytes.length + offset; } }); } /** Decodes strings in base16. */ function getBase16Decoder() { return createDecoder({ read(bytes, offset) { const value = bytes.slice(offset).reduce((str, byte) => str + byte.toString(16).padStart(2, "0"), ""); return [value, bytes.length]; } }); } /** Encodes and decodes strings in base16. */ function getBase16Codec() { return combineCodec(getBase16Encoder(), getBase16Decoder()); } const base16 = getBase16Codec(); //#endregion //#region src/keys/signatures.ts let base16Encoder; function assertIsSignature(putativeSignature) { if (!base16Encoder) base16Encoder = getBase16Encoder(); if (putativeSignature.length < 64 || putativeSignature.length > 88) throw new Error(`Invalid signature length: ${putativeSignature.length} out of range`); const bytes = base16Encoder.encode(putativeSignature); const numBytes = bytes.byteLength; if (numBytes !== 64) throw new Error(`Invalid signature length: ${numBytes} bytes`); } function isSignature(putativeSignature) { if (!base16Encoder) base16Encoder = getBase16Encoder(); if (putativeSignature.length < 64 || putativeSignature.length > 88) return false; const bytes = base16Encoder.encode(putativeSignature); const numBytes = bytes.byteLength; if (numBytes !== 64) return false; return true; } async function signBytes(key, data) { assertSigningCapabilityIsAvailable(); const signedData = await crypto.subtle.sign("Ed25519", key, data); return new Uint8Array(signedData); } function signature(putativeSignature) { assertIsSignature(putativeSignature); return putativeSignature; } async function verifySignature(key, signature$1, data) { assertVerificationCapabilityIsAvailable(); return await crypto.subtle.verify("Ed25519", key, signature$1, data); } //#endregion //#region src/keys/keys.ts function addPkcs8Header(bytes) { return new Uint8Array([ 48, 46, 2, 1, 0, 48, 5, 6, 3, 43, 101, 112, 4, 34, 4, 32, ...bytes ]); } async function createPrivateKeyFromBytes(bytes, extractable) { const actualLength = bytes.byteLength; if (actualLength !== 32) throw new Error(`Invalid private key length: ${actualLength}`); const privateKeyBytesPkcs8 = addPkcs8Header(bytes); return _crypto.subtle.importKey("pkcs8", privateKeyBytesPkcs8, "Ed25519", extractable ?? false, ["sign"]); } async function getPublicKeyFromPrivateKey(privateKey, extractable = false) { assertKeyExporterIsAvailable(); if (privateKey.extractable === false) throw new Error(`Private key ${privateKey} is not extractable`); const jwk = await _crypto.subtle.exportKey("jwk", privateKey); return await _crypto.subtle.importKey("jwk", { crv: "Ed25519", ext: extractable, key_ops: ["verify"], kty: "OKP", x: jwk.x }, "Ed25519", extractable, ["verify"]); } async function generateKeyPair() { await assertKeyGenerationIsAvailable(); const keyPair = await _crypto.subtle.generateKey("Ed25519", false, ["sign", "verify"]); return keyPair; } async function generateExtractableKeyPair() { await assertKeyGenerationIsAvailable(); const keyPair = await _crypto.subtle.generateKey("Ed25519", true, ["sign", "verify"]); return keyPair; } async function createKeyPairFromBytes(bytes, extractable) { assertPRNGIsAvailable(); if (bytes.byteLength !== 64) throw new Error(`invalid key pair length: ${bytes.byteLength}`); const [publicKey, privateKey] = await Promise.all([_crypto.subtle.importKey("raw", bytes.slice(32), "Ed25519", true, ["verify"]), createPrivateKeyFromBytes(bytes.slice(0, 32), extractable)]); const randomBytes$1 = new Uint8Array(32); _crypto.getRandomValues(randomBytes$1); const signedData = await signBytes(privateKey, randomBytes$1); const isValid = await verifySignature(publicKey, signedData, randomBytes$1); if (!isValid) throw new Error("public key must match private key"); return { privateKey, publicKey }; } async function createKeyPairFromPrivateKeyBytes(bytes, extractable = false) { const privateKeyPromise = createPrivateKeyFromBytes(bytes, extractable); const [publicKey, privateKey] = await Promise.all([(extractable ? privateKeyPromise : createPrivateKeyFromBytes(bytes, true)).then(async (privateKey$1) => await getPublicKeyFromPrivateKey(p