diff --git a/src/lib/server/db/mek.ts b/src/lib/server/db/mek.ts index cfe2a52..36b0042 100644 --- a/src/lib/server/db/mek.ts +++ b/src/lib/server/db/mek.ts @@ -2,7 +2,7 @@ import { and, or, eq, lt, desc } from "drizzle-orm"; import db from "./drizzle"; import { mek, clientMek, userClient } from "./schema"; -export interface ClientMek { +interface ClientMek { clientId: number; encMek: string; } diff --git a/src/lib/server/services/mek.ts b/src/lib/server/services/mek.ts index 5ed39ed..a358879 100644 --- a/src/lib/server/services/mek.ts +++ b/src/lib/server/services/mek.ts @@ -1,14 +1,20 @@ import { error } from "@sveltejs/kit"; -import { getAllUserClients, setUserClientStateToActive } from "$lib/server/db/client"; +import { getAllUserClients, getClient, setUserClientStateToActive } from "$lib/server/db/client"; import { getAllValidClientMeks, registerInitialMek, registerActiveMek, getNextActiveMekVersion, - type ClientMek, } from "$lib/server/db/mek"; +import { verifySignature } from "$lib/server/modules/crypto"; import { isInitialMekNeeded } from "$lib/server/modules/mek"; +interface NewClientMek { + clientId: number; + encMek: string; + sigEncMek: string; +} + export const getClientMekList = async (userId: number, clientId: number) => { const clientMeks = await getAllValidClientMeks(userId, clientId); return { @@ -24,9 +30,17 @@ export const registerInitialActiveMek = async ( userId: number, createdBy: number, encMek: string, + sigEncMek: string, ) => { if (!(await isInitialMekNeeded(userId))) { - error(403, "Forbidden"); + error(409, "Initial MEK already registered"); + } + + const client = await getClient(createdBy); + if (!client) { + error(500, "Invalid access token"); + } else if (!verifySignature(encMek, sigEncMek, client.sigPubKey)) { + error(400, "Invalid signature"); } await registerInitialMek(userId, createdBy, encMek); @@ -36,7 +50,7 @@ export const registerInitialActiveMek = async ( export const registerNewActiveMek = async ( userId: number, createdBy: number, - clientMeks: ClientMek[], + clientMeks: NewClientMek[], ) => { const userClients = await getAllUserClients(userId); const activeUserClients = userClients.filter(({ state }) => state === "active"); @@ -49,6 +63,17 @@ export const registerNewActiveMek = async ( error(400, "Invalid key list"); } + const client = await getClient(createdBy); + if (!client) { + error(500, "Invalid access token"); + } else if ( + !clientMeks.every(({ encMek, sigEncMek }) => + verifySignature(encMek, sigEncMek, client.sigPubKey), + ) + ) { + error(400, "Invalid signature"); + } + const newMekVersion = await getNextActiveMekVersion(userId); await registerActiveMek(userId, newMekVersion, createdBy, clientMeks); }; diff --git a/src/routes/(fullscreen)/key/export/+page.svelte b/src/routes/(fullscreen)/key/export/+page.svelte index 74ee5ea..553e81c 100644 --- a/src/routes/(fullscreen)/key/export/+page.svelte +++ b/src/routes/(fullscreen)/key/export/+page.svelte @@ -71,7 +71,13 @@ ) throw new Error("Failed to upgrade token"); - if (!(await requestInitialMekRegistration(data.mekDraft, $clientKeyStore.encryptKey))) + if ( + !(await requestInitialMekRegistration( + data.mekDraft, + $clientKeyStore.encryptKey, + $clientKeyStore.signKey, + )) + ) throw new Error("Failed to register initial MEK"); await goto(data.redirectPath); diff --git a/src/routes/(fullscreen)/key/export/service.ts b/src/routes/(fullscreen)/key/export/service.ts index bd9493c..12d62c7 100644 --- a/src/routes/(fullscreen)/key/export/service.ts +++ b/src/routes/(fullscreen)/key/export/service.ts @@ -1,6 +1,6 @@ import { callAPI } from "$lib/hooks"; import { storeRSAKey } from "$lib/indexedDB"; -import { encodeToBase64, encryptRSAPlaintext } from "$lib/modules/crypto"; +import { encodeToBase64, encryptRSAPlaintext, signRSAMessage } from "$lib/modules/crypto"; import type { ClientKeys } from "$lib/stores"; export { requestTokenUpgrade } from "$lib/services/auth"; @@ -44,14 +44,19 @@ export const storeClientKeys = async (clientKeys: ClientKeys) => { export const requestInitialMekRegistration = async ( mekDraft: ArrayBuffer, encryptKey: CryptoKey, + signKey: CryptoKey, ) => { const mekDraftEncrypted = await encryptRSAPlaintext(mekDraft, encryptKey); + const mekDraftEncryptedSigned = await signRSAMessage(mekDraftEncrypted, signKey); const res = await callAPI("/api/mek/register/initial", { method: "POST", headers: { "Content-Type": "application/json", }, - body: JSON.stringify({ mek: encodeToBase64(mekDraftEncrypted) }), + body: JSON.stringify({ + mek: encodeToBase64(mekDraftEncrypted), + sigMek: encodeToBase64(mekDraftEncryptedSigned), + }), }); - return res.ok || res.status === 403; + return res.ok || res.status === 409; }; diff --git a/src/routes/api/mek/register/+server.ts b/src/routes/api/mek/register/+server.ts index 4afbacd..56f1d53 100644 --- a/src/routes/api/mek/register/+server.ts +++ b/src/routes/api/mek/register/+server.ts @@ -13,6 +13,7 @@ export const POST: RequestHandler = async ({ request, cookies }) => { z.object({ clientId: z.number(), mek: z.string().base64().nonempty(), + sigMek: z.string().base64().nonempty(), }), ), }) @@ -23,9 +24,10 @@ export const POST: RequestHandler = async ({ request, cookies }) => { await registerNewActiveMek( userId, clientId, - meks.map(({ clientId, mek }) => ({ + meks.map(({ clientId, mek, sigMek }) => ({ clientId, encMek: mek.trim(), + sigEncMek: sigMek.trim(), })), ); return text("MEK registered", { headers: { "Content-Type": "text/plain" } }); diff --git a/src/routes/api/mek/register/initial/+server.ts b/src/routes/api/mek/register/initial/+server.ts index a7b4f6b..771b886 100644 --- a/src/routes/api/mek/register/initial/+server.ts +++ b/src/routes/api/mek/register/initial/+server.ts @@ -13,11 +13,12 @@ export const POST: RequestHandler = async ({ request, cookies }) => { const zodRes = z .object({ mek: z.string().base64().nonempty(), + sigMek: z.string().base64().nonempty(), }) .safeParse(await request.json()); if (!zodRes.success) error(400, "Invalid request body"); - const { mek } = zodRes.data; + const { mek, sigMek } = zodRes.data; - await registerInitialActiveMek(userId, clientId, mek); + await registerInitialActiveMek(userId, clientId, mek, sigMek); return text("MEK registered", { headers: { "Content-Type": "text/plain" } }); };