pyaescbc 0.1.0__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.
Potentially problematic release.
This version of pyaescbc might be problematic. Click here for more details.
- pyaescbc/__init__.py +64 -0
- pyaescbc/__main__.py +40 -0
- pyaescbc/__version__.py +1 -0
- pyaescbc/auth_error.py +50 -0
- pyaescbc/check_hmac.py +56 -0
- pyaescbc/cleardata_to_encrypted_bundle.py +124 -0
- pyaescbc/create_encrypted_bundle.py +70 -0
- pyaescbc/create_hmac.py +81 -0
- pyaescbc/decrypt_AES_CBC.py +78 -0
- pyaescbc/delete_bytearray.py +49 -0
- pyaescbc/derive_key.py +82 -0
- pyaescbc/encrypt_AES_CBC.py +76 -0
- pyaescbc/encrypted_bundle_to_cleardata.py +131 -0
- pyaescbc/extract_cryptography_components.py +55 -0
- pyaescbc/generate_pin_iterations.py +109 -0
- pyaescbc/generate_random_iterations.py +83 -0
- pyaescbc/random_bytearray.py +44 -0
- pyaescbc/random_iv.py +26 -0
- pyaescbc/random_salt.py +26 -0
- pyaescbc/resources/__init__.py +0 -0
- pyaescbc/resources/decrypt.png +0 -0
- pyaescbc/resources/encrypt.png +0 -0
- pyaescbc-0.1.0.dist-info/METADATA +114 -0
- pyaescbc-0.1.0.dist-info/RECORD +27 -0
- pyaescbc-0.1.0.dist-info/WHEEL +5 -0
- pyaescbc-0.1.0.dist-info/licenses/LICENSE +13 -0
- pyaescbc-0.1.0.dist-info/top_level.txt +1 -0
pyaescbc/__init__.py
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
# Copyright 2025 Artezaru
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
# you may not use this file except in compliance with the License.
|
|
5
|
+
# You may obtain a copy of the License at
|
|
6
|
+
#
|
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
#
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
# See the License for the specific language governing permissions and
|
|
13
|
+
# limitations under the License.
|
|
14
|
+
|
|
15
|
+
from .__version__ import __version__
|
|
16
|
+
|
|
17
|
+
from .decrypt_AES_CBC import decrypt_AES_CBC
|
|
18
|
+
from .derive_key import derive_key
|
|
19
|
+
from .encrypt_AES_CBC import encrypt_AES_CBC
|
|
20
|
+
|
|
21
|
+
from .cleardata_to_encrypted_bundle import cleardata_to_encrypted_bundle
|
|
22
|
+
encrypt = cleardata_to_encrypted_bundle
|
|
23
|
+
from .encrypted_bundle_to_cleardata import encrypted_bundle_to_cleardata
|
|
24
|
+
decrypt = encrypted_bundle_to_cleardata
|
|
25
|
+
|
|
26
|
+
from .create_encrypted_bundle import create_encrypted_bundle
|
|
27
|
+
from .extract_cryptography_components import extract_cryptography_components
|
|
28
|
+
|
|
29
|
+
from .generate_random_iterations import generate_random_iterations
|
|
30
|
+
from .generate_pin_iterations import generate_pin_iterations
|
|
31
|
+
|
|
32
|
+
from .random_bytearray import random_bytearray
|
|
33
|
+
from .random_iv import random_iv
|
|
34
|
+
from .random_salt import random_salt
|
|
35
|
+
|
|
36
|
+
from .create_hmac import create_hmac
|
|
37
|
+
from .check_hmac import check_hmac
|
|
38
|
+
from .auth_error import AuthError
|
|
39
|
+
|
|
40
|
+
from .delete_bytearray import delete_bytearray
|
|
41
|
+
|
|
42
|
+
__all__ = [
|
|
43
|
+
"__version__",
|
|
44
|
+
"decrypt_AES_CBC",
|
|
45
|
+
"derive_key",
|
|
46
|
+
"encrypt_AES_CBC",
|
|
47
|
+
"cleardata_to_encrypted_bundle",
|
|
48
|
+
"encrypt",
|
|
49
|
+
"encrypted_bundle_to_cleardata",
|
|
50
|
+
"decrypt",
|
|
51
|
+
"create_encrypted_bundle",
|
|
52
|
+
"extract_cryptography_components",
|
|
53
|
+
"generate_random_iterations",
|
|
54
|
+
"generate_pin_iterations",
|
|
55
|
+
"random_bytearray",
|
|
56
|
+
"random_iv",
|
|
57
|
+
"random_salt",
|
|
58
|
+
"create_hmac",
|
|
59
|
+
"check_hmac",
|
|
60
|
+
"AuthError",
|
|
61
|
+
"delete_bytearray",
|
|
62
|
+
]
|
|
63
|
+
|
|
64
|
+
|
pyaescbc/__main__.py
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# Copyright 2025 Artezaru
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
# you may not use this file except in compliance with the License.
|
|
5
|
+
# You may obtain a copy of the License at
|
|
6
|
+
#
|
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
#
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
# See the License for the specific language governing permissions and
|
|
13
|
+
# limitations under the License.
|
|
14
|
+
|
|
15
|
+
def __main__() -> None:
|
|
16
|
+
r"""
|
|
17
|
+
Main entry point of the package.
|
|
18
|
+
|
|
19
|
+
This method contains the script to run if the user enter the name of the package on the command line.
|
|
20
|
+
|
|
21
|
+
.. code-block:: console
|
|
22
|
+
|
|
23
|
+
pyaescbc
|
|
24
|
+
|
|
25
|
+
"""
|
|
26
|
+
raise NotImplementedError("The main entry point is not implemented yet.")
|
|
27
|
+
|
|
28
|
+
def __main_gui__() -> None:
|
|
29
|
+
r"""
|
|
30
|
+
Graphical user interface entry point of the package.
|
|
31
|
+
|
|
32
|
+
This method contains the script to run if the user enter the name of the package on the command line with the ``gui`` extension.
|
|
33
|
+
|
|
34
|
+
.. code-block:: console
|
|
35
|
+
|
|
36
|
+
pyaescbc-gui
|
|
37
|
+
|
|
38
|
+
"""
|
|
39
|
+
raise NotImplementedError("The graphical user interface entry point is not implemented yet.")
|
|
40
|
+
|
pyaescbc/__version__.py
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.1.0"
|
pyaescbc/auth_error.py
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# Copyright 2025 Artezaru
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
# you may not use this file except in compliance with the License.
|
|
5
|
+
# You may obtain a copy of the License at
|
|
6
|
+
#
|
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
#
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
# See the License for the specific language governing permissions and
|
|
13
|
+
# limitations under the License.
|
|
14
|
+
|
|
15
|
+
from typing import Optional
|
|
16
|
+
|
|
17
|
+
class AuthError(Exception):
|
|
18
|
+
"""
|
|
19
|
+
Exception raised for errors related to the decryption key in ``pyaescbc``.
|
|
20
|
+
|
|
21
|
+
This exception is typically raised when the provided password or key
|
|
22
|
+
is incorrect.
|
|
23
|
+
|
|
24
|
+
The display message will be shown in the following format if a code is provided:
|
|
25
|
+
|
|
26
|
+
.. code-block:: console
|
|
27
|
+
|
|
28
|
+
AuthError: [<code>] <message>
|
|
29
|
+
|
|
30
|
+
Parameters
|
|
31
|
+
----------
|
|
32
|
+
message : str, optional
|
|
33
|
+
The error message to be displayed. Default is ""
|
|
34
|
+
code : Optional[int], optional
|
|
35
|
+
An optional error code associated with the exception. Default is None.
|
|
36
|
+
|
|
37
|
+
Notes
|
|
38
|
+
-----
|
|
39
|
+
You can raise this exception when the HMAC verification fails
|
|
40
|
+
during decryption, which usually means the wrong key was used (if the data were not
|
|
41
|
+
"""
|
|
42
|
+
def __init__(self, message: str = "", code: Optional[int] = None) -> None:
|
|
43
|
+
super().__init__(message)
|
|
44
|
+
self.code = code
|
|
45
|
+
self.message = message
|
|
46
|
+
|
|
47
|
+
def __str__(self):
|
|
48
|
+
if self.code is not None:
|
|
49
|
+
return f"[{self.code}] {self.message}"
|
|
50
|
+
return self.message
|
pyaescbc/check_hmac.py
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# Copyright 2025 Artezaru
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
# you may not use this file except in compliance with the License.
|
|
5
|
+
# You may obtain a copy of the License at
|
|
6
|
+
#
|
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
#
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
# See the License for the specific language governing permissions and
|
|
13
|
+
# limitations under the License.
|
|
14
|
+
|
|
15
|
+
import hmac
|
|
16
|
+
|
|
17
|
+
def check_hmac(given_hmac: bytearray, expected_hmac: bytearray) -> bool:
|
|
18
|
+
"""
|
|
19
|
+
Verifies if the derived 32-byte given HMAC matches the expected HMAC.
|
|
20
|
+
|
|
21
|
+
Parameters
|
|
22
|
+
----------
|
|
23
|
+
given_hmac : bytearray
|
|
24
|
+
The 32-byte long HMAC to verify.
|
|
25
|
+
|
|
26
|
+
expected_hmac : bytearray
|
|
27
|
+
The 32-byte long expected HMAC.
|
|
28
|
+
|
|
29
|
+
Returns
|
|
30
|
+
-------
|
|
31
|
+
bool
|
|
32
|
+
True if the HMACs match, False otherwise.
|
|
33
|
+
|
|
34
|
+
Raises
|
|
35
|
+
------
|
|
36
|
+
TypeError
|
|
37
|
+
If any argument is not a `bytearray` instance.
|
|
38
|
+
ValueError
|
|
39
|
+
If the given_hmac or expected_hmac isn't 32 bytes.
|
|
40
|
+
"""
|
|
41
|
+
# Check the types of the parameters
|
|
42
|
+
if not isinstance(given_hmac, bytearray):
|
|
43
|
+
raise TypeError('Parameter given_hmac is not bytearray instance.')
|
|
44
|
+
if not isinstance(expected_hmac, bytearray):
|
|
45
|
+
raise TypeError('Parameter expected_hmac is not bytearray instance.')
|
|
46
|
+
|
|
47
|
+
# Check the value of the parameters
|
|
48
|
+
if len(given_hmac) != 32:
|
|
49
|
+
raise ValueError(f'{given_hmac=} is not 32 bytes long.')
|
|
50
|
+
if len(expected_hmac) != 32:
|
|
51
|
+
raise ValueError(f'{expected_hmac=} is not 32 bytes long.')
|
|
52
|
+
|
|
53
|
+
# Compare the HMACs
|
|
54
|
+
result = hmac.compare_digest(given_hmac, expected_hmac)
|
|
55
|
+
|
|
56
|
+
return result
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
# Copyright 2025 Artezaru
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
# you may not use this file except in compliance with the License.
|
|
5
|
+
# You may obtain a copy of the License at
|
|
6
|
+
#
|
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
#
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
# See the License for the specific language governing permissions and
|
|
13
|
+
# limitations under the License.
|
|
14
|
+
|
|
15
|
+
from typing import Optional
|
|
16
|
+
|
|
17
|
+
from .random_salt import random_salt
|
|
18
|
+
from .random_iv import random_iv
|
|
19
|
+
from .derive_key import derive_key
|
|
20
|
+
from .encrypt_AES_CBC import encrypt_AES_CBC
|
|
21
|
+
from .create_hmac import create_hmac
|
|
22
|
+
from .create_encrypted_bundle import create_encrypted_bundle
|
|
23
|
+
from .delete_bytearray import delete_bytearray
|
|
24
|
+
|
|
25
|
+
def cleardata_to_encrypted_bundle(
|
|
26
|
+
cleardata: bytearray,
|
|
27
|
+
password: bytearray,
|
|
28
|
+
iterations: int,
|
|
29
|
+
authdata: Optional[bytearray] = None,
|
|
30
|
+
delete_keys: bool = True
|
|
31
|
+
) -> bytearray:
|
|
32
|
+
"""
|
|
33
|
+
cleardata_to_encrypted_bundle encrypts the clear data to generate the encrypted bundle.
|
|
34
|
+
|
|
35
|
+
The number of iterations can be generated using the function :func:`pyaescbc.generate_random_iterations` or :func:`pyaescbc.generate_pin_iterations`.
|
|
36
|
+
|
|
37
|
+
.. note::
|
|
38
|
+
|
|
39
|
+
The cleardata, the password and the authdata are deleted from memory at the end of the function if delete_keys is True.
|
|
40
|
+
Otherwise, they need to be deleted after dealing with Exception.
|
|
41
|
+
|
|
42
|
+
.. note::
|
|
43
|
+
|
|
44
|
+
An alias for this function is ``encrypt``
|
|
45
|
+
|
|
46
|
+
.. code-block:: python
|
|
47
|
+
|
|
48
|
+
import pyaescbc as aes
|
|
49
|
+
|
|
50
|
+
cleardata = bytearray("Hello, World!", 'utf-8')
|
|
51
|
+
password = bytearray("password", 'utf-8')
|
|
52
|
+
iterations = aes.generate_random_iterations()
|
|
53
|
+
encrypted_bundle = aes.encrypt(cleardata, password, iterations, delete_keys=True)
|
|
54
|
+
# Or use : encrypted_bundle = aes.cleardata_to_encrypted_bundle(cleardata, password, iterations, delete_keys=True)
|
|
55
|
+
|
|
56
|
+
Parameters
|
|
57
|
+
----------
|
|
58
|
+
cleardata : bytearray
|
|
59
|
+
The clear message to encrypt using AES in CBC mode.
|
|
60
|
+
|
|
61
|
+
password : bytearray
|
|
62
|
+
The user password. It must not be empty.
|
|
63
|
+
|
|
64
|
+
iterations : int
|
|
65
|
+
The number of iterations for PBKDF2. It must be a strictly positive integer.
|
|
66
|
+
|
|
67
|
+
authdata : Optional[bytearray]
|
|
68
|
+
The authentication data to use in the HMAC. Default is None.
|
|
69
|
+
If not None, it will be used to create the HMAC.
|
|
70
|
+
|
|
71
|
+
delete_keys : bool
|
|
72
|
+
Delete the cleardata, the password and authdata from memory at the end of the function. Default is True.
|
|
73
|
+
|
|
74
|
+
Returns
|
|
75
|
+
-------
|
|
76
|
+
encrypted_bundle : bytearray
|
|
77
|
+
The encrypted bundle.
|
|
78
|
+
|
|
79
|
+
Raises
|
|
80
|
+
------
|
|
81
|
+
TypeError
|
|
82
|
+
If an argument is of the wrong type.
|
|
83
|
+
ValueError
|
|
84
|
+
If password is empty or if iterations is not a strictly positive integer.
|
|
85
|
+
"""
|
|
86
|
+
# Check the types of the parameters
|
|
87
|
+
if (not isinstance(cleardata, bytearray)) or (not isinstance(password, bytearray)):
|
|
88
|
+
raise TypeError("Parameters cleardata or password is not bytearray")
|
|
89
|
+
if not isinstance(iterations, int):
|
|
90
|
+
raise TypeError("Parameter iterations is not integer")
|
|
91
|
+
if (authdata is not None) and (not isinstance(authdata, bytearray)):
|
|
92
|
+
raise TypeError("Parameter authdata is not bytearray")
|
|
93
|
+
if not isinstance(delete_keys, bool):
|
|
94
|
+
raise TypeError("Parameter delete_keys is not a boolean.")
|
|
95
|
+
|
|
96
|
+
# Encryption
|
|
97
|
+
try:
|
|
98
|
+
salt = random_salt()
|
|
99
|
+
iv = random_iv()
|
|
100
|
+
derived_key = derive_key(password, salt, iterations)
|
|
101
|
+
aes_key = derived_key[:32] # AES key is the first 32 bytes of the derived key
|
|
102
|
+
hmac_key = derived_key[32:] # HMAC key is the last 32 bytes of the derived key
|
|
103
|
+
cipherdata = encrypt_AES_CBC(cleardata, aes_key, iv)
|
|
104
|
+
expected_hmac = create_hmac(hmac_key, iv, cipherdata, authdata=authdata)
|
|
105
|
+
encrypted_bundle = create_encrypted_bundle(iv, salt, expected_hmac, cipherdata)
|
|
106
|
+
except Exception as e:
|
|
107
|
+
raise e
|
|
108
|
+
finally:
|
|
109
|
+
# Deleting from memory all critical data for security (in the order of their creation to avoid memory leaks)
|
|
110
|
+
if delete_keys:
|
|
111
|
+
delete_bytearray(cleardata)
|
|
112
|
+
delete_bytearray(password)
|
|
113
|
+
if authdata is not None:
|
|
114
|
+
delete_bytearray(authdata)
|
|
115
|
+
delete_bytearray(salt)
|
|
116
|
+
delete_bytearray(iv)
|
|
117
|
+
delete_bytearray(derived_key)
|
|
118
|
+
delete_bytearray(aes_key)
|
|
119
|
+
delete_bytearray(hmac_key)
|
|
120
|
+
delete_bytearray(cipherdata)
|
|
121
|
+
delete_bytearray(expected_hmac)
|
|
122
|
+
|
|
123
|
+
# Return the encrypted bundle
|
|
124
|
+
return encrypted_bundle
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
# Copyright 2025 Artezaru
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
# you may not use this file except in compliance with the License.
|
|
5
|
+
# You may obtain a copy of the License at
|
|
6
|
+
#
|
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
#
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
# See the License for the specific language governing permissions and
|
|
13
|
+
# limitations under the License.
|
|
14
|
+
|
|
15
|
+
def create_encrypted_bundle(iv: bytearray, salt: bytearray, expected_hmac: bytearray, cipherdata: bytearray) -> bytearray:
|
|
16
|
+
"""
|
|
17
|
+
Creates a bytearray containing all the information needed to decrypt the data.
|
|
18
|
+
|
|
19
|
+
The encrypted bundle is composed by the initialization vector (IV), the salt, the expected HMAC and the cipherdata.
|
|
20
|
+
|
|
21
|
+
.. seealso::
|
|
22
|
+
|
|
23
|
+
- function :func:`pyaescbc.extract_cryptography_components` to extract the components from the encrypted bundle.
|
|
24
|
+
|
|
25
|
+
Parameters
|
|
26
|
+
----------
|
|
27
|
+
iv : bytearray
|
|
28
|
+
The 16-byte initialization vector used for encryption.
|
|
29
|
+
|
|
30
|
+
salt : bytearray
|
|
31
|
+
The 32-byte salt used to generate the derived key.
|
|
32
|
+
|
|
33
|
+
expected_hmac : bytearray
|
|
34
|
+
The 32-byte expected HMAC.
|
|
35
|
+
|
|
36
|
+
cipherdata : bytearray
|
|
37
|
+
The encrypted message.
|
|
38
|
+
|
|
39
|
+
Returns
|
|
40
|
+
-------
|
|
41
|
+
encrypted_bundle : bytearray
|
|
42
|
+
The concatenated bytearray containing `iv + salt + expected_hmac + cipherdata`.
|
|
43
|
+
|
|
44
|
+
Raises
|
|
45
|
+
------
|
|
46
|
+
TypeError
|
|
47
|
+
If any argument is not a `bytearray` instance.
|
|
48
|
+
ValueError
|
|
49
|
+
If any of the components (salt, iv, hmac) are not the correct length.
|
|
50
|
+
"""
|
|
51
|
+
# Check the types of the parameters
|
|
52
|
+
if not isinstance(iv, bytearray):
|
|
53
|
+
raise TypeError('Parameter iv is not bytearray instance.')
|
|
54
|
+
if not isinstance(salt, bytearray):
|
|
55
|
+
raise TypeError('Parameter salt is not bytearray instance.')
|
|
56
|
+
if not isinstance(expected_hmac, bytearray):
|
|
57
|
+
raise TypeError('Parameter expected_hmac is not bytearray instance.')
|
|
58
|
+
if not isinstance(cipherdata, bytearray):
|
|
59
|
+
raise TypeError('Parameter cipherdata is not bytearray instance.')
|
|
60
|
+
|
|
61
|
+
# Check the values of the parameters
|
|
62
|
+
if len(iv) != 16:
|
|
63
|
+
raise ValueError(f'{iv=} is not 16 bytes long.')
|
|
64
|
+
if len(salt) != 32:
|
|
65
|
+
raise ValueError(f'{salt=} is not 32 bytes long.')
|
|
66
|
+
if len(expected_hmac) != 32:
|
|
67
|
+
raise ValueError(f'{expected_hmac=} is not 32 bytes long.')
|
|
68
|
+
|
|
69
|
+
# Create the encrypted bundle
|
|
70
|
+
return iv + salt + expected_hmac + cipherdata
|
pyaescbc/create_hmac.py
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
# Copyright 2025 Artezaru
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
# you may not use this file except in compliance with the License.
|
|
5
|
+
# You may obtain a copy of the License at
|
|
6
|
+
#
|
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
#
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
# See the License for the specific language governing permissions and
|
|
13
|
+
# limitations under the License.
|
|
14
|
+
|
|
15
|
+
import hmac
|
|
16
|
+
import hashlib
|
|
17
|
+
from typing import Optional
|
|
18
|
+
|
|
19
|
+
def create_hmac(hmac_key: bytearray, iv: bytearray, cipherdata: bytearray, authdata: Optional[bytearray] = None) -> bytearray:
|
|
20
|
+
"""
|
|
21
|
+
Creates the expected HMAC using the hmac_key on the iv, cipherdata, and optional auth_data.
|
|
22
|
+
The HMAC is created using the SHA-256 hash function. The HMAC is used to verify the integrity of the encrypted message.
|
|
23
|
+
The hmac_key is extracted from the derived key, which is created using PBKDF2HMAC.
|
|
24
|
+
|
|
25
|
+
.. code-block:: console
|
|
26
|
+
|
|
27
|
+
HMAC = HMAC(key, SHA256(iv + cipherdata + authdata))
|
|
28
|
+
|
|
29
|
+
.. seealso::
|
|
30
|
+
|
|
31
|
+
-function :func:`pyaescbc.derive_key` to create the derived key.
|
|
32
|
+
|
|
33
|
+
Parameters
|
|
34
|
+
----------
|
|
35
|
+
hmac_key : bytearray
|
|
36
|
+
The 32-byte long key used to create the HMAC. It is extracted from the derived key.
|
|
37
|
+
|
|
38
|
+
iv : bytearray
|
|
39
|
+
The 16-byte long initialization vector used for encryption.
|
|
40
|
+
|
|
41
|
+
cipherdata : bytearray
|
|
42
|
+
The encrypted message.
|
|
43
|
+
|
|
44
|
+
authdata : Optional[bytearray]
|
|
45
|
+
Optional additional authentication data. If provided, it is prepended to the HMAC input.
|
|
46
|
+
|
|
47
|
+
Returns
|
|
48
|
+
-------
|
|
49
|
+
expected_hmac : bytearray
|
|
50
|
+
The 32-byte expected HMAC value.
|
|
51
|
+
|
|
52
|
+
Raises
|
|
53
|
+
------
|
|
54
|
+
TypeError
|
|
55
|
+
If any argument is not a `bytearray` instance.
|
|
56
|
+
|
|
57
|
+
ValueError
|
|
58
|
+
If the hmac_key isn't 32 bytes, the IV isn't 16 bytes.
|
|
59
|
+
"""
|
|
60
|
+
# Check the types of the parameters
|
|
61
|
+
if not isinstance(hmac_key, bytearray):
|
|
62
|
+
raise TypeError('Parameter hmac_key is not bytearray instance.')
|
|
63
|
+
if not isinstance(iv, bytearray):
|
|
64
|
+
raise TypeError('Parameter iv is not bytearray instance.')
|
|
65
|
+
if not isinstance(cipherdata, bytearray):
|
|
66
|
+
raise TypeError('Parameter cipherdata is not bytearray instance.')
|
|
67
|
+
if authdata is not None and not isinstance(authdata, bytearray):
|
|
68
|
+
raise TypeError('Parameter authdata is not bytearray instance.')
|
|
69
|
+
|
|
70
|
+
# Check the value of the parameters
|
|
71
|
+
if len(hmac_key) != 32:
|
|
72
|
+
raise ValueError(f'{hmac_key=} is not 32 bytes long.')
|
|
73
|
+
if len(iv) != 16:
|
|
74
|
+
raise ValueError(f'{iv=} is not 16 bytes long.')
|
|
75
|
+
|
|
76
|
+
# Create the HMAC
|
|
77
|
+
if authdata is None:
|
|
78
|
+
authdata = bytearray()
|
|
79
|
+
expected_hmac = bytearray(hmac.new(hmac_key, iv + cipherdata + authdata, hashlib.sha256).digest())
|
|
80
|
+
|
|
81
|
+
return expected_hmac
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
# Copyright 2025 Artezaru
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
# you may not use this file except in compliance with the License.
|
|
5
|
+
# You may obtain a copy of the License at
|
|
6
|
+
#
|
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
#
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
# See the License for the specific language governing permissions and
|
|
13
|
+
# limitations under the License.
|
|
14
|
+
|
|
15
|
+
from cryptography.hazmat.primitives import padding, ciphers
|
|
16
|
+
from cryptography.hazmat.backends import default_backend
|
|
17
|
+
|
|
18
|
+
def decrypt_AES_CBC(cipherdata: bytearray, aes_key: bytearray, iv: bytearray) -> bytearray:
|
|
19
|
+
"""
|
|
20
|
+
Decrypts a cipherdata message using AES in CBC mode.
|
|
21
|
+
|
|
22
|
+
The data is unpadded using PKCS7 padding and then decrypted using AES in CBC mode.
|
|
23
|
+
The aes_key is the first 32 bytes of the derived key, and the iv is the initialization vector.
|
|
24
|
+
|
|
25
|
+
.. seealso::
|
|
26
|
+
|
|
27
|
+
function :func:`pyaescbc.derive_key` to create the derived key.
|
|
28
|
+
|
|
29
|
+
.. note::
|
|
30
|
+
|
|
31
|
+
The cipherdata, aes_key and iv must be bytearrays.
|
|
32
|
+
|
|
33
|
+
Parameters
|
|
34
|
+
----------
|
|
35
|
+
cipherdata : bytearray
|
|
36
|
+
The encrypted message to decrypt using AES in CBC mode.
|
|
37
|
+
|
|
38
|
+
aes_key : bytearray
|
|
39
|
+
The 32-byte AES key derived from the password, salt and iterations.
|
|
40
|
+
|
|
41
|
+
iv : bytearray
|
|
42
|
+
The 16-byte initialization vector (IV) to use in AES-CBC mode.
|
|
43
|
+
|
|
44
|
+
Returns
|
|
45
|
+
-------
|
|
46
|
+
cleardata : bytearray
|
|
47
|
+
The decrypted clear message.
|
|
48
|
+
|
|
49
|
+
Raises
|
|
50
|
+
------
|
|
51
|
+
TypeError
|
|
52
|
+
If a given argument is not a `bytearray` instance.
|
|
53
|
+
ValueError
|
|
54
|
+
If the `aes_key` isn't 32 bytes long or the `iv` isn't 16 bytes long.
|
|
55
|
+
"""
|
|
56
|
+
# Check the types of the parameters
|
|
57
|
+
if not isinstance(cipherdata, bytearray):
|
|
58
|
+
raise TypeError('Parameter cipherdata is not bytearray instance.')
|
|
59
|
+
if not isinstance(aes_key, bytearray):
|
|
60
|
+
raise TypeError('Parameter aes_key is not bytearray instance.')
|
|
61
|
+
if not isinstance(iv, bytearray):
|
|
62
|
+
raise TypeError('Parameter iv is not bytearray instance.')
|
|
63
|
+
|
|
64
|
+
# Check the values of the parameters
|
|
65
|
+
if len(aes_key) != 32:
|
|
66
|
+
raise ValueError(f'{aes_key=} is not 64 bytes long.')
|
|
67
|
+
if len(iv) != 16:
|
|
68
|
+
raise ValueError(f'{iv=} is not 16 bytes long.')
|
|
69
|
+
|
|
70
|
+
# Decrypt the data using AES in CBC mode
|
|
71
|
+
cipher = ciphers.Cipher(ciphers.algorithms.AES(aes_key), ciphers.modes.CBC(iv), backend=default_backend())
|
|
72
|
+
decryptor = cipher.decryptor()
|
|
73
|
+
unpadder = padding.PKCS7(128).unpadder()
|
|
74
|
+
decrypted_data = decryptor.update(cipherdata) + decryptor.finalize()
|
|
75
|
+
unpadded_data = bytearray(unpadder.update(decrypted_data) + unpadder.finalize())
|
|
76
|
+
|
|
77
|
+
# Returning the decrypted clear data
|
|
78
|
+
return unpadded_data
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# Copyright 2025 Artezaru
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
# you may not use this file except in compliance with the License.
|
|
5
|
+
# You may obtain a copy of the License at
|
|
6
|
+
#
|
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
#
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
# See the License for the specific language governing permissions and
|
|
13
|
+
# limitations under the License.
|
|
14
|
+
|
|
15
|
+
import os
|
|
16
|
+
|
|
17
|
+
def delete_bytearray(barray: bytearray) -> None:
|
|
18
|
+
r"""
|
|
19
|
+
Securely overwrites the contents of a bytearray and deletes the object from memory.
|
|
20
|
+
|
|
21
|
+
.. code-block:: python
|
|
22
|
+
|
|
23
|
+
import pyaescbc as aes
|
|
24
|
+
import os
|
|
25
|
+
|
|
26
|
+
# Create a bytearray
|
|
27
|
+
barray = bytearray(os.urandom(32)) # Example: 32 random bytes
|
|
28
|
+
|
|
29
|
+
# Securely delete the bytearray
|
|
30
|
+
aes.delete_bytearray(barray)
|
|
31
|
+
|
|
32
|
+
Parameters
|
|
33
|
+
----------
|
|
34
|
+
barray : bytearray
|
|
35
|
+
The bytearray to securely delete from memory.
|
|
36
|
+
|
|
37
|
+
Raises
|
|
38
|
+
------
|
|
39
|
+
TypeError
|
|
40
|
+
If the given argument is not a `bytearray` instance.
|
|
41
|
+
"""
|
|
42
|
+
# Check if the input is a bytearray
|
|
43
|
+
if not isinstance(barray, bytearray):
|
|
44
|
+
raise TypeError('Parameter barray is not bytearray instance.')
|
|
45
|
+
|
|
46
|
+
# Delete the bytearray by overwriting its contents with random data
|
|
47
|
+
for index in range(len(barray)):
|
|
48
|
+
barray[index] = os.urandom(1)[0] # Overwrite with random data
|
|
49
|
+
barray.clear() # Clear contents
|