Added SRP helpers to serverside

This commit is contained in:
Daniel Hougaard
2024-02-06 03:20:46 +04:00
parent 3ad0382cb0
commit 34d1bbc2ed

View File

@@ -1,4 +1,12 @@
import argon2, { argon2id } from "argon2";
import crypto from "crypto";
import jsrp from "jsrp";
import nacl from "tweetnacl";
import { encodeBase64 } from "tweetnacl-util";
import { TUserEncryptionKeys } from "@app/db/schemas";
import { decryptSymmetric, encryptAsymmetric, encryptSymmetric } from "./encryption";
export const generateSrpServerKey = async (salt: string, verifier: string) => {
// eslint-disable-next-line new-cap
@@ -24,3 +32,97 @@ export const srpCheckClientProof = async (
server.setClientPublicKey(clientPublicKey);
return server.checkClientProof(clientProof);
};
// FOR GHOST USER STUFF
export const generateUserSrpKeys = async (email: string, password: string) => {
const pair = nacl.box.keyPair();
const secretKeyUint8Array = pair.secretKey;
const publicKeyUint8Array = pair.publicKey;
const privateKey = encodeBase64(secretKeyUint8Array);
const publicKey = encodeBase64(publicKeyUint8Array);
// eslint-disable-next-line
const client = new jsrp.client();
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 argon2.hash(password, {
salt: Buffer.from(salt),
memoryCost: 65536,
timeCost: 3,
parallelism: 1,
hashLength: 32,
type: argon2id,
raw: true
});
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
} = encryptSymmetric(privateKey, key.toString("base64"));
// create the protected key by encrypting the symmetric key
// [key] with the derived key
const {
ciphertext: protectedKey,
iv: protectedKeyIV,
tag: protectedKeyTag
} = encryptSymmetric(key.toString("hex"), derivedKey.toString("base64"));
return {
protectedKey,
plainPrivateKey: privateKey,
protectedKeyIV,
protectedKeyTag,
publicKey,
encryptedPrivateKey,
encryptedPrivateKeyIV,
encryptedPrivateKeyTag,
salt,
verifier
};
};
export const getUserPrivateKey = async (password: string, user: TUserEncryptionKeys) => {
const derivedKey = await argon2.hash(password, {
salt: Buffer.from(user.salt),
memoryCost: 65536,
timeCost: 3,
parallelism: 1,
hashLength: 32,
type: argon2id,
raw: true
});
if (!derivedKey) throw new Error("Failed to derive key from password");
const key = decryptSymmetric({
ciphertext: user.protectedKey!,
iv: user.protectedKeyIV!,
tag: user.protectedKeyTag!,
key: derivedKey.toString("base64")
});
const privateKey = decryptSymmetric({
ciphertext: user.encryptedPrivateKey,
iv: user.iv,
tag: user.tag,
key
});
return privateKey;
};
export const buildUserProjectKey = async (privateKey: string, publickey: string) => {
const randomBytes = crypto.randomBytes(16).toString("hex");
const { nonce, ciphertext } = encryptAsymmetric(randomBytes, publickey, privateKey);
return { nonce, ciphertext };
};