diff --git a/js/promiseHelpers.js b/js/promiseHelpers.js new file mode 100644 index 0000000..4d46ec0 --- /dev/null +++ b/js/promiseHelpers.js @@ -0,0 +1,17 @@ +async function allSettledConcurrent(items, concurrency, fn) { + const results = new Array(items.length); + const queue = items.map((item, index) => ({ item, index })); + const doFn = async ({ item, index }) => { + try { + const value = await fn(item); + results[index] = { value, status: "fulfilled" }; + } catch (reason) { + results[index] = { reason, status: "rejected" }; + } + return queue.length && doFn(queue.shift()); + }; + const slots = queue.splice(0, concurrency).map(doFn); + await Promise.all(slots); + return results; +} +export { allSettledConcurrent }; diff --git a/js/wgHelpers.js b/js/wgHelpers.js new file mode 100644 index 0000000..6b2b0c9 --- /dev/null +++ b/js/wgHelpers.js @@ -0,0 +1,184 @@ +import { getRandomValues } from "node:crypto"; + +const clamp = (z) => { + z[31] = (z[31] & 127) | 64; + z[0] &= 248; +}; + +const generatePublicKey = (() => { + const gf = (init) => { + const r = new Float64Array(16); + if (init) { + for (let i = 0; i < init.length; ++i) { + r[i] = init[i]; + } + } + return r; + }; + + const carry = (o) => { + for (let i = 0; i < 16; ++i) { + o[(i + 1) % 16] += (i < 15 ? 1 : 38) * Math.floor(o[i] / 65536); + o[i] &= 0xffff; + } + }; + + const cswap = (p, q, b) => { + let t; + const c = ~(b - 1); + for (let i = 0; i < 16; ++i) { + t = c & (p[i] ^ q[i]); + p[i] ^= t; + q[i] ^= t; + } + }; + + const pack = (o, n) => { + let b; + const m = gf(); + const t = gf(); + for (let i = 0; i < 16; ++i) { + t[i] = n[i]; + } + carry(t); + carry(t); + carry(t); + for (let j = 0; j < 2; ++j) { + m[0] = t[0] - 0xffed; + for (let i = 1; i < 15; ++i) { + m[i] = t[i] - 0xffff - ((m[i - 1] >> 16) & 1); + m[i - 1] &= 0xffff; + } + m[15] = t[15] - 0x7fff - ((m[14] >> 16) & 1); + b = (m[15] >> 16) & 1; + m[14] &= 0xffff; + cswap(t, m, 1 - b); + } + for (let i = 0; i < 16; ++i) { + o[2 * i] = t[i] & 0xff; + o[2 * i + 1] = t[i] >> 8; + } + }; + + const add = (o, a, b) => { + for (let i = 0; i < 16; ++i) { + o[i] = (a[i] + b[i]) | 0; + } + }; + + const subtract = (o, a, b) => { + for (let i = 0; i < 16; ++i) { + o[i] = (a[i] - b[i]) | 0; + } + }; + + const multmod = (o, a, b) => { + const t = new Float64Array(31); + for (let i = 0; i < 16; ++i) { + for (let j = 0; j < 16; ++j) { + t[i + j] += a[i] * b[j]; + } + } + for (let i = 0; i < 15; ++i) { + t[i] += 38 * t[i + 16]; + } + for (let i = 0; i < 16; ++i) { + o[i] = t[i]; + } + carry(o); + carry(o); + }; + + const invert = (o, i) => { + const c = gf(); + for (let a = 0; a < 16; ++a) { + c[a] = i[a]; + } + for (let a = 253; a >= 0; --a) { + multmod(c, c, c); + if (a !== 2 && a !== 4) { + multmod(c, c, i); + } + } + for (let a = 0; a < 16; ++a) { + o[a] = c[a]; + } + }; + + const generatePublicKey = (privateKey) => { + let r; + const z = new Uint8Array(32); + const a = gf([1]); + const b = gf([9]); + const c = gf(); + const d = gf([1]); + const e = gf(); + const f = gf(); + const _121665 = gf([0xdb41, 1]); + const _9 = gf([9]); + for (let i = 0; i < 32; ++i) { + z[i] = privateKey[i]; + } + clamp(z); + for (let i = 254; i >= 0; --i) { + r = (z[i >>> 3] >>> (i & 7)) & 1; + cswap(a, b, r); + cswap(c, d, r); + add(e, a, c); + subtract(a, a, c); + add(c, b, d); + subtract(b, b, d); + multmod(d, e, e); + multmod(f, a, a); + multmod(a, c, a); + multmod(c, b, e); + add(e, a, c); + subtract(a, a, c); + multmod(b, a, a); + subtract(c, d, f); + multmod(a, c, _121665); + add(a, a, d); + multmod(c, c, a); + multmod(a, d, f); + multmod(d, b, _9); + multmod(b, e, e); + cswap(a, b, r); + cswap(c, d, r); + } + invert(c, c); + multmod(a, a, c); + pack(z, a); + return z; + }; + return generatePublicKey; +})(); +const generatePresharedKey = () => { + const privateKey = new Uint8Array(32); + getRandomValues(privateKey); + return privateKey; +}; + +const generatePrivateKey = () => { + const privateKey = generatePresharedKey(); + clamp(privateKey); + return privateKey; +}; + +const keyToBase64 = (key) => Buffer.from(key).toString("base64"); +const keyFromBase64 = (key) => Buffer.from(key, "base64"); + +const wireguard = { + presharedKey: () => keyToBase64(generatePresharedKey()), + privateKey: () => keyToBase64(generatePrivateKey()), + publicKey: (privateKey) => + keyToBase64(generatePublicKey(keyFromBase64(privateKey))), + generateKeypair: () => { + const privateKey = generatePrivateKey(); + const publicKey = generatePublicKey(privateKey); + return { + publicKey: keyToBase64(publicKey), + privateKey: keyToBase64(privateKey), + }; + }, +}; +export { wireguard };