@pact-toolbox/crypto
Version:
1,357 lines (1,343 loc) • 68.7 kB
JavaScript
//#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