-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathpysafe.py
187 lines (146 loc) · 5.24 KB
/
pysafe.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
#!/usr/bin/env python3
import base64
import getpass
import os
import pathlib
import secrets
import sys
import cryptography
from cryptography.fernet import Fernet
from cryptography.hazmat.primitives.kdf.scrypt import Scrypt
def generate_salt(size=16):
"""Generate the salt used for key derivation"""
return secrets.token_bytes(size)
def derive_key(salt, password):
"""Derive the key from the `password` using the passed `salt`"""
kdf = Scrypt(salt=salt, length=32, n=2**14, r=8, p=1)
return kdf.derive(password.encode())
def load_salt(path):
# load salt from pysalt.salt file
if os.path.exists(f"{path}conf/pysalt.salt"):
salt = open(f"{path}conf/pysalt.salt", "rb").read()
else:
salt = generate_salt()
if not os.path.exists(f"{path}conf"):
os.mkdir(f"{path}conf")
with open(f"{path}conf/pysalt.salt", "wb") as salt_file:
salt_file.write(salt)
return salt
def generate_key(password, path, salt_size=16):
"""
Generates a key from a `password` and the salt.
"""
# load existing salt
salt = load_salt(path)
# sys.exit()
# generate the key from the salt and the password
derived_key = derive_key(salt, password)
# encode it using Base 64 and return it
return base64.urlsafe_b64encode(derived_key)
def encryptor(file, key):
fkey = Fernet(key)
if "pysalt.salt" in str(file):
return
with open(file, "rb") as f:
# read all file data
file_data = f.read()
# encrypt data
try:
encrypted_data = fkey.encrypt(file_data)
except cryptography.fernet.InvalidToken:
print("[!] Invalid token, most likely the password is incorrect")
sys.exit()
print(f"[*] Encrypting {file}")
# write the encrypted file
with open(file, "wb") as f:
f.write(encrypted_data)
def encrypt(path, key):
"""
Given a path (str) and key (bytes), it encrypts the path and write it
"""
if os.path.isdir(path):
"""
if it's a folder, encrypt the entire folder.
"""
for child in pathlib.Path(path).glob("*"):
if child.is_file():
# encrypt the file
encryptor(child, key)
elif child.is_dir():
"""
if it's a folder, encrypt the entire folder by calling this function recursively
"""
encrypt(child, key)
elif os.path.isfile(path):
encryptor(path, key)
def decryptor(file, key):
fkey = Fernet(key)
if "pysalt.salt" in str(file):
return
with open(file, "rb") as f:
# read all file data
file_data = f.read()
# decrypt data
try:
decrypted_data = fkey.decrypt(file_data)
except cryptography.fernet.InvalidToken:
print("[!] Invalid token, most likely the password is incorrect")
sys.exit()
print(f"[*] Decrypting {file}")
# write the decrypted file
with open(file, "wb") as f:
f.write(decrypted_data)
def decrypt(path, key):
"""
Given a path (str) and key (bytes), it decrypts the path and write it
"""
if os.path.isdir(path):
# if it's a folder, decrypt the entire folder (i.e all the containing files)
for child in pathlib.Path(path).glob("*"):
if child.is_file():
# decrypt the file
decryptor(child, key)
elif child.is_dir():
# if it's a folder, decrypt the entire folder by calling this function recursively
decrypt(child, key)
elif os.path.isfile(path):
decryptor(path, key)
if __name__ == "__main__":
import argparse
parser = argparse.ArgumentParser(description="PySafe File Encryptor")
parser.add_argument("-p", "--path", help="Path to encrypt/decrypt")
parser.add_argument("-s", "--salt-size", required=False,
help="If this is set, a new salt with the passed size is generated", type=int)
parser.add_argument("-e", "--encrypt", action="store_true",
help="Whether to encrypt the path, only -e or -d can be specified.")
parser.add_argument("-d", "--decrypt", action="store_true",
help="Whether to decrypt the path, only -e or -d can be specified.")
args = parser.parse_args()
path = args.path
if path and not(path.endswith('/')):
path+="/"
try:
if args.encrypt:
password = getpass.getpass("[+] Enter the password for encryption: ")
elif args.decrypt:
password = getpass.getpass("[+] Enter the password you used for encryption: ")
if args.salt_size:
key = generate_key(password, path, salt_size=args.salt_size)
else:
key = generate_key(password, path)
except Exception:
parser.print_help()
sys.exit()
encrypt_ = args.encrypt
decrypt_ = args.decrypt
WAR_MESSAGE = "[!] Please specify whether you want to encrypt the path or decrypt it."
if encrypt_ and decrypt_:
raise TypeError(WAR_MESSAGE)
elif encrypt_:
encrypt(path, key)
print("\n[+] Encryption successfully completed!")
elif decrypt_:
decrypt(path, key)
print("\n[+] Decryption successfully completed!")
else:
raise TypeError(WAR_MESSAGE)