update the login flow

This commit is contained in:
Abhinav 2023-07-19 18:52:29 +05:30
parent 8acc3d085d
commit cd5dccaa56
6 changed files with 66 additions and 29 deletions

View file

@ -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,

View file

@ -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);

View file

@ -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);
}

View file

@ -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

View file

@ -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;
}

View file

@ -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<SRPSetupAttributes> => {
) => {
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<SRPSetupAttributes> => {
const cryptoWorker = await ComlinkCryptoWorker.getInstance();
const srpSalt = await cryptoWorker.generateSaltToDeriveKey();
const srpUserID = uuidv4();