MEK 관련 DB 스키마 생성

This commit is contained in:
static
2024-12-29 18:01:02 +09:00
parent 46938ef700
commit bbba449819
5 changed files with 76 additions and 11 deletions

View File

@@ -47,7 +47,7 @@ export const setUserClientStateToPending = async (userId: number, clientId: numb
export const createUserClientChallenge = async (
userId: number,
clientId: number,
challenge: string,
answer: string,
allowedIp: string,
expiresAt: Date,
) => {
@@ -56,20 +56,20 @@ export const createUserClientChallenge = async (
.values({
userId,
clientId,
challenge,
answer,
allowedIp,
expiresAt,
})
.execute();
};
export const getUserClientChallenge = async (challenge: string, ip: string) => {
export const getUserClientChallenge = async (answer: string, ip: string) => {
const challenges = await db
.select()
.from(userClientChallenge)
.where(
and(
eq(userClientChallenge.challenge, challenge),
eq(userClientChallenge.answer, answer),
eq(userClientChallenge.allowedIp, ip),
gt(userClientChallenge.expiresAt, new Date()),
),

View File

@@ -18,7 +18,6 @@ export const userClient = sqliteTable(
state: text("state", { enum: ["challenging", "pending", "active"] })
.notNull()
.default("challenging"),
encKey: text("encrypted_key"),
},
(t) => ({
pk: primaryKey({ columns: [t.userId, t.clientId] }),
@@ -33,7 +32,7 @@ export const userClientChallenge = sqliteTable("user_client_challenge", {
clientId: integer("client_id")
.notNull()
.references(() => client.id),
challenge: text("challenge").notNull().unique(), // Base64
answer: text("challenge").notNull().unique(), // Base64
allowedIp: text("allowed_ip").notNull(),
expiresAt: integer("expires_at", { mode: "timestamp_ms" }).notNull(),
});

View File

@@ -1,3 +1,4 @@
export * from "./client";
export * from "./mek";
export * from "./token";
export * from "./user";

View File

@@ -0,0 +1,65 @@
import { sqliteTable, text, integer, primaryKey, foreignKey } from "drizzle-orm/sqlite-core";
import { client } from "./client";
import { user } from "./user";
export const mek = sqliteTable(
"master_encryption_key",
{
userId: integer("user_id")
.notNull()
.references(() => user.id),
version: integer("version").notNull(),
createdBy: integer("created_by")
.notNull()
.references(() => client.id),
createdAt: integer("created_at", { mode: "timestamp_ms" }).notNull(),
state: text("state", { enum: ["pending", "active", "retired", "dead"] })
.notNull()
.default("pending"),
},
(t) => ({
pk: primaryKey({ columns: [t.userId, t.version] }),
}),
);
export const clientMek = sqliteTable(
"client_master_encryption_key",
{
userId: integer("user_id")
.notNull()
.references(() => user.id),
clientId: integer("client_id")
.notNull()
.references(() => client.id),
mekVersion: integer("master_encryption_key_version").notNull(),
encMek: text("encrypted_master_encryption_key").notNull(),
},
(t) => ({
pk: primaryKey({ columns: [t.userId, t.clientId, t.mekVersion] }),
ref: foreignKey({
columns: [t.userId, t.mekVersion],
foreignColumns: [mek.userId, mek.version],
}),
}),
);
export const mekChallenge = sqliteTable(
"master_encryption_key_challenge",
{
userId: integer("user_id")
.notNull()
.references(() => user.id),
mekVersion: integer("master_encryption_key_version").notNull(),
answer: text("answer").notNull().unique(), // Base64
challenge: text("challenge").unique(), // Base64
allowedIp: text("allowed_ip").notNull(),
expiresAt: integer("expires_at", { mode: "timestamp_ms" }).notNull(),
},
(t) => ({
pk: primaryKey({ columns: [t.userId, t.mekVersion] }),
ref: foreignKey({
columns: [t.userId, t.mekVersion],
foreignColumns: [mek.userId, mek.version],
}),
}),
);

View File

@@ -17,13 +17,13 @@ const expiresIn = ms(env.challenge.pubKeyExp);
const expiresAt = () => new Date(Date.now() + expiresIn);
const generateChallenge = async (userId: number, ip: string, clientId: number, pubKey: string) => {
const challenge = await promisify(randomBytes)(32);
const challengeBase64 = challenge.toString("base64");
await createUserClientChallenge(userId, clientId, challengeBase64, ip, expiresAt());
const answer = await promisify(randomBytes)(32);
const answerBase64 = answer.toString("base64");
await createUserClientChallenge(userId, clientId, answerBase64, ip, expiresAt());
const pubKeyPem = `-----BEGIN PUBLIC KEY-----\n${pubKey}\n-----END PUBLIC KEY-----`;
const challengeEncrypted = publicEncrypt({ key: pubKeyPem, oaepHash: "sha256" }, challenge);
return challengeEncrypted.toString("base64");
const challenge = publicEncrypt({ key: pubKeyPem, oaepHash: "sha256" }, answer);
return challenge.toString("base64");
};
export const registerPubKey = async (userId: number, ip: string, pubKey: string) => {