mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-15 04:14:38 +00:00
125 lines
3.2 KiB
TypeScript
125 lines
3.2 KiB
TypeScript
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
|
|
};
|
|
};
|