[cast] Remove unused code

This commit is contained in:
Neeraj Gupta 2024-01-22 10:37:17 +05:30
parent 73885f6f0a
commit d2c088b9ef
3 changed files with 0 additions and 796 deletions

View file

@ -1,8 +0,0 @@
/** Enums of supported locale */
export enum Language {
en = 'en',
fr = 'fr',
zh = 'zh',
nl = 'nl',
es = 'es',
}

View file

@ -1,366 +0,0 @@
import { KeyAttributes, SRPSetupAttributes } from 'types/user';
import { SESSION_KEYS, setKey } from 'utils/storage/sessionStorage';
import { getData, LS_KEYS, setData } from 'utils/storage/localStorage';
import { setRecoveryKey } from 'services/userService';
import { logError } from '@ente/shared/sentry';
import isElectron from 'is-electron';
// import safeStorageService from 'services/electron/safeStorage';
import ComlinkCryptoWorker from 'utils/comlink/ComlinkCryptoWorker';
import { PasswordStrength } from 'constants/crypto';
import zxcvbn from 'zxcvbn';
import { SRP, SrpClient } from 'fast-srp-hap';
import { convertBase64ToBuffer, convertBufferToBase64 } from 'utils/user';
import { v4 as uuidv4 } from 'uuid';
import { addLocalLog } from 'utils/logging';
import { getActualKey } from '@ente/shared/user';
import { getToken } from '@ente/shared/storage/localStorage/helpers';
const SRP_PARAMS = SRP.params['4096'];
const LOGIN_SUB_KEY_LENGTH = 32;
const LOGIN_SUB_KEY_ID = 1;
const LOGIN_SUB_KEY_CONTEXT = 'loginctx';
const LOGIN_SUB_KEY_BYTE_LENGTH = 16;
export async function generateKeyAndSRPAttributes(passphrase: string): Promise<{
keyAttributes: KeyAttributes;
masterKey: string;
srpSetupAttributes: SRPSetupAttributes;
}> {
const cryptoWorker = await ComlinkCryptoWorker.getInstance();
const masterKey = await cryptoWorker.generateEncryptionKey();
const recoveryKey = await cryptoWorker.generateEncryptionKey();
const kekSalt = await cryptoWorker.generateSaltToDeriveKey();
const kek = await cryptoWorker.deriveSensitiveKey(passphrase, kekSalt);
const masterKeyEncryptedWithKek = await cryptoWorker.encryptToB64(
masterKey,
kek.key
);
const masterKeyEncryptedWithRecoveryKey = await cryptoWorker.encryptToB64(
masterKey,
recoveryKey
);
const recoveryKeyEncryptedWithMasterKey = await cryptoWorker.encryptToB64(
recoveryKey,
masterKey
);
const keyPair = await cryptoWorker.generateKeyPair();
const encryptedKeyPairAttributes = await cryptoWorker.encryptToB64(
keyPair.privateKey,
masterKey
);
const loginSubKey = await generateLoginSubKey(kek.key);
const srpSetupAttributes = await generateSRPSetupAttributes(loginSubKey);
const keyAttributes: KeyAttributes = {
kekSalt,
encryptedKey: masterKeyEncryptedWithKek.encryptedData,
keyDecryptionNonce: masterKeyEncryptedWithKek.nonce,
publicKey: keyPair.publicKey,
encryptedSecretKey: encryptedKeyPairAttributes.encryptedData,
secretKeyDecryptionNonce: encryptedKeyPairAttributes.nonce,
opsLimit: kek.opsLimit,
memLimit: kek.memLimit,
masterKeyEncryptedWithRecoveryKey:
masterKeyEncryptedWithRecoveryKey.encryptedData,
masterKeyDecryptionNonce: masterKeyEncryptedWithRecoveryKey.nonce,
recoveryKeyEncryptedWithMasterKey:
recoveryKeyEncryptedWithMasterKey.encryptedData,
recoveryKeyDecryptionNonce: recoveryKeyEncryptedWithMasterKey.nonce,
};
return {
keyAttributes,
masterKey,
srpSetupAttributes,
};
}
// We encrypt the masterKey, with an intermediate key derived from the
// passphrase (with Interactive mem and ops limits) to avoid saving it to local
// storage in plain text. This means that on the web user will always have to
// enter their passphrase to access their masterKey.
export async function generateAndSaveIntermediateKeyAttributes(
passphrase: string,
existingKeyAttributes: KeyAttributes,
key: string
): Promise<KeyAttributes> {
const cryptoWorker = await ComlinkCryptoWorker.getInstance();
const intermediateKekSalt = await cryptoWorker.generateSaltToDeriveKey();
const intermediateKek = await cryptoWorker.deriveInteractiveKey(
passphrase,
intermediateKekSalt
);
const encryptedKeyAttributes = await cryptoWorker.encryptToB64(
key,
intermediateKek.key
);
const intermediateKeyAttributes = Object.assign(existingKeyAttributes, {
kekSalt: intermediateKekSalt,
encryptedKey: encryptedKeyAttributes.encryptedData,
keyDecryptionNonce: encryptedKeyAttributes.nonce,
opsLimit: intermediateKek.opsLimit,
memLimit: intermediateKek.memLimit,
});
setData(LS_KEYS.KEY_ATTRIBUTES, intermediateKeyAttributes);
return intermediateKeyAttributes;
}
export const saveKeyInSessionStore = async (
keyType: SESSION_KEYS,
key: string,
fromDesktop?: boolean
) => {
const cryptoWorker = await ComlinkCryptoWorker.getInstance();
const sessionKeyAttributes = await cryptoWorker.generateKeyAndEncryptToB64(
key
);
setKey(keyType, sessionKeyAttributes);
if (
isElectron() &&
!fromDesktop &&
keyType === SESSION_KEYS.ENCRYPTION_KEY
) {
// safeStorageService.setEncryptionKey(key);
}
};
export const getRecoveryKey = async () => {
let recoveryKey: string = null;
try {
const cryptoWorker = await ComlinkCryptoWorker.getInstance();
const keyAttributes: KeyAttributes = getData(LS_KEYS.KEY_ATTRIBUTES);
const {
recoveryKeyEncryptedWithMasterKey,
recoveryKeyDecryptionNonce,
} = keyAttributes;
const masterKey = await getActualKey();
if (recoveryKeyEncryptedWithMasterKey) {
recoveryKey = await cryptoWorker.decryptB64(
recoveryKeyEncryptedWithMasterKey,
recoveryKeyDecryptionNonce,
masterKey
);
} else {
recoveryKey = await createNewRecoveryKey();
}
recoveryKey = await cryptoWorker.toHex(recoveryKey);
return recoveryKey;
} catch (e) {
logError(e, 'getRecoveryKey failed');
throw e;
}
};
// Used only for legacy users for whom we did not generate recovery keys during
// sign up
async function createNewRecoveryKey() {
const masterKey = await getActualKey();
const existingAttributes = getData(LS_KEYS.KEY_ATTRIBUTES);
const cryptoWorker = await ComlinkCryptoWorker.getInstance();
const recoveryKey = await cryptoWorker.generateEncryptionKey();
const encryptedMasterKey = await cryptoWorker.encryptToB64(
masterKey,
recoveryKey
);
const encryptedRecoveryKey = await cryptoWorker.encryptToB64(
recoveryKey,
masterKey
);
const recoveryKeyAttributes = {
masterKeyEncryptedWithRecoveryKey: encryptedMasterKey.encryptedData,
masterKeyDecryptionNonce: encryptedMasterKey.nonce,
recoveryKeyEncryptedWithMasterKey: encryptedRecoveryKey.encryptedData,
recoveryKeyDecryptionNonce: encryptedRecoveryKey.nonce,
};
await setRecoveryKey(getToken(), recoveryKeyAttributes);
const updatedKeyAttributes = Object.assign(
existingAttributes,
recoveryKeyAttributes
);
setData(LS_KEYS.KEY_ATTRIBUTES, updatedKeyAttributes);
return recoveryKey;
}
export async function decryptAndStoreToken(
keyAttributes: KeyAttributes,
masterKey: string
) {
const cryptoWorker = await ComlinkCryptoWorker.getInstance();
const user = getData(LS_KEYS.USER);
let decryptedToken = null;
const { encryptedToken } = user;
if (encryptedToken && encryptedToken.length > 0) {
const secretKey = await cryptoWorker.decryptB64(
keyAttributes.encryptedSecretKey,
keyAttributes.secretKeyDecryptionNonce,
masterKey
);
const urlUnsafeB64DecryptedToken = await cryptoWorker.boxSealOpen(
encryptedToken,
keyAttributes.publicKey,
secretKey
);
const decryptedTokenBytes = await cryptoWorker.fromB64(
urlUnsafeB64DecryptedToken
);
decryptedToken = await cryptoWorker.toURLSafeB64(decryptedTokenBytes);
setData(LS_KEYS.USER, {
...user,
token: decryptedToken,
encryptedToken: null,
});
}
}
export async function encryptWithRecoveryKey(key: string) {
const cryptoWorker = await ComlinkCryptoWorker.getInstance();
const hexRecoveryKey = await getRecoveryKey();
const recoveryKey = await cryptoWorker.fromHex(hexRecoveryKey);
const encryptedKey = await cryptoWorker.encryptToB64(key, recoveryKey);
return encryptedKey;
}
export async function decryptDeleteAccountChallenge(
encryptedChallenge: string
) {
const cryptoWorker = await ComlinkCryptoWorker.getInstance();
const masterKey = await getActualKey();
const keyAttributes = getData(LS_KEYS.KEY_ATTRIBUTES);
const secretKey = await cryptoWorker.decryptB64(
keyAttributes.encryptedSecretKey,
keyAttributes.secretKeyDecryptionNonce,
masterKey
);
const b64DecryptedChallenge = await cryptoWorker.boxSealOpen(
encryptedChallenge,
keyAttributes.publicKey,
secretKey
);
const utf8DecryptedChallenge = atob(b64DecryptedChallenge);
return utf8DecryptedChallenge;
}
export function estimatePasswordStrength(password: string): PasswordStrength {
if (!password) {
return PasswordStrength.WEAK;
}
const zxcvbnResult = zxcvbn(password);
if (zxcvbnResult.score < 2) {
return PasswordStrength.WEAK;
} else if (zxcvbnResult.score < 3) {
return PasswordStrength.MODERATE;
} else {
return PasswordStrength.STRONG;
}
}
export const isWeakPassword = (password: string) => {
return estimatePasswordStrength(password) === PasswordStrength.WEAK;
};
export const generateLoginSubKey = async (kek: string) => {
const cryptoWorker = await ComlinkCryptoWorker.getInstance();
const kekSubKeyString = await cryptoWorker.generateSubKey(
kek,
LOGIN_SUB_KEY_LENGTH,
LOGIN_SUB_KEY_ID,
LOGIN_SUB_KEY_CONTEXT
);
const kekSubKey = await cryptoWorker.fromB64(kekSubKeyString);
// use first 16 bytes of generated kekSubKey as loginSubKey
const loginSubKey = await cryptoWorker.toB64(
kekSubKey.slice(0, LOGIN_SUB_KEY_BYTE_LENGTH)
);
return loginSubKey;
};
export const generateSRPSetupAttributes = async (
loginSubKey: string
): Promise<SRPSetupAttributes> => {
const cryptoWorker = await ComlinkCryptoWorker.getInstance();
const srpSalt = await cryptoWorker.generateSaltToDeriveKey();
const srpUserID = uuidv4();
const srpVerifierBuffer = SRP.computeVerifier(
SRP_PARAMS,
convertBase64ToBuffer(srpSalt),
Buffer.from(srpUserID),
convertBase64ToBuffer(loginSubKey)
);
const srpVerifier = convertBufferToBase64(srpVerifierBuffer);
addLocalLog(
() => `SRP setup attributes generated',
${JSON.stringify({
srpSalt,
srpUserID,
srpVerifier,
loginSubKey,
})}`
);
return {
srpUserID,
srpSalt,
srpVerifier,
loginSubKey,
};
};
export const computeVerifierHelper = (
srpSalt: string,
srpUserID: string,
loginSubKey: string
) => {
const srpVerifierBuffer = SRP.computeVerifier(
SRP_PARAMS,
convertBase64ToBuffer(srpSalt),
Buffer.from(srpUserID),
convertBase64ToBuffer(loginSubKey)
);
return convertBufferToBase64(srpVerifierBuffer);
};
export const generateSRPClient = async (
srpSalt: string,
srpUserID: string,
loginSubKey: string
) => {
return new Promise<SrpClient>((resolve, reject) => {
SRP.genKey(function (err, secret1) {
try {
if (err) {
reject(err);
}
const srpClient = new SrpClient(
SRP_PARAMS,
convertBase64ToBuffer(srpSalt),
Buffer.from(srpUserID),
convertBase64ToBuffer(loginSubKey),
secret1,
false
);
resolve(srpClient);
} catch (e) {
reject(e);
}
});
});
};

View file

@ -1,422 +0,0 @@
import sodium, { StateAddress } from 'libsodium-wrappers';
import { ENCRYPTION_CHUNK_SIZE } from 'constants/crypto';
import { B64EncryptionResult } from 'types/crypto';
import { CustomError } from '@ente/shared/error';
export async function decryptChaChaOneShot(
data: Uint8Array,
header: Uint8Array,
key: string
) {
await sodium.ready;
const pullState = sodium.crypto_secretstream_xchacha20poly1305_init_pull(
header,
await fromB64(key)
);
const pullResult = sodium.crypto_secretstream_xchacha20poly1305_pull(
pullState,
data,
null
);
return pullResult.message;
}
export async function decryptChaCha(
data: Uint8Array,
header: Uint8Array,
key: string
) {
await sodium.ready;
const pullState = sodium.crypto_secretstream_xchacha20poly1305_init_pull(
header,
await fromB64(key)
);
const decryptionChunkSize =
ENCRYPTION_CHUNK_SIZE +
sodium.crypto_secretstream_xchacha20poly1305_ABYTES;
let bytesRead = 0;
const decryptedData = [];
let tag = sodium.crypto_secretstream_xchacha20poly1305_TAG_MESSAGE;
while (tag !== sodium.crypto_secretstream_xchacha20poly1305_TAG_FINAL) {
let chunkSize = decryptionChunkSize;
if (bytesRead + chunkSize > data.length) {
chunkSize = data.length - bytesRead;
}
const buffer = data.slice(bytesRead, bytesRead + chunkSize);
const pullResult = sodium.crypto_secretstream_xchacha20poly1305_pull(
pullState,
buffer
);
if (!pullResult.message) {
throw new Error(CustomError.PROCESSING_FAILED);
}
for (let index = 0; index < pullResult.message.length; index++) {
decryptedData.push(pullResult.message[index]);
}
tag = pullResult.tag;
bytesRead += chunkSize;
}
return Uint8Array.from(decryptedData);
}
export async function initChunkDecryption(header: Uint8Array, key: Uint8Array) {
await sodium.ready;
const pullState = sodium.crypto_secretstream_xchacha20poly1305_init_pull(
header,
key
);
const decryptionChunkSize =
ENCRYPTION_CHUNK_SIZE +
sodium.crypto_secretstream_xchacha20poly1305_ABYTES;
const tag = sodium.crypto_secretstream_xchacha20poly1305_TAG_MESSAGE;
return { pullState, decryptionChunkSize, tag };
}
export async function decryptFileChunk(
data: Uint8Array,
pullState: StateAddress
) {
await sodium.ready;
const pullResult = sodium.crypto_secretstream_xchacha20poly1305_pull(
pullState,
data
);
if (!pullResult.message) {
throw new Error(CustomError.PROCESSING_FAILED);
}
const newTag = pullResult.tag;
return { decryptedData: pullResult.message, newTag };
}
export async function encryptChaChaOneShot(data: Uint8Array, key: string) {
await sodium.ready;
const uintkey: Uint8Array = await fromB64(key);
const initPushResult =
sodium.crypto_secretstream_xchacha20poly1305_init_push(uintkey);
const [pushState, header] = [initPushResult.state, initPushResult.header];
const pushResult = sodium.crypto_secretstream_xchacha20poly1305_push(
pushState,
data,
null,
sodium.crypto_secretstream_xchacha20poly1305_TAG_FINAL
);
return {
key: await toB64(uintkey),
file: {
encryptedData: pushResult,
decryptionHeader: await toB64(header),
},
};
}
export async function encryptChaCha(data: Uint8Array) {
await sodium.ready;
const uintkey: Uint8Array =
sodium.crypto_secretstream_xchacha20poly1305_keygen();
const initPushResult =
sodium.crypto_secretstream_xchacha20poly1305_init_push(uintkey);
const [pushState, header] = [initPushResult.state, initPushResult.header];
let bytesRead = 0;
let tag = sodium.crypto_secretstream_xchacha20poly1305_TAG_MESSAGE;
const encryptedData = [];
while (tag !== sodium.crypto_secretstream_xchacha20poly1305_TAG_FINAL) {
let chunkSize = ENCRYPTION_CHUNK_SIZE;
if (bytesRead + chunkSize >= data.length) {
chunkSize = data.length - bytesRead;
tag = sodium.crypto_secretstream_xchacha20poly1305_TAG_FINAL;
}
const buffer = data.slice(bytesRead, bytesRead + chunkSize);
bytesRead += chunkSize;
const pushResult = sodium.crypto_secretstream_xchacha20poly1305_push(
pushState,
buffer,
null,
tag
);
for (let index = 0; index < pushResult.length; index++) {
encryptedData.push(pushResult[index]);
}
}
return {
key: await toB64(uintkey),
file: {
encryptedData: new Uint8Array(encryptedData),
decryptionHeader: await toB64(header),
},
};
}
export async function initChunkEncryption() {
await sodium.ready;
const key = sodium.crypto_secretstream_xchacha20poly1305_keygen();
const initPushResult =
sodium.crypto_secretstream_xchacha20poly1305_init_push(key);
const [pushState, header] = [initPushResult.state, initPushResult.header];
return {
key: await toB64(key),
decryptionHeader: await toB64(header),
pushState,
};
}
export async function encryptFileChunk(
data: Uint8Array,
pushState: sodium.StateAddress,
isFinalChunk: boolean
) {
await sodium.ready;
const tag = isFinalChunk
? sodium.crypto_secretstream_xchacha20poly1305_TAG_FINAL
: sodium.crypto_secretstream_xchacha20poly1305_TAG_MESSAGE;
const pushResult = sodium.crypto_secretstream_xchacha20poly1305_push(
pushState,
data,
null,
tag
);
return pushResult;
}
export async function encryptToB64(data: string, key: string) {
await sodium.ready;
const encrypted = await encrypt(await fromB64(data), await fromB64(key));
return {
encryptedData: await toB64(encrypted.encryptedData),
key: await toB64(encrypted.key),
nonce: await toB64(encrypted.nonce),
} as B64EncryptionResult;
}
export async function generateKeyAndEncryptToB64(data: string) {
await sodium.ready;
const key = sodium.crypto_secretbox_keygen();
return await encryptToB64(data, await toB64(key));
}
export async function encryptUTF8(data: string, key: string) {
const b64Data = await toB64(await fromUTF8(data));
return await encryptToB64(b64Data, key);
}
export async function decryptB64(data: string, nonce: string, key: string) {
await sodium.ready;
const decrypted = await decrypt(
await fromB64(data),
await fromB64(nonce),
await fromB64(key)
);
return await toB64(decrypted);
}
export async function decryptToUTF8(data: string, nonce: string, key: string) {
await sodium.ready;
const decrypted = await decrypt(
await fromB64(data),
await fromB64(nonce),
await fromB64(key)
);
return sodium.to_string(decrypted);
}
async function encrypt(data: Uint8Array, key: Uint8Array) {
await sodium.ready;
const nonce = sodium.randombytes_buf(sodium.crypto_secretbox_NONCEBYTES);
const encryptedData = sodium.crypto_secretbox_easy(data, nonce, key);
return {
encryptedData,
key,
nonce,
};
}
async function decrypt(data: Uint8Array, nonce: Uint8Array, key: Uint8Array) {
await sodium.ready;
return sodium.crypto_secretbox_open_easy(data, nonce, key);
}
export async function initChunkHashing() {
await sodium.ready;
const hashState = sodium.crypto_generichash_init(
null,
sodium.crypto_generichash_BYTES_MAX
);
return hashState;
}
export async function hashFileChunk(
hashState: sodium.StateAddress,
chunk: Uint8Array
) {
await sodium.ready;
sodium.crypto_generichash_update(hashState, chunk);
}
export async function completeChunkHashing(hashState: sodium.StateAddress) {
await sodium.ready;
const hash = sodium.crypto_generichash_final(
hashState,
sodium.crypto_generichash_BYTES_MAX
);
const hashString = toB64(hash);
return hashString;
}
export async function deriveKey(
passphrase: string,
salt: string,
opsLimit: number,
memLimit: number
) {
await sodium.ready;
return await toB64(
sodium.crypto_pwhash(
sodium.crypto_secretbox_KEYBYTES,
await fromUTF8(passphrase),
await fromB64(salt),
opsLimit,
memLimit,
sodium.crypto_pwhash_ALG_ARGON2ID13
)
);
}
export async function deriveSensitiveKey(passphrase: string, salt: string) {
await sodium.ready;
const minMemLimit = sodium.crypto_pwhash_MEMLIMIT_MIN;
let opsLimit = sodium.crypto_pwhash_OPSLIMIT_SENSITIVE;
let memLimit = sodium.crypto_pwhash_MEMLIMIT_SENSITIVE;
while (memLimit > minMemLimit) {
try {
const key = await deriveKey(passphrase, salt, opsLimit, memLimit);
return {
key,
opsLimit,
memLimit,
};
} catch (e) {
opsLimit *= 2;
memLimit /= 2;
}
}
}
export async function deriveInteractiveKey(passphrase: string, salt: string) {
await sodium.ready;
const key = await toB64(
sodium.crypto_pwhash(
sodium.crypto_secretbox_KEYBYTES,
await fromUTF8(passphrase),
await fromB64(salt),
sodium.crypto_pwhash_OPSLIMIT_INTERACTIVE,
sodium.crypto_pwhash_MEMLIMIT_INTERACTIVE,
sodium.crypto_pwhash_ALG_ARGON2ID13
)
);
return {
key,
opsLimit: sodium.crypto_pwhash_OPSLIMIT_INTERACTIVE,
memLimit: sodium.crypto_pwhash_MEMLIMIT_INTERACTIVE,
};
}
export async function generateEncryptionKey() {
await sodium.ready;
return await toB64(sodium.crypto_kdf_keygen());
}
export async function generateSaltToDeriveKey() {
await sodium.ready;
return await toB64(sodium.randombytes_buf(sodium.crypto_pwhash_SALTBYTES));
}
export async function generateKeyPair() {
await sodium.ready;
const keyPair: sodium.KeyPair = sodium.crypto_box_keypair();
return {
privateKey: await toB64(keyPair.privateKey),
publicKey: await toB64(keyPair.publicKey),
};
}
export async function boxSealOpen(
input: string,
publicKey: string,
secretKey: string
) {
await sodium.ready;
return await toB64(
sodium.crypto_box_seal_open(
await fromB64(input),
await fromB64(publicKey),
await fromB64(secretKey)
)
);
}
export async function boxSeal(input: string, publicKey: string) {
await sodium.ready;
return await toB64(
sodium.crypto_box_seal(await fromB64(input), await fromB64(publicKey))
);
}
export async function generateSubKey(
key: string,
subKeyLength: number,
subKeyID: number,
context: string
) {
await sodium.ready;
return await toB64(
sodium.crypto_kdf_derive_from_key(
subKeyLength,
subKeyID,
context,
await fromB64(key)
)
);
}
export async function fromB64(input: string) {
await sodium.ready;
return sodium.from_base64(input, sodium.base64_variants.ORIGINAL);
}
export async function toB64(input: Uint8Array) {
await sodium.ready;
return sodium.to_base64(input, sodium.base64_variants.ORIGINAL);
}
export async function toURLSafeB64(input: Uint8Array) {
await sodium.ready;
return sodium.to_base64(input, sodium.base64_variants.URLSAFE);
}
export async function fromUTF8(input: string) {
await sodium.ready;
return sodium.from_string(input);
}
export async function toUTF8(input: string) {
await sodium.ready;
return sodium.to_string(await fromB64(input));
}
export async function toHex(input: string) {
await sodium.ready;
return sodium.to_hex(await fromB64(input));
}
export async function fromHex(input: string) {
await sodium.ready;
return await toB64(sodium.from_hex(input));
}