diff --git a/client/src/services/auth.ts b/client/src/services/auth.ts index 9169f98..62291c4 100644 --- a/client/src/services/auth.ts +++ b/client/src/services/auth.ts @@ -147,6 +147,7 @@ export interface LoginResult { encryptedMasterKeyRecTag: string userId: string username: string + prfFirst?: ArrayBuffer } } @@ -231,7 +232,8 @@ export async function loginUser(username?: string): Promise { encryptedMasterKeyRecIv: result.encryptedMasterKeyRecIv, encryptedMasterKeyRecTag: result.encryptedMasterKeyRecTag, userId: result.userId, - username: resolvedUsername + username: resolvedUsername, + prfFirst: prfResults?.results?.first } } } @@ -245,6 +247,7 @@ export async function completeLoginWithRecovery( encryptedMasterKeyRecIv: string encryptedMasterKeyRecTag: string userId: string + prfFirst?: ArrayBuffer } ): Promise { try { @@ -255,6 +258,32 @@ export async function completeLoginWithRecovery( encryptedPayloads.encryptedMasterKeyRecTag, recoveryKey ) + + // If PRF results are available from the login challenge, enroll them now + if (encryptedPayloads.prfFirst) { + try { + const prfKey = await deriveKeyFromPrf(encryptedPayloads.prfFirst) + const encryptedPrf = await encryptBuffer(decryptedMaster, prfKey) + const enrollRes = await fetch(`${API_BASE}/enroll-prf`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'X-User-Id': encryptedPayloads.userId + }, + body: JSON.stringify({ + encryptedMasterKeyPrf: encryptedPrf.ciphertext, + encryptedMasterKeyPrfIv: encryptedPrf.iv, + encryptedMasterKeyPrfTag: encryptedPrf.tag + }) + }) + if (!enrollRes.ok) { + console.warn('Server rejected PRF enrollment') + } + } catch (err) { + console.error('Failed to encrypt/enroll master key with PRF key:', err) + } + } + setActiveMasterKey(decryptedMaster) localStorage.setItem('active_username', username) localStorage.setItem('active_userid', encryptedPayloads.userId) diff --git a/server/src/routes/auth.ts b/server/src/routes/auth.ts index 934a635..49078f8 100644 --- a/server/src/routes/auth.ts +++ b/server/src/routes/auth.ts @@ -253,4 +253,41 @@ router.delete('/delete-account', async (req: any, res) => { } }) +// 6. Enroll PRF encrypted master key +router.post('/enroll-prf', async (req: any, res) => { + try { + const userId = req.headers['x-user-id'] + if (!userId) { + return res.status(401).json({ error: 'Unauthorized: X-User-Id header missing' }) + } + + const { encryptedMasterKeyPrf, encryptedMasterKeyPrfIv, encryptedMasterKeyPrfTag } = req.body + if (!encryptedMasterKeyPrf || !encryptedMasterKeyPrfIv || !encryptedMasterKeyPrfTag) { + return res.status(400).json({ error: 'Missing required PRF key fields' }) + } + + if ( + typeof encryptedMasterKeyPrf !== 'string' || + typeof encryptedMasterKeyPrfIv !== 'string' || + typeof encryptedMasterKeyPrfTag !== 'string' + ) { + return res.status(400).json({ error: 'Invalid PRF key fields format' }) + } + + await prisma.user.update({ + where: { id: userId }, + data: { + encryptedMasterKeyPrf, + encryptedMasterKeyPrfIv, + encryptedMasterKeyPrfTag + } + }) + + return res.json({ success: true }) + } catch (error: any) { + console.error('Error enrolling PRF key:', error) + return res.status(500).json({ error: error.message || 'Internal server error' }) + } +}) + export default router