hf / src /crypto-utils.js
incognitolm
update
3666265
const crypto = require("node:crypto");
function toBase64Url(buffer) {
return Buffer.from(buffer).toString("base64url");
}
function fromBase64Url(value) {
return Buffer.from(value, "base64url");
}
function deriveMasterKey(encryptionKey) {
return crypto.scryptSync(encryptionKey, "hf-home-master-key", 32);
}
function deriveTokenKey(masterKey, sessionSecret, sessionId) {
return crypto.hkdfSync(
"sha256",
Buffer.from(sessionSecret, "utf8"),
masterKey,
Buffer.from(`hf-home:${sessionId}`, "utf8"),
32,
);
}
function encryptText(plainText, key, aad = "") {
const iv = crypto.randomBytes(12);
const cipher = crypto.createCipheriv("aes-256-gcm", key, iv);
if (aad) {
cipher.setAAD(Buffer.from(aad, "utf8"));
}
const ciphertext = Buffer.concat([cipher.update(plainText, "utf8"), cipher.final()]);
const tag = cipher.getAuthTag();
return `${toBase64Url(iv)}.${toBase64Url(ciphertext)}.${toBase64Url(tag)}`;
}
function decryptText(payload, key, aad = "") {
const [ivPart, ciphertextPart, tagPart] = String(payload).split(".");
if (!ivPart || !ciphertextPart || !tagPart) {
throw new Error("Invalid encrypted payload.");
}
const decipher = crypto.createDecipheriv("aes-256-gcm", key, fromBase64Url(ivPart));
if (aad) {
decipher.setAAD(Buffer.from(aad, "utf8"));
}
decipher.setAuthTag(fromBase64Url(tagPart));
const plaintext = Buffer.concat([
decipher.update(fromBase64Url(ciphertextPart)),
decipher.final(),
]);
return plaintext.toString("utf8");
}
function encryptJson(value, key, aad = "") {
return encryptText(JSON.stringify(value), key, aad);
}
function decryptJson(payload, key, aad = "") {
return JSON.parse(decryptText(payload, key, aad));
}
function hashSessionSecret(secret, salt = crypto.randomBytes(16)) {
const hash = crypto.scryptSync(secret, salt, 64);
return {
salt: toBase64Url(salt),
hash: toBase64Url(hash),
};
}
function verifySessionSecret(secret, expectedHash, salt) {
const actual = crypto.scryptSync(secret, fromBase64Url(salt), 64);
const expected = fromBase64Url(expectedHash);
if (actual.length !== expected.length) {
return false;
}
return crypto.timingSafeEqual(actual, expected);
}
function generateToken(size = 32) {
return toBase64Url(crypto.randomBytes(size));
}
function createCsrfToken(masterKey, sessionId, sessionSecret) {
return crypto
.createHmac("sha256", masterKey)
.update(`csrf:${sessionId}:${sessionSecret}`, "utf8")
.digest("base64url");
}
module.exports = {
createCsrfToken,
decryptJson,
decryptText,
deriveMasterKey,
deriveTokenKey,
encryptJson,
encryptText,
generateToken,
hashSessionSecret,
verifySessionSecret,
};