Files
MemRelay/scripts/create_test_vault_account.mjs
T

89 lines
2.6 KiB
JavaScript

#!/usr/bin/env node
import {
createCipheriv,
createHmac,
generateKeyPairSync,
pbkdf2Sync,
randomBytes,
} from "node:crypto";
function hkdfExpand(key, info, length) {
const output = [];
let previous = Buffer.alloc(0);
let counter = 1;
while (Buffer.concat(output).length < length) {
previous = createHmac("sha256", key)
.update(Buffer.concat([previous, Buffer.from(info, "utf8"), Buffer.from([counter])]))
.digest();
output.push(previous);
counter += 1;
}
return Buffer.concat(output).subarray(0, length);
}
function encrypt(plaintext, key) {
const encryptionKey = key.subarray(0, 32);
const macKey = key.subarray(32, 64);
const iv = randomBytes(16);
const cipher = createCipheriv("aes-256-cbc", encryptionKey, iv);
const ciphertext = Buffer.concat([cipher.update(plaintext), cipher.final()]);
const mac = createHmac("sha256", macKey).update(Buffer.concat([iv, ciphertext])).digest();
return `2.${iv.toString("base64")}|${ciphertext.toString("base64")}|${mac.toString("base64")}`;
}
async function main() {
const [server, rawEmail, password] = process.argv.slice(2);
if (!server || !rawEmail || !password) {
throw new Error("Usage: create_test_vault_account.mjs <server> <email> <password>");
}
const email = rawEmail.trim().toLowerCase();
const iterations = 600_000;
const masterKey = pbkdf2Sync(password, email, iterations, 32, "sha256");
const masterPasswordHash = pbkdf2Sync(masterKey, password, 1, 32, "sha256").toString(
"base64",
);
const stretchedMasterKey = Buffer.concat([
hkdfExpand(masterKey, "enc", 32),
hkdfExpand(masterKey, "mac", 32),
]);
const userKey = randomBytes(64);
const key = encrypt(userKey, stretchedMasterKey);
const { publicKey, privateKey } = generateKeyPairSync("rsa", {
modulusLength: 2048,
publicKeyEncoding: { type: "spki", format: "der" },
privateKeyEncoding: { type: "pkcs8", format: "der" },
});
const response = await fetch(`${server.replace(/\/$/, "")}/identity/accounts/register`, {
method: "POST",
headers: { "content-type": "application/json" },
body: JSON.stringify({
email,
name: "MemRelay Stage Zero",
masterPasswordHash,
masterPasswordHint: null,
key,
keys: {
encryptedPrivateKey: encrypt(privateKey, userKey),
publicKey: publicKey.toString("base64"),
},
kdf: 0,
kdfIterations: iterations,
kdfMemory: null,
kdfParallelism: null,
}),
});
const body = await response.text();
if (!response.ok) {
throw new Error(`Vault registration failed (${response.status}): ${body}`);
}
console.log(body);
}
await main();