From 7d96848386eb13ee8b8b863e9d3bd2e962dbcd87 Mon Sep 17 00:00:00 2001 From: Fijxu Date: Mon, 24 Mar 2025 21:38:33 -0300 Subject: [PATCH 10/13] feat: add support for encrypted query parameters --- src/lib/helpers/config.ts | 9 ++++ src/lib/helpers/encrypter.ts | 56 ++++++++++++++++++++ src/routes/invidious_routes/dashManifest.ts | 18 +++++-- src/routes/invidious_routes/latestVersion.ts | 14 ++++- 4 files changed, 91 insertions(+), 6 deletions(-) create mode 100644 src/lib/helpers/encrypter.ts diff --git a/src/lib/helpers/config.ts b/src/lib/helpers/config.ts index a2f6be0..83ce0fa 100644 --- a/src/lib/helpers/config.ts +++ b/src/lib/helpers/config.ts @@ -14,6 +14,15 @@ const ConfigSchema = z.object({ max_dash_resolution: z.number().default( Number(Deno.env.get("SERVER_MAX_DASH_RESOLUTION")), ), + encrypt_query_params: z + .boolean() + .default( + Deno.env.get("ENCRYPT_QUERY_PARAMS") === "true" + ? true + : Deno.env.get("ENCRYPT_QUERY_PARAMS") === "false" + ? false + : true, + ), }).strict().default({}), cache: z.object({ enabled: z.boolean().default(true), diff --git a/src/lib/helpers/encrypter.ts b/src/lib/helpers/encrypter.ts new file mode 100644 index 0000000..4b45590 --- /dev/null +++ b/src/lib/helpers/encrypter.ts @@ -0,0 +1,56 @@ +import { decodeBase64, encodeBase64 } from "@std/encoding/base64"; +import { Aes } from "crypto/aes.ts"; +import { Ecb, Padding } from "crypto/block-modes.ts"; +import { Config } from "./config.ts"; + +export const encryptQuery = ( + queryParams: string, + config: Config, +): string => { + try { + const cipher = new Ecb( + Aes, + new TextEncoder().encode(config.server.secret_key.substring(0, 16)), + Padding.PKCS7, + ); + + const encodedData = new TextEncoder().encode( + queryParams, + ); + + const encryptedData = cipher.encrypt(encodedData); + + return encodeBase64(encryptedData).replace(/\+/g, "-").replace( + /\//g, + "_", + ); + } catch (_) { + return ""; + } +}; + +export const decryptQuery = ( + queryParams: string, + config: Config, +): string => { + try { + const decipher = new Ecb( + Aes, + new TextEncoder().encode(config.server.secret_key.substring(0, 16)), + Padding.PKCS7, + ); + + const decryptedData = new TextDecoder().decode( + decipher.decrypt( + decodeBase64( + queryParams.replace(/-/g, "+").replace(/_/g, "/"), + ), + ), + ); + + console.log(decryptedData); + return decryptedData; + } catch (_) { + return ""; + } +}; diff --git a/src/routes/invidious_routes/dashManifest.ts b/src/routes/invidious_routes/dashManifest.ts index 68ae21d..a4c0950 100644 --- a/src/routes/invidious_routes/dashManifest.ts +++ b/src/routes/invidious_routes/dashManifest.ts @@ -6,6 +6,7 @@ import { } from "../../lib/helpers/youtubePlayerHandling.ts"; import { verifyRequest } from "../../lib/helpers/verifyRequest.ts"; import { HTTPException } from "hono/http-exception"; +import { encryptQuery } from "../../lib/helpers/encrypter.ts"; const dashManifest = new Hono(); @@ -88,14 +89,23 @@ dashManifest.get("/:videoId", async (c) => { videoInfo.page[0].video_details?.is_post_live_dvr, (url: URL) => { let dashUrl = url; + let queryParams = dashUrl.search.substring(1) + "&host=" + + dashUrl.host; + if (local) { - // Can't create URL type without host part - dashUrl = config.networking.external_videoplayback_proxy + - (dashUrl.pathname + dashUrl.search + "&host=" + - dashUrl.host) as unknown as URL; if (config.networking.ump) { dashUrl = dashUrl + "&ump=1" as unknown as URL; } + if (config.server.encrypt_query_params) { + queryParams = "enc=yes&data=" + encryptQuery( + queryParams, + config, + ); + } + // Can't create URL type without host part + dashUrl = config.networking.external_videoplayback_proxy + + (dashUrl.pathname + "?" + + queryParams) as unknown as URL; return dashUrl; } else { return dashUrl; diff --git a/src/routes/invidious_routes/latestVersion.ts b/src/routes/invidious_routes/latestVersion.ts index a8ead67..f1a7605 100644 --- a/src/routes/invidious_routes/latestVersion.ts +++ b/src/routes/invidious_routes/latestVersion.ts @@ -5,6 +5,7 @@ import { youtubeVideoInfo, } from "../../lib/helpers/youtubePlayerHandling.ts"; import { verifyRequest } from "../../lib/helpers/verifyRequest.ts"; +import { encryptQuery } from "../../lib/helpers/encrypter.ts"; const latestVersion = new Hono(); @@ -63,10 +64,19 @@ latestVersion.get("/", async (c) => { const itagUrl = selectedItagFormat[0].url as string; const itagUrlParsed = new URL(itagUrl); let urlToRedirect = itagUrlParsed.toString(); + let queryParams = itagUrlParsed.search.substring(1) + "&host=" + + itagUrlParsed.host; + if (local) { + if (config.server.encrypt_query_params) { + queryParams = "enc=yes&data=" + encryptQuery( + queryParams, + config, + ); + } + urlToRedirect = config.networking.external_videoplayback_proxy + - itagUrlParsed.pathname + itagUrlParsed.search + - "&host=" + itagUrlParsed.host; + itagUrlParsed.pathname + "?" + queryParams; } if (title) urlToRedirect += `&title=${encodeURIComponent(title)}`; -- 2.49.0