const express = require("express"); const ed = require("@noble/ed25519"); const { sha512 } = require("@noble/hashes/sha2.js"); ed.hashes.sha512 = sha512; const config = require("./config.json"); const fs = require("node:fs"); const { parse: parseUrl } = require("node:url"); const dns = require("node:dns"); const fetch = require("sync-fetch"); const app = express(); const deasync = require("deasync"); const keysToBase64 = (keys) => { return { secretKey: new Buffer(keys.secretKey) .toString("base64") .replace("+", "-") .replace("/", "_") .replace(/=+$/, ""), publicKey: new Buffer(keys.publicKey) .toString("base64") .replace("+", "-") .replace("/", "_") .replace(/=+$/, ""), }; }; const base64ToKeys = (keys) => { const fixBase64 = (str) => str .replace("-", "+") .replace("_", "/") .padEnd(str.length + ((4 - (str.length % 4)) % 4), "="); return { secretKey: new Uint8Array(Buffer.from(fixBase64(keys.secretKey), "base64")), publicKey: new Uint8Array(Buffer.from(fixBase64(keys.publicKey), "base64")), }; }; if (!config.secretKey) { const keys = ed.keygen(); config.secretKey = keys.secretKey; config.publicKey = keys.publicKey; fs.writeFileSync( "config.json", JSON.stringify({ ...config, ...keysToBase64(config) }), ); } const delegation = (name) => { const res = fetch(`https://${name}/.well-known/matrix/server`); if (res.status === 200) { const json = res.json(); if (!json["m.server"].includes(":")) { return delegation(json["m.server"]); } return { domain: json["m.server"], host: json["m.server"] }; } else { const resolve = deasync(dns.resolveSrv); try { const res = resolve(`_matrix-fed._tcp.${name}`)[0]; return { domain: `${res.name}:${res.port}`, host: name || res.name, }; } catch (_e) { try { const res = resolve(`_matrix._tcp.${name}`)[0]; return { domain: `${res.name}:${res.port}`, host: name || res.name }; } catch (_e) { return { domain: `${name}:8448`, host: name || res.name, }; } } } }; const makeSignedRequest = (origin, method, url) => { const parsed = parseUrl(url); let payload = { method: method, uri: parsed.path, destination: parsed.hostname, origin: origin, }; payload = Object.fromEntries( Object.entries(payload).sort(([keyA], [keyB]) => keyA.localeCompare(keyB)), ); const delegate = delegation(parsed.hostname); const keys = base64ToKeys(config); const signature = ed.sign( new TextEncoder().encode(JSON.stringify(payload)), keys.secretKey, ); console.log( `X-Matrix origin="${origin}",destination="${parsed.hostname}",key="ed25519:${config.publicKey.slice(0, 5)}",sig="${new Buffer(signature).toString("base64").replace("+", "-").replace("/", "_").replace(/=+$/, "")}"`, ); return fetch( `https://${delegate.domain}${parsed.path}`, { headers: { Host: delegate.host, Authorization: `X-Matrix origin="${origin}",destination="${parsed.hostname}",key="ed25519:${config.publicKey.slice(0, 5)}",sig="${new Buffer(signature).toString("base64").replace("+", "-").replace("/", "_").replace(/=+$/, "")}"`, }, }, ); }; app.get("/.well-known/matrix/server", (req, res) => { return res.json({ "m.server": req.host.includes(":") ? req.host : `${req.host}:443`, }); }); app.get("/_matrix/federation/v1/version", (_req, res) => { res.json({ server: { name: "idk", version: "0.0.0.0.0.0.0.1", }, }); }); app.get("/_matrix/key/v2/server", (req, res) => { const payload = { old_verify_keys: {}, server_name: req.host.replace(":443", ""), valid_until_ts: Number.MAX_SAFE_INTEGER, verify_keys: {}, }; payload.verify_keys[`ed25519:${config.publicKey.slice(0, 5)}`] = { key: config.publicKey, }; const keys = base64ToKeys(config); const signature = ed.sign( new TextEncoder().encode( JSON.stringify( Object.fromEntries( Object.entries(payload).sort(([keyA], [keyB]) => keyA.localeCompare(keyB), ), ), ), ), keys.secretKey, ); payload.signatures = {}; payload.signatures[req.host.replace(":443", "")] = {}; payload.signatures[req.host.replace(":443", "")][ `ed25519:${config.publicKey.slice(0, 5)}` ] = new Buffer(signature) .toString("base64") .replace("+", "-") .replace("/", "_") .replace(/=+$/, ""); res.json(payload); }); app.get("/_test/version", (req, res) => { const result = delegation(req.query.domain); console.log(result); res.json( fetch(`https://${result.domain}/_matrix/federation/v1/version`, { headers: { Host: result.host }, }).json(), ); }); app.get("/_test/alias", (req, res) => { res.json( makeSignedRequest( req.host.replace(":443", ""), "GET", "https://yjsryd6gyhgl5eyikakf2xe5o4.srv.us/_matrix/federation/v1/query/directory?room_alias=%23ok%3Aorehus.club", ).json(), ); }); app.listen(3000, () => console.log("Listening at 3000"));