10
10
import argparse
11
11
import sys
12
12
import io
13
+ import base64
14
+ import zlib
13
15
14
- # Dummy stream to handle write calls when sys.stderr is None
15
16
class DummyStream (io .StringIO ):
16
17
def write (self , txt ):
17
18
pass
18
19
19
- # Check if stderr is None and replace it with the dummy stream
20
20
if sys .stderr is None :
21
21
sys .stderr = DummyStream ()
22
22
23
- # Constants
24
23
SALT_SIZE = 16
25
24
NUM_ITERATIONS = 100000
26
- KEY_SIZE = 32 # 256 bits for AES-256
25
+ KEY_SIZE = 32
27
26
IV_SIZE = 16
28
27
NUM_LAYERS = 7
29
- FILENAME_SIZE = 255 # Maximum filename length
28
+ FILENAME_SIZE = 255
30
29
31
- # Function to encrypt data with AES and then encrypt AES key with RSA
32
30
def encrypt_data (data , public_key_path ):
33
31
with open (public_key_path , 'rb' ) as f :
34
32
public_key = RSA .import_key (f .read ())
35
33
cipher_rsa = PKCS1_OAEP .new (public_key )
36
34
37
- session_key = get_random_bytes (16 ) # AES key length (128 bits)
35
+ session_key = get_random_bytes (16 )
38
36
salt = get_random_bytes (SALT_SIZE )
39
37
key = PBKDF2 (session_key , salt , dkLen = KEY_SIZE , count = NUM_ITERATIONS )
40
38
39
+ data = zlib .compress (data )
40
+
41
41
for _ in range (NUM_LAYERS ):
42
42
iv = get_random_bytes (IV_SIZE )
43
43
cipher_aes = AES .new (key , AES .MODE_CBC , iv )
44
44
data = cipher_aes .encrypt (pad (data , AES .block_size ))
45
- data = iv + data # Prepend IV to the ciphertext for each layer
45
+ data = iv + data
46
46
47
47
enc_session_key = cipher_rsa .encrypt (session_key )
48
- return enc_session_key , salt , data
48
+ enc_session_key_b64 = base64 .b64encode (enc_session_key ).decode ('utf-8' )
49
+ salt_b64 = base64 .b64encode (salt ).decode ('utf-8' )
50
+ data_b64 = base64 .b64encode (data ).decode ('utf-8' )
51
+ return enc_session_key_b64 , salt_b64 , data_b64
49
52
50
- # Function to decrypt AES key with RSA and then decrypt data with AES
51
- def decrypt_data (enc_session_key , salt , data , private_key_path , passphrase ):
53
+ def decrypt_data (enc_session_key_b64 , salt_b64 , data_b64 , private_key_path , passphrase ):
52
54
with open (private_key_path , 'rb' ) as f :
53
55
private_key = RSA .import_key (f .read (), passphrase = passphrase )
54
56
cipher_rsa = PKCS1_OAEP .new (private_key )
55
57
56
- session_key = cipher_rsa .decrypt (enc_session_key )
58
+ session_key = cipher_rsa .decrypt (base64 .b64decode (enc_session_key_b64 ))
59
+ salt = base64 .b64decode (salt_b64 )
60
+ data = base64 .b64decode (data_b64 )
61
+
57
62
key = PBKDF2 (session_key , salt , dkLen = KEY_SIZE , count = NUM_ITERATIONS )
58
63
59
64
for _ in range (NUM_LAYERS ):
@@ -62,21 +67,16 @@ def decrypt_data(enc_session_key, salt, data, private_key_path, passphrase):
62
67
cipher_aes = AES .new (key , AES .MODE_CBC , iv )
63
68
data = unpad (cipher_aes .decrypt (data ), AES .block_size )
64
69
70
+ # Decompress the data after decrypting
71
+ data = zlib .decompress (data )
72
+
65
73
return data
66
74
67
-
68
-
69
-
70
75
if __name__ == '__main__' :
71
-
72
76
root = tk .Tk ()
73
- root .withdraw () # Hide the root window
77
+ root .withdraw ()
74
78
75
- # Check if any arguments were provided
76
79
if len (sys .argv ) > 1 :
77
-
78
-
79
- # Add command line argument parsing
80
80
parser = argparse .ArgumentParser (description = "File Hider" )
81
81
parser .add_argument ('--hide' , action = 'store_true' , help = 'Hide and encrypt a file' )
82
82
parser .add_argument ('--unhide' , action = 'store_true' , help = 'Decrypt and unhide a file' )
@@ -86,13 +86,7 @@ def decrypt_data(enc_session_key, salt, data, private_key_path, passphrase):
86
86
parser .add_argument ('--private-key' , type = str , help = 'Path to the private key file' )
87
87
parser .add_argument ('--output' , type = str , help = 'Path to save the modified host file or extracted file' )
88
88
parser .add_argument ('--passphrase' , type = str , help = 'Passphrase for the private key' , default = '' )
89
- try :
90
- args = parser .parse_args ()
91
- except SystemExit :
92
- # When bad arguments are provided, show the help message in a messagebox
93
- error_message = parser .format_help ()
94
- messagebox .showerror ("Argument Error" , error_message )
95
- sys .exit (1 )
89
+ args = parser .parse_args ()
96
90
97
91
if args .hide and args .host and args .file and args .public_key and args .output :
98
92
try :
@@ -102,23 +96,24 @@ def decrypt_data(enc_session_key, salt, data, private_key_path, passphrase):
102
96
with open (args .file , 'rb' ) as hidden_file :
103
97
hidden_data = hidden_file .read ()
104
98
105
- enc_session_key , salt , encrypted_hidden_data = encrypt_data (hidden_data , args .public_key )
99
+ enc_session_key_b64 , salt_b64 , encrypted_hidden_data_b64 = encrypt_data (hidden_data , args .public_key )
106
100
107
101
full_filename = os .path .basename (args .file ).encode ('utf-8' )
108
- full_filename += b' ' * (FILENAME_SIZE - len (full_filename ))
102
+ full_filename_b64 = base64 .b64encode (full_filename ).decode ('utf-8' )
103
+ full_filename_b64 += ' ' * (FILENAME_SIZE - len (full_filename_b64 ))
109
104
110
105
with open (args .output , 'wb' ) as output_file :
111
106
output_file .write (host_data )
112
- output_file .write (enc_session_key )
113
- output_file .write (salt )
114
- output_file .write (full_filename )
115
- output_file .write (encrypted_hidden_data )
116
- output_file .write (struct .pack ('<IIII' , len (enc_session_key ), len (salt ), FILENAME_SIZE , len (encrypted_hidden_data )))
107
+ output_file .write (enc_session_key_b64 . encode ( 'utf-8' ) )
108
+ output_file .write (salt_b64 . encode ( 'utf-8' ) )
109
+ output_file .write (full_filename_b64 . encode ( 'utf-8' ) )
110
+ output_file .write (encrypted_hidden_data_b64 . encode ( 'utf-8' ) )
111
+ output_file .write (struct .pack ('<IIII' , len (enc_session_key_b64 ), len (salt_b64 ), FILENAME_SIZE , len (encrypted_hidden_data_b64 )))
117
112
118
113
print ("The file has been successfully hidden and encrypted within the host file!" )
119
114
except Exception as e :
120
115
print ("Error:" , str (e ))
121
- messagebox .showerror ("Argument Error" , "Missing arguments for hiding a file." )
116
+ messagebox .showerror ("Error" , "An error occurred while hiding the file." )
122
117
sys .exit ()
123
118
124
119
elif args .unhide and args .host and args .private_key and args .output :
@@ -128,45 +123,39 @@ def decrypt_data(enc_session_key, salt, data, private_key_path, passphrase):
128
123
enc_session_key_size , salt_size , filename_size , encrypted_hidden_data_size = struct .unpack ('<IIII' , host_file .read (16 ))
129
124
host_file .seek (0 )
130
125
host_data = host_file .read ()
131
- enc_session_key = host_data [- (16 + enc_session_key_size + salt_size + filename_size + encrypted_hidden_data_size ):- 16 - salt_size - filename_size - encrypted_hidden_data_size ]
132
- salt = host_data [- (16 + salt_size + filename_size + encrypted_hidden_data_size ):- 16 - filename_size - encrypted_hidden_data_size ]
133
- full_filename = host_data [- (16 + filename_size + encrypted_hidden_data_size ):- 16 - encrypted_hidden_data_size ].rstrip (b ' ' )
134
- encrypted_hidden_data = host_data [- (16 + encrypted_hidden_data_size ):- 16 ]
126
+ enc_session_key_b64 = host_data [- (16 + enc_session_key_size + salt_size + filename_size + encrypted_hidden_data_size ):- 16 - salt_size - filename_size - encrypted_hidden_data_size ]. decode ( 'utf-8' )
127
+ salt_b64 = host_data [- (16 + salt_size + filename_size + encrypted_hidden_data_size ):- 16 - filename_size - encrypted_hidden_data_size ]. decode ( 'utf-8' )
128
+ full_filename_b64 = host_data [- (16 + filename_size + encrypted_hidden_data_size ):- 16 - encrypted_hidden_data_size ].decode ( 'utf-8' ). rstrip (' ' )
129
+ encrypted_hidden_data_b64 = host_data [- (16 + encrypted_hidden_data_size ):- 16 ]. decode ( 'utf-8' )
135
130
136
- decrypted_data = decrypt_data (enc_session_key , salt , encrypted_hidden_data , args .private_key , args .passphrase )
131
+ decrypted_data = decrypt_data (enc_session_key_b64 , salt_b64 , encrypted_hidden_data_b64 , args .private_key , args .passphrase )
137
132
138
133
with open (args .output , 'wb' ) as output_file :
139
134
output_file .write (decrypted_data )
140
135
141
136
print (f"Hidden file was successfully decrypted and extracted to { args .output } !" )
142
137
except Exception as e :
143
138
print ("Error:" , str (e ))
144
- messagebox .showerror ("Argument Error" , "Missing arguments for unhiding a file." )
145
-
139
+ messagebox .showerror ("Error" , "An error occurred while unhiding the file." )
146
140
sys .exit ()
147
141
148
142
else :
149
- messagebox .showerror ("Argument Error" , "Missing arguments" )
150
-
151
- sys .exit ()
143
+ messagebox .showerror ("Error" , "Missing arguments" )
144
+ sys .exit ()
152
145
153
146
else :
154
-
155
-
156
-
157
147
root = tk .Tk ()
158
148
root .title ("File Hider" )
159
149
160
150
# Center the window on the screen
161
- window_width = 400
162
- window_height = 150
151
+ window_width = 300
152
+ window_height = 100
163
153
screen_width = root .winfo_screenwidth ()
164
154
screen_height = root .winfo_screenheight ()
165
155
center_x = int (screen_width / 2 - window_width / 2 )
166
156
center_y = int (screen_height / 2 - window_height / 2 )
167
157
root .geometry (f'{ window_width } x{ window_height } +{ center_x } +{ center_y } ' )
168
158
169
- # Function to hide and encrypt a file within another file
170
159
def hide_file ():
171
160
host_file_path = filedialog .askopenfilename (title = "Select the host file" , filetypes = [("All Files" , "*.*" )])
172
161
if not host_file_path :
@@ -191,26 +180,24 @@ def hide_file():
191
180
with open (hidden_file_path , 'rb' ) as hidden_file :
192
181
hidden_data = hidden_file .read ()
193
182
194
- enc_session_key , salt , encrypted_hidden_data = encrypt_data (hidden_data , public_key_path )
183
+ enc_session_key_b64 , salt_b64 , encrypted_hidden_data_b64 = encrypt_data (hidden_data , public_key_path )
195
184
196
- # Save the file extension and filename of the hidden file
197
185
full_filename = os .path .basename (hidden_file_path ).encode ('utf-8' )
198
- full_filename += b' ' * (FILENAME_SIZE - len (full_filename )) # Pad the full filename to FILENAME_SIZE
186
+ full_filename_b64 = base64 .b64encode (full_filename ).decode ('utf-8' )
187
+ full_filename_b64 += ' ' * (FILENAME_SIZE - len (full_filename_b64 ))
199
188
200
189
with open (output_path , 'wb' ) as output_file :
201
190
output_file .write (host_data )
202
- output_file .write (enc_session_key )
203
- output_file .write (salt )
204
- output_file .write (full_filename ) # Write the padded full filename
205
- output_file .write (encrypted_hidden_data )
206
- # Store the sizes of the encrypted session key, salt, full filename, and encrypted hidden data
207
- output_file .write (struct .pack ('<IIII' , len (enc_session_key ), len (salt ), FILENAME_SIZE , len (encrypted_hidden_data )))
191
+ output_file .write (enc_session_key_b64 .encode ('utf-8' ))
192
+ output_file .write (salt_b64 .encode ('utf-8' ))
193
+ output_file .write (full_filename_b64 .encode ('utf-8' ))
194
+ output_file .write (encrypted_hidden_data_b64 .encode ('utf-8' ))
195
+ output_file .write (struct .pack ('<IIII' , len (enc_session_key_b64 ), len (salt_b64 ), FILENAME_SIZE , len (encrypted_hidden_data_b64 )))
208
196
209
197
messagebox .showinfo ("Success" , "The file has been successfully hidden and encrypted within the host file!" )
210
198
except Exception as e :
211
- messagebox .showerror ("Error" , str (e ))
199
+ messagebox .showerror ("Error" , "An error occurred while hiding the file: " + str (e ))
212
200
213
- # Function to extract and decrypt the hidden file from the host file
214
201
def unhide_file ():
215
202
host_file_path = filedialog .askopenfilename (title = "Select the modified host file" , filetypes = [("All Files" , "*.*" )])
216
203
if not host_file_path :
@@ -221,45 +208,42 @@ def unhide_file():
221
208
return
222
209
223
210
passphrase = simpledialog .askstring ("Passphrase" , "Enter the passphrase for the private key:" , show = "*" )
224
- if passphrase is None : # Allow empty passphrase (not recommended)
211
+ if passphrase is None :
225
212
passphrase = ''
226
213
227
214
try :
228
215
with open (host_file_path , 'rb' ) as host_file :
229
- host_file .seek (- 16 , os .SEEK_END ) # Seek to the last 16 bytes where the sizes are stored
216
+ host_file .seek (- 16 , os .SEEK_END )
230
217
enc_session_key_size , salt_size , filename_size , encrypted_hidden_data_size = struct .unpack ('<IIII' , host_file .read (16 ))
231
218
host_file .seek (0 )
232
219
host_data = host_file .read ()
233
- # Extract the encrypted session key, salt, full filename, and encrypted hidden data
234
- enc_session_key = host_data [- (16 + enc_session_key_size + salt_size + filename_size + encrypted_hidden_data_size ):- 16 - salt_size - filename_size - encrypted_hidden_data_size ]
235
- salt = host_data [- (16 + salt_size + filename_size + encrypted_hidden_data_size ):- 16 - filename_size - encrypted_hidden_data_size ]
236
- full_filename = host_data [- (16 + filename_size + encrypted_hidden_data_size ):- 16 - encrypted_hidden_data_size ].rstrip (b' ' ) # Remove padding
237
- encrypted_hidden_data = host_data [- (16 + encrypted_hidden_data_size ):- 16 ]
220
+ enc_session_key_b64 = host_data [- (16 + enc_session_key_size + salt_size + filename_size + encrypted_hidden_data_size ):- 16 - salt_size - filename_size - encrypted_hidden_data_size ].decode ('utf-8' )
221
+ salt_b64 = host_data [- (16 + salt_size + filename_size + encrypted_hidden_data_size ):- 16 - filename_size - encrypted_hidden_data_size ].decode ('utf-8' )
222
+ full_filename_b64 = host_data [- (16 + filename_size + encrypted_hidden_data_size ):- 16 - encrypted_hidden_data_size ].decode ('utf-8' ).rstrip (' ' )
223
+ encrypted_hidden_data_b64 = host_data [- (16 + encrypted_hidden_data_size ):- 16 ].decode ('utf-8' )
238
224
239
- decrypted_data = decrypt_data (enc_session_key , salt , encrypted_hidden_data , private_key_path , passphrase )
225
+ decrypted_data = decrypt_data (enc_session_key_b64 , salt_b64 , encrypted_hidden_data_b64 , private_key_path , passphrase )
226
+
227
+ original_filename = base64 .b64decode (full_filename_b64 ).decode ('utf-8' )
240
228
241
- # Ask for the directory to save the file
242
229
output_directory = filedialog .askdirectory (title = "Select the output directory" )
243
230
if not output_directory :
244
231
return
245
232
246
- # Ask for the new filename, providing the original as the default
247
- original_filename = full_filename .decode ('utf-8' )
248
233
new_filename = simpledialog .askstring ("New Filename" , "Enter a new filename or use the original:" , initialvalue = original_filename )
249
234
if new_filename is None :
250
235
return
251
236
252
- # Automatically generate the output path with the new filename and extension
253
237
output_path = os .path .join (output_directory , new_filename )
254
238
255
239
with open (output_path , 'wb' ) as output_file :
256
240
output_file .write (decrypted_data )
257
241
258
242
messagebox .showinfo ("Success" , f"Hidden file was successfully decrypted and extracted to { output_path } !" )
259
243
except Exception as e :
260
- messagebox .showerror ("Error" , str (e ))
244
+ messagebox .showerror ("Error" , "An error occurred while unhiding the file: " + str (e ))
245
+
261
246
262
- # GUI buttons
263
247
hide_button = tk .Button (root , text = "Hide and Encrypt File" , command = hide_file )
264
248
hide_button .pack (fill = tk .X , expand = True , padx = 10 , pady = 5 )
265
249
0 commit comments