This commit is contained in:
1 parent
8efd032cc6
commit
df38dccbf1
735 files changed
+128321
-120870
No files matched your search
@@ -10,68 +10,74 @@ import { createCipheriv, createDecipheriv, randomBytes } from "node:crypto";
|
||||
* } )
|
||||
*/
|
||||
export class LaravelEncrypter {
|
||||
private readonly key: Buffer;
|
||||
private readonly key: Buffer;
|
||||
|
||||
/** APP_KEY is "base64:...." (or a raw 32-byte string). */
|
||||
constructor(appKey: string) {
|
||||
const raw = appKey.startsWith("base64:")
|
||||
? Buffer.from(appKey.slice("base64:".length), "base64")
|
||||
: Buffer.from(appKey, "utf8");
|
||||
if (raw.length !== 32) {
|
||||
throw new Error(`APP_KEY must decode to 32 bytes for AES-256-GCM (got ${raw.length})`);
|
||||
}
|
||||
this.key = raw;
|
||||
}
|
||||
/** APP_KEY is "base64:...." (or a raw 32-byte string). */
|
||||
constructor(appKey: string) {
|
||||
const raw = appKey.startsWith("base64:")
|
||||
? Buffer.from(appKey.slice("base64:".length), "base64")
|
||||
: Buffer.from(appKey, "utf8");
|
||||
if (raw.length !== 32) {
|
||||
throw new Error(
|
||||
`APP_KEY must decode to 32 bytes for AES-256-GCM (got ${raw.length})`,
|
||||
);
|
||||
}
|
||||
this.key = raw;
|
||||
}
|
||||
|
||||
encrypt(value: string, serialize = true): string {
|
||||
const iv = randomBytes(12);
|
||||
const data = serialize ? phpSerializeString(value) : value;
|
||||
const cipher = createCipheriv("aes-256-gcm", this.key, iv);
|
||||
const valueB64 = cipher.update(data, "utf8", "base64") + cipher.final("base64");
|
||||
const tag = cipher.getAuthTag();
|
||||
const ivB64 = iv.toString("base64");
|
||||
const tagB64 = tag.toString("base64");
|
||||
const payload = JSON.stringify({ iv: ivB64, value: valueB64, tag: tagB64 });
|
||||
return Buffer.from(payload, "utf8").toString("base64");
|
||||
}
|
||||
encrypt(value: string, serialize = true): string {
|
||||
const iv = randomBytes(12);
|
||||
const data = serialize ? phpSerializeString(value) : value;
|
||||
const cipher = createCipheriv("aes-256-gcm", this.key, iv);
|
||||
const valueB64 =
|
||||
cipher.update(data, "utf8", "base64") + cipher.final("base64");
|
||||
const tag = cipher.getAuthTag();
|
||||
const ivB64 = iv.toString("base64");
|
||||
const tagB64 = tag.toString("base64");
|
||||
const payload = JSON.stringify({ iv: ivB64, value: valueB64, tag: tagB64 });
|
||||
return Buffer.from(payload, "utf8").toString("base64");
|
||||
}
|
||||
|
||||
decrypt(payload: string, serialize = true): string {
|
||||
const json = JSON.parse(Buffer.from(payload, "base64").toString("utf8")) as {
|
||||
iv: string;
|
||||
value: string;
|
||||
tag: string;
|
||||
};
|
||||
const iv = Buffer.from(json.iv, "base64");
|
||||
const tag = Buffer.from(json.tag, "base64");
|
||||
const decipher = createDecipheriv("aes-256-gcm", this.key, iv);
|
||||
decipher.setAuthTag(tag);
|
||||
const plain = decipher.update(json.value, "base64", "utf8") + decipher.final("utf8");
|
||||
return serialize ? phpUnserializeString(plain) : plain;
|
||||
}
|
||||
decrypt(payload: string, serialize = true): string {
|
||||
const json = JSON.parse(
|
||||
Buffer.from(payload, "base64").toString("utf8"),
|
||||
) as {
|
||||
iv: string;
|
||||
value: string;
|
||||
tag: string;
|
||||
};
|
||||
const iv = Buffer.from(json.iv, "base64");
|
||||
const tag = Buffer.from(json.tag, "base64");
|
||||
const decipher = createDecipheriv("aes-256-gcm", this.key, iv);
|
||||
decipher.setAuthTag(tag);
|
||||
const plain =
|
||||
decipher.update(json.value, "base64", "utf8") + decipher.final("utf8");
|
||||
return serialize ? phpUnserializeString(plain) : plain;
|
||||
}
|
||||
|
||||
encryptString(value: string): string {
|
||||
return this.encrypt(value, false);
|
||||
}
|
||||
encryptString(value: string): string {
|
||||
return this.encrypt(value, false);
|
||||
}
|
||||
|
||||
decryptString(payload: string): string {
|
||||
return this.decrypt(payload, false);
|
||||
}
|
||||
decryptString(payload: string): string {
|
||||
return this.decrypt(payload, false);
|
||||
}
|
||||
}
|
||||
|
||||
/** PHP serialize() for a string: s:<byteLength>:"<value>"; */
|
||||
export function phpSerializeString(value: string): string {
|
||||
return `s:${Buffer.byteLength(value, "utf8")}:"${value}";`;
|
||||
return `s:${Buffer.byteLength(value, "utf8")}:"${value}";`;
|
||||
}
|
||||
|
||||
/** PHP unserialize() for a serialized string payload. */
|
||||
export function phpUnserializeString(serialized: string): string {
|
||||
const m = /^s:(\d+):"/.exec(serialized);
|
||||
if (!m) throw new Error("Not a serialized PHP string");
|
||||
const byteLen = Number(m[1]);
|
||||
const start = m[0].length;
|
||||
// Slice by BYTE length (PHP counts bytes), then back to a JS string.
|
||||
const bytes = Buffer.from(serialized, "utf8").subarray(
|
||||
Buffer.byteLength(serialized.slice(0, start), "utf8"),
|
||||
);
|
||||
return bytes.subarray(0, byteLen).toString("utf8");
|
||||
const m = /^s:(\d+):"/.exec(serialized);
|
||||
if (!m) throw new Error("Not a serialized PHP string");
|
||||
const byteLen = Number(m[1]);
|
||||
const start = m[0].length;
|
||||
// Slice by BYTE length (PHP counts bytes), then back to a JS string.
|
||||
const bytes = Buffer.from(serialized, "utf8").subarray(
|
||||
Buffer.byteLength(serialized.slice(0, start), "utf8"),
|
||||
);
|
||||
return bytes.subarray(0, byteLen).toString("utf8");
|
||||
}
|
||||
Reference in new issue
Block a user