From ff98d3c9a0acf7a935666eae553ef1df8162185e Mon Sep 17 00:00:00 2001 From: shiftinv Date: Sun, 4 Sep 2022 14:39:03 +0200 Subject: [PATCH] feat: make url signing optional --- lib/config.ts | 2 +- lib/crypto.ts | 31 ++++++++++++++++++++++--------- lib/handler.ts | 11 ++++++----- main.ts | 4 ++++ 4 files changed, 33 insertions(+), 15 deletions(-) diff --git a/lib/config.ts b/lib/config.ts index 724155d..ae1399d 100644 --- a/lib/config.ts +++ b/lib/config.ts @@ -13,7 +13,7 @@ export default { debug: parseBool(get("DEBUG", "0")), hostname: get("HOSTNAME", "127.0.0.1"), port: parseInt(get("PORT", "8080")), - signKey: get("SIGN_KEY"), + signKey: get("SIGN_KEY", null), maxWebhookRetries: parseInt(get("MAX_RETRIES", "3")), maxWebhookRetryMs: parseInt(get("MAX_RETRY_MS", "30000")), }; diff --git a/lib/crypto.ts b/lib/crypto.ts index 1bc32ed..0dc6d9a 100644 --- a/lib/crypto.ts +++ b/lib/crypto.ts @@ -1,24 +1,37 @@ import config from "./config.ts"; import { hex } from "../deps.ts"; -const signKey = await crypto.subtle.importKey( - "raw", - hex.decode(new TextEncoder().encode(config.signKey)), - { name: "HMAC", hash: "SHA-256" }, - false, - ["sign", "verify"], -); +export const hasKey = !!config.signKey; const encoder = new TextEncoder(); +let _signKey: CryptoKey | undefined = undefined; + +async function getKey(): Promise { + if (_signKey === undefined) { + if (!config.signKey) throw new Error("Signature requested but no key configured"); + + _signKey = await crypto.subtle.importKey( + "raw", + hex.decode(new TextEncoder().encode(config.signKey)), + { name: "HMAC", hash: "SHA-256" }, + false, + ["sign", "verify"], + ); + } + return _signKey; +} + export async function sign(input: string): Promise { + const key = await getKey(); const inputData = encoder.encode(input); - const sig = await crypto.subtle.sign("HMAC", signKey, inputData); + const sig = await crypto.subtle.sign("HMAC", key, inputData); return new TextDecoder().decode(hex.encode(new Uint8Array(sig))); } export async function verify(input: string, signature: string): Promise { + const key = await getKey(); const signatureData = hex.decode(encoder.encode(signature)); const inputData = encoder.encode(input); - return await crypto.subtle.verify("HMAC", signKey, signatureData, inputData); + return await crypto.subtle.verify("HMAC", key, signatureData, inputData); } diff --git a/lib/handler.ts b/lib/handler.ts index c3e86e2..b60fc6a 100644 --- a/lib/handler.ts +++ b/lib/handler.ts @@ -1,5 +1,5 @@ import { http, log } from "../deps.ts"; -import { verify } from "./crypto.ts"; +import { hasKey, verify } from "./crypto.ts"; import filterWebhook from "./filter.ts"; import { UrlConfig } from "./types.d.ts"; import { parseBool } from "./util.ts"; @@ -13,14 +13,15 @@ export default async function handle(req: Request): Promise { // split url into parts const url = new URL(req.url); const [, id, token] = url.pathname.split("/"); - const signature = url.searchParams.get("sig"); - if (!id || !token || !signature) { + if (!id || !token) { throw http.createHttpError(400); } // verify signature - if (!(await verify(`${id}/${token}`, signature))) { - throw http.createHttpError(403); + if (hasKey) { + const signature = url.searchParams.get("sig"); + if (!signature) throw http.createHttpError(400); + if (!(await verify(`${id}/${token}`, signature))) throw http.createHttpError(403); } // extract data diff --git a/main.ts b/main.ts index 5332a40..c766cac 100644 --- a/main.ts +++ b/main.ts @@ -44,6 +44,10 @@ async function handleRequest(req: Request, connInfo: http.ConnInfo): Promise