Skip to content

Commit 72b447a

Browse files
author
FileShot
committed
v1.4.4
1 parent 2a69466 commit 72b447a

8 files changed

Lines changed: 1091 additions & 112 deletions

File tree

assets/logonew.png

60.8 KB
Loading

main.js

Lines changed: 39 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -69,9 +69,9 @@ function safeStat(p) {
6969

7070
/**
7171
* Add a file to the vault with AES-256-GCM encryption.
72-
* The file is encrypted using a randomly generated key stored in the vault metadata.
72+
* Uses password-based encryption with PBKDF2 key derivation.
7373
*/
74-
async function addFileToVault(sourcePath) {
74+
async function addFileToVault(sourcePath, passphrase) {
7575
ensureVaultDirs();
7676
const st = safeStat(sourcePath);
7777
if (!st || !st.isFile()) return null;
@@ -81,12 +81,13 @@ async function addFileToVault(sourcePath) {
8181
const storedName = `${id}.fszk`; // Always use .fszk extension for encrypted files
8282
const destPath = path.join(getVaultFilesDir(), storedName);
8383

84-
// Encrypt the file using ZKE format
84+
// Encrypt the file using ZKE format with PASSWORD-BASED encryption
8585
const result = await encryptFileToZkeContainer({
8686
inputPath: sourcePath,
8787
outputPath: destPath,
8888
originalName: name,
89-
mode: 'raw' // Use random key (stored in vault metadata)
89+
mode: 'passphrase', // Use password-based encryption
90+
passphrase: passphrase
9091
});
9192

9293
const encryptedStat = safeStat(destPath);
@@ -100,7 +101,8 @@ async function addFileToVault(sourcePath) {
100101
localPath: destPath,
101102
sourcePath,
102103
encrypted: true,
103-
encryptionKey: result.rawKey // Base64url encoded AES-256 key
104+
encryptionMode: 'passphrase' // Indicate password-based encryption
105+
// Note: No key stored - user must remember password
104106
};
105107
}
106108

@@ -1014,14 +1016,17 @@ ipcMain.handle('vault-list', async () => {
10141016
return { items, totalBytes: vaultTotalBytes(items) };
10151017
});
10161018

1017-
ipcMain.handle('vault-add', async (_event, paths) => {
1019+
ipcMain.handle('vault-add', async (_event, { paths, passphrase }) => {
10181020
const list = Array.isArray(paths) ? paths : [];
1021+
if (!passphrase || passphrase.length < 4) {
1022+
return { success: false, error: 'Password must be at least 4 characters' };
1023+
}
10191024
const items = getVaultItems();
10201025
let added = 0;
10211026

10221027
for (const p of list) {
10231028
try {
1024-
const entry = await addFileToVault(p);
1029+
const entry = await addFileToVault(p, passphrase);
10251030
if (entry) {
10261031
items.unshift(entry);
10271032
added++;
@@ -1035,8 +1040,11 @@ ipcMain.handle('vault-add', async (_event, paths) => {
10351040
return { success: true, added };
10361041
});
10371042

1038-
ipcMain.handle('vault-add-folder', async (_event, folderPath) => {
1043+
ipcMain.handle('vault-add-folder', async (_event, { folderPath, passphrase }) => {
10391044
const folder = String(folderPath || '');
1045+
if (!passphrase || passphrase.length < 4) {
1046+
return { success: false, error: 'Password must be at least 4 characters' };
1047+
}
10401048
const st = safeStat(folder);
10411049
if (!st || !st.isDirectory()) return { success: false, error: 'Folder not found' };
10421050

@@ -1046,7 +1054,7 @@ ipcMain.handle('vault-add-folder', async (_event, folderPath) => {
10461054

10471055
for (const p of files) {
10481056
try {
1049-
const entry = await addFileToVault(p);
1057+
const entry = await addFileToVault(p, passphrase);
10501058
if (entry) {
10511059
items.unshift(entry);
10521060
added++;
@@ -1081,41 +1089,34 @@ ipcMain.handle('vault-remove', async (_event, localId) => {
10811089
return { success: true };
10821090
});
10831091

1084-
ipcMain.handle('vault-reveal-key', async (_event, localId) => {
1085-
const id = String(localId || '');
1086-
const items = getVaultItems();
1087-
const it = items.find((x) => String(x.id) === id);
1088-
if (!it) return { success: false };
1089-
// Return the encryption key for this vault item
1090-
return {
1091-
success: true,
1092-
encryptionKey: it.encryptionKey || null,
1093-
shareKey: it?.lastUpload?.shareKey || null,
1094-
shareUrl: it?.lastUpload?.shareUrl || null
1095-
};
1096-
});
1097-
10981092
// Open/preview a vault file by decrypting to temp and opening
1099-
ipcMain.handle('vault-open', async (_event, localId) => {
1093+
ipcMain.handle('vault-open', async (_event, { localId, passphrase }) => {
11001094
const id = String(localId || '');
11011095
const items = getVaultItems();
11021096
const it = items.find((x) => String(x.id) === id);
11031097
if (!it) return { success: false, error: 'Not found' };
11041098
if (!it.localPath || !fs.existsSync(it.localPath)) return { success: false, error: 'File missing' };
11051099

1106-
// For encrypted files, decrypt to temp folder
1107-
if (it.encrypted && it.encryptionKey) {
1100+
// Encrypted files require password to decrypt
1101+
if (it.encrypted) {
1102+
if (!passphrase) {
1103+
return { success: false, error: 'Password required', needPassword: true };
1104+
}
11081105
try {
11091106
ensureVaultDirs();
11101107
const tmpPath = path.join(getVaultTmpDir(), it.name);
11111108
await decryptZkeContainer({
11121109
inputPath: it.localPath,
11131110
outputPath: tmpPath,
1114-
rawKeyBase64Url: it.encryptionKey
1111+
passphrase: passphrase
11151112
});
11161113
await shell.openPath(tmpPath);
11171114
return { success: true, tmpPath };
11181115
} catch (e) {
1116+
// Check if it's a decryption error (wrong password)
1117+
if (e.message && (e.message.includes('decrypt') || e.message.includes('auth') || e.message.includes('tag'))) {
1118+
return { success: false, error: 'Incorrect password', wrongPassword: true };
1119+
}
11191120
return { success: false, error: e.message || String(e) };
11201121
}
11211122
}
@@ -1130,29 +1131,37 @@ ipcMain.handle('vault-open', async (_event, localId) => {
11301131
});
11311132

11321133
// Export a single vault file (decrypt and save to user-chosen location)
1133-
ipcMain.handle('vault-export-file', async (_event, localId) => {
1134+
ipcMain.handle('vault-export-file', async (_event, { localId, passphrase }) => {
11341135
const id = String(localId || '');
11351136
const items = getVaultItems();
11361137
const it = items.find((x) => String(x.id) === id);
11371138
if (!it) return { success: false, error: 'Not found' };
11381139
if (!it.localPath || !fs.existsSync(it.localPath)) return { success: false, error: 'File missing' };
11391140

1141+
// Check password requirement first
1142+
if (it.encrypted && !passphrase) {
1143+
return { success: false, error: 'Password required', needPassword: true };
1144+
}
1145+
11401146
const result = await dialog.showSaveDialog(mainWindow, {
11411147
defaultPath: it.name,
11421148
title: 'Export Decrypted File'
11431149
});
11441150

11451151
if (result.canceled || !result.filePath) return { success: false, canceled: true };
11461152

1147-
if (it.encrypted && it.encryptionKey) {
1153+
if (it.encrypted) {
11481154
try {
11491155
await decryptZkeContainer({
11501156
inputPath: it.localPath,
11511157
outputPath: result.filePath,
1152-
rawKeyBase64Url: it.encryptionKey
1158+
passphrase: passphrase
11531159
});
11541160
return { success: true, path: result.filePath };
11551161
} catch (e) {
1162+
if (e.message && (e.message.includes('decrypt') || e.message.includes('auth') || e.message.includes('tag'))) {
1163+
return { success: false, error: 'Incorrect password', wrongPassword: true };
1164+
}
11561165
return { success: false, error: e.message || String(e) };
11571166
}
11581167
}

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "fileshot-desktop",
3-
"version": "1.4.2",
3+
"version": "1.4.4",
44
"description": "FileShot.io Desktop Application - Fast, Private File Sharing",
55
"main": "main.js",
66
"scripts": {

preload.js

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -30,12 +30,11 @@ contextBridge.exposeInMainWorld('electronAPI', {
3030

3131
// Local vault
3232
vaultList: () => ipcRenderer.invoke('vault-list'),
33-
vaultAdd: (paths) => ipcRenderer.invoke('vault-add', paths),
34-
vaultAddFolder: (folderPath) => ipcRenderer.invoke('vault-add-folder', folderPath),
33+
vaultAdd: (paths, passphrase) => ipcRenderer.invoke('vault-add', { paths, passphrase }),
34+
vaultAddFolder: (folderPath, passphrase) => ipcRenderer.invoke('vault-add-folder', { folderPath, passphrase }),
3535
vaultRemove: (localId) => ipcRenderer.invoke('vault-remove', localId),
36-
vaultRevealKey: (localId) => ipcRenderer.invoke('vault-reveal-key', localId),
37-
vaultOpen: (localId) => ipcRenderer.invoke('vault-open', localId),
38-
vaultExportFile: (localId) => ipcRenderer.invoke('vault-export-file', localId),
36+
vaultOpen: (localId, passphrase) => ipcRenderer.invoke('vault-open', { localId, passphrase }),
37+
vaultExportFile: (localId, passphrase) => ipcRenderer.invoke('vault-export-file', { localId, passphrase }),
3938
vaultExportAll: () => ipcRenderer.invoke('vault-export-all'),
4039
vaultImport: () => ipcRenderer.invoke('vault-import'),
4140

0 commit comments

Comments
 (0)