@@ -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 }
0 commit comments