diff --git a/src/lib/server/db/client.ts b/src/lib/server/db/client.ts index d22030d..ebd1815 100644 --- a/src/lib/server/db/client.ts +++ b/src/lib/server/db/client.ts @@ -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()), ), diff --git a/src/lib/server/db/schema/client.ts b/src/lib/server/db/schema/client.ts index f905308..75c9fc4 100644 --- a/src/lib/server/db/schema/client.ts +++ b/src/lib/server/db/schema/client.ts @@ -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(), }); diff --git a/src/lib/server/db/schema/index.ts b/src/lib/server/db/schema/index.ts index 68981bb..0cae4a4 100644 --- a/src/lib/server/db/schema/index.ts +++ b/src/lib/server/db/schema/index.ts @@ -1,3 +1,4 @@ export * from "./client"; +export * from "./mek"; export * from "./token"; export * from "./user"; diff --git a/src/lib/server/db/schema/mek.ts b/src/lib/server/db/schema/mek.ts new file mode 100644 index 0000000..e7314bd --- /dev/null +++ b/src/lib/server/db/schema/mek.ts @@ -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], + }), + }), +); diff --git a/src/lib/server/services/key.ts b/src/lib/server/services/key.ts index da86b2b..96cacbf 100644 --- a/src/lib/server/services/key.ts +++ b/src/lib/server/services/key.ts @@ -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) => {