p3lib 1.1.99__py3-none-any.whl → 1.1.101__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.
- p3lib/file_io.py +154 -0
- p3lib/pconfig.py +4 -4
- {p3lib-1.1.99.dist-info → p3lib-1.1.101.dist-info}/METADATA +1 -1
- {p3lib-1.1.99.dist-info → p3lib-1.1.101.dist-info}/RECORD +7 -6
- {p3lib-1.1.99.dist-info → p3lib-1.1.101.dist-info}/WHEEL +1 -1
- {p3lib-1.1.99.dist-info → p3lib-1.1.101.dist-info}/LICENSE +0 -0
- {p3lib-1.1.99.dist-info → p3lib-1.1.101.dist-info}/top_level.txt +0 -0
p3lib/file_io.py
ADDED
@@ -0,0 +1,154 @@
|
|
1
|
+
import json
|
2
|
+
import os
|
3
|
+
from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC
|
4
|
+
from cryptography.hazmat.primitives.hashes import SHA256
|
5
|
+
from cryptography.hazmat.backends import default_backend
|
6
|
+
from cryptography.fernet import Fernet
|
7
|
+
from base64 import urlsafe_b64encode
|
8
|
+
|
9
|
+
class CryptFile(object):
|
10
|
+
"""@brief Responsible for encrypting and decrypting data to/from files using a password."""
|
11
|
+
|
12
|
+
def __init__(self,
|
13
|
+
filename: str,
|
14
|
+
password: str,
|
15
|
+
add_enc_extension: bool = True,
|
16
|
+
dict_data: bool = True):
|
17
|
+
"""@brief Constructor
|
18
|
+
@param filename The filename to save the data into.
|
19
|
+
@param password The password to used encrypt data to and load encrypted data from file.
|
20
|
+
@param add_enc_extension If True then the .enc extension is added to the filename
|
21
|
+
supplied.
|
22
|
+
@param dict_data If True the data is a python dictionary."""
|
23
|
+
self._filename = filename
|
24
|
+
self._password = password
|
25
|
+
self._add_enc_extension = add_enc_extension
|
26
|
+
self._dict_data = dict_data
|
27
|
+
|
28
|
+
if not self._filename:
|
29
|
+
raise Exception("No filename defined to save data to.")
|
30
|
+
|
31
|
+
if len(self._filename) < 1:
|
32
|
+
raise Exception("No filename defined. String length = 0.")
|
33
|
+
|
34
|
+
if not self._password:
|
35
|
+
raise Exception("No password defined to encrypt/decrypt data.")
|
36
|
+
|
37
|
+
if len(self._password) < 1:
|
38
|
+
raise Exception("No password defined. String length = 0.")
|
39
|
+
|
40
|
+
self._add_extension()
|
41
|
+
|
42
|
+
def save(self,
|
43
|
+
data):
|
44
|
+
"""@brief Save the data to an encrypted file.
|
45
|
+
@param data The data to be encrypted.
|
46
|
+
"""
|
47
|
+
encrypted_data = self._encrypt_data(data)
|
48
|
+
with open(self._filename, "wb") as file:
|
49
|
+
file.write(encrypted_data)
|
50
|
+
|
51
|
+
def load(self):
|
52
|
+
"""@brief Load data from an encrypted file.
|
53
|
+
@return The decrypted data.
|
54
|
+
"""
|
55
|
+
with open(self._filename, "rb") as file:
|
56
|
+
data_bytes = file.read()
|
57
|
+
return self._decrypt_data(data_bytes)
|
58
|
+
|
59
|
+
def _decrypt_data(self, encrypted_data):
|
60
|
+
# Extract the salt (first 16 bytes) from the encrypted data
|
61
|
+
salt = encrypted_data[:16]
|
62
|
+
encrypted_content = encrypted_data[16:]
|
63
|
+
key = self._derive_key_from_password(salt)
|
64
|
+
fernet = Fernet(key)
|
65
|
+
decrypted_data = fernet.decrypt(encrypted_content)
|
66
|
+
if self._dict_data:
|
67
|
+
# Convert bytes back to a dict
|
68
|
+
data = json.loads(decrypted_data.decode())
|
69
|
+
|
70
|
+
else:
|
71
|
+
data = decrypted_data
|
72
|
+
|
73
|
+
return data
|
74
|
+
|
75
|
+
def get_file(self):
|
76
|
+
"""@return Get the name of the encrypted file."""
|
77
|
+
return self._filename
|
78
|
+
|
79
|
+
def _add_extension(self):
|
80
|
+
"""@brief Add the enc extension to the filename if required."""
|
81
|
+
if self._add_enc_extension and self._filename and not self._filename.endswith('.enc') :
|
82
|
+
self._filename = self._filename + ".enc"
|
83
|
+
|
84
|
+
def _derive_key_from_password(self,
|
85
|
+
salt: bytes) -> bytes:
|
86
|
+
kdf = PBKDF2HMAC(
|
87
|
+
algorithm=SHA256(),
|
88
|
+
length=32,
|
89
|
+
salt=salt,
|
90
|
+
iterations=100_000,
|
91
|
+
backend=default_backend(),
|
92
|
+
)
|
93
|
+
return urlsafe_b64encode(kdf.derive(self._password.encode()))
|
94
|
+
|
95
|
+
def _encrypt_data(self,
|
96
|
+
data):
|
97
|
+
# Generate a random salt for key derivation
|
98
|
+
salt = os.urandom(16)
|
99
|
+
key = self._derive_key_from_password(salt)
|
100
|
+
fernet = Fernet(key)
|
101
|
+
# If we expect a dict
|
102
|
+
if self._dict_data:
|
103
|
+
data_bytes = json.dumps(data).encode() # Convert JSON to bytes
|
104
|
+
|
105
|
+
# else check we have bytes
|
106
|
+
elif isinstance(data, bytes):
|
107
|
+
data_bytes = data
|
108
|
+
|
109
|
+
else:
|
110
|
+
raise Exception("data to be stored is not a bytes instance.")
|
111
|
+
|
112
|
+
encrypted_data = fernet.encrypt(data_bytes)
|
113
|
+
return salt + encrypted_data # Store the salt with the encrypted data
|
114
|
+
|
115
|
+
# Example usage
|
116
|
+
if __name__ == "__main__":
|
117
|
+
|
118
|
+
password = input("Enter a password for encryption: ")
|
119
|
+
|
120
|
+
# JSON data to encrypt
|
121
|
+
json_data = {
|
122
|
+
"name": "Alice",
|
123
|
+
"age": 30,
|
124
|
+
"is_admin": True,
|
125
|
+
"preferences": {
|
126
|
+
"theme": "dark",
|
127
|
+
"language": "English"
|
128
|
+
}
|
129
|
+
}
|
130
|
+
|
131
|
+
filename = "afile.txt"
|
132
|
+
|
133
|
+
# Save and load a python dict
|
134
|
+
cjf = CryptFile(filename=filename,
|
135
|
+
password=password)
|
136
|
+
cjf.save(json_data)
|
137
|
+
print(f"Saved {cjf.get_file()}")
|
138
|
+
|
139
|
+
decrypted_data = cjf.load()
|
140
|
+
print(f"Decrypted data: {decrypted_data}")
|
141
|
+
|
142
|
+
|
143
|
+
# Save and load data bytes
|
144
|
+
data_bytes = "123".encode()
|
145
|
+
cjf = CryptFile(filename=filename,
|
146
|
+
password=password,
|
147
|
+
dict_data=False)
|
148
|
+
cjf.save(data_bytes)
|
149
|
+
print(f"Saved {cjf.get_file()}")
|
150
|
+
|
151
|
+
decrypted_data = cjf.load()
|
152
|
+
print("Decrypted data:")
|
153
|
+
for _byte in decrypted_data:
|
154
|
+
print(f"_byte={_byte}")
|
p3lib/pconfig.py
CHANGED
@@ -338,7 +338,7 @@ class ConfigManager(object):
|
|
338
338
|
configPath = configPath.strip()
|
339
339
|
|
340
340
|
return join( configPath, cfgFilename )
|
341
|
-
|
341
|
+
|
342
342
|
def __init__(self, uio, cfgFilename, defaultConfig, addDotToFilename=True, encrypt=False, cfgPath=None):
|
343
343
|
"""@brief Constructor
|
344
344
|
@param uio A UIO (User Input Output) instance. May be set to None if no user messages are required.
|
@@ -773,11 +773,11 @@ class DotConfigManager(ConfigManager):
|
|
773
773
|
|
774
774
|
if not os.path.isdir(homePath):
|
775
775
|
raise Exception(f"{homePath} HOME path does not exist.")
|
776
|
-
|
776
|
+
|
777
777
|
# Create the ~/.config folder if it does not exist
|
778
778
|
if not os.path.isdir(configFolder):
|
779
779
|
# Create the ~/.config folder
|
780
|
-
os.
|
780
|
+
os.makedirs(configFolder)
|
781
781
|
|
782
782
|
progName = sys.argv[0]
|
783
783
|
if progName.endswith('.py'):
|
@@ -802,7 +802,7 @@ class DotConfigManager(ConfigManager):
|
|
802
802
|
@param encrypt If True then data will be encrypted in the saved files.
|
803
803
|
The encryption uses part of the the local SSH RSA private key.
|
804
804
|
This is not secure but assuming the private key has not been compromised it's
|
805
|
-
probably the best we can do.
|
805
|
+
probably the best we can do.
|
806
806
|
!!! Therefore if encrypt is set True then the an ssh key must be present !!!
|
807
807
|
||| in the ~/.ssh folder named id_rsa. !!!"""
|
808
808
|
super().__init__(uio, DotConfigManager.GetDefaultConfigFilename(), defaultConfig, encrypt=encrypt)
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: p3lib
|
3
|
-
Version: 1.1.
|
3
|
+
Version: 1.1.101
|
4
4
|
Summary: A group of python modules for networking, plotting data, config storage, automating boot scripts, ssh access and user input output.
|
5
5
|
Home-page: https://github.com/pjaos/p3lib
|
6
6
|
Author: Paul Austen
|
@@ -5,18 +5,19 @@ p3lib/bokeh_gui.py,sha256=55sajP_x9O1lE0uP3w3-T5f2oMzk7jSolLqxlEdLeLg,40245
|
|
5
5
|
p3lib/boot_manager.py,sha256=l7WlaoIRt4cu2jAdCs3FnDeNO2v2jzqideNZ3qg2r_4,19652
|
6
6
|
p3lib/conduit.py,sha256=jPkjdtyCx2I6SFqcEo8y2g7rgnZ-jNY7oCuYIETzT5Q,6046
|
7
7
|
p3lib/database_if.py,sha256=XKu1w3zftGbj4Rh54wrWJnoCtqHkhCzJUPN2S70XIKg,11915
|
8
|
+
p3lib/file_io.py,sha256=A7_GKYPlmjRjq6U1YuWhmB0OhLhNm6cWQfQX8qfgYTk,5041
|
8
9
|
p3lib/helper.py,sha256=xTKPgpziwr4zyaoc0sjZRFr0M91fo7Tok_nSAvtiTZE,12020
|
9
10
|
p3lib/json_networking.py,sha256=6u4s1SmypjTYPnSxHP712OgQ3ZJaxOqIkgHQ1J7Qews,9738
|
10
11
|
p3lib/mqtt_rpc.py,sha256=6LmFA1kR4HSJs9eWbOJORRHNY01L_lHWjvtE2fmY8P8,10511
|
11
12
|
p3lib/netif.py,sha256=3QV5OGdHhELIf4MBj6mx5MNCtVeZ7JXoNEkeu4KzCaE,9796
|
12
13
|
p3lib/netplotly.py,sha256=PMDx-w1jtRVW6Od5u_kuKbBxNpTS_Y88mMF60puMxLM,9363
|
13
14
|
p3lib/ngt.py,sha256=Y4HsSiaBEYi7OFWkqkDDJende_yyxsvIjUremfArXgA,39204
|
14
|
-
p3lib/pconfig.py,sha256=
|
15
|
+
p3lib/pconfig.py,sha256=2yZUK5nkqpAm4Y_xcJq5H9Lxmc8dfYei1Pi0Ek2Crh0,35376
|
15
16
|
p3lib/ssh.py,sha256=OyoAQ_h1L2RfkjTAChDrvLFfl4Fe_gBNdX5rvK-wKiw,42125
|
16
17
|
p3lib/table_plot.py,sha256=RPncwVlGUkkx5Fw0dHQedXo0TSPlTi__VrJBDzaMsuI,32116
|
17
18
|
p3lib/uio.py,sha256=Aaxc99XiE3d2f9vLjaN-bZsckoNxay5t0ujdK6PXGrw,23265
|
18
|
-
p3lib-1.1.
|
19
|
-
p3lib-1.1.
|
20
|
-
p3lib-1.1.
|
21
|
-
p3lib-1.1.
|
22
|
-
p3lib-1.1.
|
19
|
+
p3lib-1.1.101.dist-info/LICENSE,sha256=igqTy5u0kVWM1n-NUZMvAlinY6lVjAXKoag0okkS8V8,1067
|
20
|
+
p3lib-1.1.101.dist-info/METADATA,sha256=gdbF0zh48zjUc6LXovhTgsfcFldo1mzLJBClcS9iJL4,919
|
21
|
+
p3lib-1.1.101.dist-info/WHEEL,sha256=A3WOREP4zgxI0fKrHUG8DC8013e3dK3n7a6HDbcEIwE,91
|
22
|
+
p3lib-1.1.101.dist-info/top_level.txt,sha256=SDCpXYh-19yCFp4Z8ZK4B-3J4NvTCJElZ42NPgcR6-U,6
|
23
|
+
p3lib-1.1.101.dist-info/RECORD,,
|
File without changes
|
File without changes
|