Remove client-side isLoggedIn value
[ProtonMail-WebClient.git] / packages / srp / lib / passwords.ts
blobaf450efb993b1ac2017e7c5be25f2577ca786519
1 import bcrypt from 'bcryptjs';
3 import { CryptoProxy } from '@proton/crypto';
4 import {
5     arrayToBinaryString,
6     arrayToHexString,
7     binaryStringToArray,
8     encodeBase64,
9     encodeUtf8,
10 } from '@proton/crypto/lib/utils';
11 import mergeUint8Arrays from '@proton/utils/mergeUint8Arrays';
13 import { BCRYPT_PREFIX } from './constants';
14 import { cleanUsername } from './utils/username';
16 /**
17  * Expand a hash
18  */
19 export const expandHash = async (input: Uint8Array) => {
20     const promises = [];
21     const arr = mergeUint8Arrays([input, new Uint8Array([0])]);
22     for (let i = 1; i <= 4; i++) {
23         promises.push(CryptoProxy.computeHash({ algorithm: 'SHA512', data: arr }));
24         arr[arr.length - 1] = i;
25     }
26     return mergeUint8Arrays(await Promise.all(promises));
29 /**
30  * Format a hash
31  */
32 const formatHash = async (password: string, salt: string, modulus: Uint8Array) => {
33     const unexpandedHash = await bcrypt.hash(password, BCRYPT_PREFIX + salt);
34     return expandHash(mergeUint8Arrays([binaryStringToArray(unexpandedHash), modulus]));
37 /**
38  * Hash password in version 3.
39  */
40 const hashPassword3 = (password: string, salt: string, modulus: Uint8Array) => {
41     const saltBinary = binaryStringToArray(`${salt}proton`);
42     return formatHash(password, bcrypt.encodeBase64(saltBinary, 16), modulus);
45 /**
46  * Hash password in version 1.
47  */
48 const hashPassword1 = async (password: string, username: string, modulus: Uint8Array) => {
49     const value = binaryStringToArray(encodeUtf8(username.toLowerCase()));
50     const salt = arrayToHexString(await CryptoProxy.computeHash({ algorithm: 'unsafeMD5', data: value }));
51     return formatHash(password, salt, modulus);
54 /**
55  * Hash password in version 0.
56  */
57 const hashPassword0 = async (password: string, username: string, modulus: Uint8Array) => {
58     const value = await CryptoProxy.computeHash({
59         algorithm: 'SHA512',
60         data: binaryStringToArray(username.toLowerCase() + encodeUtf8(password)),
61     });
62     const prehashed = encodeBase64(arrayToBinaryString(value));
63     return hashPassword1(prehashed, username, modulus);
66 /**
67  * Hash a password.
68  */
69 export const hashPassword = ({
70     password,
71     salt,
72     username,
73     modulus,
74     version,
75 }: {
76     password: string;
77     salt?: string;
78     username?: string;
79     modulus: Uint8Array;
80     version: number;
81 }) => {
82     if (version === 4 || version === 3) {
83         if (!salt) {
84             throw new Error('Missing salt');
85         }
86         return hashPassword3(password, salt, modulus);
87     }
89     if (version === 2) {
90         return hashPassword1(password, cleanUsername(username), modulus);
91     }
93     if (version === 1) {
94         if (!username) {
95             throw new Error('Missing username');
96         }
97         return hashPassword1(password, username, modulus);
98     }
100     if (version === 0) {
101         if (!username) {
102             throw new Error('Missing username');
103         }
104         return hashPassword0(password, username, modulus);
105     }
107     throw new Error('Unsupported auth version');