mirror of
https://github.com/kmc7468/arkvault.git
synced 2025-12-14 22:08:45 +00:00
114 lines
3.2 KiB
TypeScript
114 lines
3.2 KiB
TypeScript
import { error } from "@sveltejs/kit";
|
|
import ms from "ms";
|
|
import {
|
|
createClient,
|
|
getClient,
|
|
getClientByPubKeys,
|
|
countClientByPubKey,
|
|
createUserClient,
|
|
getAllUserClients,
|
|
getUserClient,
|
|
setUserClientStateToPending,
|
|
registerUserClientChallenge,
|
|
getUserClientChallenge,
|
|
markUserClientChallengeAsUsed,
|
|
} from "$lib/server/db/client";
|
|
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,
|
|
})),
|
|
};
|
|
};
|
|
|
|
const expiresIn = ms(env.challenge.userClientExp);
|
|
const expiresAt = () => new Date(Date.now() + expiresIn);
|
|
|
|
const createUserClientChallenge = async (
|
|
userId: number,
|
|
ip: string,
|
|
clientId: number,
|
|
encPubKey: string,
|
|
) => {
|
|
const { answer, challenge } = await generateChallenge(32, encPubKey);
|
|
await registerUserClientChallenge(userId, clientId, answer.toString("base64"), ip, expiresAt());
|
|
return challenge.toString("base64");
|
|
};
|
|
|
|
export const registerUserClient = async (
|
|
userId: number,
|
|
ip: string,
|
|
encPubKey: string,
|
|
sigPubKey: string,
|
|
) => {
|
|
let clientId;
|
|
|
|
const client = await getClientByPubKeys(encPubKey, sigPubKey);
|
|
if (client) {
|
|
const userClient = await getUserClient(userId, client.id);
|
|
if (userClient) {
|
|
error(409, "Client already registered");
|
|
}
|
|
|
|
await createUserClient(userId, client.id);
|
|
clientId = client.id;
|
|
} else {
|
|
if (!verifyPubKey(encPubKey) || !verifyPubKey(sigPubKey)) {
|
|
error(400, "Invalid public key(s)");
|
|
} else if (encPubKey === sigPubKey) {
|
|
error(400, "Public keys must be different");
|
|
} else if (
|
|
(await countClientByPubKey(encPubKey)) > 0 ||
|
|
(await countClientByPubKey(sigPubKey)) > 0
|
|
) {
|
|
error(409, "Public key(s) already registered");
|
|
}
|
|
|
|
clientId = await createClient(encPubKey, sigPubKey, userId);
|
|
}
|
|
|
|
return { challenge: await createUserClientChallenge(userId, ip, clientId, encPubKey) };
|
|
};
|
|
|
|
export const getUserClientStatus = async (userId: number, clientId: number) => {
|
|
const userClient = await getUserClient(userId, clientId);
|
|
if (!userClient) {
|
|
error(500, "Invalid access token");
|
|
}
|
|
|
|
return {
|
|
state: userClient.state,
|
|
isInitialMekNeeded: await isInitialMekNeeded(userId),
|
|
};
|
|
};
|
|
|
|
export const verifyUserClient = async (
|
|
userId: number,
|
|
ip: string,
|
|
answer: string,
|
|
sigAnswer: string,
|
|
) => {
|
|
const challenge = await getUserClientChallenge(answer, ip);
|
|
if (!challenge) {
|
|
error(401, "Invalid challenge answer");
|
|
} else if (challenge.userId !== userId) {
|
|
error(403, "Forbidden");
|
|
}
|
|
|
|
const client = await getClient(challenge.clientId);
|
|
if (!client) {
|
|
error(500, "Invalid challenge answer");
|
|
} else if (!verifySignature(Buffer.from(answer, "base64"), sigAnswer, client.sigPubKey)) {
|
|
error(401, "Invalid challenge answer signature");
|
|
}
|
|
|
|
await markUserClientChallengeAsUsed(challenge.id);
|
|
await setUserClientStateToPending(userId, challenge.clientId);
|
|
};
|