From 4b9a95ded7913af6e79e149bcc74dc7b21213ef7 Mon Sep 17 00:00:00 2001 From: Abdullah Atta Date: Wed, 11 Dec 2024 14:49:26 +0500 Subject: [PATCH] core: add support for fallback hashing on ios some users using passwords with multi byte characters generates the wrong hash. This commit adds support for fallback to allow those users to continue to login once the fix has been applied. --- packages/core/src/api/user-manager.ts | 72 ++++++++++++++++++++------- packages/core/src/database/kv.ts | 4 +- packages/core/src/interfaces.ts | 8 ++- 3 files changed, 65 insertions(+), 19 deletions(-) diff --git a/packages/core/src/api/user-manager.ts b/packages/core/src/api/user-manager.ts index 96ca60514..87eeabee4 100644 --- a/packages/core/src/api/user-manager.ts +++ b/packages/core/src/api/user-manager.ts @@ -136,17 +136,39 @@ class UserManager { hashedPassword = await this.db.storage().hash(password, email); } try { + let usesFallback = false; await this.tokenManager.saveToken( - await http.post( - `${constants.AUTH_HOST}${ENDPOINTS.token}`, - { - grant_type: "mfa_password", - client_id: "notesnook", - scope: "notesnook.sync offline_access IdentityServerApi", - password: hashedPassword - }, - token.access_token - ) + await http + .post( + `${constants.AUTH_HOST}${ENDPOINTS.token}`, + { + grant_type: "mfa_password", + client_id: "notesnook", + scope: "notesnook.sync offline_access IdentityServerApi", + password: hashedPassword + }, + token.access_token + ) + .catch(async (e) => { + if (e instanceof Error && e.message === "invalid_grant") { + hashedPassword = await this.db + .storage() + .hash(password, email, { usesFallback: true }); + if (hashedPassword === null) return Promise.reject(e); + usesFallback = true; + return await http.post( + `${constants.AUTH_HOST}${ENDPOINTS.token}`, + { + grant_type: "mfa_password", + client_id: "notesnook", + scope: "notesnook.sync offline_access IdentityServerApi", + password: hashedPassword + }, + token.access_token + ); + } + return Promise.reject(e); + }) ); const user = await this.fetchUser(); @@ -157,10 +179,18 @@ class UserManager { await this.db.syncer.devices.register(); } - await this.db.storage().deriveCryptoKey({ - password, - salt: user.salt - }); + if (usesFallback) { + await this.db.storage().deriveCryptoKeyFallback({ + password, + salt: user.salt + }); + } else { + await this.db.storage().deriveCryptoKey({ + password, + salt: user.salt + }); + } + await this.db.kv().write("usesFallbackPWHash", usesFallback); EV.publish(EVENTS.userLoggedIn, user); } catch (e) { await this.tokenManager.saveToken(token); @@ -301,7 +331,11 @@ class UserManager { await http.post( `${constants.API_HOST}${ENDPOINTS.deleteUser}`, - { password: await this.db.storage().hash(password, user.email) }, + { + password: await this.db.storage().hash(password, user.email, { + usesFallback: await this.db.kv().read("usesFallbackPWHash") + }) + }, token ); await this.logout(false, "Account deleted."); @@ -450,7 +484,9 @@ class UserManager { { type: "change_email", new_email: newEmail, - password: await this.db.storage().hash(password, email), + password: await this.db.storage().hash(password, email, { + usesFallback: await this.db.kv().read("usesFallbackPWHash") + }), verification_code: code }, token @@ -529,7 +565,9 @@ class UserManager { } if (old_password) - old_password = await this.db.storage().hash(old_password, email); + old_password = await this.db.storage().hash(old_password, email, { + usesFallback: await this.db.kv().read("usesFallbackPWHash") + }); if (new_password) new_password = await this.db.storage().hash(new_password, email); diff --git a/packages/core/src/database/kv.ts b/packages/core/src/database/kv.ts index 7d9266d23..e09ab7346 100644 --- a/packages/core/src/database/kv.ts +++ b/packages/core/src/database/kv.ts @@ -30,6 +30,7 @@ interface KV { deviceId: string; lastBackupTime: number; fullOfflineMode: boolean; + usesFallbackPWHash: boolean; } export const KEYS: (keyof KV)[] = [ @@ -40,7 +41,8 @@ export const KEYS: (keyof KV)[] = [ "monographs", "deviceId", "lastBackupTime", - "fullOfflineMode" + "fullOfflineMode", + "usesFallbackPWHash" ]; export class KVStorage { diff --git a/packages/core/src/interfaces.ts b/packages/core/src/interfaces.ts index f46dedd28..f176f559c 100644 --- a/packages/core/src/interfaces.ts +++ b/packages/core/src/interfaces.ts @@ -58,10 +58,16 @@ export interface IStorage { items: Cipher<"base64">[] ): Promise; deriveCryptoKey(credentials: SerializedKey): Promise; - hash(password: string, email: string): Promise; + hash( + password: string, + email: string, + options?: { usesFallback?: boolean } + ): Promise; getCryptoKey(): Promise; generateCryptoKey(password: string, salt?: string): Promise; + deriveCryptoKeyFallback(credentials: SerializedKey): Promise; + // async generateRandomKey() { // const passwordBytes = randomBytes(124); // const password = passwordBytes.toString("base64");