Files
arkvault/src/lib/server/db/mek.ts
2025-01-20 19:40:38 +09:00

101 lines
2.7 KiB
TypeScript

import pg from "pg";
import { IntegrityError } from "./error";
import db from "./kysely";
import type { MekState } from "./schema";
interface Mek {
userId: number;
version: number;
state: MekState;
}
interface ClientMekWithDetails extends Mek {
clientId: number;
encMek: string;
encMekSig: string;
}
export const registerInitialMek = async (
userId: number,
createdBy: number,
encMek: string,
encMekSig: string,
) => {
await db.transaction().execute(async (trx) => {
try {
await trx
.insertInto("master_encryption_key")
.values({
user_id: userId,
version: 1,
state: "active",
})
.execute();
await trx
.insertInto("client_master_encryption_key")
.values({
user_id: userId,
client_id: createdBy,
version: 1,
encrypted_key: encMek,
encrypted_key_signature: encMekSig,
})
.execute();
await trx
.insertInto("master_encryption_key_log")
.values({
user_id: userId,
master_encryption_key_version: 1,
timestamp: new Date(),
action: "create",
action_by: createdBy,
})
.execute();
} catch (e) {
if (e instanceof pg.DatabaseError && e.code === "23505") {
throw new IntegrityError("MEK already registered");
}
throw e;
}
});
};
export const getInitialMek = async (userId: number) => {
const mek = await db
.selectFrom("master_encryption_key")
.selectAll()
.where("user_id", "=", userId)
.where("version", "=", 1)
.limit(1)
.executeTakeFirst();
return mek
? ({ userId: mek.user_id, version: mek.version, state: mek.state } satisfies Mek)
: null;
};
export const getAllValidClientMeks = async (userId: number, clientId: number) => {
const clientMeks = await db
.selectFrom("client_master_encryption_key")
.innerJoin("master_encryption_key", (join) =>
join
.onRef("client_master_encryption_key.user_id", "=", "master_encryption_key.user_id")
.onRef("client_master_encryption_key.version", "=", "master_encryption_key.version"),
)
.selectAll()
.where("client_master_encryption_key.user_id", "=", userId)
.where("client_master_encryption_key.client_id", "=", clientId)
.where((eb) => eb.or([eb("state", "=", "active"), eb("state", "=", "retired")]))
.execute();
return clientMeks.map(
({ user_id, client_id, version, state, encrypted_key, encrypted_key_signature }) =>
({
userId: user_id,
version,
state: state as "active" | "retired",
clientId: client_id,
encMek: encrypted_key,
encMekSig: encrypted_key_signature,
}) satisfies ClientMekWithDetails,
);
};