diff --git a/src/util/Signing.ts b/src/util/Signing.ts index 141ca0ffc..4aa29a24d 100644 --- a/src/util/Signing.ts +++ b/src/util/Signing.ts @@ -19,6 +19,9 @@ import { Config } from "@spacebar/util"; import { createHmac, timingSafeEqual } from "node:crypto"; import ms, { StringValue } from "ms"; +import { TextDecoder } from "node:util"; +import { ConsoleUtils } from "./util/ConsoleUtils"; +import { ColorUtils } from "./util/ColorUtils"; export class NewUrlUserSignatureData { ip?: string; @@ -132,11 +135,14 @@ export const getUrlSignature = (data: NewUrlSignatureData): UrlSignResult => { function calculateHash(request: UrlSignatureData): UrlSignResult { const { cdnSignatureKey } = Config.get().security; + const newData = createHmac("sha256", cdnSignatureKey as string); const data = createHmac("sha256", cdnSignatureKey as string) .update(request.path!) .update(request.issuedAt) .update(request.expiresAt); + let ipData = new Int8Array(); + if (Config.get().security.cdnSignatureIncludeIp) { if (!request.ip) console.log( @@ -145,9 +151,11 @@ function calculateHash(request: UrlSignatureData): UrlSignResult { else { if (process.env["LOG_CDN_SIGNATURES"]) console.log("[Signing] CDN Signature IP is enabled, adding IP to hash:", request.ip); data.update(request.ip!); + ipData = new Int8Array(new TextEncoder().encode(request.ip!)); } } + let userAgentData = new Int8Array(); if (Config.get().security.cdnSignatureIncludeUserAgent) { if (!request.userAgent) console.log( @@ -156,10 +164,29 @@ function calculateHash(request: UrlSignatureData): UrlSignResult { else { if (process.env["LOG_CDN_SIGNATURES"]) console.log("[Signing] CDN Signature User-Agent is enabled, adding User-Agent to hash:", request.userAgent); data.update(request.userAgent!); + userAgentData = new Int8Array(new TextEncoder().encode(request.userAgent!)); } } + const rawData = new Int8Array([ + ...new TextEncoder().encode(request.path!), + ...new TextEncoder().encode(request.issuedAt), + ...new TextEncoder().encode(request.expiresAt), + ...ipData, + ...userAgentData, + ]); + if (process.env["LOG_CDN_SIGNATURES"]) { + console.log("[Signing] Signature data for ", request.path!); + hexdump(rawData); + } + newData.update(rawData); + const hash = data.digest("hex"); + const newHash = newData.digest("hex"); + if (process.env["LOG_CDN_SIGNATURES"]) { + console.log(hash); + console.log(newHash); + } const result = new UrlSignResult({ path: request.path, issuedAt: request.issuedAt, @@ -257,3 +284,29 @@ export const hasValidSignature = (req: NewUrlUserSignatureData, sig: UrlSignResu return isHashValid; }; + +// port of https://github.com/TheArcaneBrony/ArcaneLibs/blob/master/ArcaneLibs/Extensions/DictionaryExtensions.cs#L80 +function hexdump(arr: Int8Array, width: number = 32, colorize: boolean = true) { + const colorizeFunc = colorize ? (val: number, str: string) => ConsoleUtils.ColoredString(str, ColorUtils.cnv8To24(val)) : (val: number, str: string) => str; + for (let i = 0; i < arr.length; i += width) { + const end = Math.min(i + width, arr.length); + const section = arr.slice(i, end); + console.log( + Array.from(section) + .map((x) => colorizeFunc(x, x.toString(16).toUpperCase())) + .join(" ") + .padEnd(3 * section.length), + "|", + new TextDecoder("utf-8") + .decode(section) + .replaceAll("\n", ".") + .replaceAll("\r", ".") + .replaceAll("\0", ".") + .replaceAll("\t", ".") + .replaceAll("\v", ".") + .replaceAll("\b", ".") + .replaceAll("\x07", ".") // \a + .replaceAll("\f", "."), + ); + } +} diff --git a/src/util/util/ColorUtils.ts b/src/util/util/ColorUtils.ts new file mode 100644 index 000000000..8b9343f22 --- /dev/null +++ b/src/util/util/ColorUtils.ts @@ -0,0 +1,42 @@ +/* + Spacebar: A FOSS re-implementation and extension of the Discord.com backend. + Copyright (C) 2023 Spacebar and Spacebar Contributors + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . +*/ + +export class RgbValue { + r: number; + g: number; + b: number; + constructor(r: number, g: number, b: number) { + this.r = r; + this.g = g; + this.b = b; + } + + public asHex(withSigil: boolean = true) { + return `${withSigil ? "#" : ""}${this.r.toString(16).toUpperCase()}${this.g.toString(16).toUpperCase()}${this.b.toString(16).toUpperCase()}`; + } + + public asAnsiEscapeSequence() { + return `\x1b[38;2;${this.r};${this.g};${this.b}m`; + } +} + +export class ColorUtils { + public static cnv8To24(val: number): RgbValue { + return new RgbValue((val >> 5) * 32, ((val & 28) >> 2) * 32, (val & 3) * 64); + } +} diff --git a/src/util/util/ConsoleUtils.ts b/src/util/util/ConsoleUtils.ts new file mode 100644 index 000000000..99f219335 --- /dev/null +++ b/src/util/util/ConsoleUtils.ts @@ -0,0 +1,32 @@ +/* + Spacebar: A FOSS re-implementation and extension of the Discord.com backend. + Copyright (C) 2026 Spacebar and Spacebar Contributors + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . +*/ + +// Minor port of https://github.com/TheArcaneBrony/ArcaneLibs/blob/master/ArcaneLibs/ConsoleUtils.cs +import { RgbValue } from "./ColorUtils"; + +export class ConsoleUtils { + public static ColorSequence(rgb: RgbValue) { + return rgb.asAnsiEscapeSequence(); + } + public static SetConsoleColor(rgb: RgbValue) { + console.log(rgb.asAnsiEscapeSequence()); + } + public static ColoredString(text: string, rgb: RgbValue) { + return rgb.asAnsiEscapeSequence() + text + "\x1b[0m"; + } +} diff --git a/src/util/util/Logo.ts b/src/util/util/Logo.ts index a1daebb6a..539c8d75e 100644 --- a/src/util/util/Logo.ts +++ b/src/util/util/Logo.ts @@ -1,3 +1,21 @@ +/* + Spacebar: A FOSS re-implementation and extension of the Discord.com backend. + Copyright (C) 2023 Spacebar and Spacebar Contributors + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . +*/ + import { KittyLogo } from "."; import { blueBright } from "picocolors";