securefilehandler-abs 0.1.4__tar.gz

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.
@@ -0,0 +1,7 @@
1
+ Metadata-Version: 2.4
2
+ Name: securefilehandler-abs
3
+ Version: 0.1.4
4
+ Summary: Librería de manejo seguro de datos (criptografia) sobre archivos
5
+ Author: Specter
6
+ Requires-Python: >=3.10
7
+ Description-Content-Type: text/markdown
File without changes
@@ -0,0 +1,15 @@
1
+ [build-system]
2
+ requires = ["setuptools>=61.0"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "securefilehandler-abs"
7
+ version = "0.1.4"
8
+ description = "Librería de manejo seguro de datos (criptografia) sobre archivos"
9
+ readme = "README.md"
10
+ authors = [{ name="Specter" }]
11
+ requires-python = ">=3.10"
12
+
13
+ [tool.setuptools.packages.find]
14
+ include = ["securefilehandler*"]
15
+ exclude = ["docs*"]
@@ -0,0 +1,110 @@
1
+ # Library import
2
+ from abc import ABC, abstractmethod
3
+ from typing import Any
4
+ import os
5
+ from cryptography.hazmat.primitives.ciphers.aead import AESGCM
6
+ from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC
7
+ from cryptography.hazmat.primitives import hashes
8
+ from cryptography.hazmat.backends import default_backend
9
+ import handlers
10
+ from handlers import FileHandlerInterface
11
+ from handlers import *
12
+
13
+ # Classes definition
14
+ class SecureFileHandler(FileHandlerInterface):
15
+ # Class properties definition
16
+ MAGIC: bytes = b"SFILEv1"
17
+ SALT_SIZE: int = 16
18
+ NONCE_SIZE: int = 12
19
+ ITERATIONS: int = 200_000
20
+
21
+ def __init__(self,
22
+ file_handler: FileHandlerInterface,
23
+ password: str
24
+ ) -> None:
25
+ # Instance properties assignment
26
+ self._file_handler = file_handler
27
+ self._password = password.encode("UTF-8")
28
+
29
+ @property
30
+ def is_opened(self) -> bool:
31
+ return self._file_handler.is_opened
32
+
33
+ # Private methods
34
+ def _derive_key(self, salt: bytes) -> bytes:
35
+ KDF = PBKDF2HMAC(
36
+ algorithm=hashes.SHA256(),
37
+ length=32, # AES-256
38
+ salt=salt,
39
+ iterations=self.ITERATIONS,
40
+ backend=default_backend()
41
+ )
42
+
43
+ return KDF.derive(self._password)
44
+
45
+ # Public methods
46
+ def open(self):
47
+ self._file_handler.open()
48
+
49
+ def close(self):
50
+ self._file_handler.close()
51
+
52
+ def write(self, data: bytes):
53
+ if not self.is_opened:
54
+ raise RuntimeError("File not opened")
55
+
56
+ encrypted = self.encrypt(data)
57
+ self._file_handler.write(encrypted)
58
+
59
+ def append_write(self, data: bytes):
60
+ if not self.is_opened:
61
+ raise RuntimeError("File not opened")
62
+
63
+ try:
64
+ decrypted = self.read()
65
+ except:
66
+ decrypted = b""
67
+
68
+ new_encrypted_data = self.encrypt(decrypted + data)
69
+ self._file_handler.write(new_encrypted_data)
70
+
71
+ def read(self) -> bytes:
72
+ if not self.is_opened:
73
+ raise RuntimeError("File not opened")
74
+
75
+ encrypted = self._file_handler.read()
76
+ return self.decrypt(encrypted)
77
+
78
+ def encrypt(self, data: bytes) -> bytes:
79
+ salt = os.urandom(self.SALT_SIZE)
80
+ key = self._derive_key(salt)
81
+
82
+ aesgcm = AESGCM(key)
83
+ nonce = os.urandom(self.NONCE_SIZE)
84
+
85
+ ciphertext = aesgcm.encrypt(nonce, data, self.MAGIC)
86
+
87
+ return self.MAGIC + salt + nonce + ciphertext
88
+
89
+ def decrypt(self, data: bytes) -> bytes:
90
+ if not data.startswith(self.MAGIC):
91
+ raise ValueError("Invalid or corrupted file format")
92
+
93
+ min_size = len(self.MAGIC) + self.SALT_SIZE + self.NONCE_SIZE + 16 # tag mínimo
94
+ if len(data) < min_size:
95
+ raise ValueError("File too small or corrupted")
96
+
97
+ offset = len(self.MAGIC)
98
+
99
+ salt = data[offset:offset + self.SALT_SIZE]
100
+ offset += self.SALT_SIZE
101
+
102
+ nonce = data[offset:offset + self.NONCE_SIZE]
103
+ offset += self.NONCE_SIZE
104
+
105
+ ciphertext = data[offset:]
106
+
107
+ key = self._derive_key(salt)
108
+ aesgcm = AESGCM(key)
109
+
110
+ return aesgcm.decrypt(nonce, ciphertext, self.MAGIC)
@@ -0,0 +1,25 @@
1
+ # Library import
2
+ from abc import ABC, abstractmethod
3
+
4
+ # Classes definition
5
+ class FileHandlerInterface(ABC):
6
+ # Properties
7
+ @property
8
+ @abstractmethod
9
+ def is_opened(self) -> bool: raise NotImplementedError
10
+
11
+ # Public methods
12
+ @abstractmethod
13
+ def open(self) -> None: raise NotImplementedError
14
+
15
+ @abstractmethod
16
+ def close(self) -> None: raise NotImplementedError
17
+
18
+ @abstractmethod
19
+ def write(self, data: bytes) -> None: raise NotImplementedError
20
+
21
+ @abstractmethod
22
+ def read(self) -> bytes: raise NotImplementedError
23
+
24
+ from localfilehandler import *
25
+ from virtualfilehandler import *
@@ -0,0 +1,39 @@
1
+ # Library import
2
+ from typing import Optional, BinaryIO
3
+ from . import FileHandlerInterface
4
+
5
+ # Classes definition
6
+ class LocalFileHandler(FileHandlerInterface):
7
+ def __init__(self,
8
+ filepath: str,
9
+ mode: str
10
+ ) -> None:
11
+ # Instance properties assignment
12
+ self._filepath = filepath
13
+ self._mode = mode
14
+ self._file_handler: Optional[BinaryIO] = None
15
+
16
+ # Properties
17
+ @property
18
+ def is_opened(self) -> bool:
19
+ return self._file_handler is not None and not self._file_handler.closed
20
+
21
+ # Public methods
22
+ def open(self):
23
+ self._file_handler = open(self._filepath, self._mode)
24
+
25
+ def close(self):
26
+ if self._file_handler and not self._file_handler.closed:
27
+ self._file_handler.close()
28
+
29
+ def write(self, data: bytes):
30
+ self._file_handler.seek(0)
31
+ self._file_handler.truncate(0)
32
+
33
+ self._file_handler.write(data)
34
+ self._file_handler.flush()
35
+
36
+ def read(self):
37
+ self._file_handler.seek(0)
38
+ data = self._file_handler.read()
39
+ return data
@@ -0,0 +1,39 @@
1
+ # Library import
2
+ from typing import Optional
3
+ from io import BytesIO
4
+ from . import FileHandlerInterface
5
+
6
+ class VirtualFileHandler(FileHandlerInterface):
7
+ def __init__(self) -> None:
8
+ self._buffer: Optional[BytesIO] = None
9
+
10
+ # Properties
11
+ @property
12
+ def is_opened(self) -> bool:
13
+ return self._buffer is not None
14
+
15
+ # Public methods
16
+ def open(self):
17
+ if not self.is_opened:
18
+ self._buffer = BytesIO()
19
+
20
+ def close(self):
21
+ if self._buffer is not None:
22
+ self._buffer.close()
23
+ self._buffer = None
24
+
25
+ def write(self, data: bytes):
26
+ if not self.is_opened:
27
+ raise RuntimeError("File not opened")
28
+
29
+ # Sobrescritura completa
30
+ self._buffer.seek(0)
31
+ self._buffer.truncate(0)
32
+ self._buffer.write(data)
33
+
34
+ def read(self) -> bytes:
35
+ if not self.is_opened:
36
+ raise RuntimeError("File not opened")
37
+
38
+ self._buffer.seek(0)
39
+ return self._buffer.read()
@@ -0,0 +1,7 @@
1
+ Metadata-Version: 2.4
2
+ Name: securefilehandler-abs
3
+ Version: 0.1.4
4
+ Summary: Librería de manejo seguro de datos (criptografia) sobre archivos
5
+ Author: Specter
6
+ Requires-Python: >=3.10
7
+ Description-Content-Type: text/markdown
@@ -0,0 +1,10 @@
1
+ README.md
2
+ pyproject.toml
3
+ securefilehandler/__init__.py
4
+ securefilehandler/handlers/__init__.py
5
+ securefilehandler/handlers/localfilehandler.py
6
+ securefilehandler/handlers/virtualfilehandler.py
7
+ securefilehandler_abs.egg-info/PKG-INFO
8
+ securefilehandler_abs.egg-info/SOURCES.txt
9
+ securefilehandler_abs.egg-info/dependency_links.txt
10
+ securefilehandler_abs.egg-info/top_level.txt
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+