Repository navigation
Expand file tree
/
Copy pathcrypto.ts
More file actions
62 lines (50 loc) · 1.8 KB
/
Copy pathcrypto.ts
File metadata and controls
62 lines (50 loc) · 1.8 KB
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
52
53
54
55
56
57
58
59
60
61
62
import { createCipheriv, createDecipheriv, createHash, randomBytes } from "node:crypto";
import { getAppMetadataConfiguration } from "@/lib/config";
export class MetadataDecryptionError extends Error {
constructor(message = "Encrypted metadata could not be decrypted.") {
super(message);
this.name = "MetadataDecryptionError";
}
}
export function isMetadataDecryptionError(
error: unknown,
): error is MetadataDecryptionError {
return error instanceof MetadataDecryptionError;
}
function getEncryptionKey() {
return createHash("sha256")
.update(getAppMetadataConfiguration().encryptionKey, "utf8")
.digest();
}
export function encryptJson(value: unknown) {
const iv = randomBytes(12);
const cipher = createCipheriv("aes-256-gcm", getEncryptionKey(), iv);
const plaintext = Buffer.from(JSON.stringify(value), "utf8");
const ciphertext = Buffer.concat([cipher.update(plaintext), cipher.final()]);
const authTag = cipher.getAuthTag();
return [iv, authTag, ciphertext].map((part) => part.toString("base64url")).join(".");
}
export function decryptJson<T>(value: string): T {
const [ivPart, tagPart, ciphertextPart] = value.split(".");
if (!ivPart || !tagPart || !ciphertextPart) {
throw new MetadataDecryptionError("Encrypted metadata is malformed.");
}
try {
const decipher = createDecipheriv(
"aes-256-gcm",
getEncryptionKey(),
Buffer.from(ivPart, "base64url"),
);
decipher.setAuthTag(Buffer.from(tagPart, "base64url"));
const plaintext = Buffer.concat([
decipher.update(Buffer.from(ciphertextPart, "base64url")),
decipher.final(),
]);
return JSON.parse(plaintext.toString("utf8")) as T;
} catch (error) {
if (error instanceof MetadataDecryptionError) {
throw error;
}
throw new MetadataDecryptionError();
}
}