fastcrypter 2.3.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.
@@ -0,0 +1,119 @@
1
+ """
2
+ Algorithm implementations for the Encrypter package.
3
+
4
+ This package contains various compression and encryption algorithms
5
+ that can be used with the core components.
6
+ """
7
+
8
+ from abc import ABC, abstractmethod
9
+ from typing import Any, Dict, Optional
10
+
11
+
12
+ class CompressionAlgorithm(ABC):
13
+ """
14
+ Abstract base class for compression algorithms.
15
+
16
+ All compression algorithms must inherit from this class
17
+ and implement the required methods.
18
+ """
19
+
20
+ @abstractmethod
21
+ def compress(self, data: bytes) -> bytes:
22
+ """
23
+ Compress the given data.
24
+
25
+ Args:
26
+ data (bytes): Data to compress.
27
+
28
+ Returns:
29
+ bytes: Compressed data.
30
+ """
31
+ pass
32
+
33
+ @abstractmethod
34
+ def decompress(self, data: bytes) -> bytes:
35
+ """
36
+ Decompress the given data.
37
+
38
+ Args:
39
+ data (bytes): Compressed data to decompress.
40
+
41
+ Returns:
42
+ bytes: Decompressed data.
43
+ """
44
+ pass
45
+
46
+ @property
47
+ @abstractmethod
48
+ def name(self) -> str:
49
+ """Get the algorithm name."""
50
+ pass
51
+
52
+ @property
53
+ @abstractmethod
54
+ def settings(self) -> Dict[str, Any]:
55
+ """Get the algorithm settings."""
56
+ pass
57
+
58
+
59
+ class EncryptionAlgorithm(ABC):
60
+ """
61
+ Abstract base class for encryption algorithms.
62
+
63
+ All encryption algorithms must inherit from this class
64
+ and implement the required methods.
65
+ """
66
+
67
+ @abstractmethod
68
+ def encrypt(self, data: bytes, key: bytes, **kwargs) -> bytes:
69
+ """
70
+ Encrypt the given data.
71
+
72
+ Args:
73
+ data (bytes): Data to encrypt.
74
+ key (bytes): Encryption key.
75
+ **kwargs: Additional algorithm-specific parameters.
76
+
77
+ Returns:
78
+ bytes: Encrypted data.
79
+ """
80
+ pass
81
+
82
+ @abstractmethod
83
+ def decrypt(self, data: bytes, key: bytes, **kwargs) -> bytes:
84
+ """
85
+ Decrypt the given data.
86
+
87
+ Args:
88
+ data (bytes): Encrypted data to decrypt.
89
+ key (bytes): Decryption key.
90
+ **kwargs: Additional algorithm-specific parameters.
91
+
92
+ Returns:
93
+ bytes: Decrypted data.
94
+ """
95
+ pass
96
+
97
+ @property
98
+ @abstractmethod
99
+ def name(self) -> str:
100
+ """Get the algorithm name."""
101
+ pass
102
+
103
+ @property
104
+ @abstractmethod
105
+ def key_size(self) -> int:
106
+ """Get the required key size in bytes."""
107
+ pass
108
+
109
+ @property
110
+ @abstractmethod
111
+ def settings(self) -> Dict[str, Any]:
112
+ """Get the algorithm settings."""
113
+ pass
114
+
115
+
116
+ __all__ = [
117
+ "CompressionAlgorithm",
118
+ "EncryptionAlgorithm",
119
+ ]
@@ -0,0 +1,17 @@
1
+ """
2
+ Compression algorithm implementations.
3
+
4
+ This module contains various compression algorithms including
5
+ ZLIB, LZMA, and Brotli implementations.
6
+ """
7
+
8
+ # Import will be added when algorithm files are created
9
+ # from .zlib_compressor import ZlibCompressor
10
+ # from .lzma_compressor import LzmaCompressor
11
+ # from .brotli_compressor import BrotliCompressor
12
+
13
+ __all__ = [
14
+ # "ZlibCompressor",
15
+ # "LzmaCompressor",
16
+ # "BrotliCompressor",
17
+ ]
@@ -0,0 +1,17 @@
1
+ """
2
+ Encryption algorithm implementations.
3
+
4
+ This module contains various encryption algorithms including
5
+ AES, ChaCha20, and RSA implementations.
6
+ """
7
+
8
+ # Import will be added when algorithm files are created
9
+ # from .aes_encryptor import AESEncryptor
10
+ # from .chacha20_encryptor import ChaCha20Encryptor
11
+ # from .rsa_encryptor import RSAEncryptor
12
+
13
+ __all__ = [
14
+ # "AESEncryptor",
15
+ # "ChaCha20Encryptor",
16
+ # "RSAEncryptor",
17
+ ]
@@ -0,0 +1,16 @@
1
+ """
2
+ Core modules for the Encrypter package.
3
+
4
+ This package contains the fundamental components for compression,
5
+ encryption, and key management.
6
+ """
7
+
8
+ from .compressor import Compressor
9
+ from .encryptor import Encryptor
10
+ from .key_manager import KeyManager
11
+
12
+ __all__ = [
13
+ "Compressor",
14
+ "Encryptor",
15
+ "KeyManager",
16
+ ]
@@ -0,0 +1,409 @@
1
+ """
2
+ Data Compression System for the Encrypter package.
3
+
4
+ This module provides high-performance data compression functionality
5
+ with support for multiple compression algorithms and optimization levels.
6
+ """
7
+
8
+ import zlib
9
+ import lzma
10
+ import brotli
11
+ import io
12
+ from typing import Union, Dict, Any, Optional, Tuple
13
+ from enum import Enum
14
+
15
+ from ..exceptions import CompressionError, ValidationError, ErrorCodes
16
+ from ..algorithms import CompressionAlgorithm
17
+
18
+
19
+ class CompressionLevel(Enum):
20
+ """Compression level enumeration."""
21
+ FASTEST = 1
22
+ FAST = 3
23
+ BALANCED = 6
24
+ BEST = 9
25
+
26
+
27
+ class CompressionAlgorithmType(Enum):
28
+ """Supported compression algorithms."""
29
+ ZLIB = "zlib"
30
+ LZMA = "lzma"
31
+ BROTLI = "brotli"
32
+
33
+
34
+ class Compressor:
35
+ """
36
+ High-performance data compression system.
37
+
38
+ Supports multiple compression algorithms with configurable compression
39
+ levels and automatic algorithm selection based on data characteristics.
40
+ """
41
+
42
+ # Algorithm-specific settings
43
+ ALGORITHM_SETTINGS = {
44
+ CompressionAlgorithmType.ZLIB: {
45
+ 'min_level': 1,
46
+ 'max_level': 9,
47
+ 'default_level': 6,
48
+ 'chunk_size': 64 * 1024, # 64KB
49
+ },
50
+ CompressionAlgorithmType.LZMA: {
51
+ 'min_level': 0,
52
+ 'max_level': 9,
53
+ 'default_level': 6,
54
+ 'chunk_size': 128 * 1024, # 128KB
55
+ },
56
+ CompressionAlgorithmType.BROTLI: {
57
+ 'min_level': 0,
58
+ 'max_level': 11,
59
+ 'default_level': 6,
60
+ 'chunk_size': 64 * 1024, # 64KB
61
+ },
62
+ }
63
+
64
+ def __init__(self,
65
+ algorithm: Union[CompressionAlgorithmType, str] = CompressionAlgorithmType.ZLIB,
66
+ level: Union[CompressionLevel, int] = CompressionLevel.BALANCED,
67
+ auto_select: bool = False):
68
+ """
69
+ Initialize the Compressor.
70
+
71
+ Args:
72
+ algorithm: Compression algorithm to use.
73
+ level: Compression level (1-9 for most algorithms).
74
+ auto_select: Whether to automatically select best algorithm.
75
+
76
+ Raises:
77
+ ValidationError: If parameters are invalid.
78
+ """
79
+ # Handle string algorithm input
80
+ if isinstance(algorithm, str):
81
+ try:
82
+ algorithm = CompressionAlgorithmType(algorithm.lower())
83
+ except ValueError:
84
+ raise ValidationError(
85
+ f"Unsupported compression algorithm: {algorithm}",
86
+ ErrorCodes.UNSUPPORTED_COMPRESSION
87
+ )
88
+
89
+ # Handle integer level input
90
+ if isinstance(level, int):
91
+ level = min(max(level, 1), 9) # Clamp to valid range
92
+ elif isinstance(level, CompressionLevel):
93
+ level = level.value
94
+
95
+ self.algorithm = algorithm
96
+ self.level = level
97
+ self.auto_select = auto_select
98
+
99
+ # Validate level for selected algorithm
100
+ settings = self.ALGORITHM_SETTINGS[algorithm]
101
+ if not (settings['min_level'] <= level <= settings['max_level']):
102
+ raise ValidationError(
103
+ f"Invalid compression level {level} for {algorithm.value}. "
104
+ f"Must be between {settings['min_level']} and {settings['max_level']}",
105
+ ErrorCodes.INVALID_CONFIGURATION
106
+ )
107
+
108
+ self.chunk_size = settings['chunk_size']
109
+
110
+ def compress(self, data: Union[str, bytes]) -> bytes:
111
+ """
112
+ Compress data using the configured algorithm.
113
+
114
+ Args:
115
+ data: Data to compress (string or bytes).
116
+
117
+ Returns:
118
+ bytes: Compressed data with metadata header.
119
+
120
+ Raises:
121
+ CompressionError: If compression fails.
122
+ ValidationError: If input data is invalid.
123
+ """
124
+ # Convert string to bytes if necessary
125
+ if isinstance(data, str):
126
+ data = data.encode('utf-8')
127
+
128
+ if not isinstance(data, bytes):
129
+ raise ValidationError(
130
+ "Input data must be string or bytes",
131
+ ErrorCodes.INVALID_INPUT_FORMAT
132
+ )
133
+
134
+ if len(data) == 0:
135
+ raise ValidationError(
136
+ "Cannot compress empty data",
137
+ ErrorCodes.INVALID_INPUT_SIZE
138
+ )
139
+
140
+ # Auto-select algorithm if enabled
141
+ if self.auto_select:
142
+ algorithm = self._select_best_algorithm(data)
143
+ else:
144
+ algorithm = self.algorithm
145
+
146
+ try:
147
+ # Compress data
148
+ if algorithm == CompressionAlgorithmType.ZLIB:
149
+ compressed = self._compress_zlib(data)
150
+ elif algorithm == CompressionAlgorithmType.LZMA:
151
+ compressed = self._compress_lzma(data)
152
+ elif algorithm == CompressionAlgorithmType.BROTLI:
153
+ compressed = self._compress_brotli(data)
154
+ else:
155
+ raise CompressionError(
156
+ f"Unsupported algorithm: {algorithm}",
157
+ ErrorCodes.UNSUPPORTED_COMPRESSION
158
+ )
159
+
160
+ # Add metadata header
161
+ header = self._create_header(algorithm, len(data))
162
+ result = header + compressed
163
+
164
+ # Validate compression ratio
165
+ ratio = len(result) / len(data)
166
+ if ratio > 1.1: # If compressed size is more than 110% of original
167
+ # Return original data with special header indicating no compression
168
+ no_comp_header = self._create_header(None, len(data))
169
+ result = no_comp_header + data
170
+
171
+ return result
172
+
173
+ except Exception as e:
174
+ raise CompressionError(
175
+ f"Compression failed: {str(e)}",
176
+ ErrorCodes.COMPRESSION_FAILED,
177
+ details={"algorithm": algorithm.value if algorithm else None, "error": str(e)}
178
+ )
179
+
180
+ def decompress(self, data: bytes) -> bytes:
181
+ """
182
+ Decompress data.
183
+
184
+ Args:
185
+ data: Compressed data with metadata header.
186
+
187
+ Returns:
188
+ bytes: Decompressed data.
189
+
190
+ Raises:
191
+ CompressionError: If decompression fails.
192
+ ValidationError: If input data is invalid.
193
+ """
194
+ if not isinstance(data, bytes):
195
+ raise ValidationError(
196
+ "Input data must be bytes",
197
+ ErrorCodes.INVALID_INPUT_FORMAT
198
+ )
199
+
200
+ if len(data) < 8: # Minimum header size
201
+ raise ValidationError(
202
+ "Data too small to contain valid header",
203
+ ErrorCodes.INVALID_INPUT_SIZE
204
+ )
205
+
206
+ try:
207
+ # Parse header
208
+ algorithm, original_size = self._parse_header(data)
209
+ compressed_data = data[8:] # Skip 8-byte header
210
+
211
+ # Handle uncompressed data
212
+ if algorithm is None:
213
+ return compressed_data
214
+
215
+ # Decompress data
216
+ if algorithm == CompressionAlgorithmType.ZLIB:
217
+ result = self._decompress_zlib(compressed_data)
218
+ elif algorithm == CompressionAlgorithmType.LZMA:
219
+ result = self._decompress_lzma(compressed_data)
220
+ elif algorithm == CompressionAlgorithmType.BROTLI:
221
+ result = self._decompress_brotli(compressed_data)
222
+ else:
223
+ raise CompressionError(
224
+ f"Unsupported algorithm in header: {algorithm}",
225
+ ErrorCodes.UNSUPPORTED_COMPRESSION
226
+ )
227
+
228
+ # Validate decompressed size
229
+ if len(result) != original_size:
230
+ raise CompressionError(
231
+ f"Decompressed size mismatch: expected {original_size}, got {len(result)}",
232
+ ErrorCodes.DECOMPRESSION_FAILED
233
+ )
234
+
235
+ return result
236
+
237
+ except CompressionError:
238
+ raise
239
+ except Exception as e:
240
+ raise CompressionError(
241
+ f"Decompression failed: {str(e)}",
242
+ ErrorCodes.DECOMPRESSION_FAILED,
243
+ details={"error": str(e)}
244
+ )
245
+
246
+ def _compress_zlib(self, data: bytes) -> bytes:
247
+ """Compress data using zlib."""
248
+ return zlib.compress(data, level=self.level)
249
+
250
+ def _decompress_zlib(self, data: bytes) -> bytes:
251
+ """Decompress zlib data."""
252
+ return zlib.decompress(data)
253
+
254
+ def _compress_lzma(self, data: bytes) -> bytes:
255
+ """Compress data using LZMA."""
256
+ return lzma.compress(
257
+ data,
258
+ format=lzma.FORMAT_ALONE,
259
+ preset=self.level
260
+ )
261
+
262
+ def _decompress_lzma(self, data: bytes) -> bytes:
263
+ """Decompress LZMA data."""
264
+ return lzma.decompress(data, format=lzma.FORMAT_ALONE)
265
+
266
+ def _compress_brotli(self, data: bytes) -> bytes:
267
+ """Compress data using Brotli."""
268
+ return brotli.compress(data, quality=self.level)
269
+
270
+ def _decompress_brotli(self, data: bytes) -> bytes:
271
+ """Decompress Brotli data."""
272
+ return brotli.decompress(data)
273
+
274
+ def _select_best_algorithm(self, data: bytes) -> CompressionAlgorithmType:
275
+ """
276
+ Automatically select the best compression algorithm for the data.
277
+
278
+ Args:
279
+ data: Data to analyze.
280
+
281
+ Returns:
282
+ CompressionAlgorithmType: Best algorithm for the data.
283
+ """
284
+ # For small data, use zlib (fastest)
285
+ if len(data) < 1024:
286
+ return CompressionAlgorithmType.ZLIB
287
+
288
+ # Analyze data characteristics
289
+ entropy = self._calculate_entropy(data)
290
+
291
+ # High entropy data (already compressed/encrypted) - use fastest
292
+ if entropy > 7.5:
293
+ return CompressionAlgorithmType.ZLIB
294
+
295
+ # Low entropy data - use best compression
296
+ if entropy < 4.0:
297
+ return CompressionAlgorithmType.LZMA
298
+
299
+ # Medium entropy - use balanced approach
300
+ return CompressionAlgorithmType.BROTLI
301
+
302
+ def _calculate_entropy(self, data: bytes) -> float:
303
+ """Calculate Shannon entropy of data."""
304
+ if not data:
305
+ return 0.0
306
+
307
+ # Count byte frequencies
308
+ frequencies = {}
309
+ for byte in data:
310
+ frequencies[byte] = frequencies.get(byte, 0) + 1
311
+
312
+ # Calculate entropy
313
+ entropy = 0.0
314
+ length = len(data)
315
+ for count in frequencies.values():
316
+ probability = count / length
317
+ if probability > 0:
318
+ import math
319
+ entropy -= probability * math.log2(probability)
320
+
321
+ return entropy
322
+
323
+ def _create_header(self, algorithm: Optional[CompressionAlgorithmType], original_size: int) -> bytes:
324
+ """
325
+ Create metadata header for compressed data.
326
+
327
+ Header format (8 bytes):
328
+ - 1 byte: Algorithm ID (0=none, 1=zlib, 2=lzma, 3=brotli)
329
+ - 1 byte: Compression level
330
+ - 2 bytes: Reserved
331
+ - 4 bytes: Original size (little-endian)
332
+ """
333
+ if algorithm is None:
334
+ algo_id = 0
335
+ else:
336
+ algo_map = {
337
+ CompressionAlgorithmType.ZLIB: 1,
338
+ CompressionAlgorithmType.LZMA: 2,
339
+ CompressionAlgorithmType.BROTLI: 3,
340
+ }
341
+ algo_id = algo_map[algorithm]
342
+
343
+ header = bytearray(8)
344
+ header[0] = algo_id
345
+ header[1] = self.level
346
+ header[2:4] = b'\x00\x00' # Reserved
347
+ header[4:8] = original_size.to_bytes(4, 'little')
348
+
349
+ return bytes(header)
350
+
351
+ def _parse_header(self, data: bytes) -> Tuple[Optional[CompressionAlgorithmType], int]:
352
+ """
353
+ Parse metadata header from compressed data.
354
+
355
+ Returns:
356
+ Tuple[Optional[CompressionAlgorithmType], int]: (algorithm, original_size)
357
+ """
358
+ header = data[:8]
359
+
360
+ algo_id = header[0]
361
+ if algo_id == 0:
362
+ algorithm = None
363
+ else:
364
+ algo_map = {
365
+ 1: CompressionAlgorithmType.ZLIB,
366
+ 2: CompressionAlgorithmType.LZMA,
367
+ 3: CompressionAlgorithmType.BROTLI,
368
+ }
369
+ algorithm = algo_map.get(algo_id)
370
+ if algorithm is None:
371
+ raise CompressionError(
372
+ f"Unknown algorithm ID in header: {algo_id}",
373
+ ErrorCodes.INVALID_INPUT_FORMAT
374
+ )
375
+
376
+ original_size = int.from_bytes(header[4:8], 'little')
377
+
378
+ return algorithm, original_size
379
+
380
+ def get_compression_ratio(self, original_data: bytes, compressed_data: bytes) -> float:
381
+ """
382
+ Calculate compression ratio.
383
+
384
+ Args:
385
+ original_data: Original uncompressed data.
386
+ compressed_data: Compressed data.
387
+
388
+ Returns:
389
+ float: Compression ratio (compressed_size / original_size).
390
+ """
391
+ if len(original_data) == 0:
392
+ return 0.0
393
+
394
+ return len(compressed_data) / len(original_data)
395
+
396
+ def get_info(self) -> Dict[str, Any]:
397
+ """
398
+ Get information about the compressor configuration.
399
+
400
+ Returns:
401
+ Dict[str, Any]: Configuration information.
402
+ """
403
+ return {
404
+ "algorithm": self.algorithm.value,
405
+ "level": self.level,
406
+ "auto_select": self.auto_select,
407
+ "chunk_size": self.chunk_size,
408
+ "supported_algorithms": [algo.value for algo in CompressionAlgorithmType],
409
+ }