From cd5dccaa569faedba1a12c36d4be32af4f4da7c3 Mon Sep 17 00:00:00 2001 From: Abhinav Date: Wed, 19 Jul 2023 18:52:29 +0530 Subject: [PATCH] update the login flow --- apps/photos/src/components/Login.tsx | 2 +- apps/photos/src/components/SignUp.tsx | 6 ++- apps/photos/src/pages/credentials/index.tsx | 6 ++- apps/photos/src/services/userService.ts | 57 ++++++++++++++------- apps/photos/src/types/user/index.ts | 12 ++--- apps/photos/src/utils/crypto/index.ts | 12 ++++- 6 files changed, 66 insertions(+), 29 deletions(-) diff --git a/apps/photos/src/components/Login.tsx b/apps/photos/src/components/Login.tsx index 975c38262..744bc9876 100644 --- a/apps/photos/src/components/Login.tsx +++ b/apps/photos/src/components/Login.tsx @@ -80,7 +80,7 @@ export default function Login(props: LoginProps) { token, id, twoFactorSessionID, - } = await loginViaSRP(srpAttributes.srpSalt, email, passphrase); + } = await loginViaSRP(srpAttributes, passphrase); if (twoFactorSessionID) { setData(LS_KEYS.USER, { email, diff --git a/apps/photos/src/components/SignUp.tsx b/apps/photos/src/components/SignUp.tsx index a8ba4eff5..38509e8fd 100644 --- a/apps/photos/src/components/SignUp.tsx +++ b/apps/photos/src/components/SignUp.tsx @@ -8,6 +8,7 @@ import SubmitButton from 'components/SubmitButton'; import { generateAndSaveIntermediateKeyAttributes, generateKeyAttributes, + generateLoginSubKey, generateSRPSetupAttributes, isWeakPassword, saveKeyInSessionStore, @@ -81,12 +82,15 @@ export default function SignUp(props: SignUpProps) { const { keyAttributes, masterKey } = await generateKeyAttributes(passphrase); - const srpSetupAttributes = await generateSRPSetupAttributes( + const loginSubKey = await generateLoginSubKey( passphrase, keyAttributes.kekSalt, keyAttributes.memLimit, keyAttributes.opsLimit ); + const srpSetupAttributes = await generateSRPSetupAttributes( + loginSubKey + ); setData(LS_KEYS.ORIGINAL_KEY_ATTRIBUTES, keyAttributes); setData(LS_KEYS.SRP_SETUP_ATTRIBUTES, srpSetupAttributes); diff --git a/apps/photos/src/pages/credentials/index.tsx b/apps/photos/src/pages/credentials/index.tsx index 3fd6e1b1f..6027eef6b 100644 --- a/apps/photos/src/pages/credentials/index.tsx +++ b/apps/photos/src/pages/credentials/index.tsx @@ -9,6 +9,7 @@ import { SESSION_KEYS, getKey } from 'utils/storage/sessionStorage'; import { decryptAndStoreToken, generateAndSaveIntermediateKeyAttributes, + generateLoginSubKey, generateSRPSetupAttributes, saveKeyInSessionStore, } from 'utils/crypto'; @@ -87,12 +88,15 @@ export default function Credentials() { const userSRPSetupPending = getUserSRPSetupPending(); addLocalLog(() => `userSRPSetupPending ${userSRPSetupPending}`); if (userSRPSetupPending) { - const srpSetupAttributes = await generateSRPSetupAttributes( + const loginSubKey = await generateLoginSubKey( passphrase, keyAttributes.kekSalt, keyAttributes.memLimit, keyAttributes.opsLimit ); + const srpSetupAttributes = await generateSRPSetupAttributes( + loginSubKey + ); // we don't have access to kek here, so we will have to re-derive it from the passphrase await configureSRP(srpSetupAttributes); } diff --git a/apps/photos/src/services/userService.ts b/apps/photos/src/services/userService.ts index 0be12fc82..e35065755 100644 --- a/apps/photos/src/services/userService.ts +++ b/apps/photos/src/services/userService.ts @@ -6,7 +6,11 @@ import { clearData, getData, LS_KEYS } from 'utils/storage/localStorage'; import localForage from 'utils/storage/localForage'; import { getToken } from 'utils/common/key'; import HTTPService from './HTTPService'; -import { generateSRPClient, getRecoveryKey } from 'utils/crypto'; +import { + generateLoginSubKey, + generateSRPClient, + getRecoveryKey, +} from 'utils/crypto'; import { logError } from 'utils/sentry'; import { eventBus, Events } from './events'; import { @@ -19,15 +23,15 @@ import { UserDetails, DeleteChallengeResponse, GetRemoteStoreValueResponse, - GetSRPAttributesResponse, SetupSRPRequest, - ExchangeSRPABResponse, + CreateSRPSessionResponse, EmailVerificationResponse, GetFeatureFlagResponse, SetupSRPResponse, CompleteSRPSetupRequest, CompleteSRPSetupResponse, SRPSetupAttributes, + SRPAttributes, } from 'types/user'; import { ServerErrorCodes } from 'utils/error'; import isElectron from 'is-electron'; @@ -471,7 +475,7 @@ export const getSRPAttributes = async (email: string) => { const resp = await HTTPService.get(`${ENDPOINT}/users/srp/attributes`, { email, }); - return (resp.data as GetSRPAttributesResponse)?.srpAttributes; + return resp.data as SRPAttributes; } catch (e) { if (e.status?.toString() === ServerErrorCodes.NOT_FOUND) { return null; @@ -594,22 +598,35 @@ export const completeSRPSetup = async ( }; export const loginViaSRP = async ( - srpSalt: string, - email: string, - password: string + srpAttributes: SRPAttributes, + passphrase: string ) => { try { - const srpClient = await generateSRPClient(srpSalt, email, password); + const loginSubKey = await generateLoginSubKey( + passphrase, + srpAttributes.kekSalt, + srpAttributes.memLimit, + srpAttributes.opsLimit + ); + const srpClient = await generateSRPClient( + srpAttributes.srpSalt, + srpAttributes.srpUserID, + loginSubKey + ); const srpA = srpClient.computeA(); - const { srpB } = await exchangeAB(email, convertBufferToBase64(srpA)); + const { srpB, sessionID } = await createSRPSession( + srpAttributes.srpUserID, + convertBufferToBase64(srpA) + ); srpClient.setB(convertBase64ToBuffer(srpB)); const k = srpClient.computeK(); addLocalLog(() => `srp k: ${convertBufferToBase64(k)}`); const m1 = srpClient.computeM1(); addLocalLog(() => `srp m1: ${convertBufferToBase64(m1)}`); - const verificationResponse = await verifySRP( - email, + const verificationResponse = await verifySRPSession( + sessionID, + srpAttributes.srpUserID, convertBufferToBase64(m1) ); addLocalLog(() => `srp verify successful, ${verificationResponse}`); @@ -620,17 +637,16 @@ export const loginViaSRP = async ( } }; -export const exchangeAB = async (email: string, srpA: string) => { +export const createSRPSession = async (srpUserID: string, srpA: string) => { try { const resp = await HTTPService.post( `${ENDPOINT}/users/srp/exchange-ab`, { - email, + srpUserID, srpA, - }, - null + } ); - return resp.data as ExchangeSRPABResponse; + return resp.data as CreateSRPSessionResponse; } catch (e) { logError(e, 'exchangeAB failed'); throw e; @@ -657,12 +673,17 @@ export const updateMapEnabledStatus = async (newStatus: boolean) => { } }; -export const verifySRP = async (email: string, srpM1: string) => { +export const verifySRPSession = async ( + sessionID: string, + srpUserID: string, + srpM1: string +) => { try { const resp = await HTTPService.post( `${ENDPOINT}/users/srp/verify`, { - email, + sessionID, + srpUserID, srpM1, }, null diff --git a/apps/photos/src/types/user/index.ts b/apps/photos/src/types/user/index.ts index 2286b6025..434aec3ec 100644 --- a/apps/photos/src/types/user/index.ts +++ b/apps/photos/src/types/user/index.ts @@ -104,12 +104,11 @@ export interface UpdateRemoteStoreValueRequest { } export interface SRPAttributes { + srpUserID: string; srpSalt: string; - srpGroup: string; -} - -export interface GetSRPAttributesResponse { - srpAttributes: SRPAttributes; + memLimit: number; + opsLimit: number; + kekSalt: string; } export interface SRPSetupAttributes { @@ -141,7 +140,8 @@ export interface CompleteSRPSetupResponse { srpM2: string; } -export interface ExchangeSRPABResponse { +export interface CreateSRPSessionResponse { + sessionID: string; srpB: string; } diff --git a/apps/photos/src/utils/crypto/index.ts b/apps/photos/src/utils/crypto/index.ts index b678b1da8..14b175579 100644 --- a/apps/photos/src/utils/crypto/index.ts +++ b/apps/photos/src/utils/crypto/index.ts @@ -251,12 +251,12 @@ export const isWeakPassword = (password: string) => { return estimatePasswordStrength(password) === PasswordStrength.WEAK; }; -export const generateSRPSetupAttributes = async ( +export const generateLoginSubKey = async ( password: string, kekSalt: string, memLimit: number, opsLimit: number -): Promise => { +) => { const cryptoWorker = await ComlinkCryptoWorker.getInstance(); const kek = await cryptoWorker.deriveKey( password, @@ -270,6 +270,14 @@ export const generateSRPSetupAttributes = async ( LOGIN_SUB_KEY_ID, LOGIN_SUB_KEY_CONTEXT ); + return loginSubKey; +}; + +export const generateSRPSetupAttributes = async ( + loginSubKey: string +): Promise => { + const cryptoWorker = await ComlinkCryptoWorker.getInstance(); + const srpSalt = await cryptoWorker.generateSaltToDeriveKey(); const srpUserID = uuidv4();