Skip to content

Commit ba934d2

Browse files
Add files via upload
1 parent 619c527 commit ba934d2

File tree

1 file changed

+272
-0
lines changed

1 file changed

+272
-0
lines changed
Lines changed: 272 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,272 @@
1+
import hashlib
2+
import hmac
3+
import scrypt
4+
import secrets
5+
import os
6+
import time
7+
import re
8+
import psutil
9+
from Crypto.Cipher import AES
10+
from Crypto.Util.Padding import pad, unpad
11+
import base64
12+
import torch
13+
import torchcsprng as csprng
14+
import numpy as np
15+
16+
def hkdf_scrypt(password, salt, length, n, r, p):
17+
# HKDF extraction step
18+
prk = hmac.new(salt.encode(), password.encode(), hashlib.sha256).digest()
19+
20+
# HKDF expansion step
21+
info = b'Scrypt key derivation'
22+
t = b''
23+
okm = b''
24+
while len(okm) < length:
25+
t = hmac.new(prk, t + info + bytes([len(t) + 1]), hashlib.sha256).digest()
26+
okm += scrypt.hash(t, salt.encode(), n, r, p, length)
27+
28+
return okm[:length]
29+
30+
31+
def encrypt_file(file_path, key, ratio, device):
32+
# Read the file contents
33+
with open(file_path, 'rb') as file:
34+
plaintext = file.read()
35+
36+
# Workload division according to the ratio between cpu and gpu
37+
workload_division = int(len(plaintext) * ratio)
38+
39+
# plaintext for cpu
40+
cpu_plaintext = plaintext[:workload_division]
41+
42+
# Generate a random IV (Initialization Vector)
43+
iv = secrets.token_bytes(AES.block_size)
44+
45+
# Create AES cipher object with key and mode (ECB)
46+
cipher = AES.new(key, AES.MODE_ECB)
47+
48+
# Pad the cpu plaintext to match AES block size
49+
padded_cpu_plaintext = pad(cpu_plaintext, AES.block_size)
50+
51+
# Encrypt the padded cpu plaintext
52+
cpu_ciphertext = cipher.encrypt(padded_cpu_plaintext)
53+
54+
if(ratio != 1): # if GPU is now available, only use CPU
55+
# Encrypt in GPU
56+
57+
# transfer key into tensor to use in GPU
58+
key_array = np.frombuffer(key, dtype=np.uint8)
59+
gpu_key = torch.from_numpy(key_array)
60+
gpu_key = gpu_key.to(device)
61+
62+
# plaintext for gpu
63+
gpu_plaintext = plaintext[workload_division:]
64+
65+
# transfer gpu_plaintext into tensor to use in GPU
66+
gpu_plaintext_array = np.frombuffer(gpu_plaintext, dtype=np.uint8)
67+
gpu_plaintext_tensor = torch.from_numpy(gpu_plaintext_array)
68+
gpu_plaintext_tensor = gpu_plaintext_tensor.to(device)
69+
70+
# Pad the gpu_plaintext_tensor to match AES block size
71+
padded_gpu_plaintext = pad(gpu_plaintext, 16)
72+
73+
padded_gpu_plaintext_array = np.frombuffer(padded_gpu_plaintext, dtype=np.uint8)
74+
padded_gpu_plaintext_tensor = torch.from_numpy(padded_gpu_plaintext_array)
75+
padded_gpu_plaintext_tensor = padded_gpu_plaintext_tensor.to(device)
76+
77+
# encrypt padded_gpu_plaintext_tensor in GPU
78+
gpu_encrypted = torch.empty(len(padded_gpu_plaintext_tensor), dtype=torch.int8, device=device)
79+
csprng.encrypt(padded_gpu_plaintext_tensor, gpu_encrypted, gpu_key, "aes128", "ecb")
80+
81+
# transfer gpu_encrypted results into bytes to use in cpu
82+
gpu_encrypted_numpy = gpu_encrypted.cpu().numpy()
83+
gpu_ciphertext = gpu_encrypted_numpy.tobytes()
84+
85+
# merge iv, cpu_ciphertext, gpu_ciphertext
86+
results = iv + cpu_ciphertext + gpu_ciphertext
87+
else:
88+
results = iv + cpu_ciphertext
89+
90+
# Write the IV and encrypted data back to the file
91+
with open(file_path, 'wb') as file:
92+
file.write(results)
93+
94+
def decrypt_file(file_path, key, ratio, device):
95+
# Read the file contents
96+
with open(file_path, 'rb') as file:
97+
ciphertext = file.read()
98+
99+
# Extract the IV and ciphertext from the file
100+
iv = ciphertext[:AES.block_size]
101+
ciphertext = ciphertext[AES.block_size:]
102+
103+
# Workload division according to the ratio between cpu and gpu
104+
workload_division = 16 + (int((len(ciphertext) - 16) * ratio / 16)) * 16
105+
106+
# ciphertext for cput
107+
cpu_ciphertext = ciphertext[:workload_division]
108+
109+
# Create AES cipher object with key and mode (ECB)
110+
cipher = AES.new(key, AES.MODE_ECB)
111+
112+
# Decrypt the cpu_ciphertext
113+
cpu_plaintext = cipher.decrypt(cpu_ciphertext)
114+
115+
# Unpad the decrypted cpu_plaintext
116+
cpu_plaintext = unpad(cpu_plaintext, AES.block_size)
117+
118+
119+
if(ratio != 1): # if GPU is now available, only use CPU
120+
# Decrypt in GPU
121+
122+
# transfer key into tensor to use in GPU
123+
key_array = np.frombuffer(key, dtype=np.uint8)
124+
gpu_key = torch.from_numpy(key_array)
125+
gpu_key = gpu_key.to(device)
126+
127+
# cipertext for gpu
128+
gpu_ciphertext = ciphertext[workload_division:]
129+
130+
# transfer gpu_ciphertext into tensor to use in GPU
131+
encrypted_input_array = np.frombuffer(gpu_ciphertext, dtype=np.uint8)
132+
encrypted_input_tensor = torch.from_numpy(encrypted_input_array)
133+
encrypted_input_tensor = encrypted_input_tensor.to(device)
134+
135+
# Decrypt encrypted_input_tensor in GPU
136+
gpu_decrypted = torch.empty_like(encrypted_input_tensor)
137+
csprng.decrypt(encrypted_input_tensor, gpu_decrypted, gpu_key, "aes128", "ecb")
138+
139+
# transfer gpu_encrypted results into bytes to use in cpu
140+
gpu_decrypted_numpy = gpu_decrypted.cpu().numpy()
141+
gpu_plaintext = gpu_decrypted_numpy.tobytes()
142+
143+
# Unpad the decrypted gpu_plaintext
144+
gpu_plaintext = unpad(gpu_plaintext, 16)
145+
146+
# Merge cpu_plaintext and gpu_plaintext
147+
results = cpu_plaintext + gpu_plaintext
148+
else:
149+
results = cpu_plaintext
150+
151+
# Write the decrypted data back to the file
152+
with open(file_path, 'wb') as file:
153+
file.write(results)
154+
155+
156+
def cpu_gpu_ratio():
157+
158+
#Define the ration between cpu and gpu
159+
gpu_total = torch.cuda.device_count()
160+
161+
# Usage CPU
162+
cpu_percent = psutil.cpu_percent(interval=0.1)
163+
164+
# Usage RAM
165+
memory_percent = psutil.virtual_memory().percent
166+
167+
print(f"CPU Usage: {cpu_percent}% - Memory Usage: {memory_percent}")
168+
169+
# Set the Ratio between CPU and GPU according to the CPU and RAM Usage
170+
if(gpu_total == 0):
171+
ratio = 1
172+
elif(memory_percent > 90 or cpu_percent > 90):
173+
ratio = 0.1
174+
elif(memory_percent > 70 or cpu_percent > 70):
175+
ratio = 0.3
176+
elif(memory_percent > 40 or cpu_percent > 40):
177+
ratio = 0.5
178+
else:
179+
ratio = 0.7
180+
return ratio
181+
182+
183+
def encrypt_folder(folder_path, key, mode, ratio, device):
184+
start_time = time.time()
185+
for root, dirs, files in os.walk(folder_path):
186+
for file in files:
187+
file_path = os.path.join(root, file)
188+
if mode == 'encrypt':
189+
encrypt_file(file_path, key, ratio, device)
190+
print(f'Encrypted: {file_path}')
191+
elif mode == 'decrypt':
192+
decrypt_file(file_path, key, ratio, device)
193+
print(f'Decrypted: {file_path}')
194+
195+
# Monitor system resources while processing files
196+
cpu_percent = psutil.cpu_percent(interval=0.1)
197+
memory_percent = psutil.virtual_memory().percent
198+
print(f"CPU Usage: {cpu_percent}% - Memory Usage: {memory_percent}% -Ratio between CPU and GPU: {ratio}")
199+
200+
end_time = time.time()
201+
execution_time = end_time - start_time
202+
print(f"Execution Time: {execution_time:.3f} seconds ({execution_time * 1000:.3f} milliseconds)")
203+
204+
if __name__=='__main__':
205+
# Define regular expressions for input validation
206+
password_regex = re.compile(r'^.{8,}$') # Minimum 8 characters
207+
salt_regex = re.compile(r'^[a-fA-F0-9]{32}$') # 32 hexadecimal characters
208+
folder_path_regex = re.compile(r'^[a-zA-Z0-9_./\\-]+$') # Alphanumeric, underscore, dot, forward slash, backslash, and hyphen
209+
210+
# Prompt the user for inputs
211+
while True:
212+
try:
213+
password = "1234567890" # password = input("Enter the password: ")
214+
if not password_regex.match(password):
215+
print("Invalid password. Password must be at least 8 characters long.")
216+
continue
217+
218+
salt = "12345678901234567890123456789012" # salt = input("Enter the salt: ")
219+
if not salt_regex.match(salt):
220+
print("Invalid salt. Salt must be a 32-character hexadecimal string.")
221+
continue
222+
223+
length = 16 # length = int(input("Enter the desired key length in bytes: "))
224+
n = 2^10 # n = int(input("Enter the value for 'n': "))
225+
r = 8 # r = int(input("Enter the value for 'r': "))
226+
p = 5 # p = int(input("Enter the value for 'p': "))
227+
228+
229+
230+
break
231+
except ValueError:
232+
print("Invalid input. Please try again.")
233+
234+
# Prompt the user to input the folder path
235+
# while True:
236+
237+
# if not folder_path_regex.match(folder_path):
238+
# print("Invalid folder path. Folder path must be alphanumeric and can contain underscore, dot, forward slash, backslash, and hyphen.")
239+
# continue
240+
# if os.path.isdir(folder_path):
241+
# break
242+
# else:
243+
# print("Invalid folder path. Please try again.")
244+
#folder_path = input("Enter the folder path: ")
245+
mode = 'encrypt'
246+
folder_path = "E:\enc"
247+
248+
249+
# Prompt the user to select the mode: 'encrypt' or 'decrypt'
250+
# while True:
251+
# mode = 'encrypt' #mode = input("Enter the mode ('encrypt' or 'decrypt'): ")
252+
# if mode in ['encrypt', 'decrypt']:
253+
# breakon
254+
# else:
255+
# print("Invalid mode. Please try again.")
256+
257+
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
258+
ratio = cpu_gpu_ratio()
259+
260+
# Call the hkdf_scrypt function with user inputs
261+
derived_key = hkdf_scrypt(password, salt, length, n, r, p)
262+
263+
# Encode the derived key using base64
264+
encoded_key = base64.b64encode(derived_key).decode()
265+
266+
if mode == 'encrypt':
267+
# Store the encoded key in a secure location (e.g., a file or database)
268+
with open("backup_key.txt", "w") as file:
269+
file.write(encoded_key)
270+
271+
# Encrypt or decrypt the files within the folder using AES-256
272+
encrypt_folder(folder_path, derived_key, mode, ratio, device)

0 commit comments

Comments
 (0)