File size: 1,440 Bytes
476094d
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
import crypto from "node:crypto";

function normalizeSecret(secret) {
  const text = String(secret || "").trim();
  if (!text) {
    throw new Error("POOL_CRYPTO_KEY is required.");
  }
  return crypto.createHash("sha256").update(text).digest();
}

export function encryptJson(value, secret) {
  const key = normalizeSecret(secret);
  const iv = crypto.randomBytes(12);
  const cipher = crypto.createCipheriv("aes-256-gcm", key, iv);
  const payload = Buffer.from(JSON.stringify(value), "utf8");
  const ciphertext = Buffer.concat([cipher.update(payload), cipher.final()]);
  const tag = cipher.getAuthTag();

  return JSON.stringify(
    {
      version: 1,
      iv: iv.toString("base64"),
      tag: tag.toString("base64"),
      ciphertext: ciphertext.toString("base64"),
    },
    null,
    2,
  );
}

export function decryptJson(serialized, secret) {
  const key = normalizeSecret(secret);
  const parsed = typeof serialized === "string" ? JSON.parse(serialized) : serialized;
  if (parsed?.version !== 1) {
    throw new Error("Unsupported encrypted pool payload version.");
  }

  const decipher = crypto.createDecipheriv(
    "aes-256-gcm",
    key,
    Buffer.from(parsed.iv, "base64"),
  );
  decipher.setAuthTag(Buffer.from(parsed.tag, "base64"));

  const plaintext = Buffer.concat([
    decipher.update(Buffer.from(parsed.ciphertext, "base64")),
    decipher.final(),
  ]);
  return JSON.parse(plaintext.toString("utf8"));
}