refactor(crypto): make system-wrapped DEKs the authoritative key path

DataCrypto and AuthManager now read keys through UserKeyManager: DEKs are
always unwrappable server-side, so the in-memory unlock session, DEK-in-JWT
wrapping, session-expiry data locks and ALLOW_APIKEY_DATA_UNLOCK are gone.
utils/user-crypto.ts is deleted; boot migration now cleans legacy wraps.
A one-release shim adopts DEKs from legacy dataKeyWrap tokens so active
password users migrate without re-login. Password login migrates legacy
password-wrapped DEKs via migratePasswordUserAtLogin.
This commit is contained in:
LukeGus
2026-07-16 00:19:12 -05:00
parent 16506de9da
commit ee7768dd86
14 changed files with 234 additions and 1114 deletions
+3 -6
View File
@@ -4,7 +4,7 @@ import { WebSocket } from "ws";
import { OPKSSHBinaryManager } from "../utils/opkssh-binary-manager.js";
import { sshLogger } from "../utils/logger.js";
import { createCurrentOpksshTokenRepository } from "../database/repositories/factory.js";
import { UserCrypto } from "../utils/user-crypto.js";
import { DataCrypto } from "../utils/data-crypto.js";
import { FieldCrypto } from "../utils/field-crypto.js";
import { promises as fs } from "fs";
import path from "path";
@@ -647,12 +647,10 @@ function handleOPKSSHOutput(requestId: string, output: string): void {
async function storeOPKSSHToken(session: OPKSSHAuthSession): Promise<void> {
try {
const userCrypto = UserCrypto.getInstance();
const expiresAt = new Date();
expiresAt.setHours(expiresAt.getHours() + 24);
const userDataKey = userCrypto.getUserDataKey(session.userId);
const userDataKey = DataCrypto.getUserDataKey(session.userId);
if (!userDataKey) {
throw new Error("User data key not found");
}
@@ -729,8 +727,7 @@ export async function getOPKSSHToken(
return null;
}
const userCrypto = UserCrypto.getInstance();
const userDataKey = userCrypto.getUserDataKey(userId);
const userDataKey = DataCrypto.getUserDataKey(userId);
if (!userDataKey) {
throw new Error("User data key not found");
}
+3 -4
View File
@@ -11,7 +11,7 @@ import { createCurrentHostResolutionRepository } from "../database/repositories/
import { sshLogger, authLogger } from "../utils/logger.js";
import { logAudit } from "../utils/audit-logger.js";
import { AuthManager } from "../utils/auth-manager.js";
import { UserCrypto } from "../utils/user-crypto.js";
import { DataCrypto } from "../utils/data-crypto.js";
import {
createSocks5Connection,
type SOCKS5Config,
@@ -98,7 +98,6 @@ interface WebSocketMessage {
}
const authManager = AuthManager.getInstance();
const userCrypto = UserCrypto.getInstance();
const userConnections = new Map<string, Set<WebSocket>>();
@@ -158,7 +157,7 @@ wss.on("connection", async (ws: WebSocket, req) => {
return;
}
const dataKey = userCrypto.getUserDataKey(userId);
const dataKey = DataCrypto.getUserDataKey(userId);
if (!dataKey) {
ws.send(
JSON.stringify({
@@ -268,7 +267,7 @@ wss.on("connection", async (ws: WebSocket, req) => {
}
ws.on("message", async (msg: RawData) => {
const currentDataKey = userCrypto.getUserDataKey(userId);
const currentDataKey = DataCrypto.getUserDataKey(userId);
if (!currentDataKey) {
ws.send(
JSON.stringify({
+3 -3
View File
@@ -17,7 +17,7 @@
// is re-exported here so callers have a single import surface.
import { createCurrentVaultTokenRepository } from "../database/repositories/factory.js";
import { UserCrypto } from "../utils/user-crypto.js";
import { DataCrypto } from "../utils/data-crypto.js";
import { FieldCrypto } from "../utils/field-crypto.js";
import { parseCertValidBefore } from "./vault-signer-core.js";
@@ -50,7 +50,7 @@ export async function storeVaultCert(
privateKey: string,
signedCert: string,
): Promise<string> {
const userDataKey = UserCrypto.getInstance().getUserDataKey(userId);
const userDataKey = DataCrypto.getUserDataKey(userId);
if (!userDataKey) {
throw new Error("User data key not found");
}
@@ -104,7 +104,7 @@ export async function getVaultCert(
return null;
}
const userDataKey = UserCrypto.getInstance().getUserDataKey(userId);
const userDataKey = DataCrypto.getUserDataKey(userId);
if (!userDataKey) {
throw new Error("User data key not found");
}