feat: 导入 MemRelay 初始源码
This commit is contained in:
@@ -0,0 +1,88 @@
|
||||
#!/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();
|
||||
Reference in New Issue
Block a user