import crypto from "crypto"; import jsrp from "jsrp"; import Aes256Gcm from "@app/components/utilities/cryptography/aes-256-gcm"; import { deriveArgonKey, generateKeyPair } from "@app/components/utilities/cryptography/crypto"; import { issueBackupPrivateKey, srp1 } from "@app/hooks/api/auth/queries"; export const generateUserBackupKey = async (email: string, password: string) => { // eslint-disable-next-line new-cap const clientKey = new jsrp.client(); // eslint-disable-next-line new-cap const clientPassword = new jsrp.client(); await new Promise((resolve) => { clientPassword.init({ username: email, password }, () => resolve(null)); }); const clientPublicKey = clientPassword.getPublicKey(); const srpKeys = await srp1({ clientPublicKey }); clientPassword.setSalt(srpKeys.salt); clientPassword.setServerPublicKey(srpKeys.serverPublicKey); const clientProof = clientPassword.getProof(); // called M1 const generatedKey = crypto.randomBytes(16).toString("hex"); await new Promise((resolve) => { clientKey.init({ username: email, password: generatedKey }, () => resolve(null)); }); const { salt, verifier } = await new Promise<{ salt: string; verifier: string }>( (resolve, reject) => { clientKey.createVerifier((err, res) => { if (err) return reject(err); return resolve(res); }); } ); const { ciphertext, iv, tag } = Aes256Gcm.encrypt({ text: String(localStorage.getItem("PRIVATE_KEY")), secret: generatedKey }); await issueBackupPrivateKey({ encryptedPrivateKey: ciphertext, iv, tag, salt, verifier, clientProof }); return generatedKey; }; export const generateUserPassKey = async ( email: string, password: string, fipsEnabled: boolean ) => { // eslint-disable-next-line new-cap const client = new jsrp.client(); const { publicKey, privateKey } = await generateKeyPair(fipsEnabled); await new Promise((resolve) => { client.init({ username: email, password }, () => resolve(null)); }); const { salt, verifier } = await new Promise<{ salt: string; verifier: string }>( (resolve, reject) => { client.createVerifier((err, res) => { if (err) return reject(err); return resolve(res); }); } ); const derivedKey = await deriveArgonKey({ password, salt, mem: 65536, time: 3, parallelism: 1, hashLen: 32 }); if (!derivedKey) throw new Error("Failed to derive key from password"); const key = crypto.randomBytes(32); // create encrypted private key by encrypting the private // key with the symmetric key [key] const { ciphertext: encryptedPrivateKey, iv: encryptedPrivateKeyIV, tag: encryptedPrivateKeyTag } = Aes256Gcm.encrypt({ text: privateKey, secret: key }); // create the protected key by encrypting the symmetric key // [key] with the derived key const { ciphertext: protectedKey, iv: protectedKeyIV, tag: protectedKeyTag } = Aes256Gcm.encrypt({ text: key.toString("hex"), secret: Buffer.from(derivedKey.hash) }); return { protectedKey, protectedKeyTag, protectedKeyIV, encryptedPrivateKey, encryptedPrivateKeyIV, encryptedPrivateKeyTag, publicKey, verifier, salt, privateKey }; };