mirror of
https://github.com/Portabase/portabase.git
synced 2026-07-14 11:16:13 +02:00
Working on Advanced Encryption Standard (AES), between agent backup and server.
This commit is contained in:
@@ -1,2 +1,40 @@
|
||||
import fs from "node:fs";
|
||||
import forge from "node-forge";
|
||||
|
||||
|
||||
export async function decryptedDump(file: File, aesKeyHex: string, ivHex: string): Promise<File> {
|
||||
const privateKeyPem = fs.readFileSync("private/keys/server_private.pem", "utf8");
|
||||
const privateKey = forge.pki.privateKeyFromPem(privateKeyPem);
|
||||
|
||||
// Decrypt AES key with RSA-OAEP
|
||||
const encryptedAesKey = forge.util.hexToBytes(aesKeyHex);
|
||||
const aesKey = privateKey.decrypt(encryptedAesKey, "RSA-OAEP", {
|
||||
md: forge.md.sha256.create(),
|
||||
mgf1: { md: forge.md.sha256.create() },
|
||||
});
|
||||
|
||||
// Read encrypted file content
|
||||
const encryptedBuffer = Buffer.from(await file.arrayBuffer());
|
||||
const iv = forge.util.hexToBytes(ivHex);
|
||||
|
||||
// AES decryption
|
||||
const decipher = forge.cipher.createDecipher("AES-CBC", aesKey);
|
||||
decipher.start({ iv });
|
||||
decipher.update(forge.util.createBuffer(encryptedBuffer.toString("binary")));
|
||||
const success = decipher.finish();
|
||||
|
||||
if (!success) {
|
||||
throw new Error("Decryption failed");
|
||||
}
|
||||
|
||||
const decryptedBytes = decipher.output.getBytes();
|
||||
const decryptedBuffer = Buffer.from(decryptedBytes, "binary");
|
||||
|
||||
// Return a File so you can use file.arrayBuffer() later
|
||||
return new File(
|
||||
[decryptedBuffer],
|
||||
file.name.replace(/\.enc$/, ".dump"), // rename if needed
|
||||
{type: "application/octet-stream"}
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -3,12 +3,13 @@ import {isUuidv4} from "@/utils/verify-uuid";
|
||||
import {uploadLocalPrivate, uploadS3Private} from "@/features/upload/private/upload.action";
|
||||
import {v4 as uuidv4} from "uuid";
|
||||
import {eventEmitter} from "../../../events/route";
|
||||
import * as drizzleDb from "@/db";
|
||||
import {db} from "@/db";
|
||||
import {Backup} from "@/db/schema/07_database";
|
||||
import {and, eq} from "drizzle-orm";
|
||||
import * as drizzleDb from "@/db";
|
||||
import {env} from "@/env.mjs";
|
||||
import {withUpdatedAt} from "@/db/utils";
|
||||
import {decryptedDump} from "./helpers";
|
||||
|
||||
export async function POST(
|
||||
request: Request,
|
||||
@@ -27,9 +28,13 @@ export async function POST(
|
||||
|
||||
const agentId = (await params).agentId;
|
||||
const formData = await request.formData();
|
||||
const aesKeyHex = formData.get("aes_key") as string;
|
||||
const ivHex = formData.get("iv") as string;
|
||||
const generatedId = formData.get("generatedId") as string | null;
|
||||
const method = formData.get("method") as string | null;
|
||||
|
||||
|
||||
|
||||
if (!generatedId || !isUuidv4(generatedId)) {
|
||||
return NextResponse.json(
|
||||
{error: "generatedId is not a valid UUID"},
|
||||
@@ -102,6 +107,11 @@ export async function POST(
|
||||
if (status === "success") {
|
||||
const file = formData.get("file") as File | null;
|
||||
|
||||
if (!aesKeyHex || !ivHex) {
|
||||
return NextResponse.json({error: "Missing fields"}, {status: 400});
|
||||
}
|
||||
|
||||
|
||||
if (!file) {
|
||||
return NextResponse.json(
|
||||
{error: "File is required for successful backup"},
|
||||
@@ -109,9 +119,13 @@ export async function POST(
|
||||
);
|
||||
}
|
||||
|
||||
const decryptedFile = await decryptedDump(file, aesKeyHex, ivHex);
|
||||
|
||||
const uuid = uuidv4();
|
||||
const fileName = `${uuid}.dump`;
|
||||
const buffer = Buffer.from(await file.arrayBuffer());
|
||||
// const buffer = Buffer.from(await fileDecrypted.arrayBuffer());
|
||||
const buffer = Buffer.from(await decryptedFile.arrayBuffer());
|
||||
// const buffer = fileDecrypted
|
||||
|
||||
const [settings] = await db.select().from(drizzleDb.schemas.setting).where(eq(drizzleDb.schemas.setting.name, "system")).limit(1);
|
||||
if (!settings) {
|
||||
@@ -173,4 +187,5 @@ export async function POST(
|
||||
{status: 500}
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -124,13 +124,12 @@ export async function handleDatabases(body: Body, agent: Agent, lastContact: Dat
|
||||
try {
|
||||
|
||||
if (settings.storage == "local") {
|
||||
data = await getFileUrlPresignedLocal(fileName!)
|
||||
data = await getFileUrlPresignedLocal({fileName: fileName!})
|
||||
} else if (settings.storage == "s3") {
|
||||
|
||||
data = await getFileUrlPreSignedS3Action(`backups/${backupToRestore?.database.project?.slug}/${fileName}`);
|
||||
}
|
||||
|
||||
|
||||
if (data?.data?.success) {
|
||||
urlBackup = data.data.value ?? "";
|
||||
} else {
|
||||
|
||||
@@ -20,7 +20,7 @@ export type Body = {
|
||||
|
||||
// Function to test the get file url presigned local
|
||||
export async function GET(request: Request) {
|
||||
const url = await getFileUrlPresignedLocal("d4a7fa35-2506-4d01-a612-a8ef2e2cc1c5.dump")
|
||||
const url = await getFileUrlPresignedLocal({fileName:"d4a7fa35-2506-4d01-a612-a8ef2e2cc1c5.dump"})
|
||||
return Response.json({
|
||||
message: url
|
||||
})
|
||||
|
||||
@@ -12,16 +12,21 @@ export async function GET(
|
||||
const expires = searchParams.get('expires');
|
||||
const fileName = (await params).fileName
|
||||
|
||||
const privateLocalDir = "private/uploads/files/";
|
||||
const filePath = path.join(privateLocalDir, fileName);
|
||||
console.log(token);
|
||||
console.log(fileName);
|
||||
|
||||
const uploadsDir = "private/uploads/files/";
|
||||
const keysDir = "private/keys/";
|
||||
const uploadPath = path.join(uploadsDir, fileName);
|
||||
const keyPath = path.join(keysDir, fileName);
|
||||
|
||||
const crypto = require('crypto');
|
||||
|
||||
if (!fs.existsSync(filePath)) {
|
||||
return NextResponse.json(
|
||||
{error: 'File not found'},
|
||||
{status: 404}
|
||||
);
|
||||
let filePath = uploadPath;
|
||||
if (!fs.existsSync(uploadPath)) {
|
||||
if (fs.existsSync(keyPath)) filePath = keyPath;
|
||||
else
|
||||
return NextResponse.json({error: "File not found"}, {status: 404});
|
||||
}
|
||||
|
||||
const expectedToken = crypto.createHash('sha256').update(`${fileName}${expires}`).digest('hex');
|
||||
|
||||
Reference in New Issue
Block a user