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";