escrowai-encrypt 0.0.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- escrowai_encrypt/__init__.py +0 -0
- escrowai_encrypt/encryption.py +249 -0
- escrowai_encrypt/main.py +0 -0
- escrowai_encrypt/utils.py +39 -0
- escrowai_encrypt-0.0.1.dist-info/METADATA +29 -0
- escrowai_encrypt-0.0.1.dist-info/RECORD +9 -0
- escrowai_encrypt-0.0.1.dist-info/WHEEL +5 -0
- escrowai_encrypt-0.0.1.dist-info/entry_points.txt +2 -0
- escrowai_encrypt-0.0.1.dist-info/top_level.txt +1 -0
|
File without changes
|
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import uuid
|
|
3
|
+
import yaml
|
|
4
|
+
import pathlib
|
|
5
|
+
from cryptography.hazmat.primitives import hashes
|
|
6
|
+
from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC
|
|
7
|
+
from cryptography.hazmat.primitives.ciphers.aead import AESGCM
|
|
8
|
+
from cryptography.hazmat.primitives.serialization import load_pem_public_key
|
|
9
|
+
from cryptography.hazmat.primitives.asymmetric import padding
|
|
10
|
+
from cryptography.hazmat.backends import default_backend
|
|
11
|
+
from zipfile import ZipFile
|
|
12
|
+
from azure.storage.blob import ContainerClient
|
|
13
|
+
from escrowai_encrypt.utils import encrypt_data
|
|
14
|
+
import shutil
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def generate_content_encryption_key(filename=""):
|
|
18
|
+
"""
|
|
19
|
+
Generate a Content Encryption Key (CEK), the key used to encrypt an algorithm or dataset.
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
if filename == "":
|
|
23
|
+
filename = f"cek_{uuid.uuid4()}.key"
|
|
24
|
+
|
|
25
|
+
with open(filename, "wb") as file:
|
|
26
|
+
file.write(os.urandom(32))
|
|
27
|
+
|
|
28
|
+
print(f"Content Encryption Key (CEK) downloaded successfully to {filename}.")
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def generate_wrapped_content_encryption_key(
|
|
32
|
+
content_encryption_key: str, key_encryption_key: str, filename=""
|
|
33
|
+
):
|
|
34
|
+
"""
|
|
35
|
+
Generate a Wrapped Content Encryption Key (WCEK), a Content Encryption Key (CEK) that has been encrypted
|
|
36
|
+
using a Key Encryption Key (KEK).
|
|
37
|
+
"""
|
|
38
|
+
|
|
39
|
+
if filename == "":
|
|
40
|
+
filename = f"wcek_{uuid.uuid4()}.key.bkenc"
|
|
41
|
+
|
|
42
|
+
with open(content_encryption_key, "rb") as file:
|
|
43
|
+
cek = file.read()
|
|
44
|
+
with open(key_encryption_key, "rb") as file:
|
|
45
|
+
kek = file.read()
|
|
46
|
+
|
|
47
|
+
print(
|
|
48
|
+
f"Wrapping CEK {content_encryption_key} with KEK {key_encryption_key} to file {filename}..."
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
public_key = load_pem_public_key(kek, backend=default_backend())
|
|
52
|
+
encrypted = public_key.encrypt(
|
|
53
|
+
cek,
|
|
54
|
+
padding=padding.OAEP(
|
|
55
|
+
mgf=padding.MGF1(algorithm=hashes.SHA1()),
|
|
56
|
+
algorithm=hashes.SHA1(),
|
|
57
|
+
label=None,
|
|
58
|
+
),
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
with open(filename, "wb") as file:
|
|
62
|
+
file.write(encrypted)
|
|
63
|
+
|
|
64
|
+
print(
|
|
65
|
+
f"Wrapped Content Encryption Key (WCEK) downloaded successfully to {filename}."
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def encrypt_algorithm(
|
|
70
|
+
algorithm_directory: str, content_encryption_key: str, filename=""
|
|
71
|
+
):
|
|
72
|
+
"""
|
|
73
|
+
Encrypt an Algorithm. Algorithm encryption safeguards your algorithm during storage, transmission,
|
|
74
|
+
and execution. The algorithm is encrypted using a Content Encryption Key (CEK).
|
|
75
|
+
"""
|
|
76
|
+
|
|
77
|
+
if filename == "":
|
|
78
|
+
filename = f"{algorithm_directory}_encrypted_{uuid.uuid4()}.zip"
|
|
79
|
+
|
|
80
|
+
with open(content_encryption_key, "rb") as file:
|
|
81
|
+
cek = file.read()
|
|
82
|
+
|
|
83
|
+
original_files = []
|
|
84
|
+
new_files = []
|
|
85
|
+
with open(f"{algorithm_directory}/secrets.yaml", "r") as f:
|
|
86
|
+
files = yaml.safe_load(f)
|
|
87
|
+
|
|
88
|
+
count = str(len(files["secretFiles"]))
|
|
89
|
+
n = 1
|
|
90
|
+
|
|
91
|
+
print(f"{count} secret(s) to be encrypted.")
|
|
92
|
+
for f in files["secretFiles"]:
|
|
93
|
+
print(f"Encrypting secret {f[1]} ({n}/{count})...")
|
|
94
|
+
newpaths = ["", ""]
|
|
95
|
+
newpaths[0] = f"{algorithm_directory}/{f[0]}"
|
|
96
|
+
newpaths[1] = f"{algorithm_directory}/{f[1]}"
|
|
97
|
+
|
|
98
|
+
salt = os.urandom(8)
|
|
99
|
+
pbkdf2_hash = PBKDF2HMAC(
|
|
100
|
+
algorithm=hashes.SHA256(),
|
|
101
|
+
salt=salt,
|
|
102
|
+
length=32 + 12,
|
|
103
|
+
iterations=10000,
|
|
104
|
+
backend=default_backend(),
|
|
105
|
+
)
|
|
106
|
+
|
|
107
|
+
derived_password = pbkdf2_hash.derive(cek)
|
|
108
|
+
key = derived_password[0:32]
|
|
109
|
+
iv = derived_password[32 : (32 + 12)]
|
|
110
|
+
|
|
111
|
+
with open(newpaths[1], "rb") as encrypt:
|
|
112
|
+
data = encrypt.read()
|
|
113
|
+
|
|
114
|
+
encrypted = AESGCM(key).encrypt(iv, data, None)
|
|
115
|
+
encrypted = b"Salted__" + salt + encrypted
|
|
116
|
+
|
|
117
|
+
with open(newpaths[0], "wb") as write:
|
|
118
|
+
write.write(encrypted)
|
|
119
|
+
|
|
120
|
+
original_files.append(f[1])
|
|
121
|
+
new_files.append(newpaths[0])
|
|
122
|
+
|
|
123
|
+
print(f"Secret {f[0]} encrypted ({n}/{count}).")
|
|
124
|
+
|
|
125
|
+
shutil.make_archive("temp", "zip", algorithm_directory)
|
|
126
|
+
for i in new_files:
|
|
127
|
+
pathlib.Path(i).unlink()
|
|
128
|
+
|
|
129
|
+
zip_in = ZipFile("temp.zip", "r")
|
|
130
|
+
zip_out = ZipFile(filename, "w")
|
|
131
|
+
|
|
132
|
+
for item in zip_in.infolist():
|
|
133
|
+
buffer = zip_in.read(item.filename)
|
|
134
|
+
if item.filename not in original_files:
|
|
135
|
+
zip_out.writestr(item, buffer)
|
|
136
|
+
zip_in.close()
|
|
137
|
+
zip_out.close()
|
|
138
|
+
|
|
139
|
+
pathlib.Path("temp.zip").unlink()
|
|
140
|
+
|
|
141
|
+
print(f"Algorithm successfully encrypted as {filename}.")
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
def encrypt_upload_dataset(
|
|
145
|
+
dataset_directory: str, content_encryption_key: str, dataset_sas_uri: str
|
|
146
|
+
):
|
|
147
|
+
"""
|
|
148
|
+
Encrypt and upload a Dataset to Azure Blob Storage. Dataset encryption safeguards your dataset during storage,
|
|
149
|
+
transmission, and execution. The dataset is encrypted using a Content Encryption Key (CEK) and uploaded using
|
|
150
|
+
a Shared Access Signature (SAS) URL.
|
|
151
|
+
"""
|
|
152
|
+
|
|
153
|
+
with open(content_encryption_key, "rb") as file:
|
|
154
|
+
cek = file.read()
|
|
155
|
+
|
|
156
|
+
salt = os.urandom(8)
|
|
157
|
+
pbkdf2_hash = PBKDF2HMAC(
|
|
158
|
+
algorithm=hashes.SHA256(),
|
|
159
|
+
salt=salt,
|
|
160
|
+
length=32 + 12,
|
|
161
|
+
iterations=10000,
|
|
162
|
+
backend=default_backend(),
|
|
163
|
+
)
|
|
164
|
+
|
|
165
|
+
derived_password = pbkdf2_hash.derive(cek)
|
|
166
|
+
key = derived_password[0:32]
|
|
167
|
+
iv = derived_password[32 : (32 + 12)]
|
|
168
|
+
|
|
169
|
+
count = sum(
|
|
170
|
+
len([x for x in files if x[0] != "."])
|
|
171
|
+
for _, _, files in os.walk(dataset_directory)
|
|
172
|
+
)
|
|
173
|
+
n = 1
|
|
174
|
+
|
|
175
|
+
print(f"{count} files(s) to be encrypted and uploaded.")
|
|
176
|
+
for root, _, files in os.walk(dataset_directory):
|
|
177
|
+
for filename in files:
|
|
178
|
+
file_path = os.path.join(root, filename)
|
|
179
|
+
dirname = os.path.dirname(file_path)
|
|
180
|
+
n += encrypt_data(
|
|
181
|
+
filename,
|
|
182
|
+
salt,
|
|
183
|
+
key,
|
|
184
|
+
dataset_directory,
|
|
185
|
+
dirname,
|
|
186
|
+
iv,
|
|
187
|
+
dataset_sas_uri,
|
|
188
|
+
n,
|
|
189
|
+
count,
|
|
190
|
+
)
|
|
191
|
+
|
|
192
|
+
print(f"Dataset successfully encrypted and uploaded to SAS URL {dataset_sas_uri}.")
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
def encrypt_upload_dataset_from_blob(
|
|
196
|
+
dataset_sas_uri_unencrypted: str, content_encryption_key: str, dataset_sas_uri: str
|
|
197
|
+
):
|
|
198
|
+
"""
|
|
199
|
+
Encrypt and upload a Dataset to Azure Blob Storage. Dataset encryption safeguards your dataset during storage,
|
|
200
|
+
transmission, and execution. The dataset is encrypted using a Content Encryption Key (CEK) and uploaded using
|
|
201
|
+
a Shared Access Signature (SAS) URL.
|
|
202
|
+
"""
|
|
203
|
+
|
|
204
|
+
with open(content_encryption_key, "rb") as file:
|
|
205
|
+
cek = file.read()
|
|
206
|
+
|
|
207
|
+
salt = os.urandom(8)
|
|
208
|
+
pbkdf2_hash = PBKDF2HMAC(
|
|
209
|
+
algorithm=hashes.SHA256(),
|
|
210
|
+
salt=salt,
|
|
211
|
+
length=32 + 12,
|
|
212
|
+
iterations=10000,
|
|
213
|
+
backend=default_backend(),
|
|
214
|
+
)
|
|
215
|
+
|
|
216
|
+
derived_password = pbkdf2_hash.derive(cek)
|
|
217
|
+
key = derived_password[0:32]
|
|
218
|
+
iv = derived_password[32 : (32 + 12)]
|
|
219
|
+
|
|
220
|
+
unencrypted_client = ContainerClient.from_container_url(dataset_sas_uri_unencrypted)
|
|
221
|
+
encrypted_client = ContainerClient.from_container_url(dataset_sas_uri)
|
|
222
|
+
blobs = unencrypted_client.list_blob_names()
|
|
223
|
+
|
|
224
|
+
count = 0
|
|
225
|
+
for blob in blobs:
|
|
226
|
+
count += 1
|
|
227
|
+
|
|
228
|
+
blobs = unencrypted_client.list_blob_names() # reset page
|
|
229
|
+
|
|
230
|
+
n = 1
|
|
231
|
+
print(f"{str(count)} blob(s) to be encrypted and uploaded.")
|
|
232
|
+
for blob in blobs:
|
|
233
|
+
unencrypted_client = ContainerClient.from_container_url(
|
|
234
|
+
dataset_sas_uri_unencrypted
|
|
235
|
+
)
|
|
236
|
+
encrypted_client = ContainerClient.from_container_url(dataset_sas_uri)
|
|
237
|
+
print(f"Beginning download of blob {blob} ({n}/{str(count)})...")
|
|
238
|
+
data = unencrypted_client.download_blob(blob).readall()
|
|
239
|
+
print(f"Encrypting blob {blob}...")
|
|
240
|
+
encrypted = AESGCM(key).encrypt(iv, data, None)
|
|
241
|
+
encrypted = b"Salted__" + salt + encrypted
|
|
242
|
+
print(f"Uploading blob {blob}...")
|
|
243
|
+
encrypted_client.upload_blob(blob + ".bkenc", encrypted, overwrite=True)
|
|
244
|
+
print(f"Blob {blob + '.bkenc'} uploaded ({n}/{str(count)}).")
|
|
245
|
+
n += 1
|
|
246
|
+
|
|
247
|
+
print(
|
|
248
|
+
f"Dataset {dataset_sas_uri_unencrypted} successfully encrypted and uploaded to SAS URL {dataset_sas_uri}."
|
|
249
|
+
)
|
escrowai_encrypt/main.py
ADDED
|
File without changes
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import pathlib
|
|
2
|
+
from azure.storage.blob import BlobClient
|
|
3
|
+
from cryptography.hazmat.primitives.ciphers.aead import AESGCM
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def encrypt_data(file, salt, key, base_folder, folder, iv, url, n, count):
|
|
7
|
+
if file[0] == ".":
|
|
8
|
+
return 0
|
|
9
|
+
if file.endswith(".bkenc"):
|
|
10
|
+
raise Exception(f"Error: cannot encrypt file {file}.")
|
|
11
|
+
|
|
12
|
+
print(f"Encrypting file {file} ({n}/{count})...")
|
|
13
|
+
|
|
14
|
+
with open(folder + "/" + file, "rb") as encrypt:
|
|
15
|
+
data = encrypt.read()
|
|
16
|
+
|
|
17
|
+
encrypted = AESGCM(key).encrypt(iv, data, None)
|
|
18
|
+
encrypted = b"Salted__" + salt + encrypted
|
|
19
|
+
|
|
20
|
+
# write new encrypted file, delete old
|
|
21
|
+
with open(folder + "/" + file + ".bkenc", "wb") as write:
|
|
22
|
+
write.write(encrypted)
|
|
23
|
+
|
|
24
|
+
upload_to_blob(url, folder + "/" + file + ".bkenc", base_folder)
|
|
25
|
+
|
|
26
|
+
pathlib.Path(folder + "/" + file + ".bkenc").unlink()
|
|
27
|
+
|
|
28
|
+
print(f"Uploaded file {file + '.bkenc'} ({n}/{count}).")
|
|
29
|
+
|
|
30
|
+
return 1
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def upload_to_blob(url, file, folder):
|
|
34
|
+
uri = url.partition("?")
|
|
35
|
+
new_uri = uri[0] + file.split(folder, 1)[1] + uri[1] + uri[2]
|
|
36
|
+
client = BlobClient.from_blob_url(new_uri)
|
|
37
|
+
|
|
38
|
+
with open(file, "rb") as upload:
|
|
39
|
+
client.upload_blob(upload, overwrite=True)
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
Metadata-Version: 2.2
|
|
2
|
+
Name: escrowai-encrypt
|
|
3
|
+
Version: 0.0.1
|
|
4
|
+
Summary: This package allows EscrowAI users to programmatically encrypt their artifacts and add encryption to their workflows.
|
|
5
|
+
Home-page: https://github.com/BeeKeeperAI/escrowai-encrypt
|
|
6
|
+
Author: BeeKeeperAI
|
|
7
|
+
Author-email: engineering@beekeeperai.com
|
|
8
|
+
Classifier: Development Status :: 2 - Pre-Alpha
|
|
9
|
+
Classifier: Intended Audience :: Developers
|
|
10
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
11
|
+
Classifier: Programming Language :: Python :: 3
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
13
|
+
Requires-Python: >=3.6
|
|
14
|
+
Description-Content-Type: text/markdown
|
|
15
|
+
Requires-Dist: azure-storage-blob
|
|
16
|
+
Requires-Dist: cryptography
|
|
17
|
+
Requires-Dist: pyyaml
|
|
18
|
+
Dynamic: author
|
|
19
|
+
Dynamic: author-email
|
|
20
|
+
Dynamic: classifier
|
|
21
|
+
Dynamic: description
|
|
22
|
+
Dynamic: description-content-type
|
|
23
|
+
Dynamic: home-page
|
|
24
|
+
Dynamic: requires-dist
|
|
25
|
+
Dynamic: requires-python
|
|
26
|
+
Dynamic: summary
|
|
27
|
+
|
|
28
|
+
# escrowai-encrypt
|
|
29
|
+
A module for encryption of artifacts and keys to be uploaded to EscrowAI.
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
escrowai_encrypt/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
2
|
+
escrowai_encrypt/encryption.py,sha256=HGP2AH5wEchaRIGGwFNoV0zGJMDDjvyHH7L5t-DnRqo,7901
|
|
3
|
+
escrowai_encrypt/main.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
4
|
+
escrowai_encrypt/utils.py,sha256=JQeozbUkwoSEnHG4JWFqPsJXC17Kv4lhzFJog5Lj0gA,1185
|
|
5
|
+
escrowai_encrypt-0.0.1.dist-info/METADATA,sha256=ePf7jOzp-IGMiPX53q1GDcyFjQ8OXyiykDTU2lYe9_I,988
|
|
6
|
+
escrowai_encrypt-0.0.1.dist-info/WHEEL,sha256=jB7zZ3N9hIM9adW7qlTAyycLYW9npaWKLRzaoVcLKcM,91
|
|
7
|
+
escrowai_encrypt-0.0.1.dist-info/entry_points.txt,sha256=mjB_P4wzCRMYNUOBCdP6rz8xEyuTVwU7qZM4F5VWKBE,55
|
|
8
|
+
escrowai_encrypt-0.0.1.dist-info/top_level.txt,sha256=R8QP9gXiLcbs1sVwwxPHtfEgMf5Ww0eqBhnUQkD318I,17
|
|
9
|
+
escrowai_encrypt-0.0.1.dist-info/RECORD,,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
escrowai_encrypt
|