mirror of
https://github.com/kmc7468/arkvault.git
synced 2025-12-14 22:08:45 +00:00
117 lines
3.2 KiB
TypeScript
117 lines
3.2 KiB
TypeScript
import { error } from "@sveltejs/kit";
|
|
import {
|
|
createClient,
|
|
getClient,
|
|
getClientByPubKeys,
|
|
createUserClient,
|
|
getAllUserClients,
|
|
getUserClient,
|
|
setUserClientStateToPending,
|
|
registerUserClientChallenge,
|
|
consumeUserClientChallenge,
|
|
} from "$lib/server/db/client";
|
|
import { IntegrityError } from "$lib/server/db/error";
|
|
import { verifyPubKey, verifySignature, generateChallenge } from "$lib/server/modules/crypto";
|
|
import { isInitialMekNeeded } from "$lib/server/modules/mek";
|
|
import env from "$lib/server/loadenv";
|
|
|
|
export const getUserClientList = async (userId: number) => {
|
|
const userClients = await getAllUserClients(userId);
|
|
return {
|
|
userClients: userClients.map(({ clientId, state }) => ({
|
|
id: clientId,
|
|
state: state as "pending" | "active",
|
|
})),
|
|
};
|
|
};
|
|
|
|
const expiresAt = () => new Date(Date.now() + env.challenge.userClientExp);
|
|
|
|
const createUserClientChallenge = async (
|
|
ip: string,
|
|
userId: number,
|
|
clientId: number,
|
|
encPubKey: string,
|
|
) => {
|
|
const { answer, challenge } = await generateChallenge(32, encPubKey);
|
|
const { id } = await registerUserClientChallenge(
|
|
userId,
|
|
clientId,
|
|
answer.toString("base64"),
|
|
ip,
|
|
expiresAt(),
|
|
);
|
|
return { id, challenge: challenge.toString("base64") };
|
|
};
|
|
|
|
export const registerUserClient = async (
|
|
userId: number,
|
|
ip: string,
|
|
encPubKey: string,
|
|
sigPubKey: string,
|
|
) => {
|
|
const client = await getClientByPubKeys(encPubKey, sigPubKey);
|
|
if (client) {
|
|
try {
|
|
await createUserClient(userId, client.id);
|
|
return await createUserClientChallenge(ip, userId, client.id, encPubKey);
|
|
} catch (e) {
|
|
if (e instanceof IntegrityError && e.message === "User client already exists") {
|
|
error(409, "Client already registered");
|
|
}
|
|
throw e;
|
|
}
|
|
} else {
|
|
if (encPubKey === sigPubKey) {
|
|
error(400, "Same public keys");
|
|
} else if (!verifyPubKey(encPubKey) || !verifyPubKey(sigPubKey)) {
|
|
error(400, "Invalid public key(s)");
|
|
}
|
|
|
|
try {
|
|
const { id: clientId } = await createClient(encPubKey, sigPubKey, userId);
|
|
return await createUserClientChallenge(ip, userId, clientId, encPubKey);
|
|
} catch (e) {
|
|
if (e instanceof IntegrityError && e.message === "Public key(s) already registered") {
|
|
error(409, "Public key(s) already used");
|
|
}
|
|
throw e;
|
|
}
|
|
}
|
|
};
|
|
|
|
export const verifyUserClient = async (
|
|
userId: number,
|
|
ip: string,
|
|
challengeId: number,
|
|
answerSig: string,
|
|
) => {
|
|
const challenge = await consumeUserClientChallenge(challengeId, userId, ip);
|
|
if (!challenge) {
|
|
error(403, "Invalid challenge answer");
|
|
}
|
|
|
|
const client = await getClient(challenge.clientId);
|
|
if (!client) {
|
|
error(500, "Invalid challenge answer");
|
|
} else if (
|
|
!verifySignature(Buffer.from(challenge.answer, "base64"), answerSig, client.sigPubKey)
|
|
) {
|
|
error(403, "Invalid challenge answer signature");
|
|
}
|
|
|
|
await setUserClientStateToPending(userId, client.id);
|
|
};
|
|
|
|
export const getUserClientStatus = async (userId: number, clientId: number) => {
|
|
const userClient = await getUserClient(userId, clientId);
|
|
if (!userClient) {
|
|
error(500, "Invalid session id");
|
|
}
|
|
|
|
return {
|
|
state: userClient.state as "pending" | "active",
|
|
isInitialMekNeeded: await isInitialMekNeeded(userId),
|
|
};
|
|
};
|